[cairo-commit] 118 commits - boilerplate/cairo-boilerplate.c boilerplate/cairo-boilerplate-directfb.c boilerplate/cairo-boilerplate-drm.c boilerplate/cairo-boilerplate-gl.c boilerplate/cairo-boilerplate-glitz-agl.c boilerplate/cairo-boilerplate-glitz-glx.c boilerplate/cairo-boilerplate-glitz-wgl.c boilerplate/cairo-boilerplate.h boilerplate/cairo-boilerplate-pdf.c boilerplate/cairo-boilerplate-ps.c boilerplate/cairo-boilerplate-quartz.c boilerplate/cairo-boilerplate-script.c boilerplate/cairo-boilerplate-skia.c boilerplate/cairo-boilerplate-svg.c boilerplate/cairo-boilerplate-test-surfaces.c boilerplate/cairo-boilerplate-vg.c boilerplate/cairo-boilerplate-win32.c boilerplate/cairo-boilerplate-win32-printing.c boilerplate/cairo-boilerplate-xcb.c boilerplate/cairo-boilerplate-xlib.c boilerplate/Makefile.am boilerplate/Makefile.sources boilerplate/Makefile.win32.features build/configure.ac.features build/configure.ac.warnings build/Makefile.win32.features build/Makefile.win32.features-h configure.ac NEWS perf/box-outline.c perf/cairo-perf.c perf/cairo-perf-chart.c perf/cairo-perf-diff perf/cairo-perf.h perf/cairo-perf-trace.c perf/composite-checker.c perf/dragon.c perf/fill.c perf/glyphs.c perf/intersections.c perf/long-dashed-lines.c perf/long-lines.c perf/Makefile.am perf/mask.c perf/mosaic.c perf/paint.c perf/paint-with-alpha.c perf/pattern_create_radial.c perf/pythagoras-tree.c perf/rectangles.c perf/rounded-rectangles.c perf/spiral.c perf/stroke.c perf/subimage_copy.c perf/tessellate.c perf/text.c perf/twin.c perf/unaligned-clip.c perf/world-map.c perf/zrusin.c src/cairo-arc.c src/cairo-base64-stream.c src/cairo-base85-stream.c src/cairo-bentley-ottmann.c src/cairo-bentley-ottmann-rectangular.c src/cairo-bentley-ottmann-rectilinear.c src/cairo-clip.c src/cairo-clip-private.h src/cairo-combsort-private.h src/cairo-debug.c src/cairo-fixed-private.h src/cairo-fixed-type-private.h src/cairo-freelist.c src/cairo-freelist-private.h src/cairo-ft-font.c src/cairo-gl-surface.c src/cairo-gstate.c src/cairo.h src/cairo-hash.c src/cairo-hull.c src/cairo-image-surface.c src/cairoint.h src/cairo-matrix.c src/cairo-meta-surface.c src/cairo-output-stream-private.h src/cairo-path-bounds.c src/cairo-path-fill.c src/cairo-path-fixed.c src/cairo-path-fixed-private.h src/cairo-path-stroke.c src/cairo-pattern.c src/cairo-pdf-surface.c src/cairo-pen.c src/cairo-polygon.c src/cairo-ps-surface.c src/cairo-qt-surface.cpp src/cairo-rectangle.c src/cairo-region.c src/cairo-region-private.h src/cairo-scaled-font.c src/cairo-scaled-font-private.h src/cairo-script.h src/cairo-script-surface.c src/cairo-skia.h src/cairo-skia-surface.cpp src/cairo-skiplist.c src/cairo-skiplist-private.h src/cairo-slope.c src/cairo-slope-private.h src/cairo-spans.c src/cairo-spans-private.h src/cairo-spline.c src/cairo-surface.c src/cairo-surface-fallback.c src/cairo-surface-wrapper.c src/cairo-surface-wrapper-private.h src/cairo-svg-surface.c src/cairo-tee-surface.c src/cairo-tee-surface-private.h src/cairo-tor-scan-converter.c src/cairo-toy-font-face.c src/cairo-traps.c src/cairo-types-private.h src/cairo-wideint.c src/cairo-wideint-private.h src/cairo-win32-font.c src/cairo-win32-printing-surface.c src/cairo-win32-surface.c src/cairo-xlib-display.c src/cairo-xlib-surface.c src/cairo-xlib-xrender-private.h src/cairo-xml.h src/cairo-xml-surface.c src/Makefile.am src/Makefile.sources src/Makefile.win32.features test/a1-image-sample.c test/cairo-test-runner.c test/cairo-test-trace.c test/caps-joins-alpha.ref.png test/caps-joins-alpha.xlib.ref.png test/caps-joins-curve.ref.png test/caps-joins-curve.xlib.ref.png test/clear.pdf.argb32.ref.png test/clear.ps.argb32.ref.png test/clip-fill.xlib-fallback.ref.png test/clip-operator.pdf.argb32.ref.png test/clip-operator.pdf.rgb24.ref.png test/clip-operator.ref.png test/clip-operator.rgb24.ref.png test/clip-operator.svg12.argb32.xfail.png test/clip-operator.svg12.rgb24.xfail.png test/clip-operator.xlib-fallback.ref.png test/clipped-group.pdf.ref.png test/clipped-group.ref.png test/clipped-group.svg.ref.png test/clipped-trapezoids.c test/clipped-trapezoids-ref.png test/clipped-trapezoids.ref.png test/clip-push-group.svg.ref.png test/clip-stroke.c test/clip-stroke.ref.png test/clip-stroke.xlib-fallback.ref.png test/clip-stroke.xlib.ref.png test/clip-text.svg.ref.png test/close-path-current-point.ref.png test/dash-caps-joins.ref.png test/dash-curve.ref.png test/dash-curve.xlib.ref.png test/dash-scale.ref.png test/degenerate-arc.ref.png test/degenerate-dash.ref.png test/degenerate-dash.xlib.ref.png test/degenerate-path.argb32.ref.png test/degenerate-path.ref.png test/degenerate-path.rgb24.ref.png test/degenerate-pen.ref.png test/degenerate-pen.xlib.ref.png test/device-offset-fractional.pdf.xfail.png test/extended-blend-alpha.svg12.argb32.xfail.png test/fill-and-stroke-alpha-add.ref.png test/fill-and-stroke-alpha-add.svg12.xfail.png test/fill-and-stroke-alpha.ref.png test/fill-and-stroke.argb32.ref.png test/fill-and-stroke.ref.png test/fill-and-stroke.rgb24.ref.png test/fill-and-stroke.xlib.argb32.ref.png test/fill-and-stroke.xlib.rgb24.ref.png test/filter-nearest-offset.pdf.xfail.png test/filter-nearest-transformed.pdf.xfail.png test/ft-text-vertical-layout-type1.c test/ft-text-vertical-layout-type1.ps2.ref.png test/ft-text-vertical-layout-type1.ps3.ref.png test/ft-text-vertical-layout-type1.ps.ref.png test/ft-text-vertical-layout-type1.ref.png test/ft-text-vertical-layout-type1.xlib.ref.png test/ft-text-vertical-layout-type3.c test/ft-text-vertical-layout-type3.ps2.ref.png test/ft-text-vertical-layout-type3.ps3.ref.png test/ft-text-vertical-layout-type3.ps.ref.png test/ft-text-vertical-layout-type3.xlib.ref.png test/implicit-close.c test/implicit-close.ref.png test/joins.ref.png test/leaky-dashed-rectangle.pdf.ref.png test/leaky-dashed-rectangle.xlib.ref.png test/leaky-dashed-stroke.ref.png test/leaky-dashed-stroke.xlib.ref.png test/line-width-scale.ref.png test/long-dashed-lines.ref.png test/Makefile.am test/Makefile.sources test/mask-glyphs.svg.ref.png test/mask.pdf.argb32.ref.png test/mask.pdf.rgb24.ref.png test/mask.svg.argb32.xfail.png test/mask.svg.rgb24.xfail.png test/meta-surface-pattern.pdf.argb32.ref.png test/meta-surface-pattern.pdf.rgb24.ref.png test/meta-surface-pattern.svg.argb32.ref.png test/meta-surface-pattern.svg.rgb24.ref.png test/new-sub-path.argb32.ref.png test/new-sub-path.pdf.argb32.ref.png test/new-sub-path.ref.png test/new-sub-path.rgb24.ref.png test/over-around-source.pdf.argb32.ref.png test/overlapping-glyphs.svg.argb32.ref.png test/overlapping-glyphs.svg.rgb24.ref.png test/radial-gradient.pdf.ref.png test/radial-gradient.svg.xfail.png test/random-intersections.c test/random-intersections-curves-eo.c test/random-intersections-curves-eo.pdf.ref.png test/random-intersections-curves-eo.ps.ref.png test/random-intersections-curves-eo.ref.png test/random-intersections-curves-eo.xlib-fallback.ref.png test/random-intersections-curves-eo.xlib.ref.png test/random-intersections-curves-nz.c test/random-intersections-curves-nz.pdf.ref.png test/random-intersections-curves-nz.ps.ref.png test/random-intersections-curves-nz.ref.png test/random-intersections-curves-nz.xlib-fallback.ref.png test/random-intersections-curves-nz.xlib.ref.png test/random-intersections-eo.c test/random-intersections-eo.ps.ref.png test/random-intersections-eo.quartz.ref.png test/random-intersections-eo.ref.png test/random-intersections-eo.xlib.ref.png test/random-intersections-nonzero.c test/random-intersections-nonzero.ps.ref.png test/random-intersections-nonzero.ref.png test/random-intersections-nonzero.xlib.ref.png test/random-intersections.ps2.ref.png test/random-intersections.ps3.ref.png test/random-intersections.quartz.ref.png test/random-intersections.ref.png test/random-intersections.xlib.ref.png test/reflected-stroke.ref.png test/rel-path.ref.png test/rel-path.rgb24.ref.png test/rotated-clip.c test/rotated-clip.ps.ref.png test/rotated-clip.ref.png test/rotated-clip.xlib.ref.png test/scale-offset-image.meta.xfail.png test/scale-offset-image.pdf.ref.png test/scale-offset-image.pdf.xfail.png test/scale-offset-image.script.xfail.png test/scale-offset-image.xfail.png test/scale-offset-image.xlib-fallback.xfail.png test/scale-offset-image.xlib.xfail.png test/scale-offset-similar.meta.xfail.png test/scale-offset-similar.pdf.ref.png test/scale-offset-similar.pdf.xfail.png test/scale-offset-similar.script.xfail.png test/scale-offset-similar.xfail.png test/scale-offset-similar.xlib-fallback.xfail.png test/scale-offset-similar.xlib.xfail.png test/self-intersecting.argb32.xfail.png test/self-intersecting.c test/self-intersecting.pdf.argb32.xfail.png test/self-intersecting.pdf.rgb24.xfail.png test/self-intersecting.ps.argb32.xfail.png test/self-intersecting.ps.ref.png test/self-intersecting.ps.rgb24.xfail.png test/self-intersecting.ref.png test/self-intersecting.rgb24.ref.png test/self-intersecting.rgb24.xfail.png test/self-intersecting.xlib.argb32.xfail.png test/self-intersecting.xlib.ref.png test/self-intersecting.xlib.rgb24.xfail.png test/smask.pdf.xfail.png test/smask.ps2.ref.png test/smask.ps3.ref.png test/smask.ps.ref.png test/smask.ref.png test/smask.script.ref.png test/smask-stroke.ref.png test/smask-stroke.xlib.ref.png test/smask.svg.ref.png test/smask-text.script.ref.png test/smask.xlib.ref.png test/spline-decomposition.pdf.ref.png test/spline-decomposition.ps.ref.png test/spline-decomposition.ref.png test/spline-decomposition.svg.ref.png test/spline-decomposition.xlib.ref.png test/stroke-ctm-caps.ref.png test/stroke-image.pdf.ref.png test/stroke-image.ps2.ref.png test/stroke-image.ps3.ref.png test/stroke-image.ps.ref.png test/stroke-image.ref.png test/stroke-image.xlib.ref.png test/surface-pattern.pdf.xfail.png test/text-pattern.pdf.argb32.ref.png test/text-pattern.pdf.rgb24.ref.png test/text-rotate.pdf.ref.png test/text-rotate.ref.png test/text-rotate.svg.ref.png test/text-rotate.xlib.ref.png test/twin.ps.ref.png test/twin.ref.png test/twin.xlib.ref.png test/unantialiased-shapes.ref.png test/unbounded-operator.svg12.argb32.ref.png test/unbounded-operator.svg12.argb32.xfail.png test/user-font.pdf.ref.png test/user-font.ps2.ref.png test/user-font.ps3.ref.png test/user-font.ps.ref.png test/user-font.ref.png test/user-font.xlib.ref.png util/cairo-fdr util/cairo-script util/cairo-sphinx util/cairo-trace util/.gitignore util/Makefile.am util/show-edges.c util/show-events.c util/show-traps.c util/trace-to-xml.c util/xml-to-trace.c

Chris Wilson ickle at kemper.freedesktop.org
Sat Aug 29 10:13:19 PDT 2009


 NEWS                                                      |   18 
 boilerplate/Makefile.am                                   |    3 
 boilerplate/Makefile.sources                              |    1 
 boilerplate/Makefile.win32.features                       |   28 
 boilerplate/cairo-boilerplate-directfb.c                  |    2 
 boilerplate/cairo-boilerplate-drm.c                       |    2 
 boilerplate/cairo-boilerplate-gl.c                        |    2 
 boilerplate/cairo-boilerplate-glitz-agl.c                 |    2 
 boilerplate/cairo-boilerplate-glitz-glx.c                 |    2 
 boilerplate/cairo-boilerplate-glitz-wgl.c                 |    2 
 boilerplate/cairo-boilerplate-pdf.c                       |    2 
 boilerplate/cairo-boilerplate-ps.c                        |    4 
 boilerplate/cairo-boilerplate-quartz.c                    |    2 
 boilerplate/cairo-boilerplate-script.c                    |    6 
 boilerplate/cairo-boilerplate-skia.c                      |   52 
 boilerplate/cairo-boilerplate-svg.c                       |    4 
 boilerplate/cairo-boilerplate-test-surfaces.c             |    8 
 boilerplate/cairo-boilerplate-vg.c                        |    4 
 boilerplate/cairo-boilerplate-win32-printing.c            |    2 
 boilerplate/cairo-boilerplate-win32.c                     |    2 
 boilerplate/cairo-boilerplate-xcb.c                       |    1 
 boilerplate/cairo-boilerplate-xlib.c                      |    4 
 boilerplate/cairo-boilerplate.c                           |   55 
 boilerplate/cairo-boilerplate.h                           |    1 
 build/Makefile.win32.features                             |    2 
 build/Makefile.win32.features-h                           |    7 
 build/configure.ac.features                               |    3 
 build/configure.ac.warnings                               |   12 
 configure.ac                                              |   46 
 dev/null                                                  |binary
 perf/Makefile.am                                          |    4 
 perf/box-outline.c                                        |    2 
 perf/cairo-perf-chart.c                                   |  751 +++++
 perf/cairo-perf-diff                                      |   26 
 perf/cairo-perf-trace.c                                   |   92 
 perf/cairo-perf.c                                         |   17 
 perf/cairo-perf.h                                         |    3 
 perf/composite-checker.c                                  |    2 
 perf/dragon.c                                             |   38 
 perf/fill.c                                               |    2 
 perf/glyphs.c                                             |    2 
 perf/intersections.c                                      |    4 
 perf/long-dashed-lines.c                                  |    2 
 perf/long-lines.c                                         |    2 
 perf/mask.c                                               |    2 
 perf/mosaic.c                                             |    2 
 perf/paint-with-alpha.c                                   |    2 
 perf/paint.c                                              |    2 
 perf/pattern_create_radial.c                              |    2 
 perf/pythagoras-tree.c                                    |    4 
 perf/rectangles.c                                         |    2 
 perf/rounded-rectangles.c                                 |    2 
 perf/spiral.c                                             |  149 +
 perf/stroke.c                                             |    2 
 perf/subimage_copy.c                                      |    2 
 perf/tessellate.c                                         |    2 
 perf/text.c                                               |    2 
 perf/twin.c                                               |    2 
 perf/unaligned-clip.c                                     |    6 
 perf/world-map.c                                          |    4 
 perf/zrusin.c                                             |    2 
 src/Makefile.am                                           |    7 
 src/Makefile.sources                                      |   32 
 src/Makefile.win32.features                               |   36 
 src/cairo-arc.c                                           |    2 
 src/cairo-base64-stream.c                                 |  143 +
 src/cairo-base85-stream.c                                 |    3 
 src/cairo-bentley-ottmann-rectangular.c                   |  733 +++++
 src/cairo-bentley-ottmann-rectilinear.c                   |  582 ++++
 src/cairo-bentley-ottmann.c                               | 1914 +++++++-------
 src/cairo-clip-private.h                                  |   18 
 src/cairo-clip.c                                          |  840 +++++-
 src/cairo-combsort-private.h                              |    4 
 src/cairo-debug.c                                         |   67 
 src/cairo-fixed-private.h                                 |   52 
 src/cairo-fixed-type-private.h                            |    5 
 src/cairo-freelist-private.h                              |   31 
 src/cairo-freelist.c                                      |   73 
 src/cairo-ft-font.c                                       |   12 
 src/cairo-gl-surface.c                                    |   77 
 src/cairo-gstate.c                                        |  216 +
 src/cairo-hash.c                                          |    6 
 src/cairo-hull.c                                          |    2 
 src/cairo-image-surface.c                                 |    4 
 src/cairo-matrix.c                                        |   83 
 src/cairo-meta-surface.c                                  |    2 
 src/cairo-output-stream-private.h                         |    4 
 src/cairo-path-bounds.c                                   |   31 
 src/cairo-path-fill.c                                     |  328 +-
 src/cairo-path-fixed-private.h                            |   14 
 src/cairo-path-fixed.c                                    |  137 -
 src/cairo-path-stroke.c                                   | 1038 ++++---
 src/cairo-pattern.c                                       |   27 
 src/cairo-pdf-surface.c                                   |   12 
 src/cairo-pen.c                                           |  193 -
 src/cairo-polygon.c                                       |  372 ++
 src/cairo-ps-surface.c                                    |   26 
 src/cairo-qt-surface.cpp                                  |  105 
 src/cairo-rectangle.c                                     |   23 
 src/cairo-region-private.h                                |    5 
 src/cairo-region.c                                        |   55 
 src/cairo-scaled-font-private.h                           |    3 
 src/cairo-scaled-font.c                                   |   88 
 src/cairo-script-surface.c                                |  898 ++++--
 src/cairo-script.h                                        |   52 
 src/cairo-skia-surface.cpp                                | 1174 ++++++++
 src/cairo-skia.h                                          |   84 
 src/cairo-skiplist-private.h                              |  118 
 src/cairo-skiplist.c                                      |  399 --
 src/cairo-slope-private.h                                 |   65 
 src/cairo-slope.c                                         |   13 
 src/cairo-spans-private.h                                 |   64 
 src/cairo-spans.c                                         |  249 -
 src/cairo-spline.c                                        |    2 
 src/cairo-surface-fallback.c                              |  681 +++-
 src/cairo-surface-wrapper-private.h                       |    7 
 src/cairo-surface-wrapper.c                               |   73 
 src/cairo-surface.c                                       |   38 
 src/cairo-svg-surface.c                                   |    6 
 src/cairo-tee-surface-private.h                           |   47 
 src/cairo-tee-surface.c                                   |  558 ++++
 src/cairo-tor-scan-converter.c                            |  517 +--
 src/cairo-toy-font-face.c                                 |    3 
 src/cairo-traps.c                                         |  575 +---
 src/cairo-types-private.h                                 |   18 
 src/cairo-wideint-private.h                               |   47 
 src/cairo-wideint.c                                       |   34 
 src/cairo-win32-font.c                                    |    5 
 src/cairo-win32-printing-surface.c                        |   24 
 src/cairo-win32-surface.c                                 |   60 
 src/cairo-xlib-display.c                                  |    7 
 src/cairo-xlib-surface.c                                  |   35 
 src/cairo-xlib-xrender-private.h                          |   90 
 src/cairo-xml-surface.c                                   | 1153 ++++++++
 src/cairo-xml.h                                           |   72 
 src/cairo.h                                               |   23 
 src/cairoint.h                                            |  214 -
 test/Makefile.am                                          |  126 
 test/Makefile.sources                                     |    8 
 test/a1-image-sample.c                                    |    4 
 test/cairo-test-runner.c                                  |   47 
 test/cairo-test-trace.c                                   |    5 
 test/caps-joins-alpha.ref.png                             |binary
 test/caps-joins-alpha.xlib.ref.png                        |binary
 test/caps-joins-curve.ref.png                             |binary
 test/caps-joins-curve.xlib.ref.png                        |binary
 test/clear.pdf.argb32.ref.png                             |binary
 test/clear.ps.argb32.ref.png                              |binary
 test/clip-fill.xlib-fallback.ref.png                      |binary
 test/clip-operator.pdf.argb32.ref.png                     |binary
 test/clip-operator.pdf.rgb24.ref.png                      |binary
 test/clip-operator.ref.png                                |binary
 test/clip-operator.rgb24.ref.png                          |binary
 test/clip-operator.svg12.argb32.xfail.png                 |binary
 test/clip-operator.svg12.rgb24.xfail.png                  |binary
 test/clip-operator.xlib-fallback.ref.png                  |binary
 test/clip-push-group.svg.ref.png                          |binary
 test/clip-stroke.c                                        |  121 
 test/clip-stroke.ref.png                                  |binary
 test/clip-stroke.xlib-fallback.ref.png                    |binary
 test/clip-stroke.xlib.ref.png                             |binary
 test/clip-text.svg.ref.png                                |binary
 test/clipped-group.pdf.ref.png                            |binary
 test/clipped-group.ref.png                                |binary
 test/clipped-group.svg.ref.png                            |binary
 test/clipped-trapezoids-ref.png                           |binary
 test/clipped-trapezoids.c                                 |   95 
 test/clipped-trapezoids.ref.png                           |binary
 test/close-path-current-point.ref.png                     |binary
 test/dash-caps-joins.ref.png                              |binary
 test/dash-curve.ref.png                                   |binary
 test/dash-curve.xlib.ref.png                              |binary
 test/dash-scale.ref.png                                   |binary
 test/degenerate-arc.ref.png                               |binary
 test/degenerate-dash.ref.png                              |binary
 test/degenerate-dash.xlib.ref.png                         |binary
 test/degenerate-path.argb32.ref.png                       |binary
 test/degenerate-path.rgb24.ref.png                        |binary
 test/degenerate-pen.ref.png                               |binary
 test/degenerate-pen.xlib.ref.png                          |binary
 test/device-offset-fractional.pdf.xfail.png               |binary
 test/extended-blend-alpha.svg12.argb32.xfail.png          |binary
 test/fill-and-stroke-alpha-add.ref.png                    |binary
 test/fill-and-stroke-alpha-add.svg12.xfail.png            |binary
 test/fill-and-stroke-alpha.ref.png                        |binary
 test/fill-and-stroke.argb32.ref.png                       |binary
 test/fill-and-stroke.rgb24.ref.png                        |binary
 test/fill-and-stroke.xlib.argb32.ref.png                  |binary
 test/fill-and-stroke.xlib.rgb24.ref.png                   |binary
 test/filter-nearest-offset.pdf.xfail.png                  |binary
 test/filter-nearest-transformed.pdf.xfail.png             |binary
 test/ft-text-vertical-layout-type1.c                      |    6 
 test/ft-text-vertical-layout-type1.ps.ref.png             |binary
 test/ft-text-vertical-layout-type1.ref.png                |binary
 test/ft-text-vertical-layout-type1.xlib.ref.png           |binary
 test/ft-text-vertical-layout-type3.c                      |    5 
 test/ft-text-vertical-layout-type3.ps.ref.png             |binary
 test/ft-text-vertical-layout-type3.xlib.ref.png           |binary
 test/implicit-close.c                                     |   54 
 test/implicit-close.ref.png                               |binary
 test/joins.ref.png                                        |binary
 test/leaky-dashed-rectangle.pdf.ref.png                   |binary
 test/leaky-dashed-rectangle.xlib.ref.png                  |binary
 test/leaky-dashed-stroke.ref.png                          |binary
 test/leaky-dashed-stroke.xlib.ref.png                     |binary
 test/line-width-scale.ref.png                             |binary
 test/long-dashed-lines.ref.png                            |binary
 test/mask-glyphs.svg.ref.png                              |binary
 test/mask.pdf.argb32.ref.png                              |binary
 test/mask.pdf.rgb24.ref.png                               |binary
 test/mask.svg.argb32.xfail.png                            |binary
 test/mask.svg.rgb24.xfail.png                             |binary
 test/meta-surface-pattern.pdf.argb32.ref.png              |binary
 test/meta-surface-pattern.pdf.rgb24.ref.png               |binary
 test/meta-surface-pattern.svg.argb32.ref.png              |binary
 test/meta-surface-pattern.svg.rgb24.ref.png               |binary
 test/new-sub-path.argb32.ref.png                          |binary
 test/new-sub-path.pdf.argb32.ref.png                      |binary
 test/new-sub-path.rgb24.ref.png                           |binary
 test/over-around-source.pdf.argb32.ref.png                |binary
 test/overlapping-glyphs.svg.argb32.ref.png                |binary
 test/overlapping-glyphs.svg.rgb24.ref.png                 |binary
 test/radial-gradient.pdf.ref.png                          |binary
 test/radial-gradient.svg.xfail.png                        |binary
 test/random-intersections-curves-eo.c                     |   81 
 test/random-intersections-curves-eo.pdf.ref.png           |binary
 test/random-intersections-curves-eo.ps.ref.png            |binary
 test/random-intersections-curves-eo.ref.png               |binary
 test/random-intersections-curves-eo.xlib-fallback.ref.png |binary
 test/random-intersections-curves-eo.xlib.ref.png          |binary
 test/random-intersections-curves-nz.c                     |   82 
 test/random-intersections-curves-nz.pdf.ref.png           |binary
 test/random-intersections-curves-nz.ps.ref.png            |binary
 test/random-intersections-curves-nz.ref.png               |binary
 test/random-intersections-curves-nz.xlib-fallback.ref.png |binary
 test/random-intersections-curves-nz.xlib.ref.png          |binary
 test/random-intersections-eo.c                            |   78 
 test/random-intersections-eo.ps.ref.png                   |binary
 test/random-intersections-eo.quartz.ref.png               |binary
 test/random-intersections-eo.ref.png                      |binary
 test/random-intersections-eo.xlib.ref.png                 |binary
 test/random-intersections-nonzero.c                       |   79 
 test/random-intersections-nonzero.ps.ref.png              |binary
 test/random-intersections-nonzero.ref.png                 |binary
 test/random-intersections-nonzero.xlib.ref.png            |binary
 test/random-intersections.c                               |   78 
 test/reflected-stroke.ref.png                             |binary
 test/rel-path.ref.png                                     |binary
 test/rel-path.rgb24.ref.png                               |binary
 test/rotated-clip.c                                       |  110 
 test/rotated-clip.ps.ref.png                              |binary
 test/rotated-clip.ref.png                                 |binary
 test/rotated-clip.xlib.ref.png                            |binary
 test/scale-offset-image.pdf.ref.png                       |binary
 test/scale-offset-image.script.xfail.png                  |binary
 test/scale-offset-image.xfail.png                         |binary
 test/scale-offset-image.xlib-fallback.xfail.png           |binary
 test/scale-offset-image.xlib.xfail.png                    |binary
 test/scale-offset-similar.meta.xfail.png                  |binary
 test/scale-offset-similar.pdf.ref.png                     |binary
 test/scale-offset-similar.script.xfail.png                |binary
 test/scale-offset-similar.xfail.png                       |binary
 test/scale-offset-similar.xlib-fallback.xfail.png         |binary
 test/scale-offset-similar.xlib.xfail.png                  |binary
 test/self-intersecting.c                                  |    3 
 test/self-intersecting.ps.ref.png                         |binary
 test/self-intersecting.ref.png                            |binary
 test/self-intersecting.xlib.ref.png                       |binary
 test/smask-stroke.ref.png                                 |binary
 test/smask-stroke.xlib.ref.png                            |binary
 test/smask-text.script.ref.png                            |binary
 test/smask.pdf.xfail.png                                  |binary
 test/smask.ps.ref.png                                     |binary
 test/smask.ref.png                                        |binary
 test/smask.script.ref.png                                 |binary
 test/smask.svg.ref.png                                    |binary
 test/smask.xlib.ref.png                                   |binary
 test/spline-decomposition.pdf.ref.png                     |binary
 test/spline-decomposition.ps.ref.png                      |binary
 test/spline-decomposition.ref.png                         |binary
 test/spline-decomposition.svg.ref.png                     |binary
 test/spline-decomposition.xlib.ref.png                    |binary
 test/stroke-ctm-caps.ref.png                              |binary
 test/stroke-image.pdf.ref.png                             |binary
 test/stroke-image.ps.ref.png                              |binary
 test/stroke-image.ref.png                                 |binary
 test/stroke-image.xlib.ref.png                            |binary
 test/surface-pattern.pdf.xfail.png                        |binary
 test/text-pattern.pdf.argb32.ref.png                      |binary
 test/text-pattern.pdf.rgb24.ref.png                       |binary
 test/text-rotate.pdf.ref.png                              |binary
 test/text-rotate.ref.png                                  |binary
 test/text-rotate.svg.ref.png                              |binary
 test/text-rotate.xlib.ref.png                             |binary
 test/twin.ps.ref.png                                      |binary
 test/twin.ref.png                                         |binary
 test/twin.xlib.ref.png                                    |binary
 test/unantialiased-shapes.ref.png                         |binary
 test/unbounded-operator.svg12.argb32.ref.png              |binary
 test/user-font.pdf.ref.png                                |binary
 test/user-font.ps.ref.png                                 |binary
 test/user-font.ref.png                                    |binary
 test/user-font.xlib.ref.png                               |binary
 util/.gitignore                                           |    3 
 util/Makefile.am                                          |   48 
 util/cairo-fdr/Makefile.am                                |   13 
 util/cairo-fdr/fdr.c                                      |  304 ++
 util/cairo-script/Makefile.am                             |    8 
 util/cairo-script/cairo-script-file.c                     |   10 
 util/cairo-script/cairo-script-interpreter.c              |   22 
 util/cairo-script/cairo-script-interpreter.h              |    4 
 util/cairo-script/cairo-script-objects.c                  |    4 
 util/cairo-script/cairo-script-operators.c                |  261 +
 util/cairo-script/cairo-script-private.h                  |    7 
 util/cairo-script/cairo-script-scanner.c                  |  185 +
 util/cairo-script/csi-trace.c                             |   39 
 util/cairo-sphinx/.gitignore                              |    1 
 util/cairo-sphinx/Makefile.am                             |   40 
 util/cairo-sphinx/fdr.c                                   |  260 +
 util/cairo-sphinx/sphinx.c                                | 1525 +++++++++++
 util/cairo-trace/trace.c                                  |   61 
 util/show-edges.c                                         | 1221 ++++++++
 util/show-events.c                                        |  845 ++++++
 util/show-traps.c                                         | 1239 +++++++++
 util/trace-to-xml.c                                       |   77 
 util/xml-to-trace.c                                       |  263 +
 326 files changed, 18919 insertions(+), 4728 deletions(-)

New commits:
commit 19ebf83b6717e1f02e7be14218007858edf14ef0
Merge: 40aefac... a77f193...
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 29 17:45:48 2009 +0100

    Merge branch 'stroke-with-spans'
    
    This branch brings self-intersection removal with virtually no
    performance regression. (Compare with the initial implementation that
    incurred a 5-10x slowdown due to having to tessellate whole strokes at a
    time.) The importance of self-intersection removal is the improved visual
    quality it brings - gone are those annoying sparkles on the outside of
    rounded-rectangles for instance. Most of the performance overhead
    associated with the self-intersection removal is avoided by switching from
    trapezoids to spans for strokes. Obviously we are not able to do so for
    the xlib backend as we do not yet have a polygon image type, and so the
    tessellators are overhauled instead, along with more special casing for
    frequent operations to avoid the increased complexity along the general
    paths.
    
    Speedups
    ========
     xlib-rgba             swfdec-youtube-0    11371.13 (11406.01 0.28%) -> 10450.00 (10461.84 0.66%):  1.09x speedup
    ▏
    image-rgba          firefox-talos-svg-0    73696.53 (73828.28 3.42%) -> 68324.30 (70269.79 1.36%):  1.08x speedup
    ▏
    image-rgba             swfdec-youtube-0    7843.08 (7873.89 2.57%) -> 7393.96 (7399.68 0.18%):  1.06x speedup
    
     xvfb-rgba             swfdec-youtube-0    9627.25 (9634.43 0.16%) -> 9020.55 (9040.97 0.27%):  1.07x speedup
    ▏
    Slowdowns
    =========
     xvfb-rgba         gnome-terminal-vim-0    7695.12 (7697.87 0.44%) -> 8569.45 (8588.29 0.19%):  1.11x slowdown
    ▏
     xvfb-rgba         swfdec-giant-steps-0    3811.77 (3815.06 0.23%) -> 4246.67 (4569.17 3.52%):  1.11x slowdown
    ▏
    image-rgba                       gvim-0    7150.90 (7181.96 29.36%) -> 14641.04 (14651.36 0.11%):  2.05x slowdown
    â–ˆ
    
    One method for overcoming these regressions is to reduce the complexity of
    the polygons being fed into the tessellator (both in the number of edges
    and intersections). This should be feasible by coupling into Jeff Muizelaar's
    stroke-to-path work, which early indications suggest will bring a
    significant performance improvement. On top of this, our span
    implementation for the image backend is not as efficient as we would hope
    for - and Joonas promises a much faster implementation soon.

commit a77f1933afebe28e0651906c96fd098cd8267a9f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 29 14:23:41 2009 +0100

    Use the more generic is_box when doing simple extent checks
    
    is_rectangle() is far stricter than is_box(), and is only required for a
    very limited set of operations (essentially were the rectangle must
    conform to the motion as described by cairo_rectangle). For the general
    case where we just want to know whether we have a single rectangular path
    that covers a certain area,  is_box() is sufficient.

diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index de1481e..83e9b6e 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -250,7 +250,7 @@ _path_covers_bbox (cairo_pdf_surface_t *surface,
 {
     cairo_box_t box;
 
-    return _cairo_path_fixed_is_rectangle (path, &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) &&
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index da74eb3..6f1bcfc 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -714,7 +714,7 @@ _path_covers_bbox (cairo_ps_surface_t *surface,
 {
     cairo_box_t box;
 
-    if (_cairo_path_fixed_is_rectangle (path, &box)) {
+    if (_cairo_path_fixed_is_box (path, &box)) {
 	cairo_rectangle_int_t rect;
 
 	_cairo_box_round_to_rectangle (&box, &rect);
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index b767a0c..29d2da0 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -1554,7 +1554,7 @@ _emit_path (cairo_script_surface_t *surface,
 
     if (path == NULL) {
 	_cairo_path_fixed_init (&surface->cr.current_path);
-    } else if (_cairo_path_fixed_is_rectangle (path, &box)) {
+    } else if (_cairo_path_fixed_is_box (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);
@@ -1904,7 +1904,7 @@ _cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip
 
     /* skip the trivial clip covering the surface extents */
     if (surface->width >=0 && surface->height >= 0 &&
-	_cairo_path_fixed_is_rectangle (path, &box))
+	_cairo_path_fixed_is_box (path, &box))
     {
 	if (box.p1.x <= 0 && box.p1.y <= 0 &&
 	    box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) &&
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index 0361d33..8be5eed 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -340,7 +340,7 @@ _cliprect_covers_surface (cairo_svg_surface_t *surface,
 {
     cairo_box_t box;
 
-    if (_cairo_path_fixed_is_rectangle (path, &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 &&
commit 0a548d08b5eae73a95fb41f41298c9c43379163e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 29 16:27:27 2009 +0100

    [clip] Correctly compute a geometric mask for a rectilinear + arbitrary
    
    Fix up the geometric clipper to handle intersecting a rectilinear path
    with an arbitrary path and inspecting the result to see if it becomes a
    a region.

diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 6609f28..fde4121 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -617,8 +617,10 @@ static cairo_int_status_t
 _cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path)
 {
     cairo_traps_t traps;
-    cairo_box_t box;
+    cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)];
+    cairo_box_t *boxes = stack_boxes;
     cairo_status_t status;
+    int n;
 
     /* If we have nothing to intersect with this path, then it cannot
      * magically be reduced into a region.
@@ -626,18 +628,46 @@ _cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path)
     if (clip_path->prev == NULL)
 	goto UNSUPPORTED;
 
-    /* start simple... */
-    if (! clip_path->path.maybe_fill_region)
+    /* Start simple... Intersect some boxes with an arbitrary path. */
+    if (! clip_path->path.is_rectilinear)
+	goto UNSUPPORTED;
+    if (clip_path->prev->prev != NULL)
 	goto UNSUPPORTED;
 
     _cairo_traps_init (&traps);
-    _cairo_box_from_rectangle (&box, &clip_path->extents);
-    _cairo_traps_limit (&traps, &box, 1);
+    _cairo_box_from_rectangle (&boxes[0], &clip_path->extents);
+    _cairo_traps_limit (&traps, boxes, 1);
+
+    status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
+							  clip_path->fill_rule,
+							  &traps);
+    if (unlikely (_cairo_status_is_error (status)))
+	return status;
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+	goto UNSUPPORTED;
 
+    if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) {
+	boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
+	if (unlikely (boxes == NULL))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+
+    for (n = 0; n < traps.num_traps; n++) {
+	boxes[n].p1.x = traps.traps[n].left.p1.x;
+	boxes[n].p1.y = traps.traps[n].top;
+	boxes[n].p2.x = traps.traps[n].right.p1.x;
+	boxes[n].p2.y = traps.traps[n].bottom;
+    }
+
+    _cairo_traps_clear (&traps);
+    _cairo_traps_limit (&traps, boxes, n);
     status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path,
 					      clip_path->prev->fill_rule,
 					      clip_path->prev->tolerance,
 					      &traps);
+    if (boxes != stack_boxes)
+	free (boxes);
+
     if (unlikely (status))
 	return status;
 
@@ -689,7 +719,6 @@ _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
     }
 
     /* now extract the region for ourselves */
-
     clip_path->region =
 	_cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path,
 						      clip_path->fill_rule,
@@ -1107,9 +1136,9 @@ _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
 					  CAIRO_OPERATOR_IN,
 					  &pattern.base,
 					  &prev->path,
-					  clip_path->fill_rule,
-					  clip_path->tolerance,
-					  clip_path->antialias,
+					  prev->fill_rule,
+					  prev->tolerance,
+					  prev->antialias,
 					  NULL);
 	    if (need_translate) {
 		_cairo_path_fixed_translate (&prev->path,
commit 8a323d7c8998f308fc222d65badb1289e3f7fb54
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 29 14:15:07 2009 +0100

    [clip] Apply surface offset when combining with clip mask
    
    In order to correctly combine the clip mask with the compositing mask the
    clip path must be offset so that it is relative to the destination
    surface.

diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
index f8faa75..131d4c7 100644
--- a/src/cairo-clip-private.h
+++ b/src/cairo-clip-private.h
@@ -116,7 +116,7 @@ _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *dst);
 cairo_private cairo_status_t
 _cairo_clip_combine_with_surface (cairo_clip_t *clip,
 				  cairo_surface_t *dst,
-				  int dst_x, int dst_y);
+				  const cairo_rectangle_int_t *extents);
 
 cairo_private cairo_int_status_t
 _cairo_clip_get_region (cairo_clip_t *clip,
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 0f48442..6609f28 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -973,6 +973,31 @@ _cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path,
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_status_t
+_combine_region (cairo_surface_t *surface,
+		 const cairo_region_t *region,
+		 const cairo_rectangle_int_t *extents)
+{
+    cairo_region_t clear_region;
+    cairo_status_t status;
+
+    _cairo_region_init_rectangle (&clear_region, extents);
+    status = cairo_region_subtract (&clear_region, region);
+    if (unlikely (status))
+	return status;
+
+    if (! cairo_region_is_empty (&clear_region)) {
+	cairo_region_translate (&clear_region, -extents->x, -extents->y);
+	status = _cairo_surface_fill_region (surface,
+					     CAIRO_OPERATOR_CLEAR,
+					     CAIRO_COLOR_TRANSPARENT,
+					     &clear_region);
+    }
+    _cairo_region_fini (&clear_region);
+
+    return status;
+}
+
 static cairo_surface_t *
 _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
 			      cairo_surface_t *target)
@@ -1019,7 +1044,7 @@ _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
     if (unlikely (_cairo_status_is_error (status)))
 	goto BAIL;
 
-    need_translate = clip_extents->x || clip_extents->y;
+    need_translate = clip_extents->x | clip_extents->y;
     if (status == CAIRO_STATUS_SUCCESS) {
 	if (need_translate) {
 	    cairo_region_translate (clip_path->region,
@@ -1069,18 +1094,7 @@ _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
 	    goto BAIL;
 
 	if (status == CAIRO_STATUS_SUCCESS) {
-	    if (need_translate) {
-		cairo_region_translate (prev->region,
-					-clip_extents->x, -clip_extents->y);
-	    }
-	    status = _cairo_surface_fill_region (surface,
-						 CAIRO_OPERATOR_IN,
-						 CAIRO_COLOR_WHITE,
-						 prev->region);
-	    if (need_translate) {
-		cairo_region_translate (prev->region,
-					clip_extents->x, clip_extents->y);
-	    }
+	    status = _combine_region (surface, prev->region, clip_extents);
 	    if (unlikely (status))
 		goto BAIL;
 	} else if (prev->path.is_rectilinear) {
@@ -1166,6 +1180,7 @@ _cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip)
 		 clip_path->region == NULL ? "no" : "yes",
 		 clip_path->surface == NULL ? "no" : "yes");
 	_cairo_debug_print_path (stream, &clip_path->path);
+	fprintf (stream, "\n");
     } while ((clip_path = clip_path->prev) != NULL);
 }
 
@@ -1179,12 +1194,11 @@ _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
 cairo_status_t
 _cairo_clip_combine_with_surface (cairo_clip_t *clip,
 				  cairo_surface_t *dst,
-				  int dst_x,
-				  int dst_y)
+				  const cairo_rectangle_int_t *extents)
 {
     cairo_pattern_union_t pattern;
     cairo_clip_path_t *clip_path = clip->path;
-    cairo_bool_t translate;
+    cairo_bool_t need_translate;
     cairo_status_t status;
 
     assert (clip_path != NULL);
@@ -1195,8 +1209,8 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip,
 	_cairo_pattern_init_for_surface (&pattern.surface,
 					 clip_path->surface);
 	cairo_matrix_init_translate (&pattern.base.matrix,
-				     dst_x - clip_path->extents.x,
-				     dst_y - clip_path->extents.y);
+				     extents->x - clip_path->extents.x,
+				     extents->y - clip_path->extents.y);
 	status = _cairo_surface_paint (dst,
 				       CAIRO_OPERATOR_IN,
 				       &pattern.base,
@@ -1211,26 +1225,14 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip,
 			       CAIRO_COLOR_WHITE,
 			       CAIRO_CONTENT_COLOR);
 
-    translate = dst_x | dst_y;
+    need_translate = extents->x | extents->y;
     do {
 	status = _cairo_clip_path_to_region (clip_path);
 	if (unlikely (_cairo_status_is_error (status)))
 	    return status;
 
-	if (status == CAIRO_STATUS_SUCCESS) {
-	    if (translate)
-		cairo_region_translate (clip_path->region, -dst_x, -dst_y);
-
-	    status = _cairo_surface_fill_region (dst,
-						 CAIRO_OPERATOR_IN,
-						 CAIRO_COLOR_WHITE,
-						 clip_path->region);
-
-	    if (translate)
-		cairo_region_translate (clip_path->region, dst_x, dst_y);
-
-	    return status;
-	}
+	if (status == CAIRO_STATUS_SUCCESS)
+	    return _combine_region (dst, clip_path->region, extents);
 
 	if (clip_path->surface != NULL &&
 	    clip_path->surface->backend == dst->backend)
@@ -1238,8 +1240,8 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip,
 	    _cairo_pattern_init_for_surface (&pattern.surface,
 					     clip_path->surface);
 	    cairo_matrix_init_translate (&pattern.base.matrix,
-					 dst_x - clip_path->extents.x,
-					 dst_y - clip_path->extents.y);
+					 extents->x - clip_path->extents.x,
+					 extents->y - clip_path->extents.y);
 	    status = _cairo_surface_paint (dst,
 					   CAIRO_OPERATOR_IN,
 					   &pattern.base,
@@ -1250,10 +1252,10 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip,
 	    return status;
 	}
 
-	if (translate) {
+	if (need_translate) {
 	    _cairo_path_fixed_translate (&clip_path->path,
-					_cairo_fixed_from_int (-dst_x),
-					_cairo_fixed_from_int (-dst_y));
+					_cairo_fixed_from_int (-extents->x),
+					_cairo_fixed_from_int (-extents->y));
 	}
 	status = _cairo_surface_fill (dst,
 				      CAIRO_OPERATOR_IN,
@@ -1263,11 +1265,12 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip,
 				      clip_path->tolerance,
 				      clip_path->antialias,
 				      NULL);
-	if (translate) {
+	if (need_translate) {
 	    _cairo_path_fixed_translate (&clip_path->path,
-					_cairo_fixed_from_int (dst_x),
-					_cairo_fixed_from_int (dst_y));
+					_cairo_fixed_from_int (extents->x),
+					_cairo_fixed_from_int (extents->y));
 	}
+
 	if (unlikely (status))
 	    return status;
     } while ((clip_path = clip_path->prev) != NULL);
diff --git a/src/cairo-region.c b/src/cairo-region.c
index 9983481..2148fca 100644
--- a/src/cairo-region.c
+++ b/src/cairo-region.c
@@ -426,7 +426,7 @@ slim_hidden_def (cairo_region_status);
  * Since: 1.10
  **/
 cairo_status_t
-cairo_region_subtract (cairo_region_t *dst, cairo_region_t *other)
+cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other)
 {
     if (dst->status)
 	return dst->status;
@@ -434,8 +434,12 @@ cairo_region_subtract (cairo_region_t *dst, cairo_region_t *other)
     if (other->status)
 	return _cairo_region_set_error (dst, other->status);
 
-    if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, &other->rgn))
+    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;
 }
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 0f5f821..79d719d 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -163,12 +163,8 @@ _create_composite_mask_pattern (cairo_surface_pattern_t       *mask_pattern,
     if (unlikely (status))
 	goto CLEANUP_SURFACE;
 
-    if (clip_surface) {
-	status = _cairo_clip_combine_with_surface (clip,
-						   mask,
-						   extents->x,
-						   extents->y);
-    }
+    if (clip_surface)
+	status = _cairo_clip_combine_with_surface (clip, mask, extents);
 
     _cairo_pattern_init_for_surface (mask_pattern, mask);
 
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index d6fec26..70d4c69 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -264,9 +264,7 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
 		continue;
 
 	    _left = left;
-	    if (_left.p1.x <= limits->p1.x &&
-		_left.p2.x <= limits->p1.x)
-	    {
+	    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;
@@ -274,9 +272,7 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
 	    }
 
 	    _right = right;
-	    if (_right.p1.x >= limits->p2.x &&
-		_right.p2.x >= limits->p2.x)
-	    {
+	    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;
diff --git a/src/cairo.h b/src/cairo.h
index b750c4e..a046a47 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -2518,7 +2518,7 @@ 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, cairo_region_t *other);
+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,
diff --git a/test/clip-stroke.xlib.ref.png b/test/clip-stroke.xlib.ref.png
index 142dc79..b776749 100644
Binary files a/test/clip-stroke.xlib.ref.png and b/test/clip-stroke.xlib.ref.png differ
commit 21225a7163bc93d34d3e395c840faaba24046bb6
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 29 12:08:12 2009 +0100

    [clip] Pass in destination offset for combining with clip-mask
    
    When combining a clip-mask with a subsurface, as when used to combine with
    the composite mask, we need to pass the destination surface offset to the
    clip so that the paths can be corrected for the new surface.

diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
index a7e06a9..f8faa75 100644
--- a/src/cairo-clip-private.h
+++ b/src/cairo-clip-private.h
@@ -114,7 +114,9 @@ cairo_private cairo_surface_t *
 _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *dst);
 
 cairo_private cairo_status_t
-_cairo_clip_combine_with_surface (cairo_clip_t *clip, cairo_surface_t *dst);
+_cairo_clip_combine_with_surface (cairo_clip_t *clip,
+				  cairo_surface_t *dst,
+				  int dst_x, int dst_y);
 
 cairo_private cairo_int_status_t
 _cairo_clip_get_region (cairo_clip_t *clip,
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 7f2c8be..0f48442 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -1177,12 +1177,14 @@ _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
 }
 
 cairo_status_t
-_cairo_clip_combine_with_surface (cairo_clip_t *clip, cairo_surface_t *dst)
+_cairo_clip_combine_with_surface (cairo_clip_t *clip,
+				  cairo_surface_t *dst,
+				  int dst_x,
+				  int dst_y)
 {
     cairo_pattern_union_t pattern;
     cairo_clip_path_t *clip_path = clip->path;
-    const cairo_rectangle_int_t *clip_extents = &clip_path->extents;
-    cairo_bool_t need_translate;
+    cairo_bool_t translate;
     cairo_status_t status;
 
     assert (clip_path != NULL);
@@ -1193,8 +1195,8 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip, cairo_surface_t *dst)
 	_cairo_pattern_init_for_surface (&pattern.surface,
 					 clip_path->surface);
 	cairo_matrix_init_translate (&pattern.base.matrix,
-				     -clip_path->extents.x + clip_extents->x,
-				     -clip_path->extents.y + clip_extents->y);
+				     dst_x - clip_path->extents.x,
+				     dst_y - clip_path->extents.y);
 	status = _cairo_surface_paint (dst,
 				       CAIRO_OPERATOR_IN,
 				       &pattern.base,
@@ -1209,25 +1211,23 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip, cairo_surface_t *dst)
 			       CAIRO_COLOR_WHITE,
 			       CAIRO_CONTENT_COLOR);
 
-    need_translate = clip_extents->x || clip_extents->y;
+    translate = dst_x | dst_y;
     do {
 	status = _cairo_clip_path_to_region (clip_path);
 	if (unlikely (_cairo_status_is_error (status)))
 	    return status;
 
 	if (status == CAIRO_STATUS_SUCCESS) {
-	    if (need_translate) {
-		cairo_region_translate (clip_path->region,
-					-clip_extents->x, -clip_extents->y);
-	    }
+	    if (translate)
+		cairo_region_translate (clip_path->region, -dst_x, -dst_y);
+
 	    status = _cairo_surface_fill_region (dst,
 						 CAIRO_OPERATOR_IN,
 						 CAIRO_COLOR_WHITE,
 						 clip_path->region);
-	    if (need_translate) {
-		cairo_region_translate (clip_path->region,
-					clip_extents->x, clip_extents->y);
-	    }
+
+	    if (translate)
+		cairo_region_translate (clip_path->region, dst_x, dst_y);
 
 	    return status;
 	}
@@ -1238,8 +1238,8 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip, cairo_surface_t *dst)
 	    _cairo_pattern_init_for_surface (&pattern.surface,
 					     clip_path->surface);
 	    cairo_matrix_init_translate (&pattern.base.matrix,
-					 -clip_path->extents.x + clip_extents->x,
-					 -clip_path->extents.y + clip_extents->y);
+					 dst_x - clip_path->extents.x,
+					 dst_y - clip_path->extents.y);
 	    status = _cairo_surface_paint (dst,
 					   CAIRO_OPERATOR_IN,
 					   &pattern.base,
@@ -1250,10 +1250,10 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip, cairo_surface_t *dst)
 	    return status;
 	}
 
-	if (need_translate) {
+	if (translate) {
 	    _cairo_path_fixed_translate (&clip_path->path,
-					 _cairo_fixed_from_int (-clip_extents->x),
-					 _cairo_fixed_from_int (-clip_extents->y));
+					_cairo_fixed_from_int (-dst_x),
+					_cairo_fixed_from_int (-dst_y));
 	}
 	status = _cairo_surface_fill (dst,
 				      CAIRO_OPERATOR_IN,
@@ -1263,12 +1263,11 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip, cairo_surface_t *dst)
 				      clip_path->tolerance,
 				      clip_path->antialias,
 				      NULL);
-	if (need_translate) {
+	if (translate) {
 	    _cairo_path_fixed_translate (&clip_path->path,
-					 _cairo_fixed_from_int (clip_extents->x),
-					 _cairo_fixed_from_int (clip_extents->y));
+					_cairo_fixed_from_int (dst_x),
+					_cairo_fixed_from_int (dst_y));
 	}
-
 	if (unlikely (status))
 	    return status;
     } while ((clip_path = clip_path->prev) != NULL);
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 8fa7fa9..0f5f821 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -163,8 +163,12 @@ _create_composite_mask_pattern (cairo_surface_pattern_t       *mask_pattern,
     if (unlikely (status))
 	goto CLEANUP_SURFACE;
 
-    if (clip_surface)
-	status = _cairo_clip_combine_with_surface (clip, mask);
+    if (clip_surface) {
+	status = _cairo_clip_combine_with_surface (clip,
+						   mask,
+						   extents->x,
+						   extents->y);
+    }
 
     _cairo_pattern_init_for_surface (mask_pattern, mask);
 
@@ -838,9 +842,9 @@ _composite_spans_draw_func (void                          *closure,
     cairo_composite_rectangles_t rects;
     cairo_composite_spans_info_t *info = closure;
 
-    _cairo_composite_rectangles_init (
-	&rects, extents->x, extents->y,
-	extents->width, extents->height);
+    _cairo_composite_rectangles_init (&rects,
+				      extents->x, extents->y,
+				      extents->width, extents->height);
 
     /* The incoming dst_x/y are where we're pretending the origin of
      * the dst surface is -- *not* the offset of a rectangle where
commit ac6c6fe1d39effd5b6b382f0f1199af824868ef4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 29 12:07:20 2009 +0100

    [test] Add rotated clip.
    
    Exercise a bug found in not offsetting the clip mask when combining with
    the composite mask.

diff --git a/test/Makefile.am b/test/Makefile.am
index abd5138..4ac776f 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -785,6 +785,9 @@ REFERENCE_IMAGES = \
 	rotate-image-surface-paint.quartz.ref.png \
 	rotate-image-surface-paint.ref.png \
 	rotate-image-surface-paint.svg.ref.png \
+	rotated-clip.ref.png \
+	rotated-clip.ps.ref.png \
+	rotated-clip.xlib.ref.png \
 	scale-down-source-surface-paint.ref.png \
 	scale-offset-image.gl.ref.png \
 	scale-offset-image.pdf.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index 7b51396..c4f2516 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -165,6 +165,7 @@ test_sources = \
 	rel-path.c					\
 	rgb24-ignore-alpha.c				\
 	rotate-image-surface-paint.c			\
+	rotated-clip.c					\
 	scale-down-source-surface-paint.c		\
 	scale-offset-image.c				\
 	scale-offset-similar.c				\
diff --git a/test/rotated-clip.c b/test/rotated-clip.c
new file mode 100644
index 0000000..4ca4566
--- /dev/null
+++ b/test/rotated-clip.c
@@ -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 at redneon.com>
+ *         Chris Wilson <chris at 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/rotated-clip.ps.ref.png b/test/rotated-clip.ps.ref.png
new file mode 100644
index 0000000..a2a0ace
Binary files /dev/null and b/test/rotated-clip.ps.ref.png differ
diff --git a/test/rotated-clip.ref.png b/test/rotated-clip.ref.png
new file mode 100644
index 0000000..8a4cc1b
Binary files /dev/null and b/test/rotated-clip.ref.png differ
diff --git a/test/rotated-clip.xlib.ref.png b/test/rotated-clip.xlib.ref.png
new file mode 100644
index 0000000..93427e4
Binary files /dev/null and b/test/rotated-clip.xlib.ref.png differ
commit c60280782dfac7de1abe3230a500c7c98735dc02
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 29 09:59:36 2009 +0100

    [script] Implement invert
    
    Flesh out matrix inversion.

diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index a07022a..de5dd40 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -3122,6 +3122,30 @@ _integer (csi_t *ctx)
 }
 
 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;
@@ -5768,7 +5792,7 @@ _defs[] = {
     { "image", _image },
     { "index", _index },
     { "integer", _integer },
-    { "invert", NULL },
+    { "invert", _invert },
     { "in-stroke", NULL },
     { "in-fill", NULL },
     { "known", NULL },
commit 06ca0b1475caf709fdf32b10a891dfb3b47cc6b1
Author: Damian Frank <damianf at ubuntu-vm.(none)>
Date:   Fri Aug 14 11:35:55 2009 -0500

    Fix build on systems with older Xrender headers.
    
    This patch revises xlib so that it doesn't depend on having recent
    Xrender headers to build.  In particular, some definitions were added
    to the private xrender header file, and an ifdef render version check
    CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR was changed to a run-time
    check using CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS.

diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index 880ac93..d05bfed 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -284,7 +284,14 @@ _cairo_xlib_display_get (Display *dpy,
     memset (display->cached_xrender_formats, 0,
 	    sizeof (display->cached_xrender_formats));
 
+    /* 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 = TRUE;
     display->buggy_repeat = FALSE;
 
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index cf3bdfe..65b8a69 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -156,13 +156,10 @@ static const XTransform identity = { {
 
 #define CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface)	CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 11)
 
-#if RENDER_MAJOR > 0 || RENDER_MINOR >= 11
-#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \
-     (((op) <= CAIRO_OPERATOR_SATURATE) ? TRUE : (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS (surface) ? (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY : FALSE))
-#else
-#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \
-     ((op) <= CAIRO_OPERATOR_SATURATE)
-#endif
+#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op)	\
+     ((op) <= CAIRO_OPERATOR_SATURATE ||			\
+      (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) &&	\
+       (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY))
 
 static cairo_status_t
 _cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface,
@@ -1832,7 +1829,6 @@ _render_operator (cairo_operator_t op)
     case CAIRO_OPERATOR_SATURATE:
 	return PictOpSaturate;
 
-#if RENDER_MAJOR > 0 || RENDER_MINOR >= 11
     case CAIRO_OPERATOR_MULTIPLY:
 	return PictOpMultiply;
     case CAIRO_OPERATOR_SCREEN:
@@ -1863,24 +1859,6 @@ _render_operator (cairo_operator_t op)
 	return PictOpHSLColor;
     case CAIRO_OPERATOR_HSL_LUMINOSITY:
 	return PictOpHSLLuminosity;
-#else
-    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:
-	/* silence the compiler */
-#endif
 
     default:
 	ASSERT_NOT_REACHED;
diff --git a/src/cairo-xlib-xrender-private.h b/src/cairo-xlib-xrender-private.h
index eee585c..63a0ecb 100644
--- a/src/cairo-xlib-xrender-private.h
+++ b/src/cairo-xlib-xrender-private.h
@@ -45,6 +45,16 @@
 #include <X11/extensions/Xrender.h>
 #include <X11/extensions/renderproto.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) { }
+
+
 /* We require Render >= 0.6.  The following defines were only added in
  * 0.10.  Make sure they are defined.
  */
@@ -63,6 +73,57 @@
 #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
+
+/* There doesn't appear to be a simple #define that we can conditionalize
+ * on.  Instead, use the version; gradients were introdiced in 0.10. */
+#if RENDER_MAJOR == 0 && RENDER_MINOR < 10
+#define XRenderCreateLinearGradient			_int_consume
+#define XRenderCreateRadialGradient			_int_consume
+#define XRenderCreateConicalGradient			_int_consume
+typedef struct _XCircle {
+    XFixed x;
+    XFixed y;
+    XFixed radius;
+} XCircle;
+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;
+#endif
+
+
 #else /* !CAIRO_HAS_XLIB_XRENDER_SURFACE */
 
 /* Provide dummy symbols and macros to get it compile and take the fallback
@@ -71,14 +132,6 @@
 
 /* Functions */
 
-/* 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) { }
-
 #define XRenderQueryExtension				_int_consume
 #define XRenderQueryVersion				_int_consume
 #define XRenderQueryFormats				_int_consume
@@ -221,6 +274,27 @@ typedef unsigned long	PictFormat;
 #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
 
commit 17ef949b6aaa812dd566f1db016055f8e1913320
Author: Damian Frank <damianf at ubuntu-vm.(none)>
Date:   Fri Aug 14 11:38:52 2009 -0500

    Rename cairo-script static func to avoid MinGW conflict
    
    cairo-script-operators.c's _dup function was colliding with one
    defined in io.h by MinGW (gcc 4.3.0 package).  I renamed it
    to _duplicate.

diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index 414922d..a07022a 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -1292,7 +1292,7 @@ _div (csi_t *ctx)
 }
 
 static csi_status_t
-_dup (csi_t *ctx)
+_duplicate (csi_t *ctx)
 {
     check (1);
 
@@ -5741,7 +5741,7 @@ _defs[] = {
     { "device-to-user-distance", NULL },
     { "dict", _dict },
     { "div", _div },
-    { "dup", _dup },
+    { "dup", _duplicate },
     { "eq", _eq },
     { "exch", _exch },
     { "exec", NULL },
commit 3e7e0eacef650f1035cfbe15e306116ff711f99c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 29 17:07:41 2009 +0100

    Update reference images
    
    Refresh the test reference images to match the current output where
    acceptable.

diff --git a/test/Makefile.am b/test/Makefile.am
index 29c89a0..abd5138 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -216,6 +216,7 @@ REFERENCE_IMAGES = \
 	clip-push-group.ps3.rgb24.ref.png \
 	clip-push-group.quartz.ref.png \
 	clip-push-group.ref.png \
+	clip-push-group.svg.ref.png \
 	clip-push-group.xlib.ref.png \
 	clip-stroke.ref.png \
 	clip-stroke.xlib.ref.png \
@@ -240,6 +241,7 @@ REFERENCE_IMAGES = \
 	clipped-group.pdf.ref.png \
 	clipped-group.ps2.ref.png \
 	clipped-group.ps3.ref.png \
+	clipped-group.svg.ref.png \
 	clipped-group.ref.png \
 	clipped-surface.ref.png \
 	clipped-trapezoids.ref.png \
@@ -707,6 +709,8 @@ REFERENCE_IMAGES = \
 	overlapping-glyphs.rgb24.ref.png \
 	overlapping-glyphs.pdf.argb32.xfail.png \
 	overlapping-glyphs.pdf.rgb24.xfail.png \
+	overlapping-glyphs.svg.rgb24.ref.png \
+	overlapping-glyphs.svg.argb32.ref.png \
 	paint-repeat.ref.png \
 	paint-source-alpha.ref.png \
 	paint-source-alpha.svg.ref.png \
@@ -739,6 +743,7 @@ REFERENCE_IMAGES = \
 	radial-gradient.pdf.ref.png \
 	radial-gradient.quartz.ref.png \
 	radial-gradient.ref.png \
+	radial-gradient.svg.xfail.png \
 	random-intersections-eo.ps.ref.png \
 	random-intersections-eo.quartz.ref.png \
 	random-intersections-eo.ref.png \
@@ -782,18 +787,20 @@ REFERENCE_IMAGES = \
 	rotate-image-surface-paint.svg.ref.png \
 	scale-down-source-surface-paint.ref.png \
 	scale-offset-image.gl.ref.png \
-	scale-offset-image.pdf.xfail.png \
+	scale-offset-image.pdf.ref.png \
 	scale-offset-image.ps.ref.png \
 	scale-offset-image.ref.png \
 	scale-offset-image.xfail.png \
+	scale-offset-image.script.xfail.png \
 	scale-offset-image.xlib.xfail.png \
 	scale-offset-image.xlib-fallback.xfail.png \
 	scale-offset-similar.gl.ref.png \
-	scale-offset-similar.pdf.xfail.png \
+	scale-offset-similar.pdf.ref.png \
 	scale-offset-similar.ps.ref.png \
 	scale-offset-similar.ref.png \
 	scale-offset-similar.xfail.png \
 	scale-offset-similar.meta.xfail.png \
+	scale-offset-similar.script.xfail.png \
 	scale-offset-similar.xlib.xfail.png \
 	scale-offset-similar.xlib-fallback.xfail.png \
 	scale-source-surface-paint.ref.png \
@@ -838,11 +845,13 @@ REFERENCE_IMAGES = \
 	smask-text.ps3.ref.png \
 	smask-text.ref.png \
 	smask-text.svg.ref.png \
+	smask-text.script.ref.png \
 	smask-text.xlib.ref.png \
 	smask.pdf.xfail.png \
 	smask.ps.ref.png \
 	smask.ref.png \
 	smask.svg.ref.png \
+	smask.script.ref.png \
 	smask.xlib.ref.png \
 	solid-pattern-cache-stress.ref.png \
 	source-clip-scale.gl.ref.png \
@@ -973,6 +982,7 @@ REFERENCE_IMAGES = \
 	user-font-rescale.ps3.ref.png \
 	user-font-rescale.ref.png \
 	user-font-rescale.svg.ref.png \
+	user-font.pdf.ref.png \
 	user-font.ps.ref.png \
 	user-font.ref.png \
 	user-font.svg.ref.png \
diff --git a/test/clip-fill.xlib-fallback.ref.png b/test/clip-fill.xlib-fallback.ref.png
index 72dc229..064b0cf 100644
Binary files a/test/clip-fill.xlib-fallback.ref.png and b/test/clip-fill.xlib-fallback.ref.png differ
diff --git a/test/clip-operator.pdf.argb32.ref.png b/test/clip-operator.pdf.argb32.ref.png
index 4213499..89194d6 100644
Binary files a/test/clip-operator.pdf.argb32.ref.png and b/test/clip-operator.pdf.argb32.ref.png differ
diff --git a/test/clip-operator.pdf.rgb24.ref.png b/test/clip-operator.pdf.rgb24.ref.png
index 55e7893..649bb6f 100644
Binary files a/test/clip-operator.pdf.rgb24.ref.png and b/test/clip-operator.pdf.rgb24.ref.png differ
diff --git a/test/clip-operator.ref.png b/test/clip-operator.ref.png
index 7296a2d..8d3b34d 100644
Binary files a/test/clip-operator.ref.png and b/test/clip-operator.ref.png differ
diff --git a/test/clip-operator.rgb24.ref.png b/test/clip-operator.rgb24.ref.png
index 0a4d4c0..8c81e07 100644
Binary files a/test/clip-operator.rgb24.ref.png and b/test/clip-operator.rgb24.ref.png differ
diff --git a/test/clip-operator.svg12.argb32.xfail.png b/test/clip-operator.svg12.argb32.xfail.png
index 1c21d15..a1b8072 100644
Binary files a/test/clip-operator.svg12.argb32.xfail.png and b/test/clip-operator.svg12.argb32.xfail.png differ
diff --git a/test/clip-operator.svg12.rgb24.xfail.png b/test/clip-operator.svg12.rgb24.xfail.png
index f79de48..9522770 100644
Binary files a/test/clip-operator.svg12.rgb24.xfail.png and b/test/clip-operator.svg12.rgb24.xfail.png differ
diff --git a/test/clip-operator.xlib-fallback.ref.png b/test/clip-operator.xlib-fallback.ref.png
index be1cac0..9ef8637 100644
Binary files a/test/clip-operator.xlib-fallback.ref.png and b/test/clip-operator.xlib-fallback.ref.png differ
diff --git a/test/clip-push-group.svg.ref.png b/test/clip-push-group.svg.ref.png
new file mode 100644
index 0000000..291b473
Binary files /dev/null and b/test/clip-push-group.svg.ref.png differ
diff --git a/test/clip-stroke.ref.png b/test/clip-stroke.ref.png
index 5bd877d..7af2e9b 100644
Binary files a/test/clip-stroke.ref.png and b/test/clip-stroke.ref.png differ
diff --git a/test/clip-stroke.xlib-fallback.ref.png b/test/clip-stroke.xlib-fallback.ref.png
index ac9ad79..b776749 100644
Binary files a/test/clip-stroke.xlib-fallback.ref.png and b/test/clip-stroke.xlib-fallback.ref.png differ
diff --git a/test/clip-stroke.xlib.ref.png b/test/clip-stroke.xlib.ref.png
index 7636183..142dc79 100644
Binary files a/test/clip-stroke.xlib.ref.png and b/test/clip-stroke.xlib.ref.png differ
diff --git a/test/clip-text.svg.ref.png b/test/clip-text.svg.ref.png
index 8a2dc29..a113b14 100644
Binary files a/test/clip-text.svg.ref.png and b/test/clip-text.svg.ref.png differ
diff --git a/test/clipped-group.ref.png b/test/clipped-group.ref.png
index 9855e61..b25c9f4 100644
Binary files a/test/clipped-group.ref.png and b/test/clipped-group.ref.png differ
diff --git a/test/clipped-group.svg.ref.png b/test/clipped-group.svg.ref.png
new file mode 100644
index 0000000..196aec0
Binary files /dev/null and b/test/clipped-group.svg.ref.png differ
diff --git a/test/device-offset-fractional.pdf.xfail.png b/test/device-offset-fractional.pdf.xfail.png
index 6248b4a..50bbd34 100644
Binary files a/test/device-offset-fractional.pdf.xfail.png and b/test/device-offset-fractional.pdf.xfail.png differ
diff --git a/test/extended-blend-alpha.svg12.argb32.xfail.png b/test/extended-blend-alpha.svg12.argb32.xfail.png
index d09c168..cc34416 100644
Binary files a/test/extended-blend-alpha.svg12.argb32.xfail.png and b/test/extended-blend-alpha.svg12.argb32.xfail.png differ
diff --git a/test/filter-nearest-offset.pdf.xfail.png b/test/filter-nearest-offset.pdf.xfail.png
index 4d436aa..96207dc 100644
Binary files a/test/filter-nearest-offset.pdf.xfail.png and b/test/filter-nearest-offset.pdf.xfail.png differ
diff --git a/test/filter-nearest-transformed.pdf.xfail.png b/test/filter-nearest-transformed.pdf.xfail.png
index 5ad98a7..7259914 100644
Binary files a/test/filter-nearest-transformed.pdf.xfail.png and b/test/filter-nearest-transformed.pdf.xfail.png differ
diff --git a/test/ft-text-vertical-layout-type1.svg.ref.png b/test/ft-text-vertical-layout-type1.svg.ref.png
index 326a240..0be400c 100644
Binary files a/test/ft-text-vertical-layout-type1.svg.ref.png and b/test/ft-text-vertical-layout-type1.svg.ref.png differ
diff --git a/test/ft-text-vertical-layout-type1.xlib.ref.png b/test/ft-text-vertical-layout-type1.xlib.ref.png
index 44a1ec7..9eba6bb 100644
Binary files a/test/ft-text-vertical-layout-type1.xlib.ref.png and b/test/ft-text-vertical-layout-type1.xlib.ref.png differ
diff --git a/test/ft-text-vertical-layout-type3.svg.ref.png b/test/ft-text-vertical-layout-type3.svg.ref.png
index 985a8de..cddb955 100644
Binary files a/test/ft-text-vertical-layout-type3.svg.ref.png and b/test/ft-text-vertical-layout-type3.svg.ref.png differ
diff --git a/test/ft-text-vertical-layout-type3.xlib.ref.png b/test/ft-text-vertical-layout-type3.xlib.ref.png
index 7a7f68f..e57c083 100644
Binary files a/test/ft-text-vertical-layout-type3.xlib.ref.png and b/test/ft-text-vertical-layout-type3.xlib.ref.png differ
diff --git a/test/group-unaligned.svg.argb32.xfail.png b/test/group-unaligned.svg.argb32.xfail.png
index 3855037..01c34be 100644
Binary files a/test/group-unaligned.svg.argb32.xfail.png and b/test/group-unaligned.svg.argb32.xfail.png differ
diff --git a/test/leaky-dashed-rectangle.pdf.ref.png b/test/leaky-dashed-rectangle.pdf.ref.png
index c0ba7b2..cfccaea 100644
Binary files a/test/leaky-dashed-rectangle.pdf.ref.png and b/test/leaky-dashed-rectangle.pdf.ref.png differ
diff --git a/test/mask-glyphs.svg.ref.png b/test/mask-glyphs.svg.ref.png
index 5d524dd..bbc44f2 100644
Binary files a/test/mask-glyphs.svg.ref.png and b/test/mask-glyphs.svg.ref.png differ
diff --git a/test/mask.pdf.argb32.ref.png b/test/mask.pdf.argb32.ref.png
index 8c68355..39d2d2b 100644
Binary files a/test/mask.pdf.argb32.ref.png and b/test/mask.pdf.argb32.ref.png differ
diff --git a/test/mask.pdf.rgb24.ref.png b/test/mask.pdf.rgb24.ref.png
index be5dbfc..abcdb1e 100644
Binary files a/test/mask.pdf.rgb24.ref.png and b/test/mask.pdf.rgb24.ref.png differ
diff --git a/test/mask.svg.argb32.xfail.png b/test/mask.svg.argb32.xfail.png
index 30c8db0..8672480 100644
Binary files a/test/mask.svg.argb32.xfail.png and b/test/mask.svg.argb32.xfail.png differ
diff --git a/test/mask.svg.rgb24.xfail.png b/test/mask.svg.rgb24.xfail.png
index 0203b52..743a758 100644
Binary files a/test/mask.svg.rgb24.xfail.png and b/test/mask.svg.rgb24.xfail.png differ
diff --git a/test/operator-source.svg12.argb32.xfail.png b/test/operator-source.svg12.argb32.xfail.png
index 722e5ac..ccf4315 100644
Binary files a/test/operator-source.svg12.argb32.xfail.png and b/test/operator-source.svg12.argb32.xfail.png differ
diff --git a/test/operator-source.svg12.rgb24.xfail.png b/test/operator-source.svg12.rgb24.xfail.png
index 5f445fc..827521b 100644
Binary files a/test/operator-source.svg12.rgb24.xfail.png and b/test/operator-source.svg12.rgb24.xfail.png differ
diff --git a/test/overlapping-glyphs.svg.argb32.ref.png b/test/overlapping-glyphs.svg.argb32.ref.png
new file mode 100644
index 0000000..ce38499
Binary files /dev/null and b/test/overlapping-glyphs.svg.argb32.ref.png differ
diff --git a/test/overlapping-glyphs.svg.rgb24.ref.png b/test/overlapping-glyphs.svg.rgb24.ref.png
new file mode 100644
index 0000000..ce38499
Binary files /dev/null and b/test/overlapping-glyphs.svg.rgb24.ref.png differ
diff --git a/test/radial-gradient.pdf.ref.png b/test/radial-gradient.pdf.ref.png
index 5bbd733..1c288b4 100644
Binary files a/test/radial-gradient.pdf.ref.png and b/test/radial-gradient.pdf.ref.png differ
diff --git a/test/radial-gradient.svg.xfail.png b/test/radial-gradient.svg.xfail.png
new file mode 100644
index 0000000..ab54a3d
Binary files /dev/null and b/test/radial-gradient.svg.xfail.png differ
diff --git a/test/random-intersections-curves-eo.ref.png b/test/random-intersections-curves-eo.ref.png
index ed24e8a..4456730 100644
Binary files a/test/random-intersections-curves-eo.ref.png and b/test/random-intersections-curves-eo.ref.png differ
diff --git a/test/random-intersections-curves-eo.xlib-fallback.ref.png b/test/random-intersections-curves-eo.xlib-fallback.ref.png
index d5f48f1..d91af0b 100644
Binary files a/test/random-intersections-curves-eo.xlib-fallback.ref.png and b/test/random-intersections-curves-eo.xlib-fallback.ref.png differ
diff --git a/test/random-intersections-curves-eo.xlib.ref.png b/test/random-intersections-curves-eo.xlib.ref.png
index 05fc922..d91af0b 100644
Binary files a/test/random-intersections-curves-eo.xlib.ref.png and b/test/random-intersections-curves-eo.xlib.ref.png differ
diff --git a/test/random-intersections-curves-nz.ref.png b/test/random-intersections-curves-nz.ref.png
index 3b36e5e..6f29ab9 100644
Binary files a/test/random-intersections-curves-nz.ref.png and b/test/random-intersections-curves-nz.ref.png differ
diff --git a/test/random-intersections-curves-nz.xlib-fallback.ref.png b/test/random-intersections-curves-nz.xlib-fallback.ref.png
index 499eb9e..673333d 100644
Binary files a/test/random-intersections-curves-nz.xlib-fallback.ref.png and b/test/random-intersections-curves-nz.xlib-fallback.ref.png differ
diff --git a/test/random-intersections-curves-nz.xlib.ref.png b/test/random-intersections-curves-nz.xlib.ref.png
index 729bf65..673333d 100644
Binary files a/test/random-intersections-curves-nz.xlib.ref.png and b/test/random-intersections-curves-nz.xlib.ref.png differ
diff --git a/test/scale-offset-image.pdf.ref.png b/test/scale-offset-image.pdf.ref.png
new file mode 100644
index 0000000..2de2d34
Binary files /dev/null and b/test/scale-offset-image.pdf.ref.png differ
diff --git a/test/scale-offset-image.pdf.xfail.png b/test/scale-offset-image.pdf.xfail.png
deleted file mode 100644
index 76e6fb7..0000000
Binary files a/test/scale-offset-image.pdf.xfail.png and /dev/null differ
diff --git a/test/scale-offset-image.script.xfail.png b/test/scale-offset-image.script.xfail.png
new file mode 100644
index 0000000..b89bb66
Binary files /dev/null and b/test/scale-offset-image.script.xfail.png differ
diff --git a/test/scale-offset-similar.pdf.ref.png b/test/scale-offset-similar.pdf.ref.png
new file mode 100644
index 0000000..94f1b68
Binary files /dev/null and b/test/scale-offset-similar.pdf.ref.png differ
diff --git a/test/scale-offset-similar.pdf.xfail.png b/test/scale-offset-similar.pdf.xfail.png
deleted file mode 100644
index 38b9a20..0000000
Binary files a/test/scale-offset-similar.pdf.xfail.png and /dev/null differ
diff --git a/test/scale-offset-similar.script.xfail.png b/test/scale-offset-similar.script.xfail.png
new file mode 100644
index 0000000..b89bb66
Binary files /dev/null and b/test/scale-offset-similar.script.xfail.png differ
diff --git a/test/self-intersecting.ref.png b/test/self-intersecting.ref.png
index b2f4259..32d1439 100644
Binary files a/test/self-intersecting.ref.png and b/test/self-intersecting.ref.png differ
diff --git a/test/smask-text.script.ref.png b/test/smask-text.script.ref.png
new file mode 100644
index 0000000..62b2de5
Binary files /dev/null and b/test/smask-text.script.ref.png differ
diff --git a/test/smask.pdf.xfail.png b/test/smask.pdf.xfail.png
index 1405cc0..f8b559c 100644
Binary files a/test/smask.pdf.xfail.png and b/test/smask.pdf.xfail.png differ
diff --git a/test/smask.script.ref.png b/test/smask.script.ref.png
new file mode 100644
index 0000000..3b672d6
Binary files /dev/null and b/test/smask.script.ref.png differ
diff --git a/test/text-pattern.pdf.argb32.ref.png b/test/text-pattern.pdf.argb32.ref.png
index 0c06629..37dd6c8 100644
Binary files a/test/text-pattern.pdf.argb32.ref.png and b/test/text-pattern.pdf.argb32.ref.png differ
diff --git a/test/text-pattern.pdf.rgb24.ref.png b/test/text-pattern.pdf.rgb24.ref.png
index b81de88..e80f611 100644
Binary files a/test/text-pattern.pdf.rgb24.ref.png and b/test/text-pattern.pdf.rgb24.ref.png differ
diff --git a/test/unbounded-operator.svg12.rgb24.xfail.png b/test/unbounded-operator.svg12.rgb24.xfail.png
index c369fd2..828a9db 100644
Binary files a/test/unbounded-operator.svg12.rgb24.xfail.png and b/test/unbounded-operator.svg12.rgb24.xfail.png differ
diff --git a/test/user-font-proxy.svg.ref.png b/test/user-font-proxy.svg.ref.png
index 747750a..6c45848 100644
Binary files a/test/user-font-proxy.svg.ref.png and b/test/user-font-proxy.svg.ref.png differ
diff --git a/test/user-font.pdf.ref.png b/test/user-font.pdf.ref.png
new file mode 100644
index 0000000..de86407
Binary files /dev/null and b/test/user-font.pdf.ref.png differ
commit 155e10e632cb647df5c3d54a75975bc16591287d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 29 08:04:33 2009 +0100

    [script] Fix use of freed list
    
    A typo, I missed converting the user over to the freshly sorted list,
    leaving it iterating over original but checking the sorted for termination
    conditions.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 4a89cf0..b767a0c 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -2003,7 +2003,7 @@ inactive (cairo_script_surface_t *surface)
 	cairo_list_t *operand;
 	int depth;
 
-	df = cairo_list_first_entry (&ctx->deferred,
+	df = cairo_list_first_entry (&sorted,
 				     struct deferred_finish,
 				     link);
 
commit 5393aa6d6c4676f20d316f3cd0a18bb497574e50
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 29 08:02:52 2009 +0100

    [path] Return the canonical box.
    
    When returning the single box that represents a path, always return it
    consistently wound.

diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 53350e8..c2deadc 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -247,22 +247,6 @@ _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
     assert (! path->is_empty_fill);
 
     if (_cairo_path_fixed_is_box (path, &box)) {
-	if (box.p1.x > box.p2.x) {
-	    cairo_fixed_t t;
-
-	    t = box.p1.x;
-	    box.p1.x = box.p2.x;
-	    box.p2.x = t;
-	}
-
-	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;
-	}
-
 	rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x);
 	rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y);
 	rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) -
@@ -375,22 +359,6 @@ _cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
     traps->is_rectangular = TRUE;
 
     if (_cairo_path_fixed_is_box (path, &box)) {
-	if (box.p1.x > box.p2.x) {
-	    cairo_fixed_t t;
-
-	    t = box.p1.x;
-	    box.p1.x = box.p2.x;
-	    box.p2.x = t;
-	}
-
-	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;
-	}
-
 	return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2);
     } else {
 	cairo_path_fixed_iter_t iter;
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index a1a8184..4a2ce01 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -1100,6 +1100,28 @@ _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t		*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.
  */
@@ -1152,8 +1174,7 @@ _cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
 	buf->points[2].y == buf->points[3].y &&
 	buf->points[3].x == buf->points[0].x)
     {
-	box->p1 = buf->points[0];
-	box->p2 = buf->points[2];
+	_canonical_box (box, &buf->points[0], &buf->points[2]);
 	return TRUE;
     }
 
@@ -1162,8 +1183,7 @@ _cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
 	buf->points[2].x == buf->points[3].x &&
 	buf->points[3].y == buf->points[0].y)
     {
-	box->p1 = buf->points[0];
-	box->p2 = buf->points[2];
+	_canonical_box (box, &buf->points[0], &buf->points[2]);
 	return TRUE;
     }
 
commit afea5eb79d2159fe9a5dc1a1a7b9445e40fbb474
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 28 17:10:35 2009 +0100

    [scaled-font] Fix implementation-face refleak
    
    If we found the font via the holdover cache, or if we returned due to an
    error, we would leak a reference count on the implementaton face.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 812a808..74a2c88 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -950,7 +950,6 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
 
 	_cairo_scaled_font_init_key (&key, font_face,
 				     font_matrix, ctm, options);
-
     }
     else
     {
@@ -1018,6 +1017,8 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
 		_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;
 	    }
@@ -1035,12 +1036,18 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
     /* 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);
+
 	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);
+
 	return scaled_font;
     }
 
commit e5d44937f34d35606ad7f07415be7331cf146567
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 28 17:09:54 2009 +0100

    [ft] Improve error path handling.
    
    Specifically check for an error during resolving the font and do not cache
    the error object.

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 26f4a83..036bfa0 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2234,12 +2234,6 @@ _cairo_ft_font_face_destroy (void *abstract_face)
 {
     cairo_ft_font_face_t *font_face = abstract_face;
 
-    cairo_ft_font_face_t *tmp_face = NULL;
-    cairo_ft_font_face_t *last_face = NULL;
-
-    if (font_face == NULL)
-	return;
-
     /* 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
@@ -2270,6 +2264,9 @@ _cairo_ft_font_face_destroy (void *abstract_face)
     }
 
     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;
@@ -2334,12 +2331,15 @@ _cairo_ft_font_face_get_implementation (void                     *abstract_face,
 		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 ();
commit e76856e6ee563affc0a273ed6eef865866802dec
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 28 15:51:33 2009 +0100

    [scaled-font] Refleak on error path.
    
    Perform the destroy of the local font before returning along the error
    path.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 9de92e0..812a808 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -1063,6 +1063,10 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
 
     _cairo_scaled_font_map_unlock ();
 
+    cairo_scaled_font_destroy (old);
+    if (font_face != original_font_face)
+	cairo_font_face_destroy (font_face);
+
     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
@@ -1072,11 +1076,6 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
 	return _cairo_scaled_font_create_in_error (status);
     }
 
-    cairo_scaled_font_destroy (old);
-
-    if (font_face != original_font_face)
-	cairo_font_face_destroy (font_face);
-
     return scaled_font;
 }
 slim_hidden_def (cairo_scaled_font_create);
commit d7d6f75ed26bc3ea040c618442ba71c89633ac7b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 28 15:50:13 2009 +0100

    [clip] Fix refleak of previous clipping surfaces.
    
    When combining previous clip masks, we leaked a referenced due to not
    destroying the returned reference.

diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 543f8ba..7f2c8be 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -1109,9 +1109,12 @@ _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
 	    prev = prev->prev;
 	    goto NEXT_PATH;
 	} else {
-	    _cairo_pattern_init_for_surface (&pattern.surface,
-					     _cairo_clip_path_get_surface (prev,
-									   target));
+	    cairo_surface_t *prev_surface;
+
+	    prev_surface = _cairo_clip_path_get_surface (prev, target);
+	    _cairo_pattern_init_for_surface (&pattern.surface, prev_surface);
+	    cairo_surface_destroy (prev_surface);
+
 	    cairo_matrix_init_translate (&pattern.base.matrix,
 					 -prev->extents.x + clip_extents->x,
 					 -prev->extents.y + clip_extents->y);
commit 52e5ce3a36ab1cd7da25056df7f12ac0b23e410d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 27 17:25:08 2009 +0100

    [test] Add clip-stroke
    
    Soeren was the first to report a clipping regression in the xlib backend
    with strokes, and provided a test case to exercise the bug. This is an
    extension of his test to provide coverage of different clipping and
    stroking methods.

diff --git a/test/Makefile.am b/test/Makefile.am
index eab26f0..29c89a0 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -217,6 +217,9 @@ REFERENCE_IMAGES = \
 	clip-push-group.quartz.ref.png \
 	clip-push-group.ref.png \
 	clip-push-group.xlib.ref.png \
+	clip-stroke.ref.png \
+	clip-stroke.xlib.ref.png \
+	clip-stroke.xlib-fallback.ref.png \
 	clip-text.ref.png \
 	clip-text.ps.xfail.png \
 	clip-text.svg.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index e99ab0e..7b51396 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -21,6 +21,7 @@ test_sources = \
 	clip-nesting.c					\
 	clip-operator.c					\
 	clip-push-group.c				\
+	clip-stroke.c					\
 	clip-text.c					\
 	clip-twice.c					\
 	clip-unbounded.c				\
diff --git a/test/clip-stroke.c b/test/clip-stroke.c
new file mode 100644
index 0000000..9714dd0
--- /dev/null
+++ b/test/clip-stroke.c
@@ -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 at 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-stroke.ref.png b/test/clip-stroke.ref.png
new file mode 100644
index 0000000..5bd877d
Binary files /dev/null and b/test/clip-stroke.ref.png differ
diff --git a/test/clip-stroke.xlib-fallback.ref.png b/test/clip-stroke.xlib-fallback.ref.png
new file mode 100644
index 0000000..ac9ad79
Binary files /dev/null and b/test/clip-stroke.xlib-fallback.ref.png differ
diff --git a/test/clip-stroke.xlib.ref.png b/test/clip-stroke.xlib.ref.png
new file mode 100644
index 0000000..7636183
Binary files /dev/null and b/test/clip-stroke.xlib.ref.png differ
commit 20cdb99ae8ae0cc02193468e811b1b74b3f6d3b7
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 27 16:21:30 2009 +0100

    [path-fixed] Distinguish cw and ccw boxes
    
    To correctly handle retessellating trapezods constructed from alternately
    wound boxes, then we need to pass that information from the path to the
    tessellator. We do this by switching the direction of the box if the first
    edge is horizontal as opposed to vertical.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 7b2093f..a1a8184 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -1300,8 +1300,8 @@ _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter,
 	points[2].x == points[3].x &&
 	points[3].y == points[0].y)
     {
-	box->p1 = points[0];
-	box->p2 = points[2];
+	box->p1 = points[1];
+	box->p2 = points[3];
 	*_iter = iter;
 	return TRUE;
     }
commit 219d46a9ebc47f99fa8fe9c6e3f0aa440309f032
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 27 14:45:24 2009 +0100

    [scaled-font] Eliminate intermediate path when tracing glyphs
    
    Currently the tracing code for glyphs constructs an temporary path in
    order to replay and append to the output. This temporary allocation is
    extremely wasteful as we can just directly append the glyph path to
    the output path.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 9280f72..9de92e0 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -2208,13 +2208,13 @@ CLEANUP_MASK:
 
 /* Add a single-device-unit rectangle to a path. */
 static cairo_status_t
-_add_unit_rectangle_to_path (cairo_path_fixed_t *path, int x, int y)
+_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,
-					_cairo_fixed_from_int (x),
-					_cairo_fixed_from_int (y));
+    status = _cairo_path_fixed_move_to (path, x, y);
     if (unlikely (status))
 	return status;
 
@@ -2236,11 +2236,7 @@ _add_unit_rectangle_to_path (cairo_path_fixed_t *path, int x, int y)
     if (unlikely (status))
 	return status;
 
-    status = _cairo_path_fixed_close_path (path);
-    if (unlikely (status))
-	return status;
-
-    return CAIRO_STATUS_SUCCESS;
+    return _cairo_path_fixed_close_path (path);
 }
 
 /**
@@ -2262,12 +2258,15 @@ _add_unit_rectangle_to_path (cairo_path_fixed_t *path, int x, int y)
  **/
 static cairo_status_t
 _trace_mask_to_path (cairo_image_surface_t *mask,
-		     cairo_path_fixed_t *path)
+		     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 (mask, CAIRO_FORMAT_A1);
@@ -2276,12 +2275,15 @@ _trace_mask_to_path (cairo_image_surface_t *mask,
 	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) {
@@ -2292,8 +2294,10 @@ _trace_mask_to_path (cairo_image_surface_t *mask,
 	    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,
-							  x - xoff, y - yoff);
+							  px + x0,
+							  py + y0);
 		    if (unlikely (status))
 			goto BAIL;
 		}
@@ -2315,8 +2319,6 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
 {
     cairo_status_t status;
     int	i;
-    cairo_path_fixed_t glyph_path_static;
-    cairo_path_fixed_t *glyph_path;
 
     status = scaled_font->status;
     if (unlikely (status))
@@ -2331,7 +2333,11 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
 					     CAIRO_SCALED_GLYPH_INFO_PATH,
 					     &scaled_glyph);
 	if (status == CAIRO_STATUS_SUCCESS) {
-	    glyph_path = scaled_glyph->path;
+	    status = _cairo_path_fixed_append (path,
+					       scaled_glyph->path, CAIRO_DIRECTION_FORWARD,
+					       _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.
@@ -2343,23 +2349,10 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
 	    if (unlikely (status))
 		goto BAIL;
 
-	    _cairo_path_fixed_init (&glyph_path_static);
-	    glyph_path = &glyph_path_static;
-	    status = _trace_mask_to_path (scaled_glyph->surface, glyph_path);
-	    if (unlikely (status))
-		goto BAIL;
-	} else {
-	    goto BAIL;
+	    status = _trace_mask_to_path (scaled_glyph->surface, path,
+					  glyphs[i].x, glyphs[i].y);
 	}
 
-	status = _cairo_path_fixed_append (path,
-					   glyph_path, CAIRO_DIRECTION_FORWARD,
-					   _cairo_fixed_from_double (glyphs[i].x),
-					   _cairo_fixed_from_double (glyphs[i].y));
-
-	if (glyph_path != scaled_glyph->path)
-	    _cairo_path_fixed_fini (glyph_path);
-
 	if (unlikely (status))
 	    goto BAIL;
     }
commit 2e05922737d63289a3f124699359b8d385315cbd
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 27 13:58:43 2009 +0100

    [stroke] Handle degenerate stroke extents
    
    If the stroke is degenerate, i.e. the path consists only of a single
    move-to and no edges, then the stroke may be visible due to end-capping
    (as opposed to fills which are empty). So we also need to pad out the
    extents around the current point for the degenerate case.

diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c
index 1a3cdf0..d3bc449 100644
--- a/src/cairo-path-bounds.c
+++ b/src/cairo-path-bounds.c
@@ -52,13 +52,6 @@ _cairo_path_bounder_init (cairo_path_bounder_t *bounder)
 }
 
 static void
-_cairo_path_bounder_fini (cairo_path_bounder_t *bounder)
-{
-    bounder->has_initial_point = FALSE;
-    bounder->has_point = FALSE;
-}
-
-static void
 _cairo_path_bounder_add_point (cairo_path_bounder_t *bounder,
 			       const cairo_point_t *point)
 {
@@ -79,7 +72,6 @@ _cairo_path_bounder_add_point (cairo_path_bounder_t *bounder,
 	bounder->extents.p1.y = point->y;
 	bounder->extents.p2.x = point->x;
 	bounder->extents.p2.y = point->y;
-
 	bounder->has_point = TRUE;
     }
 }
@@ -195,8 +187,6 @@ _cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path,
 	extents->x = extents->y = 0;
 	extents->width = extents->height = 0;
     }
-
-    _cairo_path_bounder_fini (&bounder);
 }
 
 /* A slightly better approximation than above - we actually decompose the
@@ -225,8 +215,6 @@ _cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path,
 	extents->x = extents->y = 0;
 	extents->width = extents->height = 0;
     }
-
-    _cairo_path_bounder_fini (&bounder);
 }
 
 void
@@ -253,8 +241,6 @@ _cairo_path_fixed_fill_extents (const cairo_path_fixed_t	*path,
 	extents->x = extents->y = 0;
 	extents->width = extents->height = 0;
     }
-
-    _cairo_path_bounder_fini (&bounder);
 }
 
 /* Adjusts the fill extents (above) by the device-space pen.  */
@@ -288,12 +274,23 @@ _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
 	bounder.extents.p2.y += _cairo_fixed_from_double (dy);
 
 	_cairo_box_round_to_rectangle (&bounder.extents, extents);
+    } else if (bounder.has_initial_point) {
+	double dx, dy;
+
+	/* accommodate capping of degenerate paths */
+
+	_cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);
+
+	bounder.extents.p1.x = bounder.current_point.x - _cairo_fixed_from_double (dx);
+	bounder.extents.p2.x = bounder.current_point.x + _cairo_fixed_from_double (dx);
+	bounder.extents.p1.y = bounder.current_point.y - _cairo_fixed_from_double (dy);
+	bounder.extents.p2.y = bounder.current_point.y + _cairo_fixed_from_double (dy);
+
+	_cairo_box_round_to_rectangle (&bounder.extents, extents);
     } else {
 	extents->x = extents->y = 0;
 	extents->width = extents->height = 0;
     }
-
-    _cairo_path_bounder_fini (&bounder);
 }
 
 cairo_status_t
@@ -354,6 +351,4 @@ _cairo_path_fixed_bounds (const cairo_path_fixed_t *path,
 	*x2 = 0.0;
 	*y2 = 0.0;
     }
-
-    _cairo_path_bounder_fini (&bounder);
 }
commit cfd78393f357bc69233d4d00d0fb3a2ff736f1a7
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 26 23:32:34 2009 +0100

    [path] Handle the implicit close for path_fixed_is_box()
    
    _cairo_path_fixed_is_box() is only called for filled paths and so must
    handle the implicit close (which was already being correctly handled by
    _cairo_path_fixed_iter_is_box).

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 9950368..7b2093f 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -1113,7 +1113,7 @@ _cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
 	return FALSE;
 
     /* Do we have the right number of ops? */
-    if (buf->num_ops != 5 && buf->num_ops != 6)
+    if (buf->num_ops < 4 || buf->num_ops > 6)
 	return FALSE;
 
     /* Check whether the ops are those that would be used for a rectangle */
@@ -1125,22 +1125,25 @@ _cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
 	return FALSE;
     }
 
-    /* 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)
+    /* 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;
-    } 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;
+	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 */
commit f22045bb4b9e700ce223c259ad41403dc7efe81f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 26 23:30:02 2009 +0100

    [fallback] Include implicit closes in the check for rectilinear paths
    
    Fixes test/implicit-close
    
    By forgetting the implicit-close when checking for rectilinear paths, we
    tried to feed the triangle (and other diagclose) into the specialised
    rectilinear tesselators which completely mishandled that final edge.

diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
index f25a45e..ce35c9e 100644
--- a/src/cairo-path-fixed-private.h
+++ b/src/cairo-path-fixed-private.h
@@ -136,6 +136,20 @@ _cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path)
 }
 
 static inline cairo_bool_t
+_cairo_path_fixed_is_rectilinear_fill (const cairo_path_fixed_t *path)
+{
+    if (! path->is_rectilinear)
+	return FALSE;
+
+    if (! path->has_current_point)
+	return TRUE;
+
+    /* 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_maybe_fill_region (const cairo_path_fixed_t *path)
 {
 #if WATCH_PATH
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index a5a529c..8fa7fa9 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -1233,6 +1233,7 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
     int num_boxes = ARRAY_LENGTH (boxes_stack);
     cairo_rectangle_int_t extents;
     cairo_bool_t is_bounded;
+    cairo_bool_t is_rectilinear;
     cairo_status_t status;
 
     is_bounded = _cairo_surface_get_extents (surface, &extents);
@@ -1286,7 +1287,8 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
     if (path->is_empty_fill)
 	goto DO_TRAPS;
 
-    if (path->is_rectilinear) {
+    is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path);
+    if (is_rectilinear) {
 	status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
 							      fill_rule,
 							      &traps);
@@ -1312,7 +1314,7 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
 	    goto CLEANUP;
     }
 
-    if (path->is_rectilinear) {
+    if (is_rectilinear) {
 	status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
 									&polygon,
 									fill_rule);
commit 92f6f275fcb5407baf908485ffd08b6787b2caf9
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 26 23:27:46 2009 +0100

    [test] Implicit close
    
    This is a simple test that broke with the determination of rectilinearity
    during path construction. I forgot the implicit close on fill and so the
    ignored the final diagonal edge and failed to draw the triangle.

diff --git a/test/Makefile.am b/test/Makefile.am
index 779f95a..eab26f0 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -527,6 +527,7 @@ REFERENCE_IMAGES = \
 	image-surface-source.ref.png \
 	image-surface-source.svg12.argb32.xfail.png \
 	image-surface-source.svg12.rgb24.xfail.png \
+	implicit-close.ref.png \
 	infinite-join.ps2.ref.png \
 	infinite-join.ps3.ref.png \
 	infinite-join.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index 5494fe9..e99ab0e 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -96,6 +96,7 @@ test_sources = \
 	huge-linear.c					\
 	huge-radial.c					\
 	image-surface-source.c				\
+	implicit-close.c				\
 	infinite-join.c					\
 	in-fill-empty-trapezoid.c			\
 	in-fill-trapezoid.c				\
diff --git a/test/implicit-close.c b/test/implicit-close.c
new file mode 100644
index 0000000..2529bb0
--- /dev/null
+++ b/test/implicit-close.c
@@ -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 at 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/implicit-close.ref.png b/test/implicit-close.ref.png
new file mode 100644
index 0000000..f15f8a3
Binary files /dev/null and b/test/implicit-close.ref.png differ
commit 8078cd194e95a10cf653c970d1ddd39049a511f2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 23 15:25:42 2009 +0100

    [boilerplate] Runtime library check
    
    For the purposes of benchmarking it is useful to run cairo-perf against a
    different library from the one it was compiled against. In order to do so,
    we need to check that the runtime library contains the required entry
    points for our targets - which we can check by using dlsym.

diff --git a/boilerplate/Makefile.am b/boilerplate/Makefile.am
index cb92c2b..10fb6af 100644
--- a/boilerplate/Makefile.am
+++ b/boilerplate/Makefile.am
@@ -23,6 +23,9 @@ libcairoboilerplate_la_SOURCES = \
 	cairo-boilerplate-constructors.c \
 	$(NULL)
 libcairoboilerplate_la_LIBADD = $(top_builddir)/src/libcairo.la
+if CAIRO_HAS_DL
+libcairoboilerplate_la_LIBADD += -ldl
+endif
 
 if CAIRO_HAS_BEOS_SURFACE
 # BeOS system headers trigger this warning
diff --git a/boilerplate/cairo-boilerplate-directfb.c b/boilerplate/cairo-boilerplate-directfb.c
index f7be030..6815dd1 100644
--- a/boilerplate/cairo-boilerplate-directfb.c
+++ b/boilerplate/cairo-boilerplate-directfb.c
@@ -211,6 +211,7 @@ 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,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -220,6 +221,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"directfb-bitmap", "directfb", NULL, NULL,
 	CAIRO_SURFACE_TYPE_DIRECTFB, CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"cairo_directfb_surface_create",
 	_cairo_boilerplate_directfb_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-drm.c b/boilerplate/cairo-boilerplate-drm.c
index 23e601e..34d67b5 100644
--- a/boilerplate/cairo-boilerplate-drm.c
+++ b/boilerplate/cairo-boilerplate-drm.c
@@ -70,6 +70,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"drm", "drm", NULL, NULL,
 	CAIRO_SURFACE_TYPE_DRM, CAIRO_CONTENT_COLOR_ALPHA, 1,
+	"cairo_drm_surface_create",
 	_cairo_boilerplate_drm_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -80,6 +81,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"drm", "drm", NULL, NULL,
 	CAIRO_SURFACE_TYPE_DRM, CAIRO_CONTENT_COLOR, 1,
+	"cairo_drm_surface_create",
 	_cairo_boilerplate_drm_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-gl.c b/boilerplate/cairo-boilerplate-gl.c
index 940fd75..0facc5c 100644
--- a/boilerplate/cairo-boilerplate-gl.c
+++ b/boilerplate/cairo-boilerplate-gl.c
@@ -141,6 +141,7 @@ 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,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -151,6 +152,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"gl", "gl", NULL, NULL,
 	CAIRO_SURFACE_TYPE_GL, CAIRO_CONTENT_COLOR, 1,
+	"cairo_gl_surface_create",
 	_cairo_boilerplate_gl_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-glitz-agl.c b/boilerplate/cairo-boilerplate-glitz-agl.c
index 61c4352..679593b 100644
--- a/boilerplate/cairo-boilerplate-glitz-agl.c
+++ b/boilerplate/cairo-boilerplate-glitz-agl.c
@@ -169,6 +169,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"glitz-agl", "glitz", NULL, NULL,
 	CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"cairo_glitz_surface_create",
 	_cairo_boilerplate_glitz_agl_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -178,6 +179,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"glitz-agl", "glitz", NULL, NULL,
 	CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR, 0,
+	"cairo_glitz_surface_create",
 	_cairo_boilerplate_glitz_agl_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-glitz-glx.c b/boilerplate/cairo-boilerplate-glitz-glx.c
index 6e3cfec..d490aec 100644
--- a/boilerplate/cairo-boilerplate-glitz-glx.c
+++ b/boilerplate/cairo-boilerplate-glitz-glx.c
@@ -243,6 +243,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"glitz-glx", "glitz", NULL, NULL,
 	CAIRO_SURFACE_TYPE_GLITZ,CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"cairo_glitz_surface_create",
 	_cairo_boilerplate_glitz_glx_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -252,6 +253,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"glitz-glx", "glitz", NULL, NULL,
 	CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR, 0,
+	"cairo_glitz_surface_create",
 	_cairo_boilerplate_glitz_glx_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-glitz-wgl.c b/boilerplate/cairo-boilerplate-glitz-wgl.c
index 3aeefdd..f50d68b 100644
--- a/boilerplate/cairo-boilerplate-glitz-wgl.c
+++ b/boilerplate/cairo-boilerplate-glitz-wgl.c
@@ -166,6 +166,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"glitz-wgl", "glitz", NULL, NULL,
 	CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"cairo_glitz_surface_create",
 	_cairo_boilerplate_glitz_wgl_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -175,6 +176,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"glitz-wgl", "glitz", NULL, NULL,
 	CAIRO_SURFACE_TYPE_GLITZ, CAIRO_CONTENT_COLOR, 0,
+	"cairo_glitz_surface_create",
 	_cairo_boilerplate_glitz_wgl_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-pdf.c b/boilerplate/cairo-boilerplate-pdf.c
index 838038a..591e16f 100644
--- a/boilerplate/cairo-boilerplate-pdf.c
+++ b/boilerplate/cairo-boilerplate-pdf.c
@@ -229,6 +229,7 @@ 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_boilerplate_pdf_force_fallbacks,
 	_cairo_boilerplate_pdf_finish_surface,
@@ -240,6 +241,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"pdf", "pdf", ".pdf", NULL,
 	CAIRO_SURFACE_TYPE_META, CAIRO_CONTENT_COLOR, 0,
+	"cairo_pdf_surface_create",
 	_cairo_boilerplate_pdf_create_surface,
 	_cairo_boilerplate_pdf_force_fallbacks,
 	_cairo_boilerplate_pdf_finish_surface,
diff --git a/boilerplate/cairo-boilerplate-ps.c b/boilerplate/cairo-boilerplate-ps.c
index 8619e47..53fecd6 100644
--- a/boilerplate/cairo-boilerplate-ps.c
+++ b/boilerplate/cairo-boilerplate-ps.c
@@ -294,6 +294,7 @@ 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_boilerplate_ps_force_fallbacks,
 	_cairo_boilerplate_ps_finish_surface,
@@ -305,6 +306,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"ps2", "ps", ".ps", NULL,
 	CAIRO_SURFACE_TYPE_META, CAIRO_CONTENT_COLOR, 0,
+	"cairo_ps_surface_create",
 	_cairo_boilerplate_ps2_create_surface,
 	_cairo_boilerplate_ps_force_fallbacks,
 	_cairo_boilerplate_ps_finish_surface,
@@ -317,6 +319,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	"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_boilerplate_ps_force_fallbacks,
 	_cairo_boilerplate_ps_finish_surface,
@@ -328,6 +331,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"ps3", "ps", ".ps", NULL,
 	CAIRO_SURFACE_TYPE_META, CAIRO_CONTENT_COLOR, 0,
+	"cairo_ps_surface_create",
 	_cairo_boilerplate_ps3_create_surface,
 	_cairo_boilerplate_ps_force_fallbacks,
 	_cairo_boilerplate_ps_finish_surface,
diff --git a/boilerplate/cairo-boilerplate-quartz.c b/boilerplate/cairo-boilerplate-quartz.c
index 4c62358..1948d83 100644
--- a/boilerplate/cairo-boilerplate-quartz.c
+++ b/boilerplate/cairo-boilerplate-quartz.c
@@ -52,6 +52,7 @@ 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,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -60,6 +61,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"quartz", "quartz", NULL, NULL,
 	CAIRO_SURFACE_TYPE_QUARTZ, CAIRO_CONTENT_COLOR, 0,
+	"cairo_quartz_surface_create",
 	_cairo_boilerplate_quartz_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-script.c b/boilerplate/cairo-boilerplate-script.c
index d1d76b5..e37fdf1 100644
--- a/boilerplate/cairo-boilerplate-script.c
+++ b/boilerplate/cairo-boilerplate-script.c
@@ -129,6 +129,7 @@ _cairo_boilerplate_script_cleanup (void *closure)
 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,
     NULL,
     _cairo_boilerplate_script_finish_surface,
diff --git a/boilerplate/cairo-boilerplate-skia.c b/boilerplate/cairo-boilerplate-skia.c
index e636420..13ab00b 100644
--- a/boilerplate/cairo-boilerplate-skia.c
+++ b/boilerplate/cairo-boilerplate-skia.c
@@ -33,6 +33,7 @@ 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,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -41,6 +42,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"skia", "skia", NULL, NULL,
 	CAIRO_SURFACE_TYPE_SKIA, CAIRO_CONTENT_COLOR, 0,
+	"cairo_skia_surface_create",
 	_cairo_boilerplate_skia_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-svg.c b/boilerplate/cairo-boilerplate-svg.c
index 56296f4..a8a4d8b 100644
--- a/boilerplate/cairo-boilerplate-svg.c
+++ b/boilerplate/cairo-boilerplate-svg.c
@@ -274,6 +274,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"svg11", "svg", NULL, NULL,
 	CAIRO_SURFACE_TYPE_SVG, CAIRO_CONTENT_COLOR_ALPHA, 1,
+	"cairo_svg_surface_create",
 	_cairo_boilerplate_svg11_create_surface,
 	_cairo_boilerplate_svg_force_fallbacks,
 	_cairo_boilerplate_svg_finish_surface,
@@ -285,6 +286,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"svg11", "svg", NULL, NULL,
 	CAIRO_SURFACE_TYPE_META, CAIRO_CONTENT_COLOR, 1,
+	"cairo_svg_surface_create",
 	_cairo_boilerplate_svg11_create_surface,
 	_cairo_boilerplate_svg_force_fallbacks,
 	_cairo_boilerplate_svg_finish_surface,
@@ -296,6 +298,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"svg12", "svg", NULL, NULL,
 	CAIRO_SURFACE_TYPE_SVG, CAIRO_CONTENT_COLOR_ALPHA, 1,
+	"cairo_svg_surface_create",
 	_cairo_boilerplate_svg12_create_surface,
 	_cairo_boilerplate_svg_force_fallbacks,
 	_cairo_boilerplate_svg_finish_surface,
@@ -307,6 +310,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"svg12", "svg", NULL, NULL,
 	CAIRO_SURFACE_TYPE_META, CAIRO_CONTENT_COLOR, 1,
+	"cairo_svg_surface_create",
 	_cairo_boilerplate_svg12_create_surface,
 	_cairo_boilerplate_svg_force_fallbacks,
 	_cairo_boilerplate_svg_finish_surface,
diff --git a/boilerplate/cairo-boilerplate-test-surfaces.c b/boilerplate/cairo-boilerplate-test-surfaces.c
index 3e7490e..7a3a16b 100644
--- a/boilerplate/cairo-boilerplate-test-surfaces.c
+++ b/boilerplate/cairo-boilerplate-test-surfaces.c
@@ -233,6 +233,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	"test-fallback", "image", NULL, NULL,
 	CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
 	CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"_cairo_test_fallback_surface_create",
 	_cairo_boilerplate_test_fallback_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -242,6 +243,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	"test-fallback", "image", NULL, NULL,
 	CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
 	CAIRO_CONTENT_COLOR, 0,
+	"_cairo_test_fallback_surface_create",
 	_cairo_boilerplate_test_fallback_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -251,6 +253,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	"test-fallback16", "image", NULL, NULL,
 	CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
 	CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"_cairo_test_fallback16_surface_create",
 	_cairo_boilerplate_test_fallback16_create_surface,
 	NULL, NULL,
 	NULL, /* _cairo_boilerplate_get_image_surface, */
@@ -260,6 +263,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	"test-fallback16", "image", NULL, NULL,
 	CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
 	CAIRO_CONTENT_COLOR, 0,
+	"_cairo_test_fallback16_surface_create",
 	_cairo_boilerplate_test_fallback16_create_surface,
 	NULL, NULL,
 	NULL, /* _cairo_boilerplate_get_image_surface, */
@@ -270,6 +274,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	"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,
 	NULL, NULL,
 	_cairo_boilerplate_test_paginated_get_image_surface,
@@ -282,6 +287,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	"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,
 	NULL, NULL,
 	_cairo_boilerplate_test_paginated_get_image_surface,
@@ -296,6 +302,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	"test-wrapping", "image", NULL, NULL,
 	CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING,
 	CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"_cairo_test_wrapping_surface_create",
 	_cairo_boilerplate_test_wrapping_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -307,6 +314,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	"null", "image", NULL, NULL,
 	CAIRO_INTERNAL_SURFACE_TYPE_NULL,
 	CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"_cairo_test_null_surface_create",
 	_cairo_boilerplate_test_null_create_surface,
 	NULL, NULL,
 	NULL, NULL, NULL,
diff --git a/boilerplate/cairo-boilerplate-vg.c b/boilerplate/cairo-boilerplate-vg.c
index 75756dd..2ea1a07 100644
--- a/boilerplate/cairo-boilerplate-vg.c
+++ b/boilerplate/cairo-boilerplate-vg.c
@@ -305,6 +305,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"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,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -315,6 +316,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"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,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -327,6 +329,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"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,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -337,6 +340,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"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,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-win32-printing.c b/boilerplate/cairo-boilerplate-win32-printing.c
index 541401a..ecaac5f 100644
--- a/boilerplate/cairo-boilerplate-win32-printing.c
+++ b/boilerplate/cairo-boilerplate-win32-printing.c
@@ -347,6 +347,7 @@ 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,
 	NULL, NULL,
 	_cairo_boilerplate_win32_printing_get_image_surface,
@@ -357,6 +358,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"win32-printing", "win32", ".ps", NULL,
 	CAIRO_SURFACE_TYPE_META, CAIRO_CONTENT_COLOR, 0,
+	"cairo_win32_printing_surface_create",
 	_cairo_boilerplate_win32_printing_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_win32_printing_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-win32.c b/boilerplate/cairo-boilerplate-win32.c
index 420989b..2b3abb1 100644
--- a/boilerplate/cairo-boilerplate-win32.c
+++ b/boilerplate/cairo-boilerplate-win32.c
@@ -52,6 +52,7 @@ 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,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -63,6 +64,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"win32", "win32", NULL, NULL,
 	CAIRO_SURFACE_TYPE_WIN32, CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"cairo_win32_surface_create_with_dib",
 	_cairo_boilerplate_win32_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-xcb.c b/boilerplate/cairo-boilerplate-xcb.c
index 63c20e8..0653d8c 100644
--- a/boilerplate/cairo-boilerplate-xcb.c
+++ b/boilerplate/cairo-boilerplate-xcb.c
@@ -119,6 +119,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"xcb", "xcb", NULL, NULL,
 	CAIRO_SURFACE_TYPE_XCB, CAIRO_CONTENT_COLOR_ALPHA, 1,
+	"cairo_xcb_surface_create_with_xrender_format",
 	_cairo_boilerplate_xcb_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate-xlib.c b/boilerplate/cairo-boilerplate-xlib.c
index d67a938..fe54d98 100644
--- a/boilerplate/cairo-boilerplate-xlib.c
+++ b/boilerplate/cairo-boilerplate-xlib.c
@@ -439,6 +439,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"xlib", "xlib", NULL, "xlib-reference",
 	CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR_ALPHA, 1,
+	"cairo_xlib_surface_create_with_xrender_format",
 	_cairo_boilerplate_xlib_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -449,6 +450,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"xlib", "xlib", NULL, "xlib-reference",
 	CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR, 1,
+	"cairo_xlib_surface_create_with_xrender_format",
 	_cairo_boilerplate_xlib_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -459,6 +461,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"xlib-reference", "xlib", NULL, NULL,
 	CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR, 1,
+	"cairo_xlib_surface_create",
 	_cairo_boilerplate_xlib_reference_create_surface,
 	NULL, NULL,
 	NULL, /* get_image */
@@ -473,6 +476,7 @@ static const cairo_boilerplate_target_t targets[] = {
     {
 	"xlib-fallback", "xlib", NULL, NULL,
 	CAIRO_SURFACE_TYPE_XLIB, CAIRO_CONTENT_COLOR, 1,
+	"cairo_xlib_surface_create",
 	_cairo_boilerplate_xlib_fallback_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
index 1abfe09..ebbad45 100644
--- a/boilerplate/cairo-boilerplate.c
+++ b/boilerplate/cairo-boilerplate.c
@@ -45,6 +45,10 @@
 #include <assert.h>
 #include <errno.h>
 
+#if HAVE_DL
+#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>
@@ -278,7 +282,7 @@ static const cairo_boilerplate_target_t builtin_targets[] = {
     {
 	"image", "image", NULL, NULL,
 	CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR_ALPHA, 0,
-	_cairo_boilerplate_image_create_surface,
+	NULL, _cairo_boilerplate_image_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
 	cairo_surface_write_to_png
@@ -286,7 +290,7 @@ static const cairo_boilerplate_target_t builtin_targets[] = {
     {
 	"image", "image", NULL, NULL,
 	CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0,
-	_cairo_boilerplate_image_create_surface,
+	NULL, _cairo_boilerplate_image_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
 	cairo_surface_write_to_png
@@ -295,6 +299,7 @@ static const cairo_boilerplate_target_t builtin_targets[] = {
     {
 	"meta", "image", NULL, NULL,
 	CAIRO_SURFACE_TYPE_META, CAIRO_CONTENT_COLOR_ALPHA, 0,
+	"cairo_meta_surface_create",
 	_cairo_boilerplate_meta_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -305,6 +310,7 @@ static const cairo_boilerplate_target_t builtin_targets[] = {
     {
 	"meta", "image", NULL, NULL,
 	CAIRO_SURFACE_TYPE_META, CAIRO_CONTENT_COLOR, 0,
+	"cairo_meta_surface_create",
 	_cairo_boilerplate_meta_create_surface,
 	NULL, NULL,
 	_cairo_boilerplate_get_image_surface,
@@ -321,6 +327,19 @@ static struct cairo_boilerplate_target_list {
     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_DL
+    return dlsym (NULL, target->probe) != NULL;
+#else
+    return TRUE;
+#endif
+}
+
 void
 _cairo_boilerplate_register_backend (const cairo_boilerplate_target_t *targets,
 				     unsigned int count)
@@ -329,9 +348,13 @@ _cairo_boilerplate_register_backend (const cairo_boilerplate_target_t *targets,
     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;
+	list->target = targets;
 	cairo_boilerplate_targets = list;
     }
 }
diff --git a/boilerplate/cairo-boilerplate.h b/boilerplate/cairo-boilerplate.h
index 377d2f9..82d42e8 100644
--- a/boilerplate/cairo-boilerplate.h
+++ b/boilerplate/cairo-boilerplate.h
@@ -157,6 +157,7 @@ typedef struct _cairo_boilerplate_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_force_fallbacks_t		 force_fallbacks;
     cairo_boilerplate_finish_surface_t		 finish_surface;
diff --git a/configure.ac b/configure.ac
index fbc1576..873e791 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,6 +36,13 @@ AC_CHECK_LIB(z, compress,
 	 [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,
+	     [AC_CHECK_HEADER(dlfcn.h, [
+	      have_dl=yes
+	      AC_DEFINE(HAVE_DL, 1, [Define to 1 if you have dl available]),
+	     ], [have_dl=no])], [have_dl=no])
+AM_CONDITIONAL(CAIRO_HAS_DL, test "x$have_dl" = "xyes")
+
 dnl ===========================================================================
 
 CAIRO_ENABLE_SURFACE_BACKEND(xlib, Xlib, auto, [
commit 7447915381fc64bd0c66f7110c1dd0b8a10d73f5
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 22 22:20:35 2009 +0100

    [script] Conditionalise the build
    
    Allow the interpreter to be disabled if we can not satisfy its dependencies.

diff --git a/configure.ac b/configure.ac
index b726cd4..fbc1576 100644
--- a/configure.ac
+++ b/configure.ac
@@ -641,6 +641,8 @@ AM_CONDITIONAL(BUILD_TRACE,
 	       test "x$have_ld_preload" = "xyes" \
 	       -a "x$have_libz" = "xyes")
 
+AM_CONDITIONAL(BUILD_SCRIPT, test "x$have_libz" = "xyes")
+
 AC_CHECK_LIB(bfd, bfd_openr,
 	 [AC_CHECK_HEADER(bfd.h, [have_bfd=yes],
 	 [have_bfd=no])], [have_bfd=no])
diff --git a/util/Makefile.am b/util/Makefile.am
index 5c110ab..fb47162 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -1,6 +1,10 @@
 include $(top_srcdir)/build/Makefile.am.common
 
-SUBDIRS = . cairo-script
+SUBDIRS = .
+
+if BUILD_SCRIPT
+SUBDIRS += cairo-script
+endif
 
 if BUILD_TRACE
 SUBDIRS += cairo-trace
@@ -20,7 +24,10 @@ AM_CPPFLAGS = -I$(top_srcdir)/src \
 	      -I$(top_srcdir)/util/cairo-script	\
 	      $(CAIRO_CFLAGS)
 
-EXTRA_PROGRAMS += show-traps show-edges show-events trace-to-xml xml-to-trace
+EXTRA_PROGRAMS += show-traps show-edges show-events
+if BUILD_SCRIPT
+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)
 
diff --git a/util/cairo-script/Makefile.am b/util/cairo-script/Makefile.am
index 8b783a9..d7e4427 100644
--- a/util/cairo-script/Makefile.am
+++ b/util/cairo-script/Makefile.am
@@ -19,7 +19,7 @@ libcairo_script_interpreter_la_SOURCES = \
 	$(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)
+libcairo_script_interpreter_la_LIBADD = $(top_builddir)/src/libcairo.la $(CAIRO_LIBS) -lz
 
 csi_replay_SOURCES = csi-replay.c
 csi_replay_CFLAGS = $(CAIRO_CFLAGS)
commit fcda9fc2f29c2ee7d0bb45c17a2261badfdf430c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 22 18:48:33 2009 +0100

    [scaled-font] Remove assert from cairo_scled_font_create()
    
    The assert() is only correct for the normal paths, but failed on the error
    path. It has been run for long enough for me to be confident that the code
    is self-consistent, so I think I can now safely remove it.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index d8406f7..9280f72 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -947,6 +947,10 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
 								ctm,
 								options);
 	}
+
+	_cairo_scaled_font_init_key (&key, font_face,
+				     font_matrix, ctm, options);
+
     }
     else
     {
@@ -1049,7 +1053,6 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
     scaled_font->original_font_face =
 	cairo_font_face_reference (original_font_face);
 
-    assert (scaled_font->hash_entry.hash == key.hash_entry.hash);
     status = _cairo_hash_table_insert (font_map->hash_table,
 				       &scaled_font->hash_entry);
     if (likely (status == CAIRO_STATUS_SUCCESS)) {
commit 93cfa7376fb505e676d113e8ef431bab9b497f56
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 22 18:45:54 2009 +0100

    [win32] Use the system scaled_font_done
    
    Pointless as both functions are empty, but lets be pedantically correct
    nevertheless.

diff --git a/src/cairo-win32-font.c b/src/cairo-win32-font.c
index 2f1127c..216c751 100644
--- a/src/cairo-win32-font.c
+++ b/src/cairo-win32-font.c
@@ -1819,7 +1819,10 @@ _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font
     free (buffer);
 
  CLEANUP_FONT:
-    cairo_win32_scaled_font_done_font (&scaled_font->base);
+    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)
commit 8654a4b35c060ab06860c124a643be912060c4a1
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 22 18:16:20 2009 +0100

    [win32] Initialize clip_region
    
    Eek, I attempted to destroy an uninitialised region on the first use
    of a clip.

diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
index 6968062..d946dfe 100644
--- a/src/cairo-win32-surface.c
+++ b/src/cairo-win32-surface.c
@@ -338,6 +338,8 @@ _cairo_win32_surface_create_for_dc (HDC             original_dc,
     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);
@@ -1707,6 +1709,7 @@ cairo_win32_surface_create (HDC hdc)
 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
     }
 
+    surface->clip_region = NULL;
     surface->image = NULL;
     surface->format = format;
 
commit 9b33a2e1c794996cbe4cb0a5c773d50566d46bd4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 22 17:40:12 2009 +0100

    [win32] Compiler warnings
    
    Innocuous warnings about the use of mismatching explicit casts (I'm really
    not convinced by the merits of this particular compiler warning, but it
    does cleanse the code slightly.)

diff --git a/src/cairo-win32-printing-surface.c b/src/cairo-win32-printing-surface.c
index 17a7b39..f4c384d 100644
--- a/src/cairo-win32-printing-surface.c
+++ b/src/cairo-win32-printing-surface.c
@@ -385,10 +385,10 @@ _cairo_win32_printing_surface_paint_meta_pattern (cairo_win32_surface_t   *surfa
 	return status;
 
     if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
-	left = (int) floor (clip.left / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x));
-	right = (int) ceil (clip.right / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x));
-	top = (int) floor (clip.top / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y));
-	bottom = (int) ceil (clip.bottom / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y));
+	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;
@@ -693,10 +693,10 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t   *surf
 
     GetClipBox (surface->dc, &clip);
     if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
-	left = (int) floor((double)clip.left/opaque_image->width);
-	right = (int) ceil((double)clip.right/opaque_image->width);
-	top = (int) floor((double)clip.top/opaque_image->height);
-	bottom = (int) ceil((double)clip.bottom/opaque_image->height);
+	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;
@@ -813,8 +813,8 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surfa
     GetClipBox (surface->dc, &clip);
 
     if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
-	range_start = (int) floor(clip.left/d);
-	range_stop = (int) ceil(clip.right/d);
+	range_start = floor (clip.left / d);
+	range_stop = ceil (clip.right / d);
     } else {
 	range_start = 0;
 	range_stop = 1;
@@ -1621,8 +1621,8 @@ _cairo_win32_printing_surface_start_page (void *abstract_surface)
     if (status)
 	return status;
 
-    x_res = (double) GetDeviceCaps(surface->dc, LOGPIXELSX);
-    y_res = (double) GetDeviceCaps(surface->dc, LOGPIXELSY);
+    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);
 
commit d1740d8782d90edb5b5e20dc5bcadb7a9eeeb4cc
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 20 21:11:11 2009 +0100

    [pattern] Ensure that no repeated pattern is clipped
    
    Previously the pattern_acquire_surface routine only had to worry about
    handling extend modes NONE or REPEAT and so the test for ! REPEAT
    sufficed when what was actually intended was a test for NONE.

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index e621058..149cdc7 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -2116,7 +2116,7 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t   *pat
     sampled_area.y += ty;
 
     if ( _cairo_surface_get_extents (surface, &extents)) {
-	if (attr->extend != CAIRO_EXTEND_REPEAT) {
+	if (attr->extend == CAIRO_EXTEND_NONE) {
 	    /* Never acquire a larger area than the source itself */
 	    is_empty = _cairo_rectangle_intersect (&extents, &sampled_area);
 	} else {
commit 958f7ab1238b8242975d3ea774c26d23e090e70c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 19 12:35:00 2009 +0100

    [script] Support running on cairo-1.8
    
    Pre 1.9 the application had to pass a resolved font to cairo -- so do so
    in the interpreter if the cairo version is less than 1.9

diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index a11c7c6..414922d 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -1854,7 +1854,7 @@ _ft_create_for_pattern (csi_t *ctx,
     struct _ft_face_data *data;
     csi_list_t *link;
     cairo_font_face_t *font_face;
-    FcPattern *pattern;
+    FcPattern *pattern, *resolved;
     csi_status_t status;
     struct mmap_vec vec;
     void *bytes;
@@ -1881,8 +1881,20 @@ _ft_create_for_pattern (csi_t *ctx,
     if (bytes != tmpl.bytes)
 	_csi_free (ctx, bytes);
 
-    font_face = cairo_ft_font_face_create_for_pattern (pattern);
-    FcPatternDestroy (pattern);
+    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 (resolved);
+	    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);
commit 87175334a574fa0f69679b1a0baeeb881eaa439b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 21 15:51:12 2009 +0100

    [gl] Use spans for trapezois.
    
    Always use spans, even for unaligned boxes. In the future (given a new
    interface) we may want to emit the common unaligned box code more
    efficient than a per-scanline computation -- but for now simply avoid the
    requirements to write a temporary CPU buffer.

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index c943447..bfe2579 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -667,7 +667,6 @@ _cairo_gl_surface_get_image (cairo_gl_surface_t      *surface,
     GLenum err;
     GLenum format, type;
     cairo_format_t cairo_format;
-    int y;
     unsigned int cpp;
 
     /* Want to use a switch statement here but the compiler gets whiny. */
@@ -1481,6 +1480,23 @@ _cairo_gl_surface_composite_trapezoids (cairo_operator_t op,
     cairo_surface_pattern_t traps_pattern;
     cairo_int_status_t status;
 
+    if (! _cairo_gl_operator_is_supported (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (_cairo_surface_check_span_renderer (op,pattern,&dst->base, antialias)) {
+	status =
+	    _cairo_surface_composite_trapezoids_as_polygon (&dst->base,
+							    op, pattern,
+							    antialias,
+							    src_x, src_y,
+							    dst_x, dst_y,
+							    width, height,
+							    traps, num_traps,
+							    clip_region);
+	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	    return status;
+    }
+
     status = _cairo_gl_get_traps_pattern (dst,
 					  dst_x, dst_y, width, height,
 					  traps, num_traps, antialias,
@@ -1810,8 +1826,7 @@ static 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,
-				       const cairo_composite_rectangles_t *rects)
+				       cairo_antialias_t	  antialias)
 {
     if (! _cairo_gl_operator_is_supported (op))
 	return FALSE;
@@ -1824,7 +1839,6 @@ _cairo_gl_surface_check_span_renderer (cairo_operator_t	  op,
     (void) pattern;
     (void) abstract_dst;
     (void) antialias;
-    (void) rects;
 }
 
 static cairo_span_renderer_t *
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 627f9ed..1d778cd 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -1483,15 +1483,13 @@ static cairo_bool_t
 _cairo_image_surface_check_span_renderer (cairo_operator_t	  op,
 					  const cairo_pattern_t  *pattern,
 					  void			 *abstract_dst,
-					  cairo_antialias_t	  antialias,
-					  const cairo_composite_rectangles_t *rects)
+					  cairo_antialias_t	  antialias)
 {
     return TRUE;
     (void) op;
     (void) pattern;
     (void) abstract_dst;
     (void) antialias;
-    (void) rects;
 }
 
 static cairo_span_renderer_t *
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 8d4c5a1..a5a529c 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -1184,10 +1184,7 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
 	    goto CLEANUP;
     }
 
-    if (antialias != CAIRO_ANTIALIAS_NONE &&
-	_cairo_surface_check_span_renderer (op, source, surface,
-					    antialias, NULL))
-    {
+    if (_cairo_surface_check_span_renderer (op, source, surface, antialias)) {
 	cairo_composite_spans_info_t info;
 
 	info.polygon = &polygon;
@@ -1327,10 +1324,7 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
     }
 
 
-    if (antialias != CAIRO_ANTIALIAS_NONE &&
-	_cairo_surface_check_span_renderer (op, source, surface,
-					    antialias, NULL))
-    {
+    if (_cairo_surface_check_span_renderer (op, source, surface, antialias)) {
 	cairo_composite_spans_info_t info;
 
 	info.polygon = &polygon;
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 624f71f..46abf25 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -2218,29 +2218,22 @@ _cairo_surface_create_span_renderer (cairo_operator_t		 op,
 }
 
 cairo_bool_t
-_cairo_surface_check_span_renderer   (cairo_operator_t		 op,
-				      const cairo_pattern_t     *pattern,
-				      cairo_surface_t		*dst,
-				      cairo_antialias_t	         antialias,
-				      const cairo_composite_rectangles_t *rects)
+_cairo_surface_check_span_renderer (cairo_operator_t		 op,
+				    const cairo_pattern_t	*pattern,
+				    cairo_surface_t		*dst,
+				    cairo_antialias_t	         antialias)
 {
-    cairo_int_status_t status;
-
     assert (dst->snapshot_of == NULL);
+    assert (dst->status == CAIRO_STATUS_SUCCESS);
+    assert (! dst->finished);
 
-    if (unlikely (dst->status))
+    /* XXX: Currently we have no mono span renderer */
+    if (antialias == CAIRO_ANTIALIAS_NONE)
 	return FALSE;
 
-    if (unlikely (dst->finished)) {
-	status = _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED);
-	return FALSE;
-    }
+    if (dst->backend->check_span_renderer != NULL)
+	return dst->backend->check_span_renderer (op, pattern, dst, antialias);
 
-    if (dst->backend->check_span_renderer) {
-	return dst->backend->check_span_renderer (op, pattern, dst,
-						  antialias,
-						  rects);
-    }
     return FALSE;
 }
 
diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
index 4139a24..6968062 100644
--- a/src/cairo-win32-surface.c
+++ b/src/cairo-win32-surface.c
@@ -2003,14 +2003,12 @@ static cairo_bool_t
 _cairo_win32_surface_check_span_renderer (cairo_operator_t	  op,
 					  const cairo_pattern_t  *pattern,
 					  void			 *abstract_dst,
-					  cairo_antialias_t	  antialias,
-					  const cairo_composite_rectangles_t *rects)
+					  cairo_antialias_t	  antialias)
 {
     (void) op;
     (void) pattern;
     (void) abstract_dst;
     (void) antialias;
-    (void) rects;
     return TRUE;
 }
 
diff --git a/src/cairoint.h b/src/cairoint.h
index 62a71ba..7d2812b 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -683,8 +683,7 @@ struct _cairo_surface_backend {
     (*check_span_renderer)	(cairo_operator_t			 op,
 				 const cairo_pattern_t			*pattern,
                                  void					*dst,
-                                 cairo_antialias_t			 antialias,
-                                 const cairo_composite_rectangles_t	*rects);
+                                 cairo_antialias_t			 antialias);
 
     cairo_warn cairo_int_status_t
     (*copy_page)		(void			*surface);
@@ -1965,6 +1964,18 @@ _cairo_surface_composite_trapezoids (cairo_operator_t	op,
 				     int		ntraps,
 				     cairo_region_t	*clip_region);
 
+cairo_private cairo_status_t
+_cairo_surface_composite_trapezoids_as_polygon (cairo_surface_t	*surface,
+						cairo_operator_t	 op,
+						const cairo_pattern_t	*pattern,
+						cairo_antialias_t	antialias,
+						int src_x, int src_y,
+						int dst_x, int dst_y,
+						int width, int height,
+						cairo_trapezoid_t	*traps,
+						int num_traps,
+						cairo_region_t	*clip_region);
+
 cairo_private cairo_span_renderer_t *
 _cairo_surface_create_span_renderer (cairo_operator_t			 op,
 				     const cairo_pattern_t		*pattern,
@@ -1977,8 +1988,7 @@ cairo_private cairo_bool_t
 _cairo_surface_check_span_renderer (cairo_operator_t			 op,
 				    const cairo_pattern_t		*pattern,
 				    cairo_surface_t			*dst,
-				    cairo_antialias_t			 antialias,
-				    const cairo_composite_rectangles_t	*rects);
+				    cairo_antialias_t			 antialias);
 
 cairo_private cairo_status_t
 _cairo_surface_acquire_source_image (cairo_surface_t         *surface,
commit e65dfacab5d123913a4b2f59967f27c0b0af0692
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 21 15:50:24 2009 +0100

    [gl] Simplify acquire_dest_image()
    
    Remove redundant code: the intersection of surface extents and the copy to
    a temporary buffer.

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index ead86fb..c943447 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -664,28 +664,11 @@ _cairo_gl_surface_get_image (cairo_gl_surface_t      *surface,
 			     cairo_rectangle_int_t   *rect_out)
 {
     cairo_image_surface_t *image;
-    cairo_rectangle_int_t extents;
     GLenum err;
-    char *temp_data;
-    int y;
-    unsigned int cpp;
     GLenum format, type;
     cairo_format_t cairo_format;
-
-    extents.x = 0;
-    extents.y = 0;
-    extents.width  = surface->width;
-    extents.height = surface->height;
-
-    if (interest != NULL) {
-	if (! _cairo_rectangle_intersect (&extents, interest)) {
-	    *image_out = NULL;
-	    return CAIRO_STATUS_SUCCESS;
-	}
-    }
-
-    if (rect_out != NULL)
-	*rect_out = extents;
+    int y;
+    unsigned int cpp;
 
     /* Want to use a switch statement here but the compiler gets whiny. */
     if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
@@ -704,14 +687,14 @@ _cairo_gl_surface_get_image (cairo_gl_surface_t      *surface,
 	type = GL_UNSIGNED_BYTE;
 	cpp = 1;
     } else {
-	fprintf (stderr, "get_image fallback: %d\n", surface->base.content);
+	ASSERT_NOT_REACHED;
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
     image = (cairo_image_surface_t*)
 	cairo_image_surface_create (cairo_format,
-				    extents.width, extents.height);
-    if (image->base.status)
+				    interest->width, interest->height);
+    if (unlikely (image->base.status))
 	return image->base.status;
 
     /* This is inefficient, as we'd rather just read the thing without making
@@ -720,30 +703,18 @@ _cairo_gl_surface_get_image (cairo_gl_surface_t      *surface,
      */
     _cairo_gl_set_destination (surface);
 
-    /* Read the data to a temporary as GL gives us bottom-to-top data
-     * screen-wise, and we want top-to-bottom.
-     */
-    temp_data = malloc (extents.width * extents.height * cpp);
-    if (temp_data == NULL)
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
     glPixelStorei (GL_PACK_ALIGNMENT, 1);
-    glReadPixels (extents.x, extents.y,
-		 extents.width, extents.height,
-		 format, type, temp_data);
-
-    for (y = 0; y < extents.height; y++) {
-	memcpy ((char *) image->data + y * image->stride,
-		temp_data + y * extents.width * cpp,
-		extents.width * cpp);
-    }
-    free (temp_data);
-
-    *image_out = image;
+    glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp);
+    glReadPixels (interest->x, interest->y,
+		  interest->width, interest->height,
+		  format, type, image->data);
 
     while ((err = glGetError ()))
 	fprintf (stderr, "GL error 0x%08x\n", (int) err);
 
+    *image_out = image;
+    if (rect_out != NULL)
+	*rect_out = *interest;
     return CAIRO_STATUS_SUCCESS;
 }
 
commit 5a13396373180ceae31287441ef9c838c771849d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 21 15:48:38 2009 +0100

    [gl] Use common ARRAY_LENGTH macro
    
    Forgo the local ARRAY_SIZE macro where the common one will suffice.

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index a8d02fe..ead86fb 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -42,8 +42,6 @@
 slim_hidden_proto (cairo_gl_context_reference);
 slim_hidden_proto (cairo_gl_context_destroy);
 
-#define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0]))
-
 #define BIAS .375
 
 static inline float
@@ -367,7 +365,7 @@ _cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op)
     };
     GLenum src_factor, dst_factor;
 
-    assert (op < ARRAY_SIZE (blend_factors));
+    assert (op < ARRAY_LENGTH (blend_factors));
 
     src_factor = blend_factors[op].src;
     dst_factor = blend_factors[op].dst;
commit efdb53425ee71b3bce6c92ce212f5baf3e2a43d7
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 21 11:58:31 2009 +0100

    [qt] Discard impossible status return from path construction
    
    As we never return an error status during the path construction, we can
    use the return value for the QPainterPath instead, greatly simplifying the
    callers.

diff --git a/src/cairo-qt-surface.cpp b/src/cairo-qt-surface.cpp
index 56f5c19..902251d 100644
--- a/src/cairo-qt-surface.cpp
+++ b/src/cairo-qt-surface.cpp
@@ -323,7 +323,7 @@ _qmatrix_from_cairo_matrix (const cairo_matrix_t& m)
 
 /** Path conversion **/
 typedef struct _qpainter_path_transform {
-    QPainterPath *path;
+    QPainterPath path;
     cairo_matrix_t *ctm_inverse;
 } qpainter_path_data;
 
@@ -331,14 +331,14 @@ typedef struct _qpainter_path_transform {
 static cairo_status_t
 _cairo_path_to_qpainterpath_move_to (void *closure, const cairo_point_t *point)
 {
-    qpainter_path_data *pdata = (qpainter_path_data *)closure;
+    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);
+    pdata->path.moveTo(x, y);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -346,19 +346,14 @@ _cairo_path_to_qpainterpath_move_to (void *closure, const cairo_point_t *point)
 static cairo_status_t
 _cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point)
 {
-    qpainter_path_data *pdata = (qpainter_path_data *)closure;
+    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);
-
-    if (pdata->path->isEmpty())
-        pdata->path->moveTo(x, y);
-    else
-        pdata->path->lineTo(x, y);
+    pdata->path.lineTo(x, y);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -366,7 +361,7 @@ _cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point)
 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 = (qpainter_path_data *)closure;
+    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);
@@ -380,7 +375,7 @@ _cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, co
         cairo_matrix_transform_point (pdata->ctm_inverse, &x2, &y2);
     }
 
-    pdata->path->cubicTo (x0, y0, x1, y1, x2, y2);
+    pdata->path.cubicTo (x0, y0, x1, y1, x2, y2);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -388,42 +383,48 @@ _cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, co
 static cairo_status_t
 _cairo_path_to_qpainterpath_close_path (void *closure)
 {
-    qpainter_path_data *pdata = (qpainter_path_data *)closure;
+    qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
 
-    pdata->path->closeSubpath();
+    pdata->path.closeSubpath();
 
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_status_t
-_cairo_quartz_cairo_path_to_qpainterpath (cairo_path_fixed_t *path,
-                                          QPainterPath *qpath,
-                                          cairo_fill_rule_t fill_rule,
-                                          cairo_matrix_t *ctm_inverse = NULL)
+static inline QPainterPath
+path_to_qt (cairo_path_fixed_t *path,
+	    cairo_matrix_t *ctm_inverse = NULL)
 {
-    qpainter_path_data pdata = { qpath, ctm_inverse };
+    qpainter_path_data data;
+    cairo_status_t status;
 
-    qpath->setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ?
-			Qt::WindingFill :
-			Qt::OddEvenFill);
+    if (ctm_inverse && _cairo_matrix_is_identity (ctm_inverse))
+	ctm_inverse = NULL;
+    data.ctm_inverse = ctm_inverse;
+
+    status = _cairo_path_fixed_interpret (path,
+					  CAIRO_DIRECTION_FORWARD,
+					  _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 _cairo_path_fixed_interpret (path,
-                                        CAIRO_DIRECTION_FORWARD,
-                                        _cairo_path_to_qpainterpath_move_to,
-                                        _cairo_path_to_qpainterpath_line_to,
-                                        _cairo_path_to_qpainterpath_curve_to,
-                                        _cairo_path_to_qpainterpath_close_path,
-                                        &pdata);
+    return data.path;
 }
 
-static cairo_status_t
-_cairo_quartz_cairo_path_to_qpainterpath (cairo_path_fixed_t *path,
-					  QPainterPath *qpath,
-					  cairo_matrix_t *ctm_inverse)
+static inline QPainterPath
+path_to_qt (cairo_path_fixed_t *path,
+	    cairo_fill_rule_t fill_rule,
+	    cairo_matrix_t *ctm_inverse = NULL)
 {
-    return _cairo_quartz_cairo_path_to_qpainterpath (path, qpath,
-            CAIRO_FILL_RULE_WINDING,
-            ctm_inverse);
+    QPainterPath qpath = path_to_qt (path, ctm_inverse);
+
+    qpath.setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ?
+			Qt::WindingFill :
+			Qt::OddEvenFill);
+
+    return qpath;
 }
 
 /**
@@ -722,17 +723,8 @@ _cairo_qt_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
             qs->p->save ();
         }
     } else {
-	QPainterPath qpath;
-	cairo_status_t status;
-
 	// XXX Antialiasing is ignored
-	status = _cairo_quartz_cairo_path_to_qpainterpath (path,
-							   &qpath,
-							   fill_rule);
-	if (unlikely (status))
-	    return status;
-
-	qs->p->setClipPath (qpath, Qt::IntersectClip);
+	qs->p->setClipPath (path_to_qt (path, fill_rule), Qt::IntersectClip);
     }
 
     return CAIRO_STATUS_SUCCESS;
@@ -1042,6 +1034,7 @@ struct PatternToBrushConverter {
 
     QBrush mBrush;
 
+    private:
     cairo_surface_t *mAcquiredImageParent;
     cairo_image_surface_t *mAcquiredImage;
     void *mAcquiredImageExtra;
@@ -1322,14 +1315,8 @@ _cairo_qt_surface_fill (void *abstract_surface,
     if (! _cairo_qt_fast_fill (qs, source,
 			       path, fill_rule, tolerance, antialias))
     {
-	QPainterPath qpath;
-	cairo_status_t status;
-
-	status = _cairo_quartz_cairo_path_to_qpainterpath (path, &qpath, fill_rule);
-	assert (status == CAIRO_STATUS_SUCCESS);
-
 	PatternToBrushConverter brush(source);
-	qs->p->fillPath (qpath, brush);
+	qs->p->fillPath (path_to_qt (path, fill_rule), brush);
     }
 
     if (qs->supports_porter_duff)
@@ -1364,14 +1351,6 @@ _cairo_qt_surface_stroke (void *abstract_surface,
     if (unlikely (int_status))
 	return int_status;
 
-    QPainterPath qpath;
-    cairo_status_t status;
-
-    if (_cairo_matrix_is_identity (ctm_inverse))
-	ctm_inverse = NULL;
-    status = _cairo_quartz_cairo_path_to_qpainterpath (path, &qpath,
-						       ctm_inverse);
-    assert (status == CAIRO_STATUS_SUCCESS);
 
     QMatrix savedMatrix = qs->p->worldMatrix();
 
@@ -1387,7 +1366,7 @@ _cairo_qt_surface_stroke (void *abstract_surface,
     PatternToPenConverter pen(source, style);
 
     qs->p->setPen(pen);
-    qs->p->drawPath(qpath);
+    qs->p->drawPath(path_to_qt (path, ctm_inverse));
     qs->p->setPen(Qt::black);
 
     qs->p->setWorldMatrix (savedMatrix, false);
commit f2cde41cabaa0c190ef965f634cf2c73b6ba2334
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 21 10:29:39 2009 +0100

    [cairoint.h] Add missing cairo_private to debug prototypes.

diff --git a/src/cairoint.h b/src/cairoint.h
index a3cc6c7..62a71ba 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2774,10 +2774,10 @@ _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface);
 
 #endif
 
-void
+cairo_private void
 _cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path);
 
-void
+cairo_private void
 _cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip);
 
 #endif
commit b6d96bba8a4e9efe1033dd7fc875aba9bd8d0dc8
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 20 20:31:49 2009 +0100

    [win32] Trust the clipping code to trim roi to our surface
    
    The higher level code ensures that the region of interest is trimmed to
    our declared surface extents, so performing the intersection again is
    redundant. Furthermore with the change in the clipping code, the
    fallback region is no longer clipped, especially as the clip that is
    currently set upon the DC is likely to be stale and incorrect for the
    fallback.
    
    Hopefully this resolves the assertion failure reported by Damian Frank,
    http://lists.cairographics.org/archives/cairo/2009-August/018015.html
    
    CC:  Damian Frank <damian.frank at gmail.com>

diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
index 12219e9..4139a24 100644
--- a/src/cairo-win32-surface.c
+++ b/src/cairo-win32-surface.c
@@ -569,13 +569,12 @@ _cairo_win32_surface_acquire_source_image (void                    *abstract_sur
 					   void                   **image_extra)
 {
     cairo_win32_surface_t *surface = abstract_surface;
-    cairo_win32_surface_t *local = NULL;
+    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;
     }
 
@@ -587,7 +586,6 @@ _cairo_win32_surface_acquire_source_image (void                    *abstract_sur
 
     *image_out = (cairo_image_surface_t *)local->image;
     *image_extra = local;
-
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -612,61 +610,28 @@ _cairo_win32_surface_acquire_dest_image (void                    *abstract_surfa
     cairo_win32_surface_t *surface = abstract_surface;
     cairo_win32_surface_t *local = NULL;
     cairo_status_t status;
-    RECT clip_box;
-    int x1, y1, x2, y2;
 
     if (surface->image) {
 	GdiFlush();
 
-	image_rect->x = 0;
-	image_rect->y = 0;
-	image_rect->width = surface->extents.width;
-	image_rect->height = surface->extents.height;
-
-	*image_out = (cairo_image_surface_t *)surface->image;
+	*image_out = (cairo_image_surface_t *) surface->image;
 	*image_extra = NULL;
-
-	return CAIRO_STATUS_SUCCESS;
-    }
-
-    if (GetClipBox (surface->dc, &clip_box) == ERROR)
-	return _cairo_win32_print_gdi_error ("_cairo_win3_surface_acquire_dest_image");
-
-    x1 = clip_box.left;
-    x2 = clip_box.right;
-    y1 = clip_box.top;
-    y2 = clip_box.bottom;
-
-    if (interest_rect->x > x1)
-	x1 = interest_rect->x;
-    if (interest_rect->y > y1)
-	y1 = interest_rect->y;
-    if ((int) (interest_rect->x + interest_rect->width) < x2)
-	x2 = interest_rect->x + interest_rect->width;
-    if ((int) (interest_rect->y + interest_rect->height) < y2)
-	y2 = interest_rect->y + interest_rect->height;
-
-    if (x1 >= x2 || y1 >= y2) {
-	*image_out = NULL;
-	*image_extra = NULL;
-
+	*image_rect = surface->extents;
 	return CAIRO_STATUS_SUCCESS;
     }
 
     status = _cairo_win32_surface_get_subimage (abstract_surface,
-						x1, y1, x2 - x1, y2 - y1,
+						interest_rect->x,
+						interest_rect->y,
+						interest_rect->width,
+						interest_rect->height,
 						&local);
     if (status)
 	return status;
 
-    *image_out = (cairo_image_surface_t *)local->image;
+    *image_out = (cairo_image_surface_t *) local->image;
     *image_extra = local;
-
-    image_rect->x = x1;
-    image_rect->y = y1;
-    image_rect->width = x2 - x1;
-    image_rect->height = y2 - y1;
-
+    *image_rect = *interest_rect;
     return CAIRO_STATUS_SUCCESS;
 }
 
commit d7faec024ac9f702c9bb305599ef8960a972491a
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Mon Jun 15 11:15:22 2009 +0100

    Add skia backend
    
    Originally written by Vladimir Vukicevic to investigate using Skia for
    Mozilla, it provides a nice integration with a rather interesting code
    base. By hooking Skia underneath Cairo it allows us to directly compare
    code paths... which is interesting.
    
    [updated by Chris Wilson]

diff --git a/boilerplate/Makefile.sources b/boilerplate/Makefile.sources
index 2ceed37..d40d7f9 100644
--- a/boilerplate/Makefile.sources
+++ b/boilerplate/Makefile.sources
@@ -36,6 +36,7 @@ cairo_boilerplate_ps_sources = cairo-boilerplate-ps.c
 cairo_boilerplate_qt_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
diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index ad4508e..03eee6e 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -109,6 +109,16 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_win32_font_private)
 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_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_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)
diff --git a/boilerplate/cairo-boilerplate-skia.c b/boilerplate/cairo-boilerplate-skia.c
new file mode 100644
index 0000000..e636420
--- /dev/null
+++ b/boilerplate/cairo-boilerplate-skia.c
@@ -0,0 +1,50 @@
+
+#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_boilerplate_skia_create_surface,
+	NULL, NULL,
+	_cairo_boilerplate_get_image_surface,
+	cairo_surface_write_to_png
+    },
+    {
+	"skia", "skia", NULL, NULL,
+	CAIRO_SURFACE_TYPE_SKIA, CAIRO_CONTENT_COLOR, 0,
+	_cairo_boilerplate_skia_create_surface,
+	NULL, NULL,
+	_cairo_boilerplate_get_image_surface,
+	cairo_surface_write_to_png
+    },
+};
+CAIRO_BOILERPLATE (skia, targets)
diff --git a/build/Makefile.win32.features b/build/Makefile.win32.features
index 884e232..bc82d0f 100644
--- a/build/Makefile.win32.features
+++ b/build/Makefile.win32.features
@@ -9,6 +9,7 @@ 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
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index 0432790..4eb5932 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -32,6 +32,9 @@ endif
 ifeq ($(CAIRO_HAS_WIN32_FONT),1)
 	@echo "#define CAIRO_HAS_WIN32_FONT 1" >> src/cairo-features.h
 endif
+ifeq ($(CAIRO_HAS_SKIA_SURFACE),1)
+	@echo "#define CAIRO_HAS_SKIA_SURFACE 1" >> src/cairo-features.h
+endif
 ifeq ($(CAIRO_HAS_OS2_SURFACE),1)
 	@echo "#define CAIRO_HAS_OS2_SURFACE 1" >> src/cairo-features.h
 endif
diff --git a/build/configure.ac.features b/build/configure.ac.features
index 99fe284..ec9cde5 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -360,6 +360,7 @@ AC_DEFUN([CAIRO_REPORT],
 	echo "  Meta:          yes (always builtin)"
 	echo "  Tee:           yes (always builtin)"
 	echo "  XML:           $use_xml"
+	echo "  Skia:          $use_skia"
 	echo "  Xlib:          $use_xlib"
 	echo "  Xlib Xrender:  $use_xlib_xrender"
 	echo "  Qt:            $use_qt"
diff --git a/configure.ac b/configure.ac
index 1bd1fc2..b726cd4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -142,6 +142,19 @@ AM_CONDITIONAL(CAIRO_CAN_TEST_WIN32_PRINTING_SURFACE, test "x$test_win32_printin
 
 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`/../mesa"])
+  skia_NONPKGCONFIG_CFLAGS="-I$skia_DIR/include/config -I$skia_DIR/include/core -I$skia_DIR/include/effects"
+  skia_NONPKGCONFIG_LIBS="$skia_DIR/out/libskia.a"
+  AC_SUBST(skia_DIR)
+])
+
+dnl ===========================================================================
+
 CAIRO_ENABLE_SURFACE_BACKEND(os2, OS/2, no, [
   case "$host" in
     *-*-os2*)
diff --git a/perf/cairo-perf-trace.c b/perf/cairo-perf-trace.c
index 52e5429..d442b63 100644
--- a/perf/cairo-perf-trace.c
+++ b/perf/cairo-perf-trace.c
@@ -113,6 +113,9 @@ target_is_measurable (const cairo_boilerplate_target_t *target)
 #if CAIRO_HAS_DRM_SURFACE
     case CAIRO_SURFACE_TYPE_DRM:
 #endif
+#if CAIRO_HAS_SKIA_SURFACE
+    case CAIRO_SURFACE_TYPE_SKIA:
+#endif
 	return TRUE;
     case CAIRO_SURFACE_TYPE_PDF:
     case CAIRO_SURFACE_TYPE_PS:
diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
index 0d60fc6..b643615 100644
--- a/perf/cairo-perf.c
+++ b/perf/cairo-perf.c
@@ -107,6 +107,9 @@ target_is_measurable (const cairo_boilerplate_target_t *target)
 #if CAIRO_HAS_DRM_SURFACE
     case CAIRO_SURFACE_TYPE_DRM:
 #endif
+#if CAIRO_HAS_SKIA_SURFACE
+    case CAIRO_SURFACE_TYPE_SKIA:
+#endif
 	return TRUE;
 
     default:
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 58eac08..38239ff 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -262,6 +262,9 @@ 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_sources = cairo-skia-surface.cpp
+
 cairo_os2_headers = cairo-os2.h
 cairo_os2_private = cairo-os2-private.h
 cairo_os2_sources = cairo-os2-surface.c
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index 5d705bc..df8384d 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -147,6 +147,20 @@ 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_sources += $(cairo_skia_sources)
+ifeq ($(CAIRO_HAS_SKIA_SURFACE),1)
+enabled_cairo_headers += $(cairo_skia_headers)
+enabled_cairo_private += $(cairo_skia_private)
+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)
diff --git a/src/cairo-skia-surface.cpp b/src/cairo-skia-surface.cpp
new file mode 100644
index 0000000..5ca3c0d
--- /dev/null
+++ b/src/cairo-skia-surface.cpp
@@ -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., 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 at 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 (CAIRO_FIXED_TO_SK_SCALAR (linear->p1.x),
+			   CAIRO_FIXED_TO_SK_SCALAR (linear->p1.y));
+	    points[1].set (CAIRO_FIXED_TO_SK_SCALAR (linear->p2.x),
+			   CAIRO_FIXED_TO_SK_SCALAR (linear->p2.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,
+					  CAIRO_DIRECTION_FORWARD,
+					  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,
+			 _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
index 0000000..046599f
--- /dev/null
+++ b/src/cairo-skia.h
@@ -0,0 +1,84 @@
+/* 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., 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 at cworth.org>
+ */
+
+#ifndef CAIRO_SKIA_H
+#define CAIRO_SKIA_H
+
+#include "cairo.h"
+
+#ifdef 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_public unsigned char *
+cairo_skia_surface_get_data (cairo_surface_t *surface);
+
+cairo_public cairo_format_t
+cairo_skia_surface_get_format (cairo_surface_t *surface);
+
+cairo_public int
+cairo_skia_surface_get_width (cairo_surface_t *surface);
+
+cairo_public int
+cairo_skia_surface_get_height (cairo_surface_t *surface);
+
+cairo_public int
+cairo_skia_surface_get_stride (cairo_surface_t *surface);
+
+cairo_public cairo_surface_t *
+cairo_skia_surface_get_image (cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else
+
+# error Cairo was not compiled with support for the Skia backend
+
+#endif
+
+#endif
diff --git a/src/cairo.h b/src/cairo.h
index 17d91dc..b750c4e 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1950,6 +1950,7 @@ cairo_surface_status (cairo_surface_t *surface);
  * @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_t is used to describe the type of a given
  * surface. The surface types are also known as "backends" or "surface
@@ -1996,7 +1997,8 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_GL,
     CAIRO_SURFACE_TYPE_DRM,
     CAIRO_SURFACE_TYPE_TEE,
-    CAIRO_SURFACE_TYPE_XML
+    CAIRO_SURFACE_TYPE_XML,
+    CAIRO_SURFACE_TYPE_SKIA
 } cairo_surface_type_t;
 
 cairo_public cairo_surface_type_t
commit af6df4af51ad75a956e3b73542647206ab534bd2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 20 12:54:12 2009 +0100

    [configure] Choose a better name for the 'temporary' egl iterator
    
    The problem is that it remains referenced by egl_NONPKGCONFIG_LIBS and so
    must not be subsequently overwritten, so give it an egl_ prefix.

diff --git a/configure.ac b/configure.ac
index af63c29..1bd1fc2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -313,14 +313,22 @@ CAIRO_ENABLE_FUNCTIONS(egl, EGL, auto, [
     if test "x$use_egl" = "xyes"; then
       egl_NONPKGCONFIG_CFLAGS=
       egl_NONPKGCONFIG_LIBS=
-      for lib in EGL egl13 egl12 egl11; do
+      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($lib, eglGetError, egl_NONPKGCONFIG_LIBS="-l$lib")
+          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)"
commit 83c2f44dd954cb491d72834d0362d6d801ef7651
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 20 12:16:55 2009 +0100

    [test] Update REFERENCE_IMAGES

diff --git a/test/Makefile.am b/test/Makefile.am
index ed72fc6..779f95a 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -268,6 +268,8 @@ REFERENCE_IMAGES = \
 	create-from-png.ref.png \
 	culled-glyphs.ps.ref.png \
 	culled-glyphs.ref.png \
+	curve-to-as-line-to.ps.xfail.png \
+	curve-to-as-line-to.ref.png \
 	dash-caps-joins.ps2.argb32.ref.png \
 	dash-caps-joins.ps2.rgb24.ref.png \
 	dash-caps-joins.ps3.argb32.ref.png \
@@ -321,8 +323,6 @@ REFERENCE_IMAGES = \
 	degenerate-rel-curve-to.ref.png \
 	degenerate-rel-curve-to.ps.xfail.png \
 	device-offset-fractional.gl.xfail.png \
-	device-offset-fractional.pdf.argb32.ref.png \
-	device-offset-fractional.pdf.ref.png \
 	device-offset-fractional.pdf.xfail.png \
 	device-offset-fractional.ps2.ref.png \
 	device-offset-fractional.ps3.ref.png \
@@ -782,7 +782,6 @@ REFERENCE_IMAGES = \
 	scale-offset-image.ps.ref.png \
 	scale-offset-image.ref.png \
 	scale-offset-image.xfail.png \
-	scale-offset-image.meta.xfail.png \
 	scale-offset-image.xlib.xfail.png \
 	scale-offset-image.xlib-fallback.xfail.png \
 	scale-offset-similar.gl.ref.png \
commit f99e1841448e98be6161be57551153a6d9e28a13
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 20 12:11:51 2009 +0100

    [build] Remove -Wcast-align
    
    Use -Wcast-align simply generates too much noise due to false-positive
    reports when casting pixels to uint32_t.

diff --git a/build/configure.ac.warnings b/build/configure.ac.warnings
index 1b973bc..b7d6eab 100644
--- a/build/configure.ac.warnings
+++ b/build/configure.ac.warnings
@@ -5,13 +5,16 @@ 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.
+
 MAYBE_WARN="-Wall -Wextra \
 -Wold-style-definition \
 -Wmissing-declarations -Werror-implicit-function-declaration \
 -Wnested-externs -Wpointer-arith -Wwrite-strings \
 -Wsign-compare -Wstrict-prototpes -Wmissig-prototyess \
 -Wpacked -Wswitch-enum -Wmissing-format-attribute \
--Wcast-align -Wbad-function-cast -Wvolatile-register-var \
+-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"
commit 5fdf5b311e06e49843e924523e9bc91ba14da3bb
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 19 08:35:01 2009 +0100

    [fallback] Reduce paint + clipmask to fill
    
    Under simple, yet common, conditions using a bounded operator and painting
    with a single complex clip we can reduce the strength of that operation to
    a fill. In effect this removes the need for a temporary mask for some
    backends (GL, drm, xlib).

diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 409679f..8d4c5a1 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -920,15 +920,6 @@ _clip_to_boxes (cairo_clip_t **clip,
 	goto EXTENTS;
     }
 
-    /* In some cases it may be preferable to always use boxes instead
-     * of a region, in particular where we can cull lots of geometry.
-     * For the time being, continue to use the old path until we can
-     * find a compelling use-case for geometry clipping.
-     */
-    status = _cairo_clip_get_region (*clip, NULL);
-    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
-	goto EXTENTS;
-
     status = _cairo_clip_get_boxes (*clip, boxes, num_boxes);
     switch ((int) status) {
     case CAIRO_STATUS_SUCCESS:
@@ -956,6 +947,7 @@ _cairo_surface_fallback_paint (cairo_surface_t		*surface,
     cairo_status_t status;
     cairo_rectangle_int_t extents;
     cairo_bool_t is_bounded;
+    cairo_clip_path_t *clip_path = clip ? clip->path : NULL;
     cairo_box_t boxes_stack[32], *boxes = boxes_stack;
     int num_boxes = ARRAY_LENGTH (boxes_stack);
     cairo_traps_t traps;
@@ -988,6 +980,22 @@ _cairo_surface_fallback_paint (cairo_surface_t		*surface,
 	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 && clip_path->prev == NULL &&
+	_cairo_operator_bounded_by_mask (op))
+    {
+	return _cairo_surface_fill (surface, op, source,
+				    &clip_path->path,
+				    clip_path->fill_rule,
+				    clip_path->tolerance,
+				    clip_path->antialias,
+				    NULL);
+    }
+
     status = _cairo_traps_init_boxes (&traps, boxes, num_boxes);
     if (unlikely (status))
 	goto CLEANUP_BOXES;
commit 425b0e35e2d7d44d99e22169b98fc3ca05763650
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 18 14:25:25 2009 +0100

    Add xml surface
    
    A very simple surface that produces a hierarchical DAG in a simple XML
    format. It is intended to be used whilst debugging, for example with the
    automatic regression finding tools of cairo-sphinx, and with test suites
    that just want to verify that their code made a particular Cairo call.

diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index e9c87ea..ad4508e 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -320,6 +320,16 @@ enabled_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
 enabled_cairo_boilerplate_private += $(cairo_boilerplate_tee_private)
 enabled_cairo_boilerplate_sources += $(cairo_boilerplate_tee_sources)
 
+supported_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_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_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)
diff --git a/build/Makefile.win32.features b/build/Makefile.win32.features
index 8f0440f..884e232 100644
--- a/build/Makefile.win32.features
+++ b/build/Makefile.win32.features
@@ -28,3 +28,4 @@ CAIRO_HAS_PS_SURFACE=1
 CAIRO_HAS_PDF_SURFACE=1
 CAIRO_HAS_SVG_SURFACE=1
 CAIRO_HAS_TEST_SURFACES=0
+CAIRO_HAS_XML_SURFACE=1
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index f647b16..0432790 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -92,5 +92,8 @@ endif
 	@echo "#define CAIRO_HAS_IMAGE_SURFACE 1" >> src/cairo-features.h
 	@echo "#define CAIRO_HAS_META_SURFACE 1" >> src/cairo-features.h
 	@echo "#define CAIRO_HAS_TEE_SURFACE 1" >> src/cairo-features.h
+ifeq ($(CAIRO_HAS_XML_SURFACE),1)
+	@echo "#define CAIRO_HAS_XML_SURFACE 1" >> src/cairo-features.h
+endif
 	@echo "#define CAIRO_HAS_USER_FONT 1" >> src/cairo-features.h
 	@echo "#endif" >>  src/cairo-features.h
diff --git a/build/configure.ac.features b/build/configure.ac.features
index 55f3786..99fe284 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -359,6 +359,7 @@ AC_DEFUN([CAIRO_REPORT],
 	echo "  Image:         yes (always builtin)"
 	echo "  Meta:          yes (always builtin)"
 	echo "  Tee:           yes (always builtin)"
+	echo "  XML:           $use_xml"
 	echo "  Xlib:          $use_xlib"
 	echo "  Xlib Xrender:  $use_xlib_xrender"
 	echo "  Qt:            $use_qt"
diff --git a/configure.ac b/configure.ac
index ec712f7..af63c29 100644
--- a/configure.ac
+++ b/configure.ac
@@ -571,6 +571,10 @@ dnl ===========================================================================
 
 CAIRO_ENABLE_SURFACE_BACKEND(meta, meta, always)
 CAIRO_ENABLE_SURFACE_BACKEND(tee, tee, always)
+CAIRO_ENABLE_SURFACE_BACKEND(xml, xml, yes, [
+    use_xml=$have_libz
+    xml_NONPKGCONFIG_LIBS=-lz
+])
 
 dnl ===========================================================================
 
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 603e887..58eac08 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -99,6 +99,7 @@ cairo_sources = \
 	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 \
@@ -190,9 +191,14 @@ 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_pdf_headers = cairo-pdf.h
 cairo_pdf_private = cairo-pdf-surface-private.h
-cairo_pdf_sources = cairo-pdf-surface.c cairo-deflate-stream.c
+cairo_pdf_sources = cairo-pdf-surface.c
+if CAIRO_HAS_PDF_SURFACE
+req_cairo_deflate_stream_sources = $(cairo_deflate_stream_sources)
+endif
 
 cairo_svg_headers = cairo-svg.h
 cairo_svg_private = cairo-svg-surface-private.h
@@ -294,5 +300,15 @@ cairo_gallium_sources = drm/cairo-drm-gallium-surface.c
 cairo_script_headers = cairo-script.h
 cairo_script_sources = cairo-script-surface.c
 
+cairo_xml_headers = cairo-xml.h
+cairo_xml_sources = cairo-xml-surface.c
+if CAIRO_HAS_XML_SURFACE
+req_cairo_deflate_stream_sources = $(cairo_deflate_stream_sources)
+endif
+
 cairo_vg_headers = cairo-vg.h
 cairo_vg_sources = cairo-vg-surface.c
+
+cairo_sources += \
+		 $(req_cairo_deflate_stream_sources) \
+		 $(NULL)
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index 8b17d5c..5d705bc 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -430,6 +430,20 @@ enabled_cairo_headers += $(cairo_tee_headers)
 enabled_cairo_private += $(cairo_tee_private)
 enabled_cairo_sources += $(cairo_tee_sources)
 
+supported_cairo_headers += $(cairo_xml_headers)
+all_cairo_headers += $(cairo_xml_headers)
+all_cairo_private += $(cairo_xml_private)
+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_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)
diff --git a/src/cairo-base64-stream.c b/src/cairo-base64-stream.c
new file mode 100644
index 0000000..2b211ff
--- /dev/null
+++ b/src/cairo-base64-stream.c
@@ -0,0 +1,143 @@
+/* -*- 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 at 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., 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.
+ *
+ * Author(s):
+ *	Kristian Høgsberg <krh at redhat.com>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairoint.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
index 9d42ef4..791e801 100644
--- a/src/cairo-base85-stream.c
+++ b/src/cairo-base85-stream.c
@@ -102,9 +102,6 @@ _cairo_base85_stream_close (cairo_output_stream_t *base)
 	_cairo_output_stream_write (stream->output, five_tuple, stream->pending + 1);
     }
 
-    /* Mark end of base85 data */
-    _cairo_output_stream_printf (stream->output, "~>");
-
     return _cairo_output_stream_get_status (stream->output);
 }
 
diff --git a/src/cairo-output-stream-private.h b/src/cairo-output-stream-private.h
index 2b3d584..5243d21 100644
--- a/src/cairo-output-stream-private.h
+++ b/src/cairo-output-stream-private.h
@@ -184,6 +184,10 @@ _cairo_null_stream_create (void);
 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);
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index d4cfa9e..da74eb3 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1919,10 +1919,14 @@ _cairo_ps_surface_emit_base85_string (cairo_ps_surface_t    *surface,
     _cairo_output_stream_write (base85_stream, data, length);
 
     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;
 }
 
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 423e41a..4a89cf0 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -1142,7 +1142,7 @@ _emit_png_surface (cairo_script_surface_t *surface,
     if (unlikely (status))
 	return status;
 
-    _cairo_output_stream_puts (surface->ctx->stream, " >> image ");
+    _cairo_output_stream_puts (surface->ctx->stream, "~> >> image ");
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -1253,7 +1253,7 @@ _emit_image_surface (cairo_script_surface_t *surface,
 	    if (unlikely (status))
 		return status;
 	}
-	_cairo_output_stream_puts (surface->ctx->stream, " >> image ");
+	_cairo_output_stream_puts (surface->ctx->stream, "~> >> image ");
 
 	cairo_surface_destroy (&clone->base);
     }
@@ -1291,7 +1291,7 @@ _emit_image_surface (cairo_script_surface_t *surface,
 	if (unlikely (status))
 	    return status;
 
-	_cairo_output_stream_puts (surface->ctx->stream, " set-mime-data\n");
+	_cairo_output_stream_puts (surface->ctx->stream, "~> set-mime-data\n");
     }
 
     cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2,
@@ -1307,7 +1307,7 @@ _emit_image_surface (cairo_script_surface_t *surface,
 	if (unlikely (status))
 	    return status;
 
-	_cairo_output_stream_puts (surface->ctx->stream, " set-mime-data\n");
+	_cairo_output_stream_puts (surface->ctx->stream, "~> set-mime-data\n");
     }
 
     _cairo_output_stream_puts (surface->ctx->stream, "pattern");
@@ -2499,7 +2499,7 @@ _emit_type42_font (cairo_script_surface_t *surface,
 
     font_private = scaled_font->surface_private;
     _cairo_output_stream_printf (surface->ctx->stream,
-				 " >> font dup /f%lu exch def set-font-face",
+				 "~> >> font dup /f%lu exch def set-font-face",
 				 font_private->id);
 
     return status;
@@ -3003,7 +3003,7 @@ _cairo_script_surface_show_text_glyphs (void			    *abstract_surface,
 		    }
 
 		    _cairo_output_stream_printf (surface->ctx->stream,
-						 " %f <~", glyphs[n].x - x);
+						 "~> %f <~", glyphs[n].x - x);
 		    base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
 		} else {
 		    _cairo_output_stream_printf (surface->ctx->stream,
@@ -3025,7 +3025,7 @@ _cairo_script_surface_show_text_glyphs (void			    *abstract_surface,
 		    }
 
 		    _cairo_output_stream_printf (surface->ctx->stream,
-						 " %f %f <~",
+						 "~> %f %f <~",
 						 ix, iy);
 		    base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
 		} else {
@@ -3067,6 +3067,8 @@ _cairo_script_surface_show_text_glyphs (void			    *abstract_surface,
 	status2 = _cairo_output_stream_destroy (base85_stream);
 	if (status == CAIRO_STATUS_SUCCESS)
 	    status = status2;
+
+	_cairo_output_stream_printf (surface->ctx->stream, "~>");
     } else {
 	_cairo_output_stream_puts (surface->ctx->stream, " ]");
     }
@@ -3105,6 +3107,8 @@ _cairo_script_surface_show_text_glyphs (void			    *abstract_surface,
 	    status = _cairo_output_stream_destroy (base85_stream);
 	    if (unlikely (status))
 		return status;
+
+	    _cairo_output_stream_puts (surface->ctx->stream, "~>");
 	}
 
 	_cairo_output_stream_printf (surface->ctx->stream,
diff --git a/src/cairo-xml-surface.c b/src/cairo-xml-surface.c
new file mode 100644
index 0000000..ec4d3fd
--- /dev/null
+++ b/src/cairo-xml-surface.c
@@ -0,0 +1,1153 @@
+/* -*- 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., 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 Chris Wilson.
+ *
+ * Contributor(s):
+ *      Chris Wilson <chris at 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-output-stream-private.h"
+#include "cairo-meta-surface-private.h"
+
+#define static cairo_warn static
+
+typedef struct _cairo_xml_surface cairo_xml_surface_t;
+
+struct _cairo_xml {
+    cairo_status_t status;
+
+    int ref;
+
+    cairo_output_stream_t *stream;
+    int indent;
+};
+
+struct _cairo_xml_surface {
+    cairo_surface_t base;
+
+    cairo_xml_t *xml;
+
+    double width, height;
+};
+
+slim_hidden_proto (cairo_xml_for_meta_surface);
+
+static const cairo_xml_t _nil_xml = {
+    CAIRO_STATUS_NO_MEMORY,
+    -1
+};
+
+static const cairo_surface_backend_t _cairo_xml_surface_backend;
+
+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 */
+    };
+    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)
+{
+    static const char *names[] = {
+	"ARGB32",	/* CAIRO_FORMAT_ARGB32 */
+	"RGB24",	/* CAIRO_FORMAT_RGB24 */
+	"A8",		/* CAIRO_FORMAT_A8 */
+	"A1"		/* CAIRO_FORMAT_A1 */
+    };
+    assert (format < ARRAY_LENGTH (names));
+    return names[format];
+}
+
+static cairo_xml_t *
+_cairo_xml_create_internal (cairo_output_stream_t *stream)
+{
+    cairo_xml_t *xml;
+
+    xml = malloc (sizeof (cairo_xml_t));
+    if (unlikely (xml == NULL)) {
+	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+	return (cairo_xml_t *) &_nil_xml;
+    }
+
+    memset (xml, 0, sizeof (cairo_xml_t));
+    xml->status = CAIRO_STATUS_SUCCESS;
+    xml->ref = 1;
+    xml->indent = 0;
+
+    xml->stream = stream;
+
+    return xml;
+}
+
+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, sizeof (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, sizeof (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_status_t
+_cairo_xml_destroy_internal (cairo_xml_t *xml)
+{
+    cairo_status_t status;
+
+    assert (xml->ref > 0);
+    if (--xml->ref)
+	return _cairo_output_stream_flush (xml->stream);
+
+    status = _cairo_output_stream_destroy (xml->stream);
+
+    free (xml);
+
+    return status;
+}
+
+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_meta_surface_create (content, &extents);
+}
+
+static cairo_status_t
+_cairo_xml_surface_finish (void *abstract_surface)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+
+    return _cairo_xml_destroy_internal (surface->xml);
+}
+
+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,
+		      cairo_path_fixed_t *path)
+{
+    cairo_status_t status;
+
+    _cairo_xml_printf_start (xml, "<path>");
+    status = _cairo_path_fixed_interpret (path,
+					CAIRO_DIRECTION_FORWARD,
+					_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_start (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_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;
+
+    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;
+	}
+    }
+
+    _cairo_xml_printf_start (surface->xml, "<clip>");
+    _cairo_xml_indent (surface->xml, 2);
+
+    _cairo_xml_emit_path (surface->xml, &clip_path->path);
+    _cairo_xml_emit_double (surface->xml, "tolerance", clip_path->tolerance);
+    _cairo_xml_emit_string (surface->xml, "antialias",
+			    _antialias_to_string (clip_path->antialias));
+    _cairo_xml_emit_string (surface->xml, "fill-rule",
+			    _fill_rule_to_string (clip_path->fill_rule));
+
+    _cairo_xml_indent (surface->xml, -2);
+    _cairo_xml_printf_end (surface->xml, "</clip>");
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface,
+			      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)
+{
+    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'>",
+		       _cairo_fixed_to_double (linear->p1.x),
+		       _cairo_fixed_to_double (linear->p1.y),
+		       _cairo_fixed_to_double (linear->p2.x),
+		       _cairo_fixed_to_double (linear->p2.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'>",
+		       _cairo_fixed_to_double (radial->c1.x),
+		       _cairo_fixed_to_double (radial->c1.y),
+		       _cairo_fixed_to_double (radial->r1),
+		       _cairo_fixed_to_double (radial->c2.x),
+		       _cairo_fixed_to_double (radial->c2.y),
+		       _cairo_fixed_to_double (radial->r2));
+    _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_meta (source)) {
+	status = cairo_xml_for_meta_surface (xml, 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,
+			  cairo_clip_t		*clip)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+    cairo_xml_t *xml = surface->xml;
+    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,
+			  cairo_clip_t		*clip)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+    cairo_xml_t *xml = surface->xml;
+    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,
+			   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_xml_surface_t *surface = abstract_surface;
+    cairo_xml_t *xml = surface->xml;
+    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) {
+	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 (surface->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,
+			 cairo_path_fixed_t	*path,
+			 cairo_fill_rule_t	 fill_rule,
+			 double			 tolerance,
+			 cairo_antialias_t	 antialias,
+			 cairo_clip_t		*clip)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+    cairo_xml_t *xml = surface->xml;
+    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 (surface->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, NULL);
+    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_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,
+			   cairo_clip_t		    *clip,
+			   int			    *remaining_glyphs)
+{
+    cairo_xml_surface_t *surface = abstract_surface;
+    cairo_xml_t *xml = surface->xml;
+    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,
+    _cairo_xml_surface_create_similar,
+    _cairo_xml_surface_finish,
+    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_xml_t *xml,
+				    cairo_content_t content,
+				    double width,
+				    double height)
+{
+    cairo_xml_surface_t *surface;
+
+    if (unlikely (xml == NULL))
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
+
+    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,
+			 content);
+
+    surface->xml = xml;
+    xml->ref++;
+
+    surface->width = width;
+    surface->height = height;
+
+    return &surface->base;
+}
+
+cairo_xml_t *
+cairo_xml_create (const char *filename)
+{
+    cairo_output_stream_t *stream;
+
+    stream = _cairo_output_stream_create_for_filename (filename);
+    if (_cairo_output_stream_get_status (stream))
+	return (cairo_xml_t *) &_nil_xml;
+
+    return _cairo_xml_create_internal (stream);
+}
+
+cairo_xml_t *
+cairo_xml_create_for_stream (cairo_write_func_t	 write_func,
+			     void		*closure)
+{
+    cairo_output_stream_t *stream;
+
+    stream = _cairo_output_stream_create (write_func, NULL, closure);
+    if (_cairo_output_stream_get_status (stream))
+	return (cairo_xml_t *) &_nil_xml;
+
+    return _cairo_xml_create_internal (stream);
+}
+
+cairo_surface_t *
+cairo_xml_surface_create (cairo_xml_t *xml,
+			  cairo_content_t content,
+			  double width, double height)
+{
+    return _cairo_xml_surface_create_internal (xml, content, width, height);
+}
+
+cairo_status_t
+cairo_xml_for_meta_surface (cairo_xml_t *xml,
+			    cairo_surface_t *meta)
+{
+    cairo_box_t bbox;
+    cairo_rectangle_int_t extents;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    if (unlikely (xml->status))
+	return xml->status;
+
+    if (unlikely (meta->status))
+	return meta->status;
+
+    if (unlikely (! _cairo_surface_is_meta (meta)))
+	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    status = _cairo_meta_surface_get_bbox ((cairo_meta_surface_t *) meta,
+					   &bbox, NULL);
+    if (unlikely (status))
+	return status;
+
+    _cairo_box_round_to_rectangle (&bbox, &extents);
+    surface = _cairo_xml_surface_create_internal (xml,
+						  meta->content,
+						  extents.width,
+						  extents.height);
+    if (unlikely (surface->status))
+	return surface->status;
+
+    _cairo_xml_printf (xml,
+		       "<surface content='%s' width='%d' height='%d'>",
+		       _content_to_string (meta->content),
+		       extents.width, extents.height);
+    _cairo_xml_indent (xml, 2);
+
+    cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
+    status = cairo_meta_surface_replay (meta, surface);
+    cairo_surface_destroy (surface);
+
+    _cairo_xml_indent (xml, -2);
+    _cairo_xml_printf (xml, "</surface>");
+
+    return status;
+}
+slim_hidden_def (cairo_xml_for_meta_surface);
+
+void
+cairo_xml_destroy (cairo_xml_t *xml)
+{
+    cairo_status_t status_ignored;
+
+    if (xml == NULL || xml->ref < 0)
+	return;
+
+    status_ignored = _cairo_xml_destroy_internal (xml);
+}
diff --git a/src/cairo-xml.h b/src/cairo-xml.h
new file mode 100644
index 0000000..9bd1735
--- /dev/null
+++ b/src/cairo-xml.h
@@ -0,0 +1,72 @@
+/* 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., 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 Chris Wilson
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_XML_H
+#define CAIRO_XML_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_XML_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_xml cairo_xml_t;
+
+cairo_public cairo_xml_t *
+cairo_xml_create (const char *filename);
+
+cairo_public cairo_xml_t *
+cairo_xml_create_for_stream (cairo_write_func_t	 write_func,
+			     void		*closure);
+
+cairo_public void
+cairo_xml_destroy (cairo_xml_t *context);
+
+cairo_public cairo_surface_t *
+cairo_xml_surface_create (cairo_xml_t *xml,
+			  cairo_content_t content,
+			  double width, double height);
+
+cairo_public cairo_status_t
+cairo_xml_for_meta_surface (cairo_xml_t *context,
+			    cairo_surface_t *meta);
+
+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.h b/src/cairo.h
index 55c0fdd..17d91dc 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1949,6 +1949,7 @@ cairo_surface_status (cairo_surface_t *surface);
  * @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_t is used to describe the type of a given
  * surface. The surface types are also known as "backends" or "surface
@@ -1994,7 +1995,8 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_VG,
     CAIRO_SURFACE_TYPE_GL,
     CAIRO_SURFACE_TYPE_DRM,
-    CAIRO_SURFACE_TYPE_TEE
+    CAIRO_SURFACE_TYPE_TEE,
+    CAIRO_SURFACE_TYPE_XML
 } cairo_surface_type_t;
 
 cairo_public cairo_surface_type_t
diff --git a/util/Makefile.am b/util/Makefile.am
index a7ce7b3..5c110ab 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -15,9 +15,16 @@ SUBDIRS += cairo-sphinx
 endif
 endif
 
-AM_CPPFLAGS = -I$(top_srcdir)/src
+AM_CPPFLAGS = -I$(top_srcdir)/src \
+	      -I$(top_builddir)/src \
+	      -I$(top_srcdir)/util/cairo-script	\
+	      $(CAIRO_CFLAGS)
 
-EXTRA_PROGRAMS += show-traps show-edges show-events
+EXTRA_PROGRAMS += show-traps show-edges show-events trace-to-xml xml-to-trace
+
+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)
diff --git a/util/cairo-script/cairo-script-scanner.c b/util/cairo-script/cairo-script-scanner.c
index 3f96a30..99606a9 100644
--- a/util/cairo-script/cairo-script-scanner.c
+++ b/util/cairo-script/cairo-script-scanner.c
@@ -667,6 +667,92 @@ base85_end (csi_t *ctx, csi_scanner_t *scan, cairo_bool_t deflate)
 	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)
 {
@@ -782,6 +868,8 @@ scan_none:
 		deflate = 1;
 	    case '~':
 		goto scan_base85;
+	    case '{':
+		goto scan_base64;
 	    default:
 		csi_file_putc (src, next);
 		goto scan_hex;
@@ -1204,6 +1292,31 @@ scan_base85:
 	}
     }
     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
diff --git a/util/trace-to-xml.c b/util/trace-to-xml.c
new file mode 100644
index 0000000..763a565
--- /dev/null
+++ b/util/trace-to-xml.c
@@ -0,0 +1,77 @@
+#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)
+{
+    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_meta_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_xml_t *xml;
+
+	xml = cairo_xml_create_for_stream (stdio_write, out);
+	cairo_xml_for_meta_surface (xml, surface);
+	cairo_xml_destroy (xml);
+
+	cairo_surface_destroy (surface);
+    }
+
+    if (in != stdin)
+	fclose (in);
+    if (out != stdout)
+	fclose (out);
+
+    return 0;
+}
diff --git a/util/xml-to-trace.c b/util/xml-to-trace.c
new file mode 100644
index 0000000..13b7e57
--- /dev/null
+++ b/util/xml-to-trace.c
@@ -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;
+}
commit 6e0b3be903a6c3ad948de95f490cff92430429a6
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 18 10:55:54 2009 +0100

    Add cairo-sphinx utility for regression analysis
    
    sphinx is an alternate version of the current cairo-test-trace. It's
    purpose is to replay a live application (which may just be a replay of a
    trace) against a backend and its reference. The improvement over the
    original cairo-test-trace is that the reference backend may be from an
    older version of cairo.

diff --git a/configure.ac b/configure.ac
index 31d79cf..ec712f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -626,6 +626,11 @@ if test "x$have_bfd" = "xyes"; then
     AC_SUBST(BFD_LIBS)
 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")
+
 dnl ===========================================================================
 
 AC_ARG_ENABLE(some-floating-point,
@@ -670,6 +675,7 @@ util/Makefile
 util/cairo-fdr/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
diff --git a/util/Makefile.am b/util/Makefile.am
index d3b96a3..a7ce7b3 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -9,6 +9,12 @@ SUBDIRS += cairo-fdr
 endif
 endif
 
+if BUILD_SPHINX
+if CAIRO_HAS_SCRIPT_SURFACE
+SUBDIRS += cairo-sphinx
+endif
+endif
+
 AM_CPPFLAGS = -I$(top_srcdir)/src
 
 EXTRA_PROGRAMS += show-traps show-edges show-events
diff --git a/util/cairo-sphinx/.gitignore b/util/cairo-sphinx/.gitignore
new file mode 100644
index 0000000..56ecd5d
--- /dev/null
+++ b/util/cairo-sphinx/.gitignore
@@ -0,0 +1 @@
+cairo-sphinx
diff --git a/util/cairo-sphinx/Makefile.am b/util/cairo-sphinx/Makefile.am
new file mode 100644
index 0000000..845888c
--- /dev/null
+++ b/util/cairo-sphinx/Makefile.am
@@ -0,0 +1,40 @@
+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) $(glib_CFLAGS)
+cairo_sphinx_LDADD = \
+        $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+        $(top_builddir)/boilerplate/libcairoboilerplate.la	\
+	$(top_builddir)/src/libcairo.la 			\
+	$(glib_LIBS) 						\
+	$(CAIRO_LDADD)						\
+	-lrt
+cairo_sphinx_DEPENDENCIES = \
+        $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+        $(top_builddir)/boilerplate/libcairoboilerplate.la	\
+	$(top_builddir)/src/libcairo.la
+
+# Install rules to rebuild the libraries and add explicit dependencies
+$(top_builddir)/boilerplate/libcairoboilerplate.la: $(top_builddir)/src/libcairo.la
+	cd $(top_builddir)/boilerplate && $(MAKE) $(AM_MAKEFLAGS) libcairoboilerplate.la
+
+$(top_builddir)/src/libcairo.la:
+	cd $(top_builddir)/src && $(MAKE) $(AM_MAKEFLAGS) libcairo.la
+
+$(top_builddir)/util/cairo-script/libcairo-script-interpreter.la: $(top_builddir)/src/libcairo.la
+	cd $(top_builddir)/util/cairo-script && $(MAKE) $(AM_MAKEFLAGS) libcairo-script-interpreter.la
diff --git a/util/cairo-sphinx/fdr.c b/util/cairo-sphinx/fdr.c
new file mode 100644
index 0000000..993baba
--- /dev/null
+++ b/util/cairo-sphinx/fdr.c
@@ -0,0 +1,260 @@
+/* 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 <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_script_context_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_context_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_append, tee, script);
+
+	DLCALL (cairo_surface_set_user_data, surface,
+		&fdr_key, tee, fdr_surface_destroy);
+    }
+
+    return DLCALL (cairo_create, tee);
+}
+
+static void
+fdr_remove_tee (cairo_surface_t *surface)
+{
+    fdr_surface_reference (surface);
+    DLCALL (cairo_surface_set_user_data, surface, &fdr_key, NULL, NULL);
+    fdr_surface_destroy (surface);
+}
+
+void
+cairo_destroy (cairo_t *cr)
+{
+    cairo_surface_t *tee;
+
+    tee = DLCALL (cairo_get_target, cr);
+    DLCALL (cairo_destroy, cr);
+
+    if (DLCALL (cairo_surface_get_reference_count, tee) == 1)
+	fdr_remove_tee (fdr_tee_surface_index (tee, 0));
+}
+
+void
+cairo_pattern_destroy (cairo_pattern_t *pattern)
+{
+    if (DLCALL (cairo_pattern_get_type, pattern) == CAIRO_PATTERN_TYPE_SURFACE) {
+	cairo_surface_t *surface;
+
+	if (DLCALL (cairo_pattern_get_surface, pattern, &surface) == CAIRO_STATUS_SUCCESS &&
+	    DLCALL (cairo_surface_get_type, surface) == CAIRO_SURFACE_TYPE_TEE &&
+	    DLCALL (cairo_surface_get_reference_count, surface) == 2)
+	{
+	    fdr_remove_tee (fdr_tee_surface_index (surface, 0));
+	}
+    }
+
+    DLCALL (cairo_pattern_destroy, pattern);
+}
+
+cairo_surface_t *
+cairo_get_target (cairo_t *cr)
+{
+    cairo_surface_t *tee;
+
+    tee = DLCALL (cairo_get_target, cr);
+    return fdr_tee_surface_index (tee, 0);
+}
+
+cairo_surface_t *
+cairo_get_group_target (cairo_t *cr)
+{
+    cairo_surface_t *tee;
+
+    tee = DLCALL (cairo_get_group_target, cr);
+    return fdr_tee_surface_index (tee, 0);
+}
+
+cairo_pattern_t *
+cairo_pattern_create_for_surface (cairo_surface_t *surface)
+{
+    cairo_surface_t *tee;
+
+    tee = fdr_surface_get_tee (surface);
+    if (tee != NULL)
+	surface = tee;
+
+    return DLCALL (cairo_pattern_create_for_surface, surface);
+}
+
+cairo_status_t
+cairo_pattern_get_surface (cairo_pattern_t *pattern,
+			   cairo_surface_t **surface)
+{
+    cairo_status_t status;
+    cairo_surface_t *tee;
+
+    status = DLCALL (cairo_pattern_get_surface, pattern, surface);
+    if (status != CAIRO_STATUS_SUCCESS)
+	return status;
+
+    tee = fdr_surface_get_tee (*surface);
+    if (tee != NULL)
+	*surface = tee;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+void
+cairo_set_source_surface (cairo_t *cr,
+			  cairo_surface_t *surface,
+			  double x, double y)
+{
+    cairo_surface_t *tee;
+
+    tee = fdr_surface_get_tee (surface);
+    if (tee != NULL)
+	surface = tee;
+
+    DLCALL (cairo_set_source_surface, cr, surface, x, y);
+}
+
+cairo_surface_t *
+cairo_surface_create_similar (cairo_surface_t *surface,
+			      cairo_content_t content,
+			      int width, int height)
+{
+    cairo_surface_t *tee;
+
+    tee = fdr_surface_get_tee (surface);
+    if (tee != NULL)
+	surface = tee;
+
+    return DLCALL (cairo_surface_create_similar,
+		   surface, content, width, height);
+}
diff --git a/util/cairo-sphinx/sphinx.c b/util/cairo-sphinx/sphinx.c
new file mode 100644
index 0000000..d6ccfc7
--- /dev/null
+++ b/util/cairo-sphinx/sphinx.c
@@ -0,0 +1,1525 @@
+#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 */
+
+#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 ()) {		/* 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 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 *meta;
+    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->meta = 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)
+{
+    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;
+    }
+
+    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_script_context_t *ctx;
+    gchar *csum;
+    char buf[4096];
+    int i;
+
+    mkdir ("output", 0777);
+
+    ctx = cairo_script_context_create ("output/cairo-sphinx.trace");
+    cairo_script_from_meta_surface (ctx, clients->meta);
+    cairo_script_context_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.meta.png", csum);
+	cairo_surface_write_to_png (clients->meta, 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->meta = NULL;
+    clients->serial = 0;
+}
+
+static void
+clients_meta (struct clients *clients, int fd, char *info)
+{
+    sscanf (info, "%p %lu", &clients->meta, &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_t) -1);
+
+    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_t) -1;
+    cairo_t *cr;
+    int width, height, stride;
+    void *data;
+    unsigned long serial;
+
+    get_surface_size (source, &width, &height, &format);
+    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 (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_meta (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_META);
+    len = sprintf (buf, ".meta %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)
+{
+    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_META) {
+	    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_meta (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=meta 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_meta_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, ".meta", 5) == 0) {
+		    clients_meta (&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;
+}
commit c980affce05590f5f52273ba340463f00773c776
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 18 10:54:26 2009 +0100

    [script] Apply device offset when replaying meta surface
    
    As we set the size of the surface to fit the ink extents of the meta
    surface, we also need to ensure that the origin  of the script lies at the
    origin of the ink extents.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index a1ae508..423e41a 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -3416,6 +3416,7 @@ cairo_script_from_meta_surface (cairo_script_context_t *context,
 	return status;
 
     _cairo_box_round_to_rectangle (&bbox, &extents);
+
     surface = &_cairo_script_surface_create_internal (context,
 						      meta->content,
 						      extents.width,
@@ -3424,6 +3425,7 @@ cairo_script_from_meta_surface (cairo_script_context_t *context,
     if (unlikely (surface->status))
 	return surface->status;
 
+    cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
     status = cairo_meta_surface_replay (meta, surface);
     cairo_surface_destroy (surface);
 
commit eb33f842dc9a2555ba8f7948f49a8335db951959
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 17 11:38:52 2009 +0100

    [meta] Missing status check
    
    We need to check the status on the created image before use.

diff --git a/src/cairo-meta-surface.c b/src/cairo-meta-surface.c
index f091a59..dcff816 100644
--- a/src/cairo-meta-surface.c
+++ b/src/cairo-meta-surface.c
@@ -257,6 +257,8 @@ _cairo_meta_surface_acquire_source_image (void			 *abstract_surface,
     image = _cairo_image_surface_create_with_content (surface->content,
 						      surface->extents.width,
 						      surface->extents.height);
+    if (unlikely (image->status))
+	return image->status;
 
     cairo_surface_set_device_offset (image,
 				     -surface->extents.x,
commit ce6a2cc5d2cb8a299759d764de2e7d2b6b655cb4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 17 10:31:47 2009 +0100

    [wrapper] Always copy clip
    
    We always need to make a local copy of the clip as the backends are free
    to modify it as they process the operation.

diff --git a/src/cairo-surface-wrapper.c b/src/cairo-surface-wrapper.c
index 0d1dc51..413ad3e 100644
--- a/src/cairo-surface-wrapper.c
+++ b/src/cairo-surface-wrapper.c
@@ -88,14 +88,18 @@ _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
     if (clip && clip->all_clipped)
 	return CAIRO_STATUS_SUCCESS;
 
-    if (clip != NULL &&
-	_cairo_surface_wrapper_needs_device_transform (wrapper,
-						       &device_transform))
-    {
-	status = _cairo_clip_init_copy_transformed (&clip_copy, clip,
-						    &device_transform);
-	if (unlikely (status))
-	    goto FINISH;
+    if (clip != NULL) {
+	if (_cairo_surface_wrapper_needs_device_transform (wrapper,
+							   &device_transform))
+	{
+	    status = _cairo_clip_init_copy_transformed (&clip_copy, clip,
+							&device_transform);
+	    if (unlikely (status))
+		goto FINISH;
+
+	} else {
+	    _cairo_clip_init_copy (&clip_copy, clip);
+	}
 
 	dev_clip = &clip_copy;
     }
@@ -125,14 +129,18 @@ _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
     if (clip && clip->all_clipped)
 	return CAIRO_STATUS_SUCCESS;
 
-    if (clip != NULL &&
-	_cairo_surface_wrapper_needs_device_transform (wrapper,
-						       &device_transform))
-    {
-	status = _cairo_clip_init_copy_transformed (&clip_copy, clip,
-						    &device_transform);
-	if (unlikely (status))
-	    goto FINISH;
+    if (clip != NULL) {
+	if (_cairo_surface_wrapper_needs_device_transform (wrapper,
+							   &device_transform))
+	{
+	    status = _cairo_clip_init_copy_transformed (&clip_copy, clip,
+							&device_transform);
+	    if (unlikely (status))
+		goto FINISH;
+
+	} else {
+	    _cairo_clip_init_copy (&clip_copy, clip);
+	}
 
 	dev_clip = &clip_copy;
     }
@@ -195,6 +203,11 @@ _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
 	cairo_matrix_multiply (&dev_ctm_inverse,
 			       &device_transform,
 			       &dev_ctm_inverse);
+    } else {
+	if (clip != NULL) {
+	    dev_clip = &clip_copy;
+	    _cairo_clip_init_copy (&clip_copy, clip);
+	}
     }
 
     status = _cairo_surface_stroke (wrapper->target, op, source,
@@ -266,6 +279,11 @@ _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
 	cairo_matrix_multiply (&dev_ctm_inverse,
 			       &device_transform,
 			       &dev_ctm_inverse);
+    } else {
+	if (clip != NULL) {
+	    dev_clip = &clip_copy;
+	    _cairo_clip_init_copy (&clip_copy, clip);
+	}
     }
 
     status = _cairo_surface_fill_stroke (wrapper->target,
@@ -325,6 +343,11 @@ _cairo_surface_wrapper_fill (cairo_surface_wrapper_t	*wrapper,
 
 	    dev_clip = &clip_copy;
 	}
+    } else {
+	if (clip != NULL) {
+	    dev_clip = &clip_copy;
+	    _cairo_clip_init_copy (&clip_copy, clip);
+	}
     }
 
     status = _cairo_surface_fill (wrapper->target, op, source,
@@ -393,6 +416,11 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
 					  &dev_glyphs[i].x,
 					  &dev_glyphs[i].y);
 	}
+    } else {
+	if (clip != NULL) {
+	    dev_clip = &clip_copy;
+	    _cairo_clip_init_copy (&clip_copy, clip);
+	}
     }
 
     status = _cairo_surface_show_text_glyphs (wrapper->target, op, source,
commit 6ff711b6305a9cf65e584d92258a6fa4e78c31ef
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 17 10:11:32 2009 +0100

    [matrix] Improve bbox finding for translation matrix
    
    If the matrix is a pure translation matrix than we can skip determination
    of the extents and just translate the input bbox.

diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
index 94ed0e9..bf20ee4 100644
--- a/src/cairo-matrix.c
+++ b/src/cairo-matrix.c
@@ -371,37 +371,39 @@ _cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix,
     double min_x, max_x;
     double min_y, max_y;
 
-    if (_cairo_matrix_is_identity (matrix)) {
-	if (is_tight)
-	    *is_tight = TRUE;
-
-	return;
-    }
-
     if (matrix->xy == 0. && matrix->yx == 0.) {
 	/* non-rotation/skew matrix, just map the two extreme points */
-	quad_x[0] = *x1;
-	quad_y[0] = *y1;
-	cairo_matrix_transform_distance (matrix, &quad_x[0], &quad_y[0]);
-
-	quad_x[1] = *x2;
-	quad_y[1] = *y2;
-	cairo_matrix_transform_distance (matrix, &quad_x[1], &quad_y[1]);
-
-	if (quad_x[0] < quad_x[1]) {
-	    *x1 = quad_x[0] + matrix->x0;
-	    *x2 = quad_x[1] + matrix->x0;
-	} else {
-	    *x1 = quad_x[1] + matrix->x0;
-	    *x2 = quad_x[0] + matrix->x0;
+
+	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 (quad_y[0] < quad_y[1]) {
-	    *y1 = quad_y[0] + matrix->y0;
-	    *y2 = quad_y[1] + matrix->y0;
-	} else {
-	    *y1 = quad_y[1] + matrix->y0;
-	    *y2 = quad_y[0] + matrix->y0;
+	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)
commit cd7b27ff5c01a533c2c065c4b455ad19df2be3bb
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 16 21:08:22 2009 +0100

    Add 'flight-data-recorder' utility.
    
    This is a simple variation on cairo-trace that wraps records the last 16
    contexts by wrapping the target surface inside a tee surface, along with a
    meta/recording surface. Then on receipt of a SIGUSR1, those last 16
    contexts are played via a script-surface into /tmp/fdr.trace.
    
    Mostly proof-of-concept, it seems to be causing a number of rendering
    glitches whilst testing with firefox -- otherwise, it seems to works.

diff --git a/configure.ac b/configure.ac
index c02b1d2..31d79cf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -667,6 +667,7 @@ test/Makefile
 test/pdiff/Makefile
 perf/Makefile
 util/Makefile
+util/cairo-fdr/Makefile
 util/cairo-script/Makefile
 util/cairo-script/examples/Makefile
 util/cairo-trace/Makefile
diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h
index dc16ea8..cdc3c73 100644
--- a/src/cairo-scaled-font-private.h
+++ b/src/cairo-scaled-font-private.h
@@ -123,4 +123,7 @@ struct _cairo_scaled_font {
     const cairo_scaled_font_backend_t *backend;
 };
 
+cairo_private void
+_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font);
+
 #endif /* CAIRO_SCALED_FONT_PRIVATE_H */
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 3d357a2..d8406f7 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -838,6 +838,22 @@ _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font)
     _cairo_user_data_array_fini (&scaled_font->user_data);
 }
 
+/* XXX: allow multiple backends to share the font */
+void
+_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font)
+{
+    if (scaled_font->surface_backend == NULL)
+	return;
+
+    _cairo_scaled_font_reset_cache (scaled_font);
+
+    if (scaled_font->surface_backend->scaled_font_fini != NULL)
+	scaled_font->surface_backend->scaled_font_fini (scaled_font);
+
+    scaled_font->surface_backend = NULL;
+    scaled_font->surface_private = NULL;
+}
+
 void
 _cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
 {
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 60f3b9a..a1ae508 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -51,6 +51,7 @@
 #include "cairo-list-private.h"
 #include "cairo-meta-surface-private.h"
 #include "cairo-output-stream-private.h"
+#include "cairo-scaled-font-private.h"
 #include "cairo-surface-clipper-private.h"
 #include "cairo-surface-wrapper-private.h"
 
@@ -445,10 +446,14 @@ static cairo_status_t
 _emit_surface (cairo_script_surface_t *surface)
 {
     _cairo_output_stream_printf (surface->ctx->stream,
-				 "<< /content %s /width %f /height %f",
-				 _content_to_string (surface->base.content),
-				 surface->width,
-				 surface->height);
+				 "<< /content //%s",
+				 _content_to_string (surface->base.content));
+    if (surface->width != -1 && surface->height != -1) {
+	_cairo_output_stream_printf (surface->ctx->stream,
+				     " /width %f /height %f",
+				     surface->width,
+				     surface->height);
+    }
 
     if (surface->base.x_fallback_resolution !=
 	CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT ||
@@ -2573,8 +2578,11 @@ _emit_scaled_font (cairo_script_surface_t *surface,
 
     surface->cr.current_scaled_font = scaled_font;
 
-    assert (scaled_font->surface_backend == NULL ||
-	    scaled_font->surface_backend == &_cairo_script_surface_backend);
+    if (! (scaled_font->surface_backend == NULL ||
+	   scaled_font->surface_backend == &_cairo_script_surface_backend))
+    {
+	_cairo_scaled_font_revoke_ownership (scaled_font);
+    }
 
     font_private = scaled_font->surface_private;
     if (font_private == NULL) {
@@ -3384,6 +3392,44 @@ cairo_script_surface_create_for_target (cairo_script_context_t *context,
 						   target)->base;
 }
 
+cairo_status_t
+cairo_script_from_meta_surface (cairo_script_context_t *context,
+				cairo_surface_t *meta)
+{
+    cairo_box_t bbox;
+    cairo_rectangle_int_t extents;
+    cairo_surface_t *surface;
+    cairo_status_t status;
+
+    if (unlikely (context->status))
+	return context->status;
+
+    if (unlikely (meta->status))
+	return meta->status;
+
+    if (unlikely (! _cairo_surface_is_meta (meta)))
+	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+    status = _cairo_meta_surface_get_bbox ((cairo_meta_surface_t *) meta,
+					   &bbox, NULL);
+    if (unlikely (status))
+	return status;
+
+    _cairo_box_round_to_rectangle (&bbox, &extents);
+    surface = &_cairo_script_surface_create_internal (context,
+						      meta->content,
+						      extents.width,
+						      extents.height,
+						      NULL)->base;
+    if (unlikely (surface->status))
+	return surface->status;
+
+    status = cairo_meta_surface_replay (meta, surface);
+    cairo_surface_destroy (surface);
+
+    return status;
+}
+
 void
 cairo_script_context_destroy (cairo_script_context_t *context)
 {
diff --git a/src/cairo-script.h b/src/cairo-script.h
index 5d85941..c418350 100644
--- a/src/cairo-script.h
+++ b/src/cairo-script.h
@@ -81,6 +81,10 @@ cairo_public cairo_surface_t *
 cairo_script_surface_create_for_target (cairo_script_context_t *context,
 					cairo_surface_t *target);
 
+cairo_public cairo_status_t
+cairo_script_from_meta_surface (cairo_script_context_t *context,
+				cairo_surface_t *meta);
+
 CAIRO_END_DECLS
 
 #else  /*CAIRO_HAS_SCRIPT_SURFACE*/
diff --git a/src/cairo.h b/src/cairo.h
index 9be5c70..55c0fdd 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -2198,6 +2198,10 @@ cairo_public void
 cairo_tee_surface_append (cairo_surface_t *surface,
 			  cairo_surface_t *target);
 
+cairo_public cairo_surface_t *
+cairo_tee_surface_index (cairo_surface_t *abstract_surface,
+			 int index);
+
 /* Pattern creation functions */
 
 cairo_public cairo_pattern_t *
diff --git a/util/Makefile.am b/util/Makefile.am
index 79bc336..d3b96a3 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -4,6 +4,9 @@ SUBDIRS = . cairo-script
 
 if BUILD_TRACE
 SUBDIRS += cairo-trace
+if CAIRO_HAS_SCRIPT_SURFACE
+SUBDIRS += cairo-fdr
+endif
 endif
 
 AM_CPPFLAGS = -I$(top_srcdir)/src
diff --git a/util/cairo-fdr/Makefile.am b/util/cairo-fdr/Makefile.am
new file mode 100644
index 0000000..5cd5798
--- /dev/null
+++ b/util/cairo-fdr/Makefile.am
@@ -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
index 0000000..4675473
--- /dev/null
+++ b/util/cairo-fdr/fdr.c
@@ -0,0 +1,304 @@
+/* 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 <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 *meta, cairo_script_context_t *ctx)
+{
+    if (meta != NULL) {
+	DLCALL (cairo_script_context_write_comment, ctx, "--- fdr ---", -1);
+	DLCALL (cairo_script_from_meta_surface, ctx, meta);
+    }
+}
+
+static void
+fdr_dump_ringbuffer (void)
+{
+    cairo_script_context_t *ctx;
+    int n;
+
+    ctx = DLCALL (cairo_script_context_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_script_context_destroy, ctx);
+}
+
+static void
+fdr_sighandler (int sig)
+{
+    fdr_dump = 1;
+}
+
+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);
+	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_meta_surface_create, content, &extents);
+	DLCALL (cairo_tee_surface_append, 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);
+}
commit 658cdc7c9aac23f82f3ea5db8df10844aeb3ac75
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 16 18:04:54 2009 +0100

    Introduce cairo_tee_surface_t
    
    Add a new surface type that multiplies it input onto several output
    surfaces. The only limitation is that it requires a master surface that is
    used whenever we need to query surface options, such as font options and
    extents.

diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index e58eed9..e9c87ea 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -312,6 +312,14 @@ enabled_cairo_boilerplate_headers += $(cairo_boilerplate_meta_headers)
 enabled_cairo_boilerplate_private += $(cairo_boilerplate_meta_private)
 enabled_cairo_boilerplate_sources += $(cairo_boilerplate_meta_sources)
 
+supported_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_sources += $(cairo_boilerplate_tee_sources)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_tee_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_tee_private)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_tee_sources)
+
 supported_cairo_boilerplate_headers += $(cairo_boilerplate_user_headers)
 all_cairo_boilerplate_headers += $(cairo_boilerplate_user_headers)
 all_cairo_boilerplate_private += $(cairo_boilerplate_user_private)
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index 88877ed..f647b16 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -91,5 +91,6 @@ ifeq ($(CAIRO_HAS_TEST_SURFACES),1)
 endif
 	@echo "#define CAIRO_HAS_IMAGE_SURFACE 1" >> src/cairo-features.h
 	@echo "#define CAIRO_HAS_META_SURFACE 1" >> src/cairo-features.h
+	@echo "#define CAIRO_HAS_TEE_SURFACE 1" >> src/cairo-features.h
 	@echo "#define CAIRO_HAS_USER_FONT 1" >> src/cairo-features.h
 	@echo "#endif" >>  src/cairo-features.h
diff --git a/build/configure.ac.features b/build/configure.ac.features
index 154774c..55f3786 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -358,6 +358,7 @@ AC_DEFUN([CAIRO_REPORT],
 	echo "The following surface backends:"
 	echo "  Image:         yes (always builtin)"
 	echo "  Meta:          yes (always builtin)"
+	echo "  Tee:           yes (always builtin)"
 	echo "  Xlib:          $use_xlib"
 	echo "  Xlib Xrender:  $use_xlib_xrender"
 	echo "  Qt:            $use_qt"
diff --git a/configure.ac b/configure.ac
index 445cb67..c02b1d2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -570,6 +570,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(image, image, always, [
 dnl ===========================================================================
 
 CAIRO_ENABLE_SURFACE_BACKEND(meta, meta, always)
+CAIRO_ENABLE_SURFACE_BACKEND(tee, tee, always)
 
 dnl ===========================================================================
 
diff --git a/src/Makefile.sources b/src/Makefile.sources
index c8da236..603e887 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -88,6 +88,7 @@ cairo_private = \
 	cairo-surface-private.h \
 	cairo-surface-clipper-private.h \
 	cairo-surface-wrapper-private.h \
+	cairo-tee-surface-private.h \
 	cairo-types-private.h \
 	cairo-user-font-private.h \
 	cairo-wideint-private.h \
@@ -146,10 +147,11 @@ cairo_sources = \
 	cairo-surface-fallback.c \
 	cairo-surface-clipper.c \
 	cairo-surface-wrapper.c \
-	cairo-tor-scan-converter.c \
 	cairo-system.c \
-	cairo-traps.c \
+	cairo-tee-surface.c \
+	cairo-tor-scan-converter.c \
 	cairo-toy-font-face.c \
+	cairo-traps.c \
 	cairo-unicode.c \
 	cairo-user-font.c \
 	cairo-version.c \
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index 048ead0..8b17d5c 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -422,6 +422,14 @@ enabled_cairo_headers += $(cairo_meta_headers)
 enabled_cairo_private += $(cairo_meta_private)
 enabled_cairo_sources += $(cairo_meta_sources)
 
+supported_cairo_headers += $(cairo_tee_headers)
+all_cairo_headers += $(cairo_tee_headers)
+all_cairo_private += $(cairo_tee_private)
+all_cairo_sources += $(cairo_tee_sources)
+enabled_cairo_headers += $(cairo_tee_headers)
+enabled_cairo_private += $(cairo_tee_private)
+enabled_cairo_sources += $(cairo_tee_sources)
+
 supported_cairo_headers += $(cairo_user_headers)
 all_cairo_headers += $(cairo_user_headers)
 all_cairo_private += $(cairo_user_private)
diff --git a/src/cairo-surface-wrapper-private.h b/src/cairo-surface-wrapper-private.h
index 3ffd9ab..7da4cda 100644
--- a/src/cairo-surface-wrapper-private.h
+++ b/src/cairo-surface-wrapper-private.h
@@ -142,6 +142,10 @@ 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);
 
diff --git a/src/cairo-surface-wrapper.c b/src/cairo-surface-wrapper.c
index 12d6783..0d1dc51 100644
--- a/src/cairo-surface-wrapper.c
+++ b/src/cairo-surface-wrapper.c
@@ -429,6 +429,13 @@ _cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper,
     return _cairo_surface_get_extents (wrapper->target, extents);
 }
 
+void
+_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t    *wrapper,
+					 cairo_font_options_t	    *options)
+{
+    return cairo_surface_get_font_options (wrapper->target, options);
+}
+
 cairo_surface_t *
 _cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper)
 {
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 84a3012..624f71f 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -42,6 +42,7 @@
 #include "cairo-clip-private.h"
 #include "cairo-meta-surface-private.h"
 #include "cairo-region-private.h"
+#include "cairo-tee-surface-private.h"
 
 #define DEFINE_NIL_SURFACE(status, name)			\
 const cairo_surface_t name = {					\
@@ -1528,6 +1529,16 @@ _cairo_surface_clone_similar (cairo_surface_t  *surface,
     if (unlikely (surface->finished))
 	return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
+    if (src->type == CAIRO_SURFACE_TYPE_TEE) {
+	cairo_surface_t *match;
+
+	match = _cairo_tee_surface_find_match (src,
+					       surface->backend,
+					       content);
+	if (match != NULL)
+	    src = match;
+    }
+
     if (surface->backend->clone_similar != NULL) {
 	status = surface->backend->clone_similar (surface, src,
 						  content,
diff --git a/src/cairo-tee-surface-private.h b/src/cairo-tee-surface-private.h
new file mode 100644
index 0000000..258816b
--- /dev/null
+++ b/src/cairo-tee-surface-private.h
@@ -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., 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):
+ *	Chris Wilson <chris at 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
index 0000000..0a7842f
--- /dev/null
+++ b/src/cairo-tee-surface.c
@@ -0,0 +1,558 @@
+/* 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., 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 Worth <cworth at cworth.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+/* This surface supports redirecting all its input to multiple surfaces.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-tee-surface-private.h"
+#include "cairo-surface-wrapper-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_append);
+
+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_append (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 meta surface for our snapshots */
+    if (_cairo_surface_is_meta (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_meta (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,
+			  cairo_clip_t		*clip)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    int n, num_slaves;
+    cairo_status_t status;
+
+    status = _cairo_surface_wrapper_paint (&surface->master, op, source, clip);
+    if (unlikely (status))
+	return 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_STATUS_SUCCESS;
+}
+
+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,
+			 cairo_clip_t		*clip)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    int n, num_slaves;
+    cairo_status_t status;
+
+    status = _cairo_surface_wrapper_mask (&surface->master,
+					  op, source, mask, clip);
+    if (unlikely (status))
+	return 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_mask (&slaves[n],
+					      op, source, mask, clip);
+	if (unlikely (status))
+	    return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_tee_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_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    int n, num_slaves;
+    cairo_status_t status;
+
+    status = _cairo_surface_wrapper_stroke (&surface->master,
+					    op, source,
+					    path, style,
+					    ctm, ctm_inverse,
+					    tolerance, antialias,
+					    clip);
+    if (unlikely (status))
+	return 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_stroke (&slaves[n],
+						op, source,
+						path, style,
+						ctm, ctm_inverse,
+						tolerance, antialias,
+						clip);
+	if (unlikely (status))
+	    return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_tee_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_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    int n, num_slaves;
+    cairo_status_t status;
+
+    status = _cairo_surface_wrapper_fill (&surface->master,
+					  op, source,
+					  path, fill_rule,
+					  tolerance, antialias,
+					  clip);
+    if (unlikely (status))
+	return 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_fill (&slaves[n],
+					      op, source,
+					      path, fill_rule,
+					      tolerance, antialias,
+					      clip);
+	if (unlikely (status))
+	    return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+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,
+				     cairo_clip_t	    *clip)
+{
+    cairo_tee_surface_t *surface = abstract_surface;
+    cairo_surface_wrapper_t *slaves;
+    int n, num_slaves;
+    cairo_status_t status;
+    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);
+
+    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);
+    if (unlikely (status))
+	goto CLEANUP;
+
+    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;
+    }
+
+  CLEANUP:
+    free (glyphs_copy);
+    return status;
+}
+
+static const cairo_surface_backend_t cairo_tee_surface_backend = {
+    CAIRO_SURFACE_TYPE_TEE,
+    _cairo_tee_surface_create_similar,
+    _cairo_tee_surface_finish,
+    _cairo_tee_surface_acquire_source_image,
+    _cairo_tee_surface_release_source_image,
+    NULL, NULL, /* 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 */
+    _cairo_tee_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    _cairo_tee_surface_get_font_options,
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+    _cairo_tee_surface_paint,
+    _cairo_tee_surface_mask,
+    _cairo_tee_surface_stroke,
+    _cairo_tee_surface_fill,
+    NULL, /* replaced by show_text_glyphs */
+
+    _cairo_tee_surface_snapshot,
+    NULL, /* is_similar */
+    NULL, /* fill_stroke */
+    NULL, /* create_solid_pattern_surface */
+    NULL, /* can_repaint_solid_pattern_surface */
+
+    _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->content);
+
+    _cairo_surface_wrapper_init (&surface->master, master);
+    /* we trust that these are already set and remain constant */
+    surface->base.device_transform = master->device_transform;
+    surface->base.device_transform_inverse = master->device_transform_inverse;
+
+    _cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t));
+
+    return &surface->base;
+}
+slim_hidden_def (cairo_tee_surface_create);
+
+void
+cairo_tee_surface_append (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 (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_append);
+
+cairo_surface_t *
+cairo_tee_surface_index (cairo_surface_t *abstract_surface,
+			 int index)
+{
+    cairo_tee_surface_t *surface;
+
+    if (unlikely (abstract_surface->status))
+	return _cairo_surface_create_in_error (abstract_surface->status);
+
+    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.h b/src/cairo.h
index e0c5343..9be5c70 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1948,6 +1948,7 @@ cairo_surface_status (cairo_surface_t *surface);
  * @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_t is used to describe the type of a given
  * surface. The surface types are also known as "backends" or "surface
@@ -1992,7 +1993,8 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_META,
     CAIRO_SURFACE_TYPE_VG,
     CAIRO_SURFACE_TYPE_GL,
-    CAIRO_SURFACE_TYPE_DRM
+    CAIRO_SURFACE_TYPE_DRM,
+    CAIRO_SURFACE_TYPE_TEE
 } cairo_surface_type_t;
 
 cairo_public cairo_surface_type_t
@@ -2187,6 +2189,15 @@ cairo_public cairo_status_t
 cairo_meta_surface_replay (cairo_surface_t *surface,
 			   cairo_surface_t *target);
 
+/* Tee-surface functions */
+
+cairo_public cairo_surface_t *
+cairo_tee_surface_create (cairo_surface_t *master);
+
+cairo_public void
+cairo_tee_surface_append (cairo_surface_t *surface,
+			  cairo_surface_t *target);
+
 /* Pattern creation functions */
 
 cairo_public cairo_pattern_t *
commit 4ae7186719b25f052b875549cc5377e16a557512
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 16 17:07:00 2009 +0100

    [script] Correct emission of get_target() for a popped context
    
    If the context is no longer on the operand stack, then we need to recall
    the surface via a definition.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 15d1ab0..60f3b9a 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -402,30 +402,31 @@ _get_target (cairo_script_surface_t *surface)
 {
     cairo_script_context_t *ctx = surface->ctx;
 
-    if (! target_is_active (surface)) {
-	int depth = target_depth (surface);
-	if (ctx->active) {
-	    if (surface->defined) {
-		_cairo_output_stream_printf (ctx->stream, "s%u ",
-					     surface->base.unique_id);
-	    } else {
+    if (surface->defined) {
+	_cairo_output_stream_printf (ctx->stream, "s%u ",
+				     surface->base.unique_id);
+    } else {
+	assert (! cairo_list_is_empty (&surface->operand.link));
+	if (! target_is_active (surface)) {
+	    int depth = target_depth (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 (surface->ctx->stream,
-					     "exch\n");
 	    } else {
-		_cairo_output_stream_printf (surface->ctx->stream,
-					     "%d -1 roll\n",
-					     depth);
+		if (depth == 1) {
+		    _cairo_output_stream_puts (surface->ctx->stream,
+					       "exch\n");
+		} else {
+		    _cairo_output_stream_printf (surface->ctx->stream,
+						 "%d -1 roll\n",
+						 depth);
+		}
+		_cairo_output_stream_puts (ctx->stream, "/target get ");
+		target_push (surface);
 	    }
+	} else {
 	    _cairo_output_stream_puts (ctx->stream, "/target get ");
-	    target_push (surface);
 	}
-    } else {
-	_cairo_output_stream_puts (ctx->stream, "/target get ");
     }
 }
 
@@ -1598,11 +1599,14 @@ _emit_scaling_matrix (cairo_script_surface_t *surface,
 		      const cairo_matrix_t *ctm,
 		      cairo_bool_t *matrix_updated)
 {
+    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.;
@@ -1611,6 +1615,10 @@ _emit_scaling_matrix (cairo_script_surface_t *surface,
     if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) {
 	_cairo_output_stream_puts (surface->ctx->stream,
 				   "identity set-matrix\n");
+    } else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) {
+	_cairo_output_stream_printf (surface->ctx->stream,
+				   "%f %f scale\n",
+				   ctm->xx, ctm->yy);
     } else {
 	_cairo_output_stream_printf (surface->ctx->stream,
 				   "[%f %f %f %f 0 0] set-matrix\n",
@@ -2234,6 +2242,7 @@ _cairo_script_surface_fill (void			*abstract_surface,
     cairo_script_surface_t *surface = abstract_surface;
     cairo_bool_t matrix_updated = FALSE;
     cairo_status_t status;
+    cairo_box_t box;
 
     if (op == CAIRO_OPERATOR_CLEAR) {
 	if (surface->is_clear)
@@ -2258,9 +2267,11 @@ _cairo_script_surface_fill (void			*abstract_surface,
     if (unlikely (status))
 	return status;
 
-    status = _emit_fill_rule (surface, fill_rule);
-    if (unlikely (status))
-	return status;
+    if (! _cairo_path_fixed_is_box (path, &box)) {
+	status = _emit_fill_rule (surface, fill_rule);
+	if (unlikely (status))
+	    return status;
+    }
 
     if (! path->is_rectilinear) {
 	status = _emit_tolerance (surface, tolerance, matrix_updated);
commit 8d1bf830c0137eac837091bda92a636c0fcb0456
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 16 14:20:35 2009 +0100

    Fix errors found by clang
    
    Shadowed variables, unused writes and some dead code.

diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index bcb9477..de1481e 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -1963,8 +1963,6 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t     *surface,
 
 #undef IMAGE_DICTIONARY
 
-    if (image_res == NULL)
-	*image_res = surface->pdf_stream.self;
     _cairo_output_stream_write (surface->output, rgb, rgb_size);
     status = _cairo_pdf_surface_close_stream (surface);
 
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 62a1256..cf3bdfe 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -822,7 +822,7 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 	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;
+	cairo_xlib_visual_info_t *visual_info = NULL;
 
 	if (surface->visual == NULL || surface->visual->class == TrueColor) {
 	    cairo_bool_t has_alpha;
@@ -888,7 +888,7 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 		int dither_adjustment = dither_row[x_off];
 
 		in_pixel = XGetPixel (ximage, x, y);
-		if (surface->visual == NULL || surface->visual->class == TrueColor) {
+		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 |
@@ -4141,7 +4141,6 @@ _emit_glyphs_chunk (cairo_xlib_surface_t *dst,
     if (n) {
 	elts[nelt].nchars = n;
 	nelt++;
-	n = 0;
     }
 
     /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the
diff --git a/util/cairo-script/cairo-script-file.c b/util/cairo-script/cairo-script-file.c
index 9ebfdaf..5779db5 100644
--- a/util/cairo-script/cairo-script-file.c
+++ b/util/cairo-script/cairo-script-file.c
@@ -316,8 +316,7 @@ _ascii85_decode (csi_file_t *file)
 	    data->buf[n+2] = 0;
 	    data->buf[n+3] = 0;
 	} else if (v == '~') {
-	    v = _getc_skip_whitespace (file->src);
-	    /* c == '>' || IO_ERROR */
+	    _getc_skip_whitespace (file->src); /* == '>' || IO_ERROR */
 	    data->eod = TRUE;
 	    break;
 	} else if (v < '!' || v > 'u') {
@@ -331,8 +330,7 @@ _ascii85_decode (csi_file_t *file)
 	    for (i = 1; i < 5; i++) {
 		int c = _getc_skip_whitespace (file->src);
 		if (c == '~') { /* short tuple */
-		    c = _getc_skip_whitespace (file->src);
-		    /* c == '>' || IO_ERROR */
+		    _getc_skip_whitespace (file->src); /* == '>' || IO_ERROR */
 		    data->eod = TRUE;
 		    switch (i) {
 		    case 0:
@@ -958,14 +956,12 @@ csi_file_putc (csi_file_t *file, int c)
 void
 csi_file_flush (csi_file_t *file)
 {
-    int c;
-
     if (file->src == NULL)
 	return;
 
     switch ((int) file->type) {
     case FILTER: /* need to eat EOD */
-	while ((c = csi_file_getc (file)) != EOF)
+	while (csi_file_getc (file) != EOF)
 	    ;
 	break;
     default:
diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index 9d55d87..a11c7c6 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -1937,6 +1937,9 @@ _ft_type42_create (csi_t *ctx,
 
     /* 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;
@@ -2512,7 +2515,7 @@ _glyph_path (csi_t *ctx)
     /* count glyphs */
     nglyphs = 0;
     for (i = 0; i < array->stack.len; i++) {
-	csi_object_t *obj = obj = &array->stack.objects[i];
+	csi_object_t *obj = &array->stack.objects[i];
 	int type = csi_object_get_type (obj);
 	switch (type) {
 	case CSI_OBJECT_TYPE_ARRAY:
@@ -2700,11 +2703,10 @@ _image_read_raw (csi_file_t *src,
     case CAIRO_FORMAT_RGB24:
 	len = 3 * width * height;
 	break;
+    default:
     case CAIRO_FORMAT_ARGB32:
 	len = 4 * width * height;
 	break;
-    default:
-	break;
     }
 
     stride = cairo_image_surface_get_stride (image);
@@ -2973,8 +2975,11 @@ _image_load_from_dictionary (csi_t *ctx,
 	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 type;
+	enum mime_type mime_type;
 	csi_object_t file;
 
 	status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
@@ -2985,7 +2990,7 @@ _image_load_from_dictionary (csi_t *ctx,
 	if (_csi_unlikely (status))
 	    return status;
 
-	type = MIME_TYPE_NONE;
+	mime_type = MIME_TYPE_NONE;
 	if (csi_dictionary_has (dict, key.datum.name)) {
 	    csi_object_t type_obj;
 	    const char *type_str;
@@ -3008,7 +3013,7 @@ _image_load_from_dictionary (csi_t *ctx,
 	    }
 
 	    if (strcmp (type_str, CAIRO_MIME_TYPE_PNG) == 0)
-		type = MIME_TYPE_PNG;
+		mime_type = MIME_TYPE_PNG;
 	}
 
 	status = csi_object_as_file (ctx, &obj, &file);
@@ -3017,7 +3022,7 @@ _image_load_from_dictionary (csi_t *ctx,
 
 	/* XXX hook for general mime-type decoder */
 
-	switch (type) {
+	switch (mime_type) {
 	case MIME_TYPE_NONE:
 	    status = _image_read_raw (file.datum.file,
 				      format, width, height, &image);
@@ -5406,6 +5411,10 @@ _surface (csi_t *ctx)
     }
     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) {
diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index 20c8555..8298b94 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -2565,7 +2565,7 @@ cairo_get_font_face (cairo_t *cr)
     ret = DLCALL (cairo_get_font_face, cr);
     font_face_id = _create_font_face_id (ret);
 
-    _emit_cairo_op (cr, "/font-face get\n");
+    _emit_cairo_op (cr, "/font-face get %% f%ld\n", font_face_id);
     _push_operand (FONT_FACE, ret);
 
     return ret;
@@ -3164,10 +3164,11 @@ cairo_surface_create_similar (cairo_surface_t *other,
 	    _trace_printf ("dup ");
 	else
 	    _trace_printf ("%d index ", current_stack_depth - obj->operand - 1);
-	_trace_printf ("%d %d //%s similar\n",
+	_trace_printf ("%d %d //%s similar %% s%ld\n",
 		       width,
 		       height,
-		       _content_to_string (content));
+		       _content_to_string (content),
+		       surface_id);
 
 	_push_operand (SURFACE, ret);
 	_write_unlock ();
@@ -3575,7 +3576,8 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
 		       "  /pattern ");
 	_emit_string_literal ((char *) parsed, -1);
 	_trace_printf (" set\n"
-		       "  font\n");
+		       "  font %% f%ld\n",
+		       font_face_id);
 	_push_operand (FONT_FACE, ret);
 	_write_unlock ();
 
@@ -3627,8 +3629,8 @@ cairo_ft_font_face_create_for_ft_face (FT_Face face, int load_flags)
 
 	_trace_printf ("<< /type 42 /source ");
 	_emit_data (data->data, data->size);
-	_trace_printf (" /index %lu /flags %d >> font\n",
-		       data->index, load_flags);
+	_trace_printf (" /index %lu /flags %d >> font %% f%ld\n",
+		       data->index, load_flags, font_face_id);
 	_push_operand (FONT_FACE, ret);
 	_write_unlock ();
     }
commit d8dbce021a4493330864154e67ca6e4a1f2f50b2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 16 13:26:59 2009 +0100

    [script] recursive active (type3 glyphs)
    
    The assumption that an active surface could not recurse into another
    operation was invalid - due to the complexity of handling type3 glyphs.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 6b6d8ff..15d1ab0 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -1934,9 +1934,8 @@ _cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip
 static void
 active (cairo_script_surface_t *surface)
 {
-    assert (surface->active == FALSE);
-    surface->active = TRUE;
-    surface->ctx->active++;
+    if (surface->active++ == 0)
+	surface->ctx->active++;
 }
 
 static void
@@ -1945,10 +1944,10 @@ inactive (cairo_script_surface_t *surface)
     cairo_script_context_t *ctx = surface->ctx;
     cairo_list_t sorted;
 
-    assert (surface->active == TRUE);
-    surface->active = FALSE;
+    if (--surface->active)
+	return;
 
-    if (--ctx->active > 0)
+    if (--ctx->active)
 	return;
 
     cairo_list_init (&sorted);
commit eba6b5126a55c84706e677b3fc88743f64cc28d7
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 16 12:47:14 2009 +0100

    [build] Add options for warning about bad casts
    
    -Wbad-function-cast in particular. Triggers quite a few warnings where we
    have explicitly cast to an integer.

diff --git a/build/configure.ac.warnings b/build/configure.ac.warnings
index c0cfe5e..1b973bc 100644
--- a/build/configure.ac.warnings
+++ b/build/configure.ac.warnings
@@ -6,12 +6,13 @@ dnl else.  If for any reason you need to force a recheck, just change
 dnl MAYBE_WARN in an ignorable way (like adding whitespace)
 
 MAYBE_WARN="-Wall -Wextra \
--Wsign-compare -Werror-implicit-function-declaration \
--Wpointer-arith -Wwrite-strings -Wstrict-prototypes \
--Wmissing-prototypes -Wmissing-declarations -Wnested-externs \
+-Wold-style-definition \
+-Wmissing-declarations -Werror-implicit-function-declaration \
+-Wnested-externs -Wpointer-arith -Wwrite-strings \
+-Wsign-compare -Wstrict-prototpes -Wmissig-prototyess \
 -Wpacked -Wswitch-enum -Wmissing-format-attribute \
+-Wcast-align -Wbad-function-cast -Wvolatile-register-var \
 -Wstrict-aliasing=2 -Winit-self -Wunsafe-loop-optimizations \
--Wdeclaration-after-statement -Wold-style-definition \
 -Wno-missing-field-initializers -Wno-unused-parameter \
 -Wno-attributes -Wno-long-long -Winline"
 
diff --git a/src/cairo-arc.c b/src/cairo-arc.c
index 2b36809..e99c79b 100644
--- a/src/cairo-arc.c
+++ b/src/cairo-arc.c
@@ -116,7 +116,7 @@ _arc_segments_needed (double	      angle,
     major_axis = _cairo_matrix_transformed_circle_major_axis (ctm, radius);
     max_angle = _arc_max_angle_for_tolerance_normalized (tolerance / major_axis);
 
-    return (int) ceil (angle / max_angle);
+    return ceil (fabs (angle) / max_angle);
 }
 
 /* We want to draw a single spline approximating a circular arc radius
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 83d5776..bcb9477 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -2950,9 +2950,9 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t    *surface,
 	dx = fabs (x2 - x1);
 	dy = fabs (y2 - y1);
 	if (dx > 1e-6)
-	    x_rep = (int) ceil (surface->width/dx);
+	    x_rep = ceil (surface->width/dx);
 	if (dy > 1e-6)
-	    y_rep = (int) ceil (surface->height/dy);
+	    y_rep = ceil (surface->height/dy);
 
 	repeat_end = MAX (x_rep, y_rep);
 	repeat_begin = -repeat_end;
@@ -3445,8 +3445,8 @@ _cairo_pdf_surface_get_extents (void		        *abstract_surface,
      * mention the arbitrary limitation of width to a short(!). We
      * may need to come up with a better interface for get_size.
      */
-    rectangle->width  = (int) ceil (surface->width);
-    rectangle->height = (int) ceil (surface->height);
+    rectangle->width  = ceil (surface->width);
+    rectangle->height = ceil (surface->height);
 
     return TRUE;
 }
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index a577d40..d4cfa9e 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -2972,9 +2972,9 @@ _cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t     *surface,
 	dx = fabs (x2 - x1);
 	dy = fabs (y2 - y1);
 	if (dx > 1e-6)
-	    x_rep = (int) ceil (surface->width/dx);
+	    x_rep = ceil (surface->width/dx);
 	if (dy > 1e-6)
-	    y_rep = (int) ceil (surface->height/dy);
+	    y_rep = ceil (surface->height/dy);
 
 	repeat_end = MAX (x_rep, y_rep);
 	repeat_begin = -repeat_end;
@@ -3214,8 +3214,8 @@ _cairo_ps_surface_get_extents (void		       *abstract_surface,
      * mention the aribitray limitation of width to a short(!). We
      * may need to come up with a better interface for get_extents.
      */
-    rectangle->width  = (int) ceil (surface->width);
-    rectangle->height = (int) ceil (surface->height);
+    rectangle->width  = ceil (surface->width);
+    rectangle->height = ceil (surface->height);
 
     return TRUE;
 }
@@ -3497,15 +3497,15 @@ _cairo_ps_surface_set_bounding_box (void		*abstract_surface,
     int x1, y1, x2, y2;
 
     if (surface->eps) {
-	x1 = (int) floor (_cairo_fixed_to_double (bbox->p1.x));
-	y1 = (int) floor (surface->height - _cairo_fixed_to_double (bbox->p2.y));
-	x2 = (int) ceil (_cairo_fixed_to_double (bbox->p2.x));
-	y2 = (int) ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y));
+	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));
     } else {
 	x1 = 0;
 	y1 = 0;
-	x2 = (int) ceil (surface->width);
-	y2 = (int) ceil (surface->height);
+	x2 = ceil (surface->width);
+	y2 = ceil (surface->height);
     }
 
     surface->page_bbox.x = x1;
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index 08e4cfc..0361d33 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -2129,8 +2129,8 @@ _cairo_svg_surface_get_extents (void		        *abstract_surface,
      * mention the arbitrary limitation of width to a short(!). We
      * may need to come up with a better interface for get_size.
      */
-    rectangle->width  = (int) ceil (surface->width);
-    rectangle->height = (int) ceil (surface->height);
+    rectangle->width  = ceil (surface->width);
+    rectangle->height = ceil (surface->height);
 
     return TRUE;
 }
diff --git a/util/cairo-script/cairo-script-objects.c b/util/cairo-script/cairo-script-objects.c
index 9cc59c9..5191622 100644
--- a/util/cairo-script/cairo-script-objects.c
+++ b/util/cairo-script/cairo-script-objects.c
@@ -720,8 +720,8 @@ csi_object_as_file (csi_t *ctx,
 		    csi_object_t *src,
 		    csi_object_t *file)
 {
-
-    switch ((int) csi_object_get_type (src)) {
+    int type = csi_object_get_type (src);
+    switch (type) {
     case CSI_OBJECT_TYPE_FILE:
 	*file = *csi_object_reference (src);
 	return CSI_STATUS_SUCCESS;
diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index 6a6279d..9d55d87 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -210,9 +210,11 @@ 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);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_BOOLEAN:
 	*out = obj->datum.boolean;
 	break;
@@ -232,9 +234,11 @@ 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);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_BOOLEAN:
 	*out = obj->datum.boolean;
 	break;
@@ -254,9 +258,11 @@ 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);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_BOOLEAN:
 	*out = obj->datum.boolean;
 	break;
@@ -400,9 +406,11 @@ 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);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_MATRIX:
 	*out = obj->datum.matrix->matrix;
 	return CSI_STATUS_SUCCESS;
@@ -432,6 +440,7 @@ _csi_dictionary_get_integer (csi_t *ctx,
 {
     csi_status_t status;
     csi_object_t key, obj;
+    int type;
 
     status = csi_name_new_static (ctx, &key, name);
     if (_csi_unlikely (status))
@@ -444,7 +453,8 @@ _csi_dictionary_get_integer (csi_t *ctx,
     if (_csi_unlikely (status))
 	return status;
 
-    switch ((int) csi_object_get_type (&obj)) {
+    type = csi_object_get_type (&obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_BOOLEAN:
 	*value = obj.datum.boolean;
 	break;
@@ -502,9 +512,11 @@ 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);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_NAME:
 	*out = (const char *) obj->datum.name;
 	break;
@@ -728,6 +740,7 @@ static csi_status_t
 _and (csi_t *ctx)
 {
     csi_object_t *a, *b;
+    int type;
 
     check (2);
 
@@ -737,7 +750,8 @@ _and (csi_t *ctx)
 	return _csi_error (CSI_STATUS_INVALID_SCRIPT);
 
     pop (2);
-    switch ((int) csi_object_get_type (a)) {
+    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);
@@ -1009,13 +1023,15 @@ 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);
 
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
 	/*XXX array, string, dictionary, etc */
     case CSI_OBJECT_TYPE_INTEGER:
 	{
@@ -1047,11 +1063,13 @@ static csi_status_t
 _copy_page (csi_t *ctx)
 {
     csi_object_t *obj;
+    int type;
 
     check (1);
 
     obj = _csi_peek_ostack (ctx, 0);
-    switch ((int) csi_object_get_type (obj)) {
+    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)
@@ -1112,12 +1130,13 @@ static csi_status_t
 _cvi (csi_t *ctx)
 {
     csi_object_t *val, obj;
+    int type;
 
     check (1);
 
     val = _csi_peek_ostack (ctx, 0);
-
-    switch ((int) csi_object_get_type (val)) {
+    type = csi_object_get_type (val);
+    switch (type) {
     case CSI_OBJECT_TYPE_INTEGER:
 	return CSI_STATUS_SUCCESS;
 
@@ -1148,12 +1167,13 @@ static csi_status_t
 _cvr (csi_t *ctx)
 {
     csi_object_t *val, obj;
+    int type;
 
     check (1);
 
     val = _csi_peek_ostack (ctx, 0);
-
-    switch ((int) csi_object_get_type (val)) {
+    type = csi_object_get_type (val);
+    switch (type) {
     case CSI_OBJECT_TYPE_REAL:
 	return CSI_STATUS_SUCCESS;
 
@@ -1919,12 +1939,14 @@ _ft_type42_create (csi_t *ctx,
     status = csi_name_new_static (ctx, &key, "pattern");
     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;
 
-	switch ((int) csi_object_get_type (&obj)) {
+	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))
@@ -1949,6 +1971,7 @@ _ft_type42_create (csi_t *ctx,
     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);
@@ -1966,7 +1989,8 @@ _ft_type42_create (csi_t *ctx,
 	status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
 	if (_csi_unlikely (status))
 	    return status;
-	switch ((int) csi_object_get_type (&obj)) {
+	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))
@@ -2259,16 +2283,17 @@ _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);
-    switch ((int) csi_object_get_type (src)) {
+    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))
+	if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
 	    return _csi_error (CSI_STATUS_INVALID_SCRIPT);
 
 	status = csi_dictionary_get (ctx,
@@ -2277,8 +2302,7 @@ _get (csi_t *ctx)
 				     &obj);
 	break;
     case CSI_OBJECT_TYPE_ARRAY:
-	if (_csi_unlikely (csi_object_get_type (key) !=
-			     CSI_OBJECT_TYPE_INTEGER))
+	if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_INTEGER))
 	    return _csi_error (CSI_STATUS_INVALID_SCRIPT);
 
 	status = csi_array_get (ctx,
@@ -2293,32 +2317,27 @@ _get (csi_t *ctx)
 #endif
 
     case CSI_OBJECT_TYPE_CONTEXT:
-	if (_csi_unlikely (csi_object_get_type (key) !=
-			     CSI_OBJECT_TYPE_NAME))
+	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))
+	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))
+	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))
+	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))
+	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);
 
@@ -2381,8 +2400,9 @@ _glyph_string (csi_t *ctx,
     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 ((int) 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++) {
@@ -2453,7 +2473,8 @@ _glyph_string (csi_t *ctx,
 	    if (i+1 == array->stack.len)
 		break;
 
-	    switch ((int) csi_object_get_type (&array->stack.objects[i+1])) {
+	    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]);
@@ -2473,7 +2494,6 @@ _glyph_string (csi_t *ctx,
 static csi_status_t
 _glyph_path (csi_t *ctx)
 {
-    csi_object_t *obj;
     csi_array_t *array;
     csi_status_t status;
     cairo_t *cr;
@@ -2492,8 +2512,9 @@ _glyph_path (csi_t *ctx)
     /* count glyphs */
     nglyphs = 0;
     for (i = 0; i < array->stack.len; i++) {
-	obj = &array->stack.objects[i];
-	switch ((int) csi_object_get_type (obj)) {
+	csi_object_t *obj = 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;
@@ -2968,12 +2989,14 @@ _image_load_from_dictionary (csi_t *ctx,
 	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;
 
-	    switch ((int) csi_object_get_type (&type_obj)){
+	    type = csi_object_get_type (&type_obj);
+	    switch (type) {
 	    case CSI_OBJECT_TYPE_STRING:
 		type_str = type_obj.datum.string->string;
 		break;
@@ -3061,11 +3084,13 @@ static csi_status_t
 _integer (csi_t *ctx)
 {
     csi_object_t *obj;
+    int type;
 
     check (1);
 
     obj = _csi_peek_ostack (ctx, 0);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_INTEGER:
 	break;
     case CSI_OBJECT_TYPE_REAL:
@@ -3201,11 +3226,13 @@ static csi_status_t
 _neg (csi_t *ctx)
 {
     csi_object_t *obj;
+    int type;
 
     check (1);
 
     obj = _csi_peek_ostack (ctx, 0);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_INTEGER:
 	obj->datum.integer = -obj->datum.integer;
 	break;
@@ -3223,11 +3250,13 @@ static csi_status_t
 _not (csi_t *ctx)
 {
     csi_object_t *obj;
+    int type;
 
     check (1);
 
     obj = _csi_peek_ostack (ctx, 0);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_BOOLEAN:
 	obj->datum.boolean = ! obj->datum.boolean;
 	break;
@@ -3435,6 +3464,7 @@ static csi_status_t
 _or (csi_t *ctx)
 {
     csi_object_t *a, *b;
+    int type;
 
     check (2);
 
@@ -3444,7 +3474,8 @@ _or (csi_t *ctx)
 	return _csi_error (CSI_STATUS_INVALID_SCRIPT);
 
     pop (2);
-    switch ((int) csi_object_get_type (a)) {
+    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);
@@ -3838,6 +3869,7 @@ _rotate (csi_t *ctx)
     csi_object_t *obj;
     csi_status_t status;
     double theta;
+    int type;
 
     check (2);
 
@@ -3846,7 +3878,8 @@ _rotate (csi_t *ctx)
 	return status;
 
     obj = _csi_peek_ostack (ctx, 1);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_CONTEXT:
 	cairo_rotate (obj->datum.cr, theta);
 	break;
@@ -3885,6 +3918,7 @@ _scale (csi_t *ctx)
     csi_object_t *obj;
     csi_status_t status;
     double x, y;
+    int type;
 
     check (3);
 
@@ -3896,7 +3930,8 @@ _scale (csi_t *ctx)
 	return status;
 
     obj = _csi_peek_ostack (ctx, 2);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_CONTEXT:
 	cairo_scale (obj->datum.cr, x, y);
 	break;
@@ -4097,6 +4132,7 @@ _set (csi_t *ctx)
 {
     csi_object_t *key, *value, *dst;
     csi_status_t status;
+    int type;
 
     check (3);
 
@@ -4104,7 +4140,8 @@ _set (csi_t *ctx)
     key = _csi_peek_ostack (ctx, 1);
     dst = _csi_peek_ostack (ctx, 2);
 
-    switch ((int) csi_object_get_type (dst)) {
+    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))
@@ -4260,6 +4297,7 @@ _set_extend (csi_t *ctx)
     csi_status_t status;
     csi_object_t *obj;
     long extend;
+    int type;
 
     check (2);
 
@@ -4268,7 +4306,8 @@ _set_extend (csi_t *ctx)
 	return status;
 
     obj = _csi_peek_ostack (ctx, 1);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_CONTEXT:
 	cairo_pattern_set_extend (cairo_get_source (obj->datum.cr),
 				  extend);
@@ -4335,6 +4374,7 @@ _set_filter (csi_t *ctx)
     csi_status_t status;
     csi_object_t *obj;
     long filter;
+    int type;
 
     check (2);
 
@@ -4343,7 +4383,8 @@ _set_filter (csi_t *ctx)
 	return status;
 
     obj = _csi_peek_ostack (ctx, 1);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_CONTEXT:
 	cairo_pattern_set_filter (cairo_get_source (obj->datum.cr),
 				  filter);
@@ -4517,6 +4558,7 @@ _set_matrix (csi_t *ctx)
     csi_object_t *obj;
     csi_status_t status;
     cairo_matrix_t m;
+    int type;
 
     check (2);
 
@@ -4525,7 +4567,8 @@ _set_matrix (csi_t *ctx)
 	return status;
 
     obj = _csi_peek_ostack (ctx, 1);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_CONTEXT:
 	cairo_set_matrix (obj->datum.cr, &m);
 	break;
@@ -4567,11 +4610,13 @@ _set_mime_data (csi_t *ctx)
     csi_object_t source;
     cairo_surface_t *surface;
     struct _mime_tag *tag;
+    int type;
 
     check (3);
 
     obj = _csi_peek_ostack (ctx, 0);
-    switch ((int) csi_object_get_type (obj)) {
+    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))
@@ -4853,6 +4898,7 @@ _transform (csi_t *ctx)
     csi_object_t *obj;
     csi_status_t status;
     cairo_matrix_t m;
+    int type;
 
     check (2);
 
@@ -4861,7 +4907,8 @@ _transform (csi_t *ctx)
 	return status;
 
     obj = _csi_peek_ostack (ctx, 1);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_CONTEXT:
 	cairo_transform (obj->datum.cr, &m);
 	break;
@@ -4892,6 +4939,7 @@ _translate (csi_t *ctx)
     csi_object_t *obj;
     csi_status_t status;
     double x, y;
+    int type;
 
     check (3);
 
@@ -4903,7 +4951,8 @@ _translate (csi_t *ctx)
 	return status;
 
     obj = _csi_peek_ostack (ctx, 2);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_CONTEXT:
 	cairo_translate (obj->datum.cr, x, y);
 	break;
@@ -4940,11 +4989,13 @@ static csi_status_t
 _show_page (csi_t *ctx)
 {
     csi_object_t *obj;
+    int type;
 
     check (1);
 
     obj = _csi_peek_ostack (ctx, 0);
-    switch ((int) csi_object_get_type (obj)) {
+    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)
@@ -5036,7 +5087,6 @@ _show_text (csi_t *ctx)
 static csi_status_t
 _show_glyphs (csi_t *ctx)
 {
-    csi_object_t *obj;
     csi_array_t *array;
     csi_status_t status;
     cairo_t *cr;
@@ -5055,8 +5105,9 @@ _show_glyphs (csi_t *ctx)
     /* count glyphs */
     nglyphs = 0;
     for (i = 0; i < array->stack.len; i++) {
-	obj = &array->stack.objects[i];
-	switch ((int) csi_object_get_type (obj)) {
+	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;
@@ -5108,6 +5159,7 @@ _show_text_glyphs (csi_t *ctx)
     cairo_glyph_t stack_glyphs[256], *glyphs;
     csi_integer_t nglyphs, nclusters, i;
     long direction;
+    int type;
 
     check (5);
 
@@ -5116,7 +5168,8 @@ _show_text_glyphs (csi_t *ctx)
 	return status;
 
     obj = _csi_peek_ostack (ctx, 1);
-    switch ((int) csi_object_get_type (obj)) {
+    type = csi_object_get_type (obj);
+    switch (type) {
     case CSI_OBJECT_TYPE_ARRAY:
 	array = obj->datum.array;
 	nclusters = array->stack.len / 2;
@@ -5171,7 +5224,8 @@ _show_text_glyphs (csi_t *ctx)
     nglyphs = 0;
     for (i = 0; i < array->stack.len; i++) {
 	obj = &array->stack.objects[i];
-	switch ((int) csi_object_get_type (obj)) {
+	type = csi_object_get_type (obj);
+	switch (type) {
 	case CSI_OBJECT_TYPE_ARRAY:
 	    nglyphs += obj->datum.array->stack.len;
 	    break;
@@ -5466,6 +5520,7 @@ _unset (csi_t *ctx)
     csi_object_t *dst;
     csi_name_t name = 0; /* silence the compiler */
     csi_status_t status;
+    int type;
 
     check (2);
 
@@ -5474,7 +5529,8 @@ _unset (csi_t *ctx)
 	return status;
 
     dst = _csi_peek_ostack (ctx, 1);
-    switch ((int) csi_object_get_type (dst)) {
+    type = csi_object_get_type (dst);
+    switch (type) {
     case CSI_OBJECT_TYPE_DICTIONARY:
 	csi_dictionary_remove (ctx, dst->datum.dictionary, name);
 	break;
@@ -5520,6 +5576,7 @@ static csi_status_t
 _xor (csi_t *ctx)
 {
     csi_object_t *a, *b;
+    int type;
 
     check (2);
 
@@ -5529,7 +5586,8 @@ _xor (csi_t *ctx)
 	return _csi_error (CSI_STATUS_INVALID_SCRIPT);
 
     pop (2);
-    switch ((int) csi_object_get_type (a)) {
+    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);
diff --git a/util/cairo-script/cairo-script-private.h b/util/cairo-script/cairo-script-private.h
index d63b639..9db50f7 100644
--- a/util/cairo-script/cairo-script-private.h
+++ b/util/cairo-script/cairo-script-private.h
@@ -873,7 +873,8 @@ csi_object_is_procedure (const csi_object_t *obj)
 static inline csi_boolean_t
 csi_object_is_number (const csi_object_t *obj)
 {
-    switch ((int) csi_object_get_type (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:
@@ -886,7 +887,8 @@ csi_object_is_number (const csi_object_t *obj)
 static inline double
 csi_number_get_value (const csi_object_t *obj)
 {
-    switch ((int) csi_object_get_type (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;
commit 33ef32af4e7adce41f035ee378279c19577469b0
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 16 12:46:20 2009 +0100

    [clip] Use the rectangular tessellator to extract boxes

diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index cd6cfec..543f8ba 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -737,6 +737,7 @@ _region_clip_to_boxes (const cairo_region_t *region,
     _cairo_traps_init (&traps);
     _cairo_traps_limit (&traps, *boxes, *num_boxes);
     traps.is_rectilinear = TRUE;
+    traps.is_rectangular = TRUE;
 
     num_rects = cairo_region_num_rectangles (region);
     for (n = 0; n < num_rects; n++) {
@@ -1459,17 +1460,19 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
     } else {
         cairo_rectangle_int_t extents;
 
-	n_rects = 1;
+	if (! _cairo_surface_get_extents (_cairo_gstate_get_target (gstate),
+					  &extents))
+	{
+	    /* unbounded surface -> unclipped */
+	    goto DONE;
+	}
 
+	n_rects = 1;
 	rectangles = malloc(sizeof (cairo_rectangle_t));
-	if (unlikely (rectangles == NULL)) {
+	if (unlikely (rectangles == NULL))
 	    return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
-	}
 
-	if (_cairo_surface_get_extents (_cairo_gstate_get_target (gstate),
-					&extents) ||
-	    ! _cairo_clip_int_rect_to_user (gstate, &extents, rectangles))
-	{
+	if (! _cairo_clip_int_rect_to_user (gstate, &extents, rectangles)) {
 	    free (rectangles);
 	    return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
 	}
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 870a987..53350e8 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -176,6 +176,20 @@ _cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_
     cairo_status_t status;
     cairo_region_t *region;
 
+    /* first try to bypass fill-to-polygon */
+    _cairo_traps_init (&traps);
+    status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+							  fill_rule,
+							  &traps);
+    if (_cairo_status_is_error (status))
+	goto CLEANUP_TRAPS;
+
+    if (status == CAIRO_STATUS_SUCCESS) {
+	status = _cairo_traps_extract_region (&traps, &region);
+	goto CLEANUP_TRAPS;
+    }
+
+    /* path is not rectangular, try extracting clipped rectilinear edges */
     _cairo_polygon_init (&polygon);
     if (extents != NULL) {
 	_cairo_box_from_rectangle (&box, extents);
@@ -190,20 +204,20 @@ _cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_
     if (polygon.num_edges == 0) {
 	region = cairo_region_create ();
     } else {
-	_cairo_traps_init (&traps);
-
-	status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
-									&polygon,
-									fill_rule);
+	status =
+	    _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
+								   &polygon,
+								   fill_rule);
 	if (likely (status == CAIRO_STATUS_SUCCESS))
 	    status = _cairo_traps_extract_region (&traps, &region);
-
-	_cairo_traps_fini (&traps);
     }
 
   CLEANUP_POLYGON:
     _cairo_polygon_fini (&polygon);
 
+  CLEANUP_TRAPS:
+    _cairo_traps_fini (&traps);
+
     if (unlikely (status)) { /* XXX _cairo_region_create_in_error() */
 	region = cairo_region_create ();
 	if (likely (region->status) == CAIRO_STATUS_SUCCESS)
@@ -230,6 +244,7 @@ _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
     cairo_region_t *region = NULL;
 
     assert (path->maybe_fill_region);
+    assert (! path->is_empty_fill);
 
     if (_cairo_path_fixed_is_box (path, &box)) {
 	if (box.p1.x > box.p2.x) {
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 2755e16..409679f 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -917,7 +917,7 @@ _clip_to_boxes (cairo_clip_t **clip,
 
     if (*clip == NULL) {
 	status = CAIRO_STATUS_SUCCESS;
-	goto OUT;
+	goto EXTENTS;
     }
 
     /* In some cases it may be preferable to always use boxes instead
@@ -927,7 +927,7 @@ _clip_to_boxes (cairo_clip_t **clip,
      */
     status = _cairo_clip_get_region (*clip, NULL);
     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
-	goto OUT;
+	goto EXTENTS;
 
     status = _cairo_clip_get_boxes (*clip, boxes, num_boxes);
     switch ((int) status) {
@@ -937,10 +937,10 @@ _clip_to_boxes (cairo_clip_t **clip,
 
     case  CAIRO_INT_STATUS_UNSUPPORTED:
 	status = CAIRO_STATUS_SUCCESS;
-	goto OUT;
+	goto EXTENTS;
     }
 
-  OUT:
+  EXTENTS:
     _cairo_box_from_rectangle (&(*boxes)[0], extents);
     *num_boxes = 1;
   DONE:
commit ab035ab2c7bec254fc94d6391398905b5039e777
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 28 10:06:04 2009 +0100

    [tessellate] Rectangular special case
    
    Add an even simpler sweep-line tessellator for rectangular trapezoids (as
    produced by the rectilinear stoker and box filler).
    
    This is so simple it even outperforms pixman's region validation code for the
    purposes of path-to-region conversion.

diff --git a/src/Makefile.sources b/src/Makefile.sources
index 156bae2..c8da236 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -100,6 +100,7 @@ cairo_sources = \
 	cairo-atomic.c \
 	cairo-base85-stream.c \
 	cairo-bentley-ottmann.c \
+	cairo-bentley-ottmann-rectangular.c \
 	cairo-bentley-ottmann-rectilinear.c \
 	cairo.c \
 	cairo-cache.c \
diff --git a/src/cairo-bentley-ottmann-rectangular.c b/src/cairo-bentley-ottmann-rectangular.c
new file mode 100644
index 0000000..a805f57
--- /dev/null
+++ b/src/cairo-bentley-ottmann-rectangular.c
@@ -0,0 +1,733 @@
+/*
+ * 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., 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 Carl Worth
+ *
+ * Contributor(s):
+ *	Carl D. Worth <cworth at cworth.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-combsort-private.h"
+#include "cairo-list-private.h"
+
+typedef struct _cairo_bo_rectangle cairo_bo_rectangle_t;
+typedef struct _cairo_bo_edge cairo_bo_edge_t;
+
+/* A deferred trapezoid of an edge */
+typedef struct _cairo_bo_trap {
+    cairo_bo_edge_t *right;
+    int32_t top;
+} cairo_bo_trap_t;
+
+struct _cairo_bo_edge {
+    int x;
+    int dir;
+    cairo_bo_trap_t deferred_trap;
+    cairo_list_t link;
+};
+
+struct _cairo_bo_rectangle {
+    cairo_bo_edge_t left, right;
+    int top, bottom;
+};
+
+/* 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;
+
+    cairo_bo_rectangle_t **elements;
+    cairo_bo_rectangle_t *elements_embedded[1024];
+} pqueue_t;
+
+typedef struct _cairo_bo_sweep_line {
+    cairo_bo_rectangle_t **rectangles;
+    pqueue_t stop;
+    cairo_list_t sweep;
+    cairo_list_t *current_left, *current_right;
+    int32_t current_y;
+    int32_t last_y;
+} cairo_bo_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
+cairo_bo_rectangle_compare_start (const cairo_bo_rectangle_t *a,
+				  const cairo_bo_rectangle_t *b)
+{
+    return a->top - b->top;
+}
+
+static inline int
+_cairo_bo_rectangle_compare_stop (const cairo_bo_rectangle_t *a,
+				  const cairo_bo_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_status_t
+_pqueue_grow (pqueue_t *pq)
+{
+    cairo_bo_rectangle_t **new_elements;
+    pq->max_size *= 2;
+
+    if (pq->elements == pq->elements_embedded) {
+	new_elements = _cairo_malloc_ab (pq->max_size,
+					 sizeof (cairo_bo_rectangle_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_rectangle_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_rectangle_t *rectangle)
+{
+    cairo_bo_rectangle_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_rectangle_compare_stop (rectangle,
+					   elements[parent = PQ_PARENT_INDEX (i)]) < 0;
+	 i = parent)
+    {
+	elements[i] = elements[parent];
+    }
+
+    elements[i] = rectangle;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline void
+_pqueue_pop (pqueue_t *pq)
+{
+    cairo_bo_rectangle_t **elements = pq->elements;
+    cairo_bo_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 &&
+	    _cairo_bo_rectangle_compare_stop (elements[child+1],
+					      elements[child]) < 0)
+	{
+	    child++;
+	}
+
+	if (_cairo_bo_rectangle_compare_stop (elements[child], tail) >= 0)
+	    break;
+
+	elements[i] = elements[child];
+    }
+    elements[i] = tail;
+}
+
+static inline cairo_bo_rectangle_t *
+_cairo_bo_rectangle_pop_start (cairo_bo_sweep_line_t *sweep_line)
+{
+    return *sweep_line->rectangles++;
+}
+
+static inline cairo_bo_rectangle_t *
+_cairo_bo_rectangle_peek_stop (cairo_bo_sweep_line_t *sweep_line)
+{
+    return sweep_line->stop.elements[PQ_FIRST_ENTRY];
+}
+
+CAIRO_COMBSORT_DECLARE (_cairo_bo_rectangle_sort,
+			cairo_bo_rectangle_t *,
+			cairo_bo_rectangle_compare_start)
+
+static void
+_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line,
+			   cairo_bo_rectangle_t	**rectangles,
+			   int			  num_rectangles)
+{
+    _cairo_bo_rectangle_sort (rectangles, num_rectangles);
+    rectangles[num_rectangles] = NULL;
+    sweep_line->rectangles = rectangles;
+
+    cairo_list_init (&sweep_line->sweep);
+    sweep_line->current_left = &sweep_line->sweep;
+    sweep_line->current_right = &sweep_line->sweep;
+    sweep_line->current_y = INT32_MIN;
+    sweep_line->last_y = INT32_MIN;
+
+    _pqueue_init (&sweep_line->stop);
+}
+
+static void
+_cairo_bo_sweep_line_fini (cairo_bo_sweep_line_t *sweep_line)
+{
+    _pqueue_fini (&sweep_line->stop);
+}
+
+static inline cairo_bo_edge_t *
+link_to_edge (cairo_list_t *elt)
+{
+    return cairo_container_of (elt, cairo_bo_edge_t, link);
+}
+
+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_line_t _left = {
+	    { left->x, trap->top },
+	    { left->x, bot },
+	}, _right = {
+	    { trap->right->x, trap->top },
+	    { trap->right->x, bot },
+	};
+	_cairo_traps_add_trap (traps, trap->top, bot, &_left, &_right);
+    }
+
+    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;
+
+    if (left->deferred_trap.right != NULL) {
+	if (right != NULL && left->deferred_trap.right->x == right->x) {
+	    /* 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 (right != NULL && left->x != right->x) {
+	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_sweep_line_t	*sweep,
+			cairo_fill_rule_t	 fill_rule,
+			cairo_traps_t	        *traps)
+{
+    int top = sweep->current_y;
+    cairo_list_t *pos = &sweep->sweep;
+    cairo_status_t status;
+
+    if (sweep->last_y == sweep->current_y)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (fill_rule == CAIRO_FILL_RULE_WINDING) {
+	do {
+	    cairo_bo_edge_t *left, *right;
+	    int in_out;
+
+	    pos = pos->next;
+	    if (pos == &sweep->sweep)
+		break;
+
+	    left = link_to_edge (pos);
+	    in_out = left->dir;
+
+	    /* Check if there is a co-linear edge with an existing trap */
+	    if (left->deferred_trap.right == NULL) {
+		right = link_to_edge (pos->next);
+		while (unlikely (right->x == left->x)) {
+		    if (right->deferred_trap.right != NULL) {
+			/* continuation on left */
+			left->deferred_trap = right->deferred_trap;
+			right->deferred_trap.right = NULL;
+			break;
+		    }
+
+		    right = link_to_edge (right->link.next);
+		}
+	    }
+
+	    /* Greedily search for the closing edge, so that we generate the
+	     * maximal span width with the minimal number of trapezoids.
+	     */
+
+	    right = link_to_edge (left->link.next);
+	    do {
+		/* End all subsumed traps */
+		if (right->deferred_trap.right != NULL) {
+		    status = _cairo_bo_edge_end_trap (right, top, traps);
+		    if (unlikely (status))
+			return status;
+		}
+
+		in_out += right->dir;
+		if (in_out == 0) {
+		    /* skip co-linear edges */
+		    if (likely (right->link.next == &sweep->sweep ||
+				right->x != link_to_edge (right->link.next)->x))
+		    {
+			break;
+		    }
+		}
+
+		right = link_to_edge (right->link.next);
+	    } while (TRUE);
+
+	    status = _cairo_bo_edge_start_or_continue_trap (left, right,
+							    top, traps);
+	    if (unlikely (status))
+		return status;
+
+	    pos = &right->link;
+	} while (TRUE);
+    } else {
+	cairo_bo_edge_t *left, *right;
+	do {
+	    int in_out = 0;
+
+	    pos = pos->next;
+	    if (pos == &sweep->sweep)
+		break;
+
+	    left = link_to_edge (pos);
+
+	    pos = pos->next;
+	    do {
+		right = link_to_edge (pos);
+		if (right->deferred_trap.right != NULL) {
+		    status = _cairo_bo_edge_end_trap (right, top, traps);
+		    if (unlikely (status))
+			return status;
+		}
+
+		if ((in_out++ & 1) == 0) {
+		    cairo_list_t *next;
+		    cairo_bool_t skip = FALSE;
+
+		    /* skip co-linear edges */
+		    next = pos->next;
+		    if (next != &sweep->sweep)
+			skip = right->x == link_to_edge (next)->x;
+
+		    if (! skip)
+			break;
+		}
+		pos = pos->next;
+	    } while (TRUE);
+
+	    right = pos == &sweep->sweep ? NULL : link_to_edge (pos);
+	    status = _cairo_bo_edge_start_or_continue_trap (left, right,
+							    top, traps);
+	    if (unlikely (status))
+		return status;
+	} while (right != NULL);
+    }
+
+    sweep->last_y = sweep->current_y;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_status_t
+_cairo_bo_sweep_line_delete_edge (cairo_bo_sweep_line_t *sweep_line,
+	                          cairo_bo_edge_t *edge,
+				  cairo_traps_t *traps)
+{
+    if (edge->deferred_trap.right != NULL) {
+	cairo_bo_edge_t *next = link_to_edge (edge->link.next);
+	if (&next->link != &sweep_line->sweep && next->x == edge->x) {
+	    next->deferred_trap = edge->deferred_trap;
+	} else {
+	    cairo_status_t status;
+
+	    status = _cairo_bo_edge_end_trap (edge,
+		                              sweep_line->current_y,
+					      traps);
+	    if (unlikely (status))
+		return status;
+	}
+    }
+
+    if (sweep_line->current_left == &edge->link)
+	sweep_line->current_left = edge->link.prev;
+
+    if (sweep_line->current_right == &edge->link)
+	sweep_line->current_right = edge->link.next;
+
+    cairo_list_del (&edge->link);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_status_t
+_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t	*sweep_line,
+			     cairo_bo_rectangle_t	*rectangle,
+			     cairo_fill_rule_t		 fill_rule,
+			     cairo_traps_t		*traps)
+{
+    cairo_status_t status;
+
+    if (rectangle->bottom != sweep_line->current_y) {
+	status = _active_edges_to_traps (sweep_line, fill_rule, traps);
+	if (unlikely (status))
+	    return status;
+
+	sweep_line->current_y = rectangle->bottom;
+    }
+
+    status = _cairo_bo_sweep_line_delete_edge (sweep_line,
+	                                       &rectangle->left,
+					       traps);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_bo_sweep_line_delete_edge (sweep_line,
+	                                       &rectangle->right,
+					       traps);
+    if (unlikely (status))
+	return status;
+
+    _pqueue_pop (&sweep_line->stop);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+validate_sweep_line (cairo_bo_sweep_line_t *sweep_line)
+{
+    int32_t last_x = INT32_MIN;
+    cairo_bo_edge_t *edge;
+    cairo_list_foreach_entry (edge, cairo_bo_edge_t, &sweep_line->sweep, link) {
+	if (edge->x < last_x)
+	    return FALSE;
+	last_x = edge->x;
+    }
+    return TRUE;
+}
+static inline cairo_status_t
+_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t	*sweep_line,
+			     cairo_bo_rectangle_t	*rectangle,
+			     cairo_fill_rule_t		 fill_rule,
+			     cairo_traps_t		*traps)
+{
+    cairo_list_t *pos;
+    cairo_status_t status;
+
+    if (rectangle->top != sweep_line->current_y) {
+	cairo_bo_rectangle_t *stop;
+
+	stop = _cairo_bo_rectangle_peek_stop (sweep_line);
+	while (stop != NULL && stop->bottom < rectangle->top) {
+	    status = _cairo_bo_sweep_line_delete (sweep_line, stop,
+						  fill_rule, traps);
+	    if (unlikely (status))
+		return status;
+
+	    stop = _cairo_bo_rectangle_peek_stop (sweep_line);
+	}
+
+	status = _active_edges_to_traps (sweep_line, fill_rule, traps);
+	if (unlikely (status))
+	    return status;
+
+	sweep_line->current_y = rectangle->top;
+    }
+
+    /* right edge */
+    pos = sweep_line->current_right;
+    if (pos != &sweep_line->sweep) {
+	int cmp;
+
+	cmp = link_to_edge (pos)->x - rectangle->right.x;
+	if (cmp < 0) {
+	    while (pos->next != &sweep_line->sweep &&
+		   link_to_edge (pos->next)->x - rectangle->right.x < 0)
+	    {
+		pos = pos->next;
+	    }
+	} else if (cmp > 0) {
+	    do {
+		pos = pos->prev;
+	    } while (pos != &sweep_line->sweep &&
+		     link_to_edge (pos)->x - rectangle->right.x > 0);
+	}
+
+	cairo_list_add (&rectangle->right.link, pos);
+    } else {
+	cairo_list_add_tail (&rectangle->right.link, pos);
+    }
+    sweep_line->current_right = &rectangle->right.link;
+    assert (validate_sweep_line (sweep_line));
+
+    /* left edge */
+    pos = sweep_line->current_left;
+    if (pos != &sweep_line->sweep) {
+	int cmp;
+
+	if (link_to_edge (pos)->x >= rectangle->right.x) {
+	    pos = rectangle->right.link.prev;
+	    if (pos == &sweep_line->sweep)
+		goto left_done;
+	}
+
+	cmp = link_to_edge (pos)->x - rectangle->left.x;
+	if (cmp < 0) {
+	    while (pos->next != &sweep_line->sweep &&
+		   link_to_edge (pos->next)->x - rectangle->left.x < 0)
+	    {
+		pos = pos->next;
+	    }
+	} else if (cmp > 0) {
+	    do {
+		pos = pos->prev;
+	    } while (pos != &sweep_line->sweep &&
+		    link_to_edge (pos)->x - rectangle->left.x > 0);
+	}
+    }
+  left_done:
+    cairo_list_add (&rectangle->left.link, pos);
+    sweep_line->current_left = &rectangle->left.link;
+    assert (validate_sweep_line (sweep_line));
+
+    return _pqueue_push (&sweep_line->stop, rectangle);
+}
+
+static cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectangular (cairo_bo_rectangle_t	**rectangles,
+					       int			  num_rectangles,
+					       cairo_fill_rule_t	  fill_rule,
+					       cairo_traps_t		 *traps)
+{
+    cairo_bo_sweep_line_t sweep_line;
+    cairo_bo_rectangle_t *rectangle;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+    _cairo_bo_sweep_line_init (&sweep_line, rectangles, num_rectangles);
+
+    while ((rectangle = _cairo_bo_rectangle_pop_start (&sweep_line)) != NULL) {
+	status = _cairo_bo_sweep_line_insert (&sweep_line, rectangle,
+					      fill_rule, traps);
+	if (unlikely (status))
+	    goto BAIL;
+    }
+
+    while ((rectangle = _cairo_bo_rectangle_peek_stop (&sweep_line)) != NULL) {
+	status = _cairo_bo_sweep_line_delete (&sweep_line, rectangle,
+					      fill_rule, traps);
+	if (unlikely (status))
+	    goto BAIL;
+    }
+
+BAIL:
+    _cairo_bo_sweep_line_fini (&sweep_line);
+    return status;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps,
+						     cairo_fill_rule_t fill_rule)
+{
+    cairo_bo_rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_rectangle_t)];
+    cairo_bo_rectangle_t *rectangles;
+    cairo_bo_rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1];
+    cairo_bo_rectangle_t **rectangles_ptrs;
+    cairo_status_t status;
+    int i;
+
+    if (unlikely (traps->num_traps == 0))
+	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 (cairo_bo_rectangle_t) +
+					  sizeof (cairo_bo_rectangle_t *),
+					  sizeof (cairo_bo_rectangle_t *));
+	if (unlikely (rectangles == NULL))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	rectangles_ptrs = (cairo_bo_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.deferred_trap.right = NULL;
+	cairo_list_init (&rectangles[i].left.link);
+
+	rectangles[i].right.deferred_trap.right = NULL;
+	cairo_list_init (&rectangles[i].right.link);
+
+	rectangles[i].top = traps->traps[i].top;
+	rectangles[i].bottom = traps->traps[i].bottom;
+
+	rectangles_ptrs[i] = &rectangles[i];
+    }
+
+    _cairo_traps_clear (traps);
+    status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, i,
+							    fill_rule,
+							    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;
+}
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 8d68e47..cd6cfec 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -755,7 +755,7 @@ _region_clip_to_boxes (const cairo_region_t *region,
 	    goto CLEANUP;
     }
 
-    status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps, CAIRO_FILL_RULE_WINDING);
+    status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING);
     if (unlikely (status))
 	goto CLEANUP;
 
@@ -804,25 +804,35 @@ _rectilinear_clip_to_boxes (const cairo_path_fixed_t *path,
     cairo_traps_t traps;
     cairo_status_t status;
 
+    _cairo_traps_init (&traps);
+    _cairo_traps_limit (&traps, *boxes, *num_boxes);
+
     _cairo_polygon_init (&polygon);
     _cairo_polygon_limit (&polygon, *boxes, *num_boxes);
 
+    status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+							  fill_rule,
+							  &traps);
+    if (unlikely (_cairo_status_is_error (status)))
+	goto CLEANUP;
+    if (status == CAIRO_STATUS_SUCCESS)
+	goto BOXES;
+
     /* tolerance will be ignored as the path is rectilinear */
     status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
     if (unlikely (status))
-	goto CLEANUP_POLYGON;
+	goto CLEANUP;
 
     if (polygon.num_edges == 0) {
 	*num_boxes = 0;
     } else {
-	_cairo_traps_init (&traps);
-
 	status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
 									&polygon,
 									fill_rule);
 	if (likely (status == CAIRO_STATUS_SUCCESS)) {
 	    int i;
 
+          BOXES:
 	    i = *size_boxes;
 	    if (i < 0)
 		i = -i;
@@ -835,7 +845,7 @@ _rectilinear_clip_to_boxes (const cairo_path_fixed_t *path,
 		new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t));
 		if (unlikely (new_boxes == NULL)) {
 		    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-		    goto CLEANUP_TRAPS;
+		    goto CLEANUP;
 		}
 
 		if (*size_boxes > 0)
@@ -853,13 +863,11 @@ _rectilinear_clip_to_boxes (const cairo_path_fixed_t *path,
 	    }
 	    *num_boxes = i;
 	}
-
-      CLEANUP_TRAPS:
-	_cairo_traps_fini (&traps);
     }
 
-  CLEANUP_POLYGON:
+  CLEANUP:
     _cairo_polygon_fini (&polygon);
+    _cairo_traps_fini (&traps);
 
     return status;
 }
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 3daf246..870a987 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -356,6 +356,9 @@ _cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
     cairo_box_t box;
     cairo_status_t status;
 
+    traps->is_rectilinear = TRUE;
+    traps->is_rectangular = TRUE;
+
     if (_cairo_path_fixed_is_box (path, &box)) {
 	if (box.p1.x > box.p2.x) {
 	    cairo_fixed_t t;
@@ -374,24 +377,11 @@ _cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
 	}
 
 	return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2);
-    } else if (fill_rule == CAIRO_FILL_RULE_WINDING) {
+    } else {
 	cairo_path_fixed_iter_t iter;
-	int last_cw = -1;
 
 	_cairo_path_fixed_iter_init (&iter, path);
 	while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
-	    int cw = 0;
-
-	    if (box.p1.x > box.p2.x) {
-		cairo_fixed_t t;
-
-		t = box.p1.x;
-		box.p1.x = box.p2.x;
-		box.p2.x = t;
-
-		cw = ! cw;
-	    }
-
 	    if (box.p1.y > box.p2.y) {
 		cairo_fixed_t t;
 
@@ -399,25 +389,23 @@ _cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
 		box.p1.y = box.p2.y;
 		box.p2.y = t;
 
-		cw = ! cw;
+		t = box.p1.x;
+		box.p1.x = box.p2.x;
+		box.p2.x = t;
 	    }
 
-	    if (last_cw < 0)
-		last_cw = cw;
-	    else if (last_cw != cw)
-		goto out;
-
 	    status = _cairo_traps_tessellate_rectangle (traps,
 							&box.p1, &box.p2);
-	    if (unlikely (status))
+	    if (unlikely (status)) {
+		_cairo_traps_clear (traps);
 		return status;
+	    }
 	}
 
 	if (_cairo_path_fixed_iter_at_end (&iter))
-	    return CAIRO_STATUS_SUCCESS;
-    }
+	    return _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, fill_rule);
 
-  out:
-    _cairo_traps_clear (traps);
-    return CAIRO_INT_STATUS_UNSUPPORTED;
+	_cairo_traps_clear (traps);
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
 }
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 69300da..5c4f4fc 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -2029,8 +2029,9 @@ _cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t	*path,
 	status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
 
     traps->is_rectilinear = 1;
+    traps->is_rectangular = 1;
     /* As we incrementally tessellate, we do not eliminate self-intersections */
-    traps->has_intersections = traps->num_traps != 0;
+    traps->has_intersections = traps->num_traps > 1;
 BAIL:
     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
 
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 4dc8ebb..2755e16 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -634,6 +634,87 @@ _clip_and_composite_region (const cairo_pattern_t *src,
     return status;
 }
 
+/* avoid using region code to re-validate boxes */
+static cairo_status_t
+_fill_rectangles (cairo_surface_t *dst,
+		  cairo_operator_t op,
+		  const cairo_pattern_t *src,
+		  cairo_traps_t *traps,
+		  cairo_clip_t *clip)
+{
+    const cairo_color_t *color;
+    cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
+    cairo_rectangle_int_t *rects = stack_rects;
+    cairo_status_t status;
+    int i;
+
+    if (! traps->is_rectilinear || ! traps->maybe_region)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* XXX: convert clip region to geometric boxes? */
+    if (clip != NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* XXX: fallback for the region_subtract() operation */
+    if (! _cairo_operator_bounded_by_mask (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (traps->has_intersections) {
+	if (traps->is_rectangular) {
+	    status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
+	} else {
+	    status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
+	}
+	if (unlikely (status))
+	    return status;
+    }
+
+    for (i = 0; i < traps->num_traps; i++) {
+	if (! _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 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);
+    }
+
+    for (i = 0; i < traps->num_traps; i++) {
+	int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x);
+	int y1 = _cairo_fixed_integer_part (traps->traps[i].top);
+	int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
+	int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
+
+	rects[i].x = x1;
+	rects[i].y = y1;
+	rects[i].width = x2 - x1;
+	rects[i].height = y2 - y1;
+    }
+
+    if (op == CAIRO_OPERATOR_CLEAR)
+	color = CAIRO_COLOR_TRANSPARENT;
+    else
+	color = &((cairo_solid_pattern_t *)src)->color;
+
+    status =  _cairo_surface_fill_rectangles (dst, op, color, rects, i);
+
+    if (rects != stack_rects)
+	free (rects);
+
+    return status;
+}
+
 /* Warning: This call modifies the coordinates of traps */
 static cairo_status_t
 _clip_and_composite_trapezoids (const cairo_pattern_t *src,
@@ -671,6 +752,10 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
     {
 	cairo_region_t *trap_region = NULL;
 
+	status = _fill_rectangles (dst, op, src, traps, clip);
+	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	    return status;
+
 	status = _cairo_traps_extract_region (traps, &trap_region);
 	if (unlikely (_cairo_status_is_error (status)))
 	    return status;
@@ -713,12 +798,12 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
 
     /* No fast path, exclude self-intersections and clip trapezoids. */
     if (traps->has_intersections) {
-	if (traps->is_rectilinear)
-	    status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps,
-									  CAIRO_FILL_RULE_WINDING);
+	if (traps->is_rectangular)
+	    status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
+	else if (traps->is_rectilinear)
+	    status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
 	else
-	    status = _cairo_bentley_ottmann_tessellate_traps (traps,
-							      CAIRO_FILL_RULE_WINDING);
+	    status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING);
 	if (unlikely (status))
 	    return status;
     }
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index d5b41e5..d6fec26 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -53,6 +53,7 @@ _cairo_traps_init (cairo_traps_t *traps)
 
     traps->maybe_region = 1;
     traps->is_rectilinear = 0;
+    traps->is_rectangular = 0;
 
     traps->num_traps = 0;
 
@@ -79,6 +80,7 @@ _cairo_traps_clear (cairo_traps_t *traps)
 
     traps->maybe_region = 1;
     traps->is_rectilinear = 0;
+    traps->is_rectangular = 0;
 
     traps->num_traps = 0;
     traps->has_intersections = FALSE;
@@ -170,6 +172,7 @@ _cairo_traps_init_boxes (cairo_traps_t	    *traps,
 
     traps->num_traps = num_boxes;
     traps->is_rectilinear = TRUE;
+    traps->is_rectangular = TRUE;
 
     trap = &traps->traps[0];
     while (num_boxes--) {
@@ -206,6 +209,12 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
     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;
@@ -214,15 +223,17 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
      top = top_left->y;
      bottom = bottom_right->y;
 
-    if (top == bottom)
-	return CAIRO_STATUS_SUCCESS;
-
-    if (left.p1.x == right.p1.x)
-	return CAIRO_STATUS_SUCCESS;
-
     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;
@@ -275,7 +286,10 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
 	    if (left.p1.x >= right.p1.x)
 		continue;
 
-	    _cairo_traps_add_trap (traps, _top, _bottom, &_left, &_right);
+	    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);
diff --git a/src/cairoint.h b/src/cairoint.h
index 4db99d6..a3cc6c7 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -963,6 +963,7 @@ typedef struct _cairo_traps {
     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;
@@ -2407,6 +2408,10 @@ _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_rectilinear_traps (cairo_traps_t *traps,
 						     cairo_fill_rule_t fill_rule);
 
commit d7b0c3b784faba756b10b66b9757e6e4c3fce38c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 22:03:06 2009 +0100

    [script] Track scaled-font
    
    Instead of emitting the (font-face, matrix and options) elements when
    setting up the desired font on the matrix, simply restore the scaled-font.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index c5544ff..6b6d8ff 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -2411,7 +2411,8 @@ _cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
     font_private = scaled_font->surface_private;
     if (font_private != NULL) {
 	_cairo_output_stream_printf (font_private->ctx->stream,
-				     "/f%lu undef\n",
+				     "/f%lu undef /sf%lu undef\n",
+				     font_private->id,
 				     font_private->id);
 
 	_bitmap_release_id (&font_private->ctx->font_id, font_private->id);
@@ -2483,7 +2484,7 @@ _emit_type42_font (cairo_script_surface_t *surface,
 
     font_private = scaled_font->surface_private;
     _cairo_output_stream_printf (surface->ctx->stream,
-				 " >> font dup /f%lu exch def set-font-face\n",
+				 " >> font dup /f%lu exch def set-font-face",
 				 font_private->id);
 
     return status;
@@ -2531,7 +2532,7 @@ _emit_scaled_font_init (cairo_script_surface_t *surface,
 				 "  /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\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,
@@ -2560,16 +2561,6 @@ _emit_scaled_font (cairo_script_surface_t *surface,
     if (! matrix_updated && surface->cr.current_scaled_font == scaled_font)
 	return CAIRO_STATUS_SUCCESS;
 
-    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;
-
     surface->cr.current_scaled_font = scaled_font;
 
     assert (scaled_font->surface_backend == NULL ||
@@ -2577,16 +2568,30 @@ _emit_scaled_font (cairo_script_surface_t *surface,
 
     font_private = scaled_font->surface_private;
     if (font_private == NULL) {
-	status = _emit_scaled_font_init (surface, scaled_font);
+	cairo_scaled_font_get_font_matrix (scaled_font, &matrix);
+	status = _emit_font_matrix (surface, &matrix);
 	if (unlikely (status))
 	    return status;
-    } else {
-	status = _emit_context (surface);
+
+	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);
+	if (unlikely (status))
+	    return status;
+
+	font_private = scaled_font->surface_private;
+	assert (font_private != NULL);
+
+	assert (target_is_active (surface));
+	_cairo_output_stream_printf (surface->ctx->stream,
+				     " /scaled-font get /sf%lu exch def\n",
+				     font_private->id);
+    } else {
 	_cairo_output_stream_printf (surface->ctx->stream,
-				     "f%lu set-font-face\n",
+				     "sf%lu set-scaled-font\n",
 				     font_private->id);
     }
 
commit 7306305cc898c9f27957c6c6717028cbdcff6a5a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 21:59:12 2009 +0100

    [script] Emit surface content
    
    Include the desired content with the creation info.

diff --git a/boilerplate/cairo-boilerplate-script.c b/boilerplate/cairo-boilerplate-script.c
index b944a55..d1d76b5 100644
--- a/boilerplate/cairo-boilerplate-script.c
+++ b/boilerplate/cairo-boilerplate-script.c
@@ -61,7 +61,7 @@ _cairo_boilerplate_script_create_surface (const char		 *name,
     xunlink (ptc->filename);
 
     ctx = cairo_script_context_create (ptc->filename);
-    surface = cairo_script_surface_create (ctx, width, height);
+    surface = cairo_script_surface_create (ctx, content, width, height);
     cairo_script_context_destroy (ctx);
 
     status = cairo_surface_set_user_data (surface,
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 541a8a9..c5544ff 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -151,6 +151,7 @@ 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,
 				       double width,
 				       double height,
 				       cairo_surface_t *passthrough);
@@ -428,11 +429,23 @@ _get_target (cairo_script_surface_t *surface)
     }
 }
 
+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_output_stream_printf (surface->ctx->stream,
-				 "<< /width %f /height %f",
+				 "<< /content %s /width %f /height %f",
+				 _content_to_string (surface->base.content),
 				 surface->width,
 				 surface->height);
 
@@ -909,17 +922,6 @@ _emit_radial_pattern (cairo_script_surface_t *surface,
     return _emit_gradient_color_stops (&radial->base, surface->ctx->stream);
 }
 
-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_meta_surface_pattern (cairo_script_surface_t *surface,
 			    const cairo_pattern_t *pattern)
@@ -944,6 +946,7 @@ _emit_meta_surface_pattern (cairo_script_surface_t *surface,
     _cairo_box_round_to_rectangle (&bbox, &rect);
 
     similar = _cairo_script_surface_create_internal (surface->ctx,
+						     source->content,
 						     rect.width,
 						     rect.height,
 						     NULL);
@@ -1677,6 +1680,7 @@ _cairo_script_surface_create_similar (void	       *abstract_surface,
     }
 
     surface = _cairo_script_surface_create_internal (ctx,
+						     content,
 						     width, height,
 						     passthrough);
     cairo_surface_destroy (passthrough);
@@ -3209,6 +3213,7 @@ _cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr)
 
 static cairo_script_surface_t *
 _cairo_script_surface_create_internal (cairo_script_context_t *ctx,
+				       cairo_content_t content,
 				       double width,
 				       double height,
 				       cairo_surface_t *passthrough)
@@ -3224,7 +3229,7 @@ _cairo_script_surface_create_internal (cairo_script_context_t *ctx,
 
     _cairo_surface_init (&surface->base,
 			 &_cairo_script_surface_backend,
-			 CAIRO_CONTENT_COLOR_ALPHA);
+			 content);
 
     _cairo_surface_wrapper_init (&surface->wrapper, passthrough);
 
@@ -3335,10 +3340,12 @@ cairo_script_context_get_mode (cairo_script_context_t *context)
 
 cairo_surface_t *
 cairo_script_surface_create (cairo_script_context_t *context,
+			     cairo_content_t content,
 			     double width,
 			     double height)
 {
     return &_cairo_script_surface_create_internal (context,
+						   content,
 						   width, height,
 						   NULL)->base;
 }
@@ -3356,6 +3363,7 @@ cairo_script_surface_create_for_target (cairo_script_context_t *context,
 	extents.width = extents.height = -1;
 
     return &_cairo_script_surface_create_internal (context,
+						   target->content,
 						   extents.width,
 						   extents.height,
 						   target)->base;
diff --git a/src/cairo-script.h b/src/cairo-script.h
index 182b65d..5d85941 100644
--- a/src/cairo-script.h
+++ b/src/cairo-script.h
@@ -73,6 +73,7 @@ cairo_script_context_destroy (cairo_script_context_t *context);
 
 cairo_public cairo_surface_t *
 cairo_script_surface_create (cairo_script_context_t *context,
+			     cairo_content_t content,
 			     double width,
 			     double height);
 
diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index 7a6219d..20c8555 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -4146,22 +4146,25 @@ cairo_xlib_surface_create_with_xrender_format (Display *dpy,
 #include <cairo-script.h>
 cairo_surface_t *
 cairo_script_surface_create (cairo_script_context_t *ctx,
+			     cairo_content_t content,
 			     double width,
 			     double height)
 {
     cairo_surface_t *ret;
     long surface_id;
 
-    ret = DLCALL (cairo_script_surface_create, ctx, width, height);
+    ret = DLCALL (cairo_script_surface_create, ctx, content, width, height);
     surface_id = _create_surface_id (ret);
 
     _emit_line_info ();
     if (_write_lock ()) {
 	_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,
 		       surface_id);
 	_surface_object_set_size (ret, width, height);
commit 052211b072788f0977dccebdcf681d4874f2487a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 20:16:51 2009 +0100

    [script] Garbage collect contexts on context switch
    
    Previously the contexts were permanently associated with the surface and
    only destroyed along with the final reference to the surface. This meant
    that we kept a large number of unwanted contexts in memory. Most
    applications only have a few contexts active at any time, so remove
    inactive contexts from the operand stack every time we perform an
    operation on a different context.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index e1ce00d..541a8a9 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -65,9 +65,18 @@ typedef struct _cairo_script_surface cairo_script_surface_t;
 typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t;
 typedef struct _cairo_script_surface_font_private cairo_script_surface_font_private_t;
 
+typedef struct _operand {
+    enum {
+	SURFACE,
+	DEFERRED,
+    } type;
+    cairo_list_t link;
+} operand_t;
+
+
 struct deferred_finish {
     cairo_list_t link;
-    cairo_list_t operand;
+    operand_t operand;
 };
 
 struct _cairo_script_context {
@@ -126,10 +135,11 @@ struct _cairo_script_surface {
     cairo_script_context_t *ctx;
     cairo_surface_clipper_t clipper;
 
-    unsigned long id;
-    cairo_list_t operand;
+    operand_t operand;
+    cairo_bool_t emitted;
     cairo_bool_t defined;
     cairo_bool_t is_clear;
+    cairo_bool_t active;
 
     double width, height;
 
@@ -155,6 +165,9 @@ 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;
@@ -358,13 +371,14 @@ _line_join_to_string (cairo_line_join_t line_join)
 static cairo_bool_t
 target_is_active (cairo_script_surface_t *surface)
 {
-    return cairo_list_is_first (&surface->operand, &surface->ctx->operands);
+    return cairo_list_is_first (&surface->operand.link,
+				&surface->ctx->operands);
 }
 
 static void
 target_push (cairo_script_surface_t *surface)
 {
-    cairo_list_move (&surface->operand, &surface->ctx->operands);
+    cairo_list_move (&surface->operand.link, &surface->ctx->operands);
 }
 
 static int
@@ -374,7 +388,7 @@ target_depth (cairo_script_surface_t *surface)
     int depth = 0;
 
     cairo_list_foreach (link, &surface->ctx->operands) {
-	if (link == &surface->operand)
+	if (link == &surface->operand.link)
 	    break;
 	depth++;
     }
@@ -391,7 +405,8 @@ _get_target (cairo_script_surface_t *surface)
 	int depth = target_depth (surface);
 	if (ctx->active) {
 	    if (surface->defined) {
-		_cairo_output_stream_printf (ctx->stream, "s%ld ", surface->id);
+		_cairo_output_stream_printf (ctx->stream, "s%u ",
+					     surface->base.unique_id);
 	    } else {
 		_cairo_output_stream_printf (ctx->stream, "%d index ", depth);
 		_cairo_output_stream_puts (ctx->stream, "/target get exch pop ");
@@ -408,20 +423,14 @@ _get_target (cairo_script_surface_t *surface)
 	    _cairo_output_stream_puts (ctx->stream, "/target get ");
 	    target_push (surface);
 	}
-    } else
+    } else {
 	_cairo_output_stream_puts (ctx->stream, "/target get ");
+    }
 }
 
 static cairo_status_t
 _emit_surface (cairo_script_surface_t *surface)
 {
-    cairo_status_t status;
-
-    status = _bitmap_next_id (&surface->ctx->surface_id,
-			      &surface->id);
-    if (unlikely (status))
-	return status;
-
     _cairo_output_stream_printf (surface->ctx->stream,
 				 "<< /width %f /height %f",
 				 surface->width,
@@ -451,27 +460,69 @@ _emit_surface (cairo_script_surface_t *surface)
     }
 
     _cairo_output_stream_puts (surface->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 = surface->ctx;
+
+    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->id == (unsigned long) -1) {
+    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 (surface->ctx->stream, "exch\n");
+	    _cairo_output_stream_puts (ctx->stream, "exch\n");
 	} else {
-	    _cairo_output_stream_printf (surface->ctx->stream,
+	    _cairo_output_stream_printf (ctx->stream,
 					 "%d -1 roll\n",
 					 depth);
 	}
@@ -900,6 +951,7 @@ _emit_meta_surface_pattern (cairo_script_surface_t *surface,
 	return similar->base.status;
 
     cairo_surface_set_device_offset (&similar->base, -rect.x, -rect.y);
+    surface->is_clear = TRUE;
 
     _get_target (surface);
     _cairo_output_stream_printf (surface->ctx->stream,
@@ -907,6 +959,7 @@ _emit_meta_surface_pattern (cairo_script_surface_t *surface,
 				 rect.width, rect.height,
 				 _content_to_string (source->content));
     target_push (similar);
+    similar->emitted = TRUE;
 
     old_cr = surface->cr;
     _cairo_script_implicit_context_init (&surface->cr);
@@ -918,7 +971,7 @@ _emit_meta_surface_pattern (cairo_script_surface_t *surface,
 	return status;
     }
 
-    cairo_list_del (&similar->operand);
+    cairo_list_del (&similar->operand.link);
     assert (target_is_active (surface));
 
     _cairo_output_stream_puts (surface->ctx->stream, "pop pattern");
@@ -1607,7 +1660,7 @@ _cairo_script_surface_create_similar (void	       *abstract_surface,
 
     ctx = other->ctx;
 
-    if (other->id == (unsigned long) -1) {
+    if (! other->emitted) {
 	status = _emit_surface (other);
 	if (unlikely (status))
 	    return _cairo_surface_create_in_error (status);
@@ -1631,19 +1684,13 @@ _cairo_script_surface_create_similar (void	       *abstract_surface,
     if (unlikely (surface->base.status))
 	return &surface->base;
 
-    status = _bitmap_next_id (&ctx->surface_id,
-			      &surface->id);
-    if (unlikely (status)) {
-	cairo_surface_destroy (&surface->base);
-	return _cairo_surface_create_in_error (status);
-    }
-
     _get_target (other);
     _cairo_output_stream_printf (ctx->stream,
-				 "%u %u //%s similar dup /s%ld exch def context\n",
+				 "%u %u //%s similar dup /s%u exch def context\n",
 				 width, height,
 				 _content_to_string (content),
-				 surface->id);
+				 surface->base.unique_id);
+    surface->emitted = TRUE;
     surface->defined = TRUE;
     surface->is_clear = TRUE;
     target_push (surface);
@@ -1732,46 +1779,47 @@ _cairo_script_surface_finish (void *abstract_surface)
     _cairo_path_fixed_fini (&surface->cr.current_path);
     _cairo_surface_clipper_reset (&surface->clipper);
 
-    if (surface->id != (unsigned long) -1) {
-	if (! surface->ctx->active) {
-	    if (cairo_list_is_first (&surface->operand,
-				     &surface->ctx->operands))
-	    {
-		_cairo_output_stream_printf (surface->ctx->stream,
-					     "pop\n");
-	    }
-	    else
-	    {
-		int depth = target_depth (surface);
-		if (depth == 1) {
+    if (surface->emitted) {
+	assert (! surface->active);
+
+	if (! cairo_list_is_empty (&surface->operand.link)) {
+	    if (! surface->ctx->active) {
+		if (target_is_active (surface)) {
 		    _cairo_output_stream_printf (surface->ctx->stream,
-						 "exch pop\n");
+						 "pop\n");
 		} else {
-		    _cairo_output_stream_printf (surface->ctx->stream,
-						 "%d -1 roll pop\n",
-						 depth);
+		    int depth = target_depth (surface);
+		    if (depth == 1) {
+			_cairo_output_stream_printf (surface->ctx->stream,
+						     "exch pop\n");
+		    } else {
+			_cairo_output_stream_printf (surface->ctx->stream,
+						     "%d -1 roll pop\n",
+						     depth);
+		    }
 		}
-	    }
-	    cairo_list_del (&surface->operand);
-	} 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 {
-		cairo_list_swap (&link->operand, &surface->operand);
-		cairo_list_add (&link->link, &surface->ctx->deferred);
+		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, &surface->ctx->deferred);
+		}
 	    }
 	}
 
 	if (surface->defined) {
 	    _cairo_output_stream_printf (surface->ctx->stream,
-					 "/s%ld undef\n",
-					 surface->id);
+					 "/s%u undef\n",
+					 surface->base.unique_id);
 	}
-
-	_bitmap_release_id (&surface->ctx->surface_id, surface->id);
     }
 
     status2 = _context_destroy (surface->ctx);
@@ -1880,17 +1928,26 @@ _cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip
 }
 
 static void
-ctx_active (cairo_script_context_t *ctx)
+active (cairo_script_surface_t *surface)
 {
-    ctx->active++;
+    assert (surface->active == FALSE);
+    surface->active = TRUE;
+    surface->ctx->active++;
 }
 
 static void
-ctx_inactive (cairo_script_context_t *ctx)
+inactive (cairo_script_surface_t *surface)
 {
+    cairo_script_context_t *ctx = surface->ctx;
+    cairo_list_t sorted;
+
+    assert (surface->active == TRUE);
+    surface->active = FALSE;
+
     if (--ctx->active > 0)
 	return;
 
+    cairo_list_init (&sorted);
     while (! cairo_list_is_empty (&ctx->deferred)) {
 	struct deferred_finish *df;
 	cairo_list_t *operand;
@@ -1902,7 +1959,41 @@ ctx_inactive (cairo_script_context_t *ctx)
 
 	depth = 0;
 	cairo_list_foreach (operand, &ctx->operands) {
-	    if (operand == &df->operand)
+	    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 (&ctx->deferred,
+				     struct deferred_finish,
+				     link);
+
+	depth = 0;
+	cairo_list_foreach (operand, &ctx->operands) {
+	    if (operand == &df->operand.link)
 		break;
 	    depth++;
 	}
@@ -1919,7 +2010,7 @@ ctx_inactive (cairo_script_context_t *ctx)
 					 depth);
 	}
 
-	cairo_list_del (&df->operand);
+	cairo_list_del (&df->operand.link);
 	cairo_list_del (&df->link);
 	free (df);
     }
@@ -1939,7 +2030,7 @@ _cairo_script_surface_paint (void			*abstract_surface,
 	    goto DONE;
     }
 
-    ctx_active (surface->ctx);
+    active (surface);
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     if (unlikely (status))
@@ -1962,7 +2053,7 @@ _cairo_script_surface_paint (void			*abstract_surface,
 
     surface->is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL;
 
-    ctx_inactive (surface->ctx);
+    inactive (surface);
 
   DONE:
     if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
@@ -1988,7 +2079,7 @@ _cairo_script_surface_mask (void			*abstract_surface,
 	    goto DONE;
     }
 
-    ctx_active (surface->ctx);
+    active (surface);
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     if (unlikely (status))
@@ -2021,7 +2112,7 @@ _cairo_script_surface_mask (void			*abstract_surface,
 
     surface->is_clear = FALSE;
 
-    ctx_inactive (surface->ctx);
+    inactive (surface);
 
   DONE:
     if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
@@ -2053,7 +2144,7 @@ _cairo_script_surface_stroke (void				*abstract_surface,
 	    goto DONE;
     }
 
-    ctx_active (surface->ctx);
+    active (surface);
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     if (unlikely (status))
@@ -2112,7 +2203,7 @@ _cairo_script_surface_stroke (void				*abstract_surface,
 
     surface->is_clear = FALSE;
 
-    ctx_inactive (surface->ctx);
+    inactive (surface);
 
   DONE:
     if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
@@ -2146,7 +2237,7 @@ _cairo_script_surface_fill (void			*abstract_surface,
 	    goto DONE;
     }
 
-    ctx_active (surface->ctx);
+    active (surface);
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     if (unlikely (status))
@@ -2192,7 +2283,7 @@ _cairo_script_surface_fill (void			*abstract_surface,
 
     surface->is_clear = FALSE;
 
-    ctx_inactive (surface->ctx);
+    inactive (surface);
 
   DONE:
     if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
@@ -2789,7 +2880,7 @@ _cairo_script_surface_show_text_glyphs (void			    *abstract_surface,
 	    goto DONE;
     }
 
-    ctx_active (surface->ctx);
+    active (surface);
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     if (unlikely (status))
@@ -2999,7 +3090,7 @@ _cairo_script_surface_show_text_glyphs (void			    *abstract_surface,
 
     surface->is_clear = FALSE;
 
-    ctx_inactive (surface->ctx);
+    inactive (surface);
 
   DONE:
     if (_cairo_surface_wrapper_is_active (&surface->wrapper)){
@@ -3103,6 +3194,19 @@ _cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr)
     cr->has_clip = FALSE;
 }
 
+static void
+_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr)
+{
+    if (cr->current_style.dash != NULL) {
+	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,
 				       double width,
@@ -3133,10 +3237,12 @@ _cairo_script_surface_create_internal (cairo_script_context_t *ctx,
     surface->width = width;
     surface->height = height;
 
+    surface->emitted = FALSE;
     surface->defined = FALSE;
     surface->is_clear = FALSE;
-    surface->id = (unsigned long) -1;
-    cairo_list_init (&surface->operand);
+    surface->active = FALSE;
+    surface->operand.type = SURFACE;
+    cairo_list_init (&surface->operand.link);
 
     _cairo_script_implicit_context_init (&surface->cr);
 
@@ -3233,8 +3339,8 @@ cairo_script_surface_create (cairo_script_context_t *context,
 			     double height)
 {
     return &_cairo_script_surface_create_internal (context,
-						     width, height,
-						     NULL)->base;
+						   width, height,
+						   NULL)->base;
 }
 
 cairo_surface_t *
commit 8f8b91d9049433c9210e0b3aad574cf659862ae0
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 18:37:01 2009 +0100

    [script] Wrap snapshot.
    
    Use the snapshot of our target surface if available.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index a47324d..e1ce00d 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -761,9 +761,10 @@ _emit_solid_pattern (cairo_script_surface_t *surface,
 	! CAIRO_COLOR_IS_OPAQUE (&solid->color))
     {
 	if (! (solid->content & CAIRO_CONTENT_COLOR) ||
-	    (solid->color.red_short   == 0 &&
-	     solid->color.green_short == 0 &&
-	     solid->color.blue_short  == 0))
+	    ! (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 (surface->ctx->stream,
 					 "%f a",
@@ -2206,6 +2207,17 @@ _cairo_script_surface_fill (void			*abstract_surface,
     return CAIRO_STATUS_SUCCESS;
 }
 
+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)
 {
@@ -3058,7 +3070,7 @@ _cairo_script_surface_backend = {
     _cairo_script_surface_fill,
     NULL,
 
-    NULL, /* _cairo_script_surface_snapshot, */
+    _cairo_script_surface_snapshot,
 
     NULL, /* is_similar */
     /* XXX need fill-stroke for passthrough */
diff --git a/src/cairo-surface-wrapper-private.h b/src/cairo-surface-wrapper-private.h
index 5ebcb86..3ffd9ab 100644
--- a/src/cairo-surface-wrapper-private.h
+++ b/src/cairo-surface-wrapper-private.h
@@ -142,6 +142,9 @@ cairo_private cairo_bool_t
 _cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper,
 				    cairo_rectangle_int_t   *extents);
 
+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);
 
diff --git a/src/cairo-surface-wrapper.c b/src/cairo-surface-wrapper.c
index b21a32d..12d6783 100644
--- a/src/cairo-surface-wrapper.c
+++ b/src/cairo-surface-wrapper.c
@@ -429,6 +429,12 @@ _cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper,
     return _cairo_surface_get_extents (wrapper->target, extents);
 }
 
+cairo_surface_t *
+_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper)
+{
+    return _cairo_surface_snapshot (wrapper->target);
+}
+
 cairo_bool_t
 _cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper)
 {
commit fee647c98506eedad0fea667a9442786cb0804b7
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 18:35:48 2009 +0100

    [script] Read from a FILE stream
    
    For ease of implementing the interpreter inside a pipeline, add a method
    to execute a FILE *.

diff --git a/util/cairo-script/cairo-script-interpreter.c b/util/cairo-script/cairo-script-interpreter.c
index 44c33f3..adb1095 100644
--- a/util/cairo-script/cairo-script-interpreter.c
+++ b/util/cairo-script/cairo-script-interpreter.c
@@ -565,6 +565,28 @@ cairo_script_interpreter_run (csi_t *ctx, const char *filename)
 }
 
 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;
diff --git a/util/cairo-script/cairo-script-interpreter.h b/util/cairo-script/cairo-script-interpreter.h
index b7950aa..52e6b3a 100644
--- a/util/cairo-script/cairo-script-interpreter.h
+++ b/util/cairo-script/cairo-script-interpreter.h
@@ -86,6 +86,10 @@ 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);
commit a9d997fecd54cea7dcd71487a24dbae14d0073a8
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 17:21:05 2009 +0100

    [script] Introduce cairo_script_context_t
    
    cairo_script_context_t is an encapsulation object for interfacing with the
    output - multiple surfaces can share the same context, meaning that they
    write to the same destination file/stream.

diff --git a/boilerplate/cairo-boilerplate-script.c b/boilerplate/cairo-boilerplate-script.c
index 24a5843..b944a55 100644
--- a/boilerplate/cairo-boilerplate-script.c
+++ b/boilerplate/cairo-boilerplate-script.c
@@ -48,6 +48,7 @@ _cairo_boilerplate_script_create_surface (const char		 *name,
 					  void			**closure)
 {
     script_target_closure_t *ptc;
+    cairo_script_context_t *ctx;
     cairo_surface_t *surface;
     cairo_status_t status;
 
@@ -59,7 +60,9 @@ _cairo_boilerplate_script_create_surface (const char		 *name,
     xasprintf (&ptc->filename, "%s.out.cs", name);
     xunlink (ptc->filename);
 
-    surface = cairo_script_surface_create (ptc->filename, width, height);
+    ctx = cairo_script_context_create (ptc->filename);
+    surface = cairo_script_surface_create (ctx, width, height);
+    cairo_script_context_destroy (ctx);
 
     status = cairo_surface_set_user_data (surface,
 					  &script_closure_key, ptc, NULL);
diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
index f15f739..1abfe09 100644
--- a/boilerplate/cairo-boilerplate.c
+++ b/boilerplate/cairo-boilerplate.c
@@ -151,17 +151,6 @@ _cairo_boilerplate_meta_create_surface (const char	     *name,
     extents.height = height;
     return cairo_meta_surface_create (content, &extents);
 }
-
-#if CAIRO_HAS_SCRIPT_SURFACE
-static cairo_status_t
-stdio_write (void *closure, const unsigned char *data, unsigned int len)
-{
-    if (fwrite (data, len, 1, closure) != 1)
-	return CAIRO_STATUS_WRITE_ERROR;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-#endif
 #endif
 
 const cairo_user_data_key_t cairo_boilerplate_output_basename_key;
@@ -172,7 +161,6 @@ _cairo_boilerplate_get_image_surface (cairo_surface_t *src,
 				      int width,
 				      int height)
 {
-    FILE *file = NULL;
     cairo_surface_t *surface, *image;
     cairo_t *cr;
     cairo_status_t status;
@@ -195,17 +183,14 @@ _cairo_boilerplate_get_image_surface (cairo_surface_t *src,
 	test_name = cairo_surface_get_user_data (src,
 						 &cairo_boilerplate_output_basename_key);
 	if (test_name != NULL) {
+	    cairo_script_context_t *ctx;
 	    char *filename;
 
 	    xasprintf (&filename, "%s.out.trace", test_name);
-	    file = fopen (filename, "w");
+	    ctx = cairo_script_context_create (filename);
+	    surface = cairo_script_surface_create_for_target (ctx, image);
+	    cairo_script_context_destroy (ctx);
 	    free (filename);
-
-	    if (file != NULL) {
-		surface = cairo_script_surface_create_for_target (image,
-								  stdio_write,
-								  file);
-	    }
 	}
     }
 #endif
@@ -224,9 +209,6 @@ _cairo_boilerplate_get_image_surface (cairo_surface_t *src,
     }
     cairo_destroy (cr);
 
-    if (file != NULL)
-	fclose (file);
-
     return image;
 }
 
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index a59f512..a47324d 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -61,7 +61,6 @@
 
 #define static cairo_warn static
 
-typedef struct _cairo_script_vmcontext cairo_script_vmcontext_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_surface_font_private cairo_script_surface_font_private_t;
@@ -71,7 +70,9 @@ struct deferred_finish {
     cairo_list_t operand;
 };
 
-struct _cairo_script_vmcontext {
+struct _cairo_script_context {
+    cairo_status_t status;
+
     int ref;
     int active;
 
@@ -93,7 +94,7 @@ struct _cairo_script_vmcontext {
 };
 
 struct _cairo_script_surface_font_private {
-    cairo_script_vmcontext_t *ctx;
+    cairo_script_context_t *ctx;
     cairo_bool_t has_sfnt;
     unsigned long id;
     unsigned long subset_glyph_index;
@@ -122,7 +123,7 @@ struct _cairo_script_surface {
 
     cairo_surface_wrapper_t wrapper;
 
-    cairo_script_vmcontext_t *ctx;
+    cairo_script_context_t *ctx;
     cairo_surface_clipper_t clipper;
 
     unsigned long id;
@@ -139,13 +140,13 @@ struct _cairo_script_surface {
 static const cairo_surface_backend_t _cairo_script_surface_backend;
 
 static cairo_script_surface_t *
-_cairo_script_surface_create_internal (cairo_script_vmcontext_t *ctx,
+_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
 				       double width,
 				       double height,
 				       cairo_surface_t *passthrough);
 
 static cairo_status_t
-_vmcontext_destroy (cairo_script_vmcontext_t *ctx);
+_context_destroy (cairo_script_context_t *ctx);
 
 static void
 _cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
@@ -384,7 +385,7 @@ target_depth (cairo_script_surface_t *surface)
 static void
 _get_target (cairo_script_surface_t *surface)
 {
-    cairo_script_vmcontext_t *ctx = surface->ctx;
+    cairo_script_context_t *ctx = surface->ctx;
 
     if (! target_is_active (surface)) {
 	int depth = target_depth (surface);
@@ -1083,7 +1084,7 @@ _emit_png_surface (cairo_script_surface_t *surface,
 }
 
 struct def {
-    cairo_script_vmcontext_t *ctx;
+    cairo_script_context_t *ctx;
     cairo_user_data_array_t *user_data;
     unsigned int tag;
     cairo_list_t link;
@@ -1600,7 +1601,7 @@ _cairo_script_surface_create_similar (void	       *abstract_surface,
 {
     cairo_script_surface_t *surface, *other = abstract_surface;
     cairo_surface_t *passthrough = NULL;
-    cairo_script_vmcontext_t *ctx;
+    cairo_script_context_t *ctx;
     cairo_status_t status;
 
     ctx = other->ctx;
@@ -1650,10 +1651,11 @@ _cairo_script_surface_create_similar (void	       *abstract_surface,
 }
 
 static cairo_status_t
-_vmcontext_destroy (cairo_script_vmcontext_t *ctx)
+_context_destroy (cairo_script_context_t *ctx)
 {
     cairo_status_t status;
 
+    assert (ctx->ref > 0);
     if (--ctx->ref)
 	return _cairo_output_stream_flush (ctx->stream);
 
@@ -1771,7 +1773,7 @@ _cairo_script_surface_finish (void *abstract_surface)
 	_bitmap_release_id (&surface->ctx->surface_id, surface->id);
     }
 
-    status2 = _vmcontext_destroy (surface->ctx);
+    status2 = _context_destroy (surface->ctx);
     if (status == CAIRO_STATUS_SUCCESS)
 	status = status2;
 
@@ -1877,13 +1879,13 @@ _cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip
 }
 
 static void
-ctx_active (cairo_script_vmcontext_t *ctx)
+ctx_active (cairo_script_context_t *ctx)
 {
     ctx->active++;
 }
 
 static void
-ctx_inactive (cairo_script_vmcontext_t *ctx)
+ctx_inactive (cairo_script_context_t *ctx)
 {
     if (--ctx->active > 0)
 	return;
@@ -3069,34 +3071,6 @@ _cairo_script_surface_backend = {
     _cairo_script_surface_show_text_glyphs
 };
 
-static cairo_bool_t
-_cairo_surface_is_script (cairo_surface_t *surface)
-{
-    return surface->backend == &_cairo_script_surface_backend;
-}
-
-static cairo_script_vmcontext_t *
-_cairo_script_vmcontext_create (cairo_output_stream_t *stream)
-{
-    cairo_script_vmcontext_t *ctx;
-
-    ctx = malloc (sizeof (cairo_script_vmcontext_t));
-    if (unlikely (ctx == NULL))
-	return NULL;
-
-    memset (ctx, 0, sizeof (cairo_script_vmcontext_t));
-
-    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);
-
-    return ctx;
-}
-
 static void
 _cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr)
 {
@@ -3118,7 +3092,7 @@ _cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr)
 }
 
 static cairo_script_surface_t *
-_cairo_script_surface_create_internal (cairo_script_vmcontext_t *ctx,
+_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
 				       double width,
 				       double height,
 				       cairo_surface_t *passthrough)
@@ -3126,7 +3100,7 @@ _cairo_script_surface_create_internal (cairo_script_vmcontext_t *ctx,
     cairo_script_surface_t *surface;
 
     if (unlikely (ctx == NULL))
-	return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+	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))
@@ -3157,132 +3131,125 @@ _cairo_script_surface_create_internal (cairo_script_vmcontext_t *ctx,
     return surface;
 }
 
-cairo_surface_t *
-cairo_script_surface_create (const char		*filename,
-			     double width,
-			     double height)
+static const cairo_script_context_t _nil_context = {
+    CAIRO_STATUS_NO_MEMORY,
+    -1
+};
+
+static cairo_script_context_t *
+_cairo_script_context_create_internal (cairo_output_stream_t *stream)
 {
-    cairo_output_stream_t *stream;
-    cairo_script_surface_t *surface;
+    cairo_script_context_t *ctx;
 
-    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));
+    ctx = malloc (sizeof (cairo_script_context_t));
+    if (unlikely (ctx == NULL)) {
+	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+	return (cairo_script_context_t *) &_nil_context;
+    }
 
+    memset (ctx, 0, sizeof (cairo_script_context_t));
+    ctx->status = CAIRO_STATUS_SUCCESS;
+    ctx->ref = 1;
 
-    surface = _cairo_script_surface_create_internal
-	(_cairo_script_vmcontext_create (stream), width, height, NULL);
-    if (surface->base.status)
-	return &surface->base;
+    cairo_list_init (&ctx->operands);
+    cairo_list_init (&ctx->deferred);
+    ctx->stream = stream;
+    ctx->mode = CAIRO_SCRIPT_MODE_ASCII;
 
-    _cairo_output_stream_puts (surface->ctx->stream, "%!CairoScript\n");
-    return &surface->base;
+    cairo_list_init (&ctx->fonts);
+    cairo_list_init (&ctx->defines);
+
+    _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n");
+
+    return ctx;
 }
 
-cairo_surface_t *
-cairo_script_surface_create_for_stream (cairo_write_func_t	 write_func,
-					void			*closure,
-					double width,
-					double height)
+cairo_script_context_t *
+cairo_script_context_create (const char *filename)
 {
     cairo_output_stream_t *stream;
 
-    stream = _cairo_output_stream_create (write_func, NULL, closure);
+    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_script_context_t *) &_nil_context;
 
-    return &_cairo_script_surface_create_internal
-	(_cairo_script_vmcontext_create (stream), width, height, NULL)->base;
+    return _cairo_script_context_create_internal (stream);
 }
 
-cairo_surface_t *
-cairo_script_surface_create_for_target (cairo_surface_t *target,
-					cairo_write_func_t write_func,
-					void *closure)
+cairo_script_context_t *
+cairo_script_context_create_for_stream (cairo_write_func_t	 write_func,
+					void			*closure)
 {
     cairo_output_stream_t *stream;
-    cairo_script_surface_t *surface;
-    cairo_rectangle_int_t extents;
-
-    if (unlikely (target->status))
-	return _cairo_surface_create_in_error (target->status);
 
     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_script_context_t *) &_nil_context;
 
-    if (! _cairo_surface_get_extents (target, &extents))
-	extents.width = extents.height = -1;
-
-    surface = _cairo_script_surface_create_internal
-	(_cairo_script_vmcontext_create (stream),
-	 extents.width, extents.height,
-	 target);
-    if (unlikely (surface->base.status))
-	return &surface->base;
-
-    _cairo_output_stream_puts (surface->ctx->stream, "%!CairoScript\n");
-    return &surface->base;
+    return _cairo_script_context_create_internal (stream);
 }
 
 void
-cairo_script_surface_write_comment (cairo_surface_t *abstract_surface,
+cairo_script_context_write_comment (cairo_script_context_t *context,
 				    const char *comment,
 				    int len)
 {
-    cairo_script_surface_t *surface;
-    cairo_status_t status_ignored;
-
-    if (! _cairo_surface_is_script (abstract_surface)) {
-	status_ignored = _cairo_surface_set_error (abstract_surface,
-				  CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
-	return;
-    }
-
-    if (abstract_surface->status)
-	return;
-
-    surface = (cairo_script_surface_t *) abstract_surface;
-
     if (len < 0)
 	len = strlen (comment);
 
-    _cairo_output_stream_puts (surface->ctx->stream, "% ");
-    _cairo_output_stream_write (surface->ctx->stream, comment, len);
-    _cairo_output_stream_puts (surface->ctx->stream, "\n");
+    _cairo_output_stream_puts (context->stream, "% ");
+    _cairo_output_stream_write (context->stream, comment, len);
+    _cairo_output_stream_puts (context->stream, "\n");
 }
 
 void
-cairo_script_surface_set_mode (cairo_surface_t *abstract_surface,
+cairo_script_context_set_mode (cairo_script_context_t *context,
 			       cairo_script_mode_t mode)
 {
-    cairo_script_surface_t *surface;
-    cairo_status_t status_ignored;
+    context->mode = mode;
+}
 
-    if (! _cairo_surface_is_script (abstract_surface)) {
-	status_ignored = _cairo_surface_set_error (abstract_surface,
-				  CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
-	return;
-    }
+cairo_script_mode_t
+cairo_script_context_get_mode (cairo_script_context_t *context)
+{
+    return context->mode;
+}
 
-    if (abstract_surface->status)
-	return;
+cairo_surface_t *
+cairo_script_surface_create (cairo_script_context_t *context,
+			     double width,
+			     double height)
+{
+    return &_cairo_script_surface_create_internal (context,
+						     width, height,
+						     NULL)->base;
+}
+
+cairo_surface_t *
+cairo_script_surface_create_for_target (cairo_script_context_t *context,
+					cairo_surface_t *target)
+{
+    cairo_rectangle_int_t extents;
 
-    surface = (cairo_script_surface_t *) abstract_surface;
-    surface->ctx->mode = mode;
+    if (unlikely (target->status))
+	return _cairo_surface_create_in_error (target->status);
+
+    if (! _cairo_surface_get_extents (target, &extents))
+	extents.width = extents.height = -1;
+
+    return &_cairo_script_surface_create_internal (context,
+						   extents.width,
+						   extents.height,
+						   target)->base;
 }
 
-cairo_script_mode_t
-cairo_script_surface_get_mode (cairo_surface_t *abstract_surface)
+void
+cairo_script_context_destroy (cairo_script_context_t *context)
 {
-    cairo_script_surface_t *surface;
+    cairo_status_t status_ignored;
 
-    if (! _cairo_surface_is_script (abstract_surface) ||
-	abstract_surface->status)
-    {
-	return CAIRO_SCRIPT_MODE_ASCII;
-    }
+    if (context == NULL || context->ref < 0)
+	return;
 
-    surface = (cairo_script_surface_t *) abstract_surface;
-    return surface->ctx->mode;
+    status_ignored = _context_destroy (context);
 }
diff --git a/src/cairo-script.h b/src/cairo-script.h
index 397080b..182b65d 100644
--- a/src/cairo-script.h
+++ b/src/cairo-script.h
@@ -42,38 +42,43 @@
 
 CAIRO_BEGIN_DECLS
 
-cairo_public cairo_surface_t *
-cairo_script_surface_create (const char		*filename,
-			     double width,
-			     double height);
+typedef struct _cairo_script_context cairo_script_context_t;
 
-cairo_public cairo_surface_t *
-cairo_script_surface_create_for_stream (cairo_write_func_t	 write_func,
-					void			*closure,
-					double width,
-					double height);
+typedef enum {
+    CAIRO_SCRIPT_MODE_BINARY,
+    CAIRO_SCRIPT_MODE_ASCII
+} cairo_script_mode_t;
 
-cairo_public cairo_surface_t *
-cairo_script_surface_create_for_target (cairo_surface_t *surface,
-					cairo_write_func_t write_func,
-					void *closure);
+cairo_public cairo_script_context_t *
+cairo_script_context_create (const char *filename);
+
+cairo_public cairo_script_context_t *
+cairo_script_context_create_for_stream (cairo_write_func_t	 write_func,
+					void			*closure);
 
 cairo_public void
-cairo_script_surface_write_comment (cairo_surface_t *abstract_surface,
+cairo_script_context_write_comment (cairo_script_context_t *context,
 				    const char *comment,
 				    int len);
 
-typedef enum {
-    CAIRO_SCRIPT_MODE_BINARY,
-    CAIRO_SCRIPT_MODE_ASCII
-} cairo_script_mode_t;
-
 cairo_public void
-cairo_script_surface_set_mode (cairo_surface_t *surface,
+cairo_script_context_set_mode (cairo_script_context_t *context,
 			       cairo_script_mode_t mode);
 
 cairo_public cairo_script_mode_t
-cairo_script_surface_get_mode (cairo_surface_t *surface);
+cairo_script_context_get_mode (cairo_script_context_t *context);
+
+cairo_public void
+cairo_script_context_destroy (cairo_script_context_t *context);
+
+cairo_public cairo_surface_t *
+cairo_script_surface_create (cairo_script_context_t *context,
+			     double width,
+			     double height);
+
+cairo_public cairo_surface_t *
+cairo_script_surface_create_for_target (cairo_script_context_t *context,
+					cairo_surface_t *target);
 
 CAIRO_END_DECLS
 
diff --git a/test/cairo-test-trace.c b/test/cairo-test-trace.c
index 3be5402..c35d6a7 100644
--- a/test/cairo-test-trace.c
+++ b/test/cairo-test-trace.c
@@ -864,14 +864,17 @@ static void
 write_trace (const char *trace, struct slave *slave)
 {
 #if CAIRO_HAS_SCRIPT_SURFACE
+    cairo_script_context_t *ctx;
     cairo_surface_t *script;
     char *filename;
     cairo_status_t status;
 
     xasprintf (&filename, "%s-fail.trace", trace);
-    script = cairo_script_surface_create (filename,
+    ctx = cairo_script_context_create (filename);
+    script = cairo_script_surface_create (ctx,
 					  slave->width,
 					  slave->height);
+    cairo_script_context_destroy (ctx);
     free (filename);
 
     status = cairo_meta_surface_replay (slave->image, script);
diff --git a/util/cairo-script/csi-trace.c b/util/cairo-script/csi-trace.c
index 418a335..d118067 100644
--- a/util/cairo-script/csi-trace.c
+++ b/util/cairo-script/csi-trace.c
@@ -6,11 +6,11 @@
 #include <libgen.h>
 
 static cairo_surface_t *
-_similar_surface_create (void *closure,
+_script_surface_create (void *closure,
 			 cairo_content_t content,
 			 double width, double height)
 {
-    return cairo_surface_create_similar (closure, content, width, height);
+    return cairo_script_surface_create (closure, width, height);
 }
 
 int
@@ -18,7 +18,7 @@ main (int argc, char **argv)
 {
     cairo_script_interpreter_t *csi;
     cairo_script_interpreter_hooks_t hooks = {
-	.surface_create = _similar_surface_create,
+	.surface_create = _script_surface_create,
     };
     int i;
 
@@ -28,13 +28,12 @@ main (int argc, char **argv)
 	char buf[4096];
 
 	snprintf (buf, sizeof (buf), "%s.trace", basename (argv[i]));
-
-	cairo_surface_destroy (hooks.closure);
-	hooks.closure = cairo_script_surface_create (buf, -1, -1);
+	cairo_script_context_destroy (hooks.closure);
+	hooks.closure = cairo_script_context_create (buf);
 	cairo_script_interpreter_install_hooks (csi, &hooks);
 	cairo_script_interpreter_run (csi, argv[i]);
     }
-    cairo_surface_destroy (hooks.closure);
+    cairo_script_context_destroy (hooks.closure);
 
     return cairo_script_interpreter_destroy (csi);
 }
diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index 365bda5..7a6219d 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -4145,23 +4145,20 @@ cairo_xlib_surface_create_with_xrender_format (Display *dpy,
 #if CAIRO_HAS_SCRIPT_SURFACE
 #include <cairo-script.h>
 cairo_surface_t *
-cairo_script_surface_create (const char *filename,
+cairo_script_surface_create (cairo_script_context_t *ctx,
 			     double width,
 			     double height)
 {
     cairo_surface_t *ret;
     long surface_id;
 
-    ret = DLCALL (cairo_script_surface_create, filename, width, height);
+    ret = DLCALL (cairo_script_surface_create, ctx, width, height);
     surface_id = _create_surface_id (ret);
 
     _emit_line_info ();
     if (_write_lock ()) {
 	_trace_printf ("dict\n"
 		       "  /type /script set\n"
-		       "  /filename ");
-	_emit_string_literal (filename, -1);
-	_trace_printf (" set\n"
 		       "  /width %g set\n"
 		       "  /height %g set\n"
 		       "  surface dup /s%ld exch def\n",
@@ -4177,28 +4174,21 @@ cairo_script_surface_create (const char *filename,
 }
 
 cairo_surface_t *
-cairo_script_surface_create_for_stream (cairo_write_func_t write_func,
-					void *data,
-					double width,
-					double height)
+cairo_script_surface_create_for_target (cairo_script_context_t *ctx,
+					cairo_surface_t *target)
 {
     cairo_surface_t *ret;
     long surface_id;
 
-    ret = DLCALL (cairo_script_surface_create_for_stream,
-		  write_func, data, width, height);
+    ret = DLCALL (cairo_script_surface_create_for_target, ctx, target);
     surface_id = _create_surface_id (ret);
 
     _emit_line_info ();
     if (_write_lock ()) {
 	_trace_printf ("dict\n"
 		       "  /type /script set\n"
-		       "  /width %g set\n"
-		       "  /height %g set\n"
 		       "  surface dup /s%ld exch def\n",
-		       width, height,
 		       surface_id);
-	_surface_object_set_size (ret, width, height);
 	_get_object (SURFACE, ret)->defined = true;
 	_push_operand (SURFACE, ret);
 	_write_unlock ();
commit 28887ac272c8a36a41da4d6d58044164b94da6f3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 16:38:08 2009 +0100

    [trace] Fix emission of similar (track operands)

diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index fb3b94d..365bda5 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -3158,18 +3158,17 @@ cairo_surface_create_similar (cairo_surface_t *other,
 	Object *obj;
 
 	obj = _get_object (SURFACE, other);
-
-	if (_pop_operands_to (SURFACE, other)) {
-	    _consume_operand ();
-	} else if (obj->defined) {
-	    _trace_printf ("s%ld", obj->token);
-	} else {
+	if (obj->defined)
+	    _trace_printf ("s%ld ", obj->token);
+	else if (current_stack_depth == obj->operand + 1)
+	    _trace_printf ("dup ");
+	else
 	    _trace_printf ("%d index ", current_stack_depth - obj->operand - 1);
-	}
 	_trace_printf ("%d %d //%s similar\n",
 		       width,
 		       height,
 		       _content_to_string (content));
+
 	_push_operand (SURFACE, ret);
 	_write_unlock ();
     }
commit dbd9438f5d01666ea118254427c0dc66a519ec3e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 15:57:30 2009 +0100

    [stroke] Only mark traps as having intersection if non-empty.
    
    We were hitting an assertion attempting to eliminate intersections inside
    the rectilinear tessellator for empty strokes. We can avoid this
    assertion, by only marking the traps as having potential intersections iff
    it is non-empty.

diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index ba169ae..69300da 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1493,9 +1493,6 @@ _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t	*stroker,
     _cairo_stroker_dash_init (&stroker->dash, stroke_style);
 
     stroker->has_bounds = FALSE;
-
-    /* As we incrementally tessellate, we do not eliminate self-intersections */
-    stroker->traps->has_intersections = TRUE;
 }
 
 static void
@@ -2032,6 +2029,8 @@ _cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t	*path,
 	status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
 
     traps->is_rectilinear = 1;
+    /* As we incrementally tessellate, we do not eliminate self-intersections */
+    traps->has_intersections = traps->num_traps != 0;
 BAIL:
     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
 
commit 60d73da9f2e148e982254c78773f0b925be184ff
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 24 08:04:12 2009 +0100

    [clip] Cache intermediate clip masks.
    
    As we now superimpose a per-operation clip, this defeats the current
    top-level caching mechanism. Instead we need to cache the mask for
    each path. This still seems quite wasteful, and an avenue would be to
    avoid caching if the path is rectilinear and reduce the number of
    required composite operations. (However, first find test case...)

diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 67079c4..8d68e47 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -964,19 +964,17 @@ _cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path,
     return CAIRO_STATUS_SUCCESS;
 }
 
-cairo_surface_t *
-_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
+static cairo_surface_t *
+_cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
+			      cairo_surface_t *target)
 {
     cairo_surface_t *surface;
     cairo_pattern_union_t pattern;
     cairo_status_t status;
-    cairo_clip_path_t *clip_path = clip->path;
     const cairo_rectangle_int_t *clip_extents = &clip_path->extents;
-    cairo_region_t *clip_region;
+    cairo_clip_path_t *prev;
     cairo_bool_t need_translate;
 
-    assert (clip_path != NULL);
-
     if (clip_path->surface != NULL &&
 	clip_path->surface->backend == target->backend)
     {
@@ -1008,22 +1006,22 @@ _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
 			       CAIRO_COLOR_WHITE,
 			       CAIRO_CONTENT_COLOR);
 
-    status = _cairo_clip_get_region (clip, &clip_region);
+    status = _cairo_clip_path_to_region (clip_path);
     if (unlikely (_cairo_status_is_error (status)))
 	goto BAIL;
 
     need_translate = clip_extents->x || clip_extents->y;
     if (status == CAIRO_STATUS_SUCCESS) {
 	if (need_translate) {
-	    cairo_region_translate (clip_region,
+	    cairo_region_translate (clip_path->region,
 				    -clip_extents->x, -clip_extents->y);
 	}
 	status = _cairo_surface_fill_region (surface,
-					     CAIRO_OPERATOR_ADD,
+					     CAIRO_OPERATOR_SOURCE,
 					     CAIRO_COLOR_WHITE,
-					     clip_region);
+					     clip_path->region);
 	if (need_translate) {
-	    cairo_region_translate (clip_region,
+	    cairo_region_translate (clip_path->region,
 				    clip_extents->x, clip_extents->y);
 	}
 	if (unlikely (status))
@@ -1037,7 +1035,7 @@ _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
 					 _cairo_fixed_from_int (-clip_extents->y));
 	}
 	status = _cairo_surface_fill (surface,
-				      CAIRO_OPERATOR_ADD,
+				      CAIRO_OPERATOR_OVER,
 				      &pattern.base,
 				      &clip_path->path,
 				      clip_path->fill_rule,
@@ -1054,79 +1052,118 @@ _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
 	    goto BAIL;
     }
 
-    while ((clip_path = clip_path->prev) != NULL) {
-	status = _cairo_clip_path_to_region (clip_path);
+    prev = clip_path->prev;
+  NEXT_PATH:
+    if (prev != NULL) {
+	status = _cairo_clip_path_to_region (prev);
 	if (unlikely (_cairo_status_is_error (status)))
 	    goto BAIL;
 
 	if (status == CAIRO_STATUS_SUCCESS) {
-	    cairo_region_translate (clip_path->region,
-				    -clip_extents->x, -clip_extents->y);
+	    if (need_translate) {
+		cairo_region_translate (prev->region,
+					-clip_extents->x, -clip_extents->y);
+	    }
 	    status = _cairo_surface_fill_region (surface,
 						 CAIRO_OPERATOR_IN,
 						 CAIRO_COLOR_WHITE,
-						 clip_path->region);
-	    cairo_region_translate (clip_path->region,
-				    clip_extents->x, clip_extents->y);
+						 prev->region);
+	    if (need_translate) {
+		cairo_region_translate (prev->region,
+					clip_extents->x, clip_extents->y);
+	    }
+	    if (unlikely (status))
+		goto BAIL;
+	} else if (prev->path.is_rectilinear) {
+	    if (need_translate) {
+		_cairo_path_fixed_translate (&prev->path,
+					     _cairo_fixed_from_int (-clip_extents->x),
+					     _cairo_fixed_from_int (-clip_extents->y));
+	    }
+	    status = _cairo_surface_fill (surface,
+					  CAIRO_OPERATOR_IN,
+					  &pattern.base,
+					  &prev->path,
+					  clip_path->fill_rule,
+					  clip_path->tolerance,
+					  clip_path->antialias,
+					  NULL);
+	    if (need_translate) {
+		_cairo_path_fixed_translate (&prev->path,
+					     _cairo_fixed_from_int (clip_extents->x),
+					     _cairo_fixed_from_int (clip_extents->y));
+	    }
+
 	    if (unlikely (status))
 		goto BAIL;
 
-	    goto DONE;
+	    prev = prev->prev;
+	    goto NEXT_PATH;
 	} else {
-	    if (clip_path->surface != NULL &&
-		clip_path->surface->backend == surface->backend)
-	    {
-		_cairo_pattern_init_for_surface (&pattern.surface,
-						 clip_path->surface);
-		cairo_matrix_init_translate (&pattern.base.matrix,
-					     -clip_path->extents.x + clip_extents->x,
-					     -clip_path->extents.y + clip_extents->y);
-		status = _cairo_surface_paint (surface,
-					       CAIRO_OPERATOR_IN,
-					       &pattern.base,
-					       NULL);
-
-		_cairo_pattern_fini (&pattern.base);
-
-		if (unlikely (status))
-		    goto BAIL;
-
-		goto DONE;
-	    } else {
-		/* XXX build intermediate surfaces? */
-		if (need_translate) {
-		    _cairo_path_fixed_translate (&clip_path->path,
-						 _cairo_fixed_from_int (-clip_extents->x),
-						 _cairo_fixed_from_int (-clip_extents->y));
-		}
-		status = _cairo_surface_fill (surface,
-					      CAIRO_OPERATOR_IN,
-					      &pattern.base,
-					      &clip_path->path,
-					      clip_path->fill_rule,
-					      clip_path->tolerance,
-					      clip_path->antialias,
-					      NULL);
-		if (need_translate) {
-		    _cairo_path_fixed_translate (&clip_path->path,
-						 _cairo_fixed_from_int (clip_extents->x),
-						 _cairo_fixed_from_int (clip_extents->y));
-		}
-	    }
+	    _cairo_pattern_init_for_surface (&pattern.surface,
+					     _cairo_clip_path_get_surface (prev,
+									   target));
+	    cairo_matrix_init_translate (&pattern.base.matrix,
+					 -prev->extents.x + clip_extents->x,
+					 -prev->extents.y + clip_extents->y);
+	    status = _cairo_surface_paint (surface,
+					   CAIRO_OPERATOR_IN,
+					   &pattern.base,
+					   NULL);
+	    _cairo_pattern_fini (&pattern.base);
+
 	    if (unlikely (status))
 		goto BAIL;
 	}
     }
 
-DONE:
-    cairo_surface_destroy (clip->path->surface);
-    return clip->path->surface = cairo_surface_reference (surface);
+  DONE:
+    cairo_surface_destroy (clip_path->surface);
+    return clip_path->surface = cairo_surface_reference (surface);
 
-BAIL:
+  BAIL:
     cairo_surface_destroy (surface);
     return _cairo_surface_create_in_error (status);
 }
 
+void
+_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip)
+{
+    cairo_clip_path_t *clip_path;
+
+    if (clip == NULL) {
+	fprintf (stream, "no clip\n");
+	return;
+    }
+
+    if (clip->all_clipped) {
+	fprintf (stream, "clip: all-clipped\n");
+	return;
+    }
+
+    if (clip->path == NULL) {
+	fprintf (stream, "clip: empty\n");
+	return;
+    }
+
+    fprintf (stream, "clip:\n");
+
+    clip_path = clip->path;
+    do {
+	fprintf (stream, "path: has region? %s, has surface? %s: ",
+		 clip_path->region == NULL ? "no" : "yes",
+		 clip_path->surface == NULL ? "no" : "yes");
+	_cairo_debug_print_path (stream, &clip_path->path);
+    } while ((clip_path = clip_path->prev) != NULL);
+}
+
+cairo_surface_t *
+_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
+{
+    assert (clip->path != NULL);
+    return _cairo_clip_path_get_surface (clip->path, target);
+}
+
 cairo_status_t
 _cairo_clip_combine_with_surface (cairo_clip_t *clip, cairo_surface_t *dst)
 {
diff --git a/src/cairoint.h b/src/cairoint.h
index 2584981..4db99d6 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2772,4 +2772,7 @@ _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface);
 void
 _cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path);
 
+void
+_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip);
+
 #endif
commit 50c7d637f3a97031c5f4e2bc8d41d17e5397db45
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 15:49:11 2009 +0100

    [trace] Emit an 'index' for an undefined surface
    
    Larry Ewing hit a bug in cairo-trace whereby it tried to create a similar
    surface referencing an undefined object. This fix checks whether the
    object has yet to be defined, and if not issues an index in order to copy
    the appropriate operand from the stack.

diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index c021d2d..fb3b94d 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -3148,7 +3148,6 @@ cairo_surface_create_similar (cairo_surface_t *other,
 			      int width, int height)
 {
     cairo_surface_t *ret;
-    long other_id;
     long surface_id;
 
     ret = DLCALL (cairo_surface_create_similar, other, content, width, height);
@@ -3156,21 +3155,21 @@ cairo_surface_create_similar (cairo_surface_t *other,
 
     _emit_line_info ();
     if (other != NULL && _write_lock ()) {
-	other_id = _get_surface_id (other);
+	Object *obj;
+
+	obj = _get_object (SURFACE, other);
 
 	if (_pop_operands_to (SURFACE, other)) {
 	    _consume_operand ();
-	    _trace_printf ("%d %d //%s similar\n",
-			   width,
-			   height,
-			   _content_to_string (content));
+	} else if (obj->defined) {
+	    _trace_printf ("s%ld", obj->token);
 	} else {
-	    _trace_printf ("s%ld %d %d //%s similar\n",
-			   other_id,
-			   width,
-			   height,
-			   _content_to_string (content));
+	    _trace_printf ("%d index ", current_stack_depth - obj->operand - 1);
 	}
+	_trace_printf ("%d %d //%s similar\n",
+		       width,
+		       height,
+		       _content_to_string (content));
 	_push_operand (SURFACE, ret);
 	_write_unlock ();
     }
commit 3a483c2896c28142a90bd0e282af3862e066adfe
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 00:19:03 2009 +0100

    [gstate] Convert simple mask() into a paint()
    
    As using mask() prevents various optimisations in the backends (for
    example the use of geometric clips) and for some may trigger fallbacks,
    perform the simplifications usually done (too late) by the pattern layer
    in the generic gstate layer. This allows us on the odd occasion to
    transform a mask() into a paint() but perhaps more importantly removes the
    need for identical transformations in each backend.

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 902de85..ed0e877 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -818,12 +818,70 @@ _cairo_gstate_path_extents (cairo_gstate_t     *gstate,
 }
 
 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 be replace with a solid.
+     */
+    switch (original->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
+    case CAIRO_PATTERN_TYPE_SURFACE:
+	break;
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+	{
+	    cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) original;
+
+	    /* fast path for gradients with less than 2 color stops */
+	    if (src->n_stops < 2) {
+		if (src->n_stops) {
+		    _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern,
+					       &src->stops->color,
+					       CAIRO_CONTENT_COLOR_ALPHA);
+		} else {
+		    _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern,
+					       CAIRO_COLOR_TRANSPARENT,
+					       CAIRO_CONTENT_ALPHA);
+		}
+
+		return;
+	    } else {
+		unsigned int i;
+
+		/* Is the gradient a uniform colour?
+		 * Happens more often than you would believe.
+		 */
+		for (i = 1; i < src->n_stops; i++) {
+		    if (! _cairo_color_equal (&src->stops[0].color,
+					      &src->stops[i].color))
+		    {
+			break;
+		    }
+		}
+		if (i == src->n_stops) {
+		    _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern,
+					       &src->stops->color,
+					       CAIRO_CONTENT_COLOR_ALPHA);
+
+		    return;
+		}
+	    }
+	}
+    }
+
+    _cairo_pattern_init_static_copy (pattern, original);
+}
+
+static void
 _cairo_gstate_copy_transformed_pattern (cairo_gstate_t  *gstate,
 					cairo_pattern_t *pattern,
-					cairo_pattern_t *original,
-					cairo_matrix_t  *ctm_inverse)
+					const cairo_pattern_t *original,
+					const cairo_matrix_t  *ctm_inverse)
 {
-    _cairo_pattern_init_static_copy (pattern, original);
+    _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) {
@@ -934,14 +992,41 @@ _cairo_gstate_mask (cairo_gstate_t  *gstate,
     if (_clipped (gstate))
 	return CAIRO_STATUS_SUCCESS;
 
+    if (_cairo_pattern_is_opaque (mask))
+	return _cairo_gstate_paint (gstate);
+
     _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
     _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask);
 
-    status = _cairo_surface_mask (gstate->target,
-				  gstate->op,
-				  &source_pattern.base,
-				  &mask_pattern.base,
-				  _gstate_get_clip (gstate, &clip));
+    /* XXX: This optimization assumes that there is no color
+     * information in mask, so this will need to change if we
+     * support RENDER-style 4-channel masks.
+     */
+    if (source_pattern.type == CAIRO_PATTERN_TYPE_SOLID &&
+	mask_pattern.type == CAIRO_PATTERN_TYPE_SOLID)
+    {
+	cairo_color_t combined;
+
+	combined = source_pattern.solid.color;
+	_cairo_color_multiply_alpha (&combined, mask_pattern.solid.color.alpha);
+
+	_cairo_pattern_init_solid (&source_pattern.solid, &combined,
+				   source_pattern.solid.content |
+				   mask_pattern.solid.content);
+
+	status = _cairo_surface_paint (gstate->target,
+				       gstate->op,
+				       &source_pattern.base,
+				       _gstate_get_clip (gstate, &clip));
+    }
+    else
+    {
+	status = _cairo_surface_mask (gstate->target,
+				      gstate->op,
+				      &source_pattern.base,
+				      &mask_pattern.base,
+				      _gstate_get_clip (gstate, &clip));
+    }
     _cairo_clip_fini (&clip);
 
     return status;
diff --git a/src/cairoint.h b/src/cairoint.h
index c4b22f2..2584981 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -937,11 +937,12 @@ typedef union {
 } cairo_gradient_pattern_union_t;
 
 typedef union {
-    cairo_pattern_t base;
+    cairo_pattern_type_t	    type;
+    cairo_pattern_t		    base;
 
-    cairo_solid_pattern_t	   solid;
-    cairo_surface_pattern_t	   surface;
-    cairo_gradient_pattern_union_t gradient;
+    cairo_solid_pattern_t	    solid;
+    cairo_surface_pattern_t	    surface;
+    cairo_gradient_pattern_union_t  gradient;
 } cairo_pattern_union_t;
 
 typedef struct _cairo_surface_attributes {
commit e2c31183e96f84e7d40a9e5e2b6b8802d47628d0
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 23:45:30 2009 +0100

    [script] Handle zero-length strings
    
    Not sure where the zero length string is coming from, but we should
    nevertheless handle it.

diff --git a/util/cairo-script/cairo-script-scanner.c b/util/cairo-script/cairo-script-scanner.c
index e409e71..3f96a30 100644
--- a/util/cairo-script/cairo-script-scanner.c
+++ b/util/cairo-script/cairo-script-scanner.c
@@ -714,7 +714,8 @@ string_read (csi_t *ctx,
 	obj->datum.string->deflate = be32 (u32);
     }
 
-    scan_read (scan, src, obj->datum.string->string, len);
+    if (_csi_likely (len))
+	scan_read (scan, src, obj->datum.string->string, len);
     obj->datum.string->string[len] = '\0';
 }
 
commit 4c215162d24453788b3a461aa47ccf2e638db35e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 23:40:54 2009 +0100

    [script] Off-by-one in allocation of string length.
    
    We need pass in the real number of bytes in the string, excluding the NUL
    terminator which is already accounted for.

diff --git a/util/cairo-script/cairo-script-scanner.c b/util/cairo-script/cairo-script-scanner.c
index a1a2df9..e409e71 100644
--- a/util/cairo-script/cairo-script-scanner.c
+++ b/util/cairo-script/cairo-script-scanner.c
@@ -704,7 +704,7 @@ string_read (csi_t *ctx,
 {
     csi_status_t status;
 
-    status = csi_string_new (ctx, obj, NULL, len + 1);
+    status = csi_string_new (ctx, obj, NULL, len);
     if (_csi_unlikely (status))
 	longjmp (scan->jmpbuf, status);
 
commit 55eddb7ef4b6b561d6692a6f074834d5d5fb94fa
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 23:40:04 2009 +0100

    [script] A little utility to convert a trace into a trace
    
    Replay the trace using the interpreter onto a script surface - useful for
    checking idempotency.

diff --git a/util/cairo-script/Makefile.am b/util/cairo-script/Makefile.am
index acde8f5..8b783a9 100644
--- a/util/cairo-script/Makefile.am
+++ b/util/cairo-script/Makefile.am
@@ -28,5 +28,11 @@ csi_replay_LDADD = libcairo-script-interpreter.la $(top_builddir)/src/libcairo.l
 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/csi-trace.c b/util/cairo-script/csi-trace.c
new file mode 100644
index 0000000..418a335
--- /dev/null
+++ b/util/cairo-script/csi-trace.c
@@ -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 *
+_similar_surface_create (void *closure,
+			 cairo_content_t content,
+			 double width, double height)
+{
+    return cairo_surface_create_similar (closure, content, width, height);
+}
+
+int
+main (int argc, char **argv)
+{
+    cairo_script_interpreter_t *csi;
+    cairo_script_interpreter_hooks_t hooks = {
+	.surface_create = _similar_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_surface_destroy (hooks.closure);
+	hooks.closure = cairo_script_surface_create (buf, -1, -1);
+	cairo_script_interpreter_install_hooks (csi, &hooks);
+	cairo_script_interpreter_run (csi, argv[i]);
+    }
+    cairo_surface_destroy (hooks.closure);
+
+    return cairo_script_interpreter_destroy (csi);
+}
commit 4f129863df6392f3deaf6e76fd15adeba98e41b8
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 23:38:42 2009 +0100

    [script] Store the current stroke matrix
    
    We can skip re-emitting stroke parameters if the values are unchanged and
    the scaling matrix is unaltered.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index e14a9bd..a59f512 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -109,6 +109,7 @@ struct _cairo_script_implicit_context {
     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;
@@ -631,6 +632,18 @@ _emit_miter_limit (cairo_script_surface_t *surface,
     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,
@@ -652,9 +665,8 @@ _emit_dash (cairo_script_surface_t *surface,
     if (! force &&
 	(surface->cr.current_style.num_dashes == num_dashes &&
 	 (num_dashes == 0 ||
-	  (surface->cr.current_style.dash_offset == offset &&
-	   memcmp (surface->cr.current_style.dash, dash,
-		   sizeof (double) * num_dashes)))))
+	  (fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 &&
+	   _dashes_equal (surface->cr.current_style.dash, dash, num_dashes)))))
     {
 	return CAIRO_STATUS_SUCCESS;
     }
@@ -1513,6 +1525,15 @@ _emit_path (cairo_script_surface_t *surface,
 
     return CAIRO_STATUS_SUCCESS;
 }
+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,
@@ -1521,13 +1542,8 @@ _emit_scaling_matrix (cairo_script_surface_t *surface,
 {
     assert (target_is_active (surface));
 
-    if (fabs (surface->cr.current_ctm.xx - ctm->xx) < 1e-5 &&
-	fabs (surface->cr.current_ctm.xy - ctm->xy) < 1e-5 &&
-	fabs (surface->cr.current_ctm.yx - ctm->yx) < 1e-5 &&
-	fabs (surface->cr.current_ctm.yy - ctm->yy) < 1e-5)
-    {
+    if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm))
 	return CAIRO_STATUS_SUCCESS;
-    }
 
     *matrix_updated = TRUE;
     surface->cr.current_ctm = *ctm;
@@ -2064,6 +2080,17 @@ _cairo_script_surface_stroke (void				*abstract_surface,
     if (unlikely (status))
 	return status;
 
+    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))
 	return status;
@@ -3083,6 +3110,7 @@ _cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr)
 			       CAIRO_CONTENT_COLOR);
     _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;
commit 858211f3944507362b2a18d56a65e9a478ccd305
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 22:43:31 2009 +0100

    [script] Suppress resetting stroke-style elements after matrix switch
    
    If the user is just using the default values, there is no point re-emitting
    them.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 4ac6232..e14a9bd 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -520,8 +520,12 @@ _emit_tolerance (cairo_script_surface_t *surface,
 {
     assert (target_is_active (surface));
 
-    if (! force && surface->cr.current_tolerance == tolerance)
+    if ((! force ||
+	 fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) &&
+	surface->cr.current_tolerance == tolerance)
+    {
 	return CAIRO_STATUS_SUCCESS;
+    }
 
     surface->cr.current_tolerance = tolerance;
 
@@ -556,8 +560,12 @@ _emit_line_width (cairo_script_surface_t *surface,
 {
     assert (target_is_active (surface));
 
-    if (! force && surface->cr.current_style.line_width == line_width)
+    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;
 
@@ -608,8 +616,12 @@ _emit_miter_limit (cairo_script_surface_t *surface,
 {
     assert (target_is_active (surface));
 
-    if (! force && surface->cr.current_style.miter_limit == miter_limit)
+    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;
 
@@ -1507,31 +1519,29 @@ _emit_scaling_matrix (cairo_script_surface_t *surface,
 		      const cairo_matrix_t *ctm,
 		      cairo_bool_t *matrix_updated)
 {
-    cairo_matrix_t current, ctm_scaling;
-
     assert (target_is_active (surface));
 
-    current = surface->cr.current_ctm;
-    current.x0 = current.y0 = 0;
-
-    ctm_scaling = *ctm;
-    ctm_scaling.x0 = ctm_scaling.y0 = 0;
-
-    if (memcmp (&current, &ctm_scaling, sizeof (cairo_matrix_t)) == 0)
+    if (fabs (surface->cr.current_ctm.xx - ctm->xx) < 1e-5 &&
+	fabs (surface->cr.current_ctm.xy - ctm->xy) < 1e-5 &&
+	fabs (surface->cr.current_ctm.yx - ctm->yx) < 1e-5 &&
+	fabs (surface->cr.current_ctm.yy - ctm->yy) < 1e-5)
+    {
 	return CAIRO_STATUS_SUCCESS;
+    }
 
     *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 (ctm)) {
+    if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) {
 	_cairo_output_stream_puts (surface->ctx->stream,
 				   "identity set-matrix\n");
     } else {
 	_cairo_output_stream_printf (surface->ctx->stream,
-				   "[%f %f %f %f %f %f] set-matrix\n",
+				   "[%f %f %f %f 0 0] set-matrix\n",
 				   ctm->xx, ctm->yx,
-				   ctm->xy, ctm->yy,
-				   ctm->x0, ctm->y0);
+				   ctm->xy, ctm->yy);
     }
 
     return CAIRO_STATUS_SUCCESS;
commit b6db3053dcde99e26471fdeaedcadd4a6f93b5ef
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 22:30:58 2009 +0100

    [script] Hide the implicit CLEAR for similar surfaces
    
    Do emit the clear that is performed by the surface layer on similar
    surfaces.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 82c3ba4..4ac6232 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -127,6 +127,7 @@ struct _cairo_script_surface {
     unsigned long id;
     cairo_list_t operand;
     cairo_bool_t defined;
+    cairo_bool_t is_clear;
 
     double width, height;
 
@@ -1616,6 +1617,7 @@ _cairo_script_surface_create_similar (void	       *abstract_surface,
 				 _content_to_string (content),
 				 surface->id);
     surface->defined = TRUE;
+    surface->is_clear = TRUE;
     target_push (surface);
 
     return &surface->base;
@@ -1903,6 +1905,11 @@ _cairo_script_surface_paint (void			*abstract_surface,
     cairo_script_surface_t *surface = abstract_surface;
     cairo_status_t status;
 
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	if (surface->is_clear)
+	    goto DONE;
+    }
+
     ctx_active (surface->ctx);
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
@@ -1924,8 +1931,11 @@ _cairo_script_surface_paint (void			*abstract_surface,
     _cairo_output_stream_puts (surface->ctx->stream,
 			       "paint\n");
 
+    surface->is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL;
+
     ctx_inactive (surface->ctx);
 
+  DONE:
     if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
 	return _cairo_surface_wrapper_paint (&surface->wrapper,
 					     op, source, clip);
@@ -1944,6 +1954,11 @@ _cairo_script_surface_mask (void			*abstract_surface,
     cairo_script_surface_t *surface = abstract_surface;
     cairo_status_t status;
 
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	if (surface->is_clear)
+	    goto DONE;
+    }
+
     ctx_active (surface->ctx);
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
@@ -1975,8 +1990,11 @@ _cairo_script_surface_mask (void			*abstract_surface,
     _cairo_output_stream_puts (surface->ctx->stream,
 			       " mask\n");
 
+    surface->is_clear = FALSE;
+
     ctx_inactive (surface->ctx);
 
+  DONE:
     if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
 	return _cairo_surface_wrapper_mask (&surface->wrapper,
 					    op, source, mask, clip);
@@ -2001,6 +2019,11 @@ _cairo_script_surface_stroke (void				*abstract_surface,
     cairo_bool_t matrix_updated = FALSE;
     cairo_status_t status;
 
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	if (surface->is_clear)
+	    goto DONE;
+    }
+
     ctx_active (surface->ctx);
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
@@ -2047,8 +2070,11 @@ _cairo_script_surface_stroke (void				*abstract_surface,
 
     _cairo_output_stream_puts (surface->ctx->stream, "stroke+\n");
 
+    surface->is_clear = FALSE;
+
     ctx_inactive (surface->ctx);
 
+  DONE:
     if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
 	return _cairo_surface_wrapper_stroke (&surface->wrapper,
 					      op, source, path,
@@ -2075,6 +2101,11 @@ _cairo_script_surface_fill (void			*abstract_surface,
     cairo_bool_t matrix_updated = FALSE;
     cairo_status_t status;
 
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	if (surface->is_clear)
+	    goto DONE;
+    }
+
     ctx_active (surface->ctx);
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
@@ -2119,8 +2150,11 @@ _cairo_script_surface_fill (void			*abstract_surface,
 
     _cairo_output_stream_puts (surface->ctx->stream, "fill+\n");
 
+    surface->is_clear = FALSE;
+
     ctx_inactive (surface->ctx);
 
+  DONE:
     if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
 	return _cairo_surface_wrapper_fill (&surface->wrapper,
 					    op, source, path,
@@ -2699,6 +2733,11 @@ _cairo_script_surface_show_text_glyphs (void			    *abstract_surface,
     int n;
     cairo_output_stream_t *base85_stream = NULL;
 
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	if (surface->is_clear)
+	    goto DONE;
+    }
+
     ctx_active (surface->ctx);
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
@@ -2907,8 +2946,11 @@ _cairo_script_surface_show_text_glyphs (void			    *abstract_surface,
 				   "] show-glyphs\n");
     }
 
+    surface->is_clear = FALSE;
+
     ctx_inactive (surface->ctx);
 
+  DONE:
     if (_cairo_surface_wrapper_is_active (&surface->wrapper)){
 	return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper,
 							op, source,
@@ -3068,6 +3110,7 @@ _cairo_script_surface_create_internal (cairo_script_vmcontext_t *ctx,
     surface->height = height;
 
     surface->defined = FALSE;
+    surface->is_clear = FALSE;
     surface->id = (unsigned long) -1;
     cairo_list_init (&surface->operand);
 
commit 005b195f062d896d76a87d608ce313b169f99201
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 22:07:12 2009 +0100

    [pattern] Ignore matrix/filter/extend when comparing solids
    
    Solid patterns do not use their matrices, filter or extend properties so
    ignore them for the purposes of comparing and hashing.

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 4303106..e621058 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -2586,9 +2586,14 @@ _cairo_pattern_hash (const cairo_pattern_t *pattern)
 	return 0;
 
     hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type));
-    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));
+    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));
+    }
 
     switch (pattern->type) {
     case CAIRO_PATTERN_TYPE_SOLID:
@@ -2745,14 +2750,16 @@ _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b)
     if (a->type != b->type)
 	return FALSE;
 
-    if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t)))
-	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->filter != b->filter)
+	    return FALSE;
 
-    if (a->extend != b->extend)
-	return FALSE;
+	if (a->extend != b->extend)
+	    return FALSE;
+    }
 
     switch (a->type) {
     case CAIRO_PATTERN_TYPE_SOLID:
commit bb919584c0054bb3e3c547f65c91cfe48302ac86
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 21:40:37 2009 +0100

    [script] Use a compact representation for horizontal offsets between glyphs
    
    Kerning is quite frequent, that is to apply a horizontal but no vertical
    offset to a glyph. For instance by discarding the vertical coordinate
    where it remains the same and only encoding the horizontal offset we
    reduce the file size by ~12.5% when tracing poppler.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 565ce07..82c3ba4 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -2763,6 +2763,9 @@ _cairo_script_surface_show_text_glyphs (void			    *abstract_surface,
 		_cairo_scaled_font_thaw_cache (scaled_font);
 		return status;
 	    }
+
+	    if ((long unsigned) scaled_glyph->surface_private > 256)
+		break;
 	}
     }
 
@@ -2783,26 +2786,45 @@ _cairo_script_surface_show_text_glyphs (void			    *abstract_surface,
 	    break;
 
 	if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) {
-	    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;
+	    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 (surface->ctx->stream,
+						 " %f <~", glyphs[n].x - x);
+		    base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
+		} else {
+		    _cairo_output_stream_printf (surface->ctx->stream,
+						 " ] %f [ ", glyphs[n].x - x);
 		}
 
-		_cairo_output_stream_printf (surface->ctx->stream,
-					     " %f %f <~",
-					     ix, iy);
-		base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
+		x = glyphs[n].x;
 	    } else {
-		_cairo_output_stream_printf (surface->ctx->stream,
-					     " ] %f %f [ ",
-					     ix, iy);
+		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 (surface->ctx->stream,
+						 " %f %f <~",
+						 ix, iy);
+		    base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
+		} else {
+		    _cairo_output_stream_printf (surface->ctx->stream,
+						 " ] %f %f [ ",
+						 ix, iy);
+		}
 	    }
 	}
 	if (base85_stream != NULL) {
diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index 5247682..6a6279d 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -2351,7 +2351,7 @@ _glyph_string (csi_t *ctx,
 	       cairo_scaled_font_t *scaled_font,
 	       cairo_glyph_t *glyphs)
 {
-    double x,y;
+    double x,y, dx;
     csi_integer_t nglyphs, i, j;
     struct glyph_advance_cache *cache;
     cairo_status_t status;
@@ -2448,12 +2448,22 @@ _glyph_string (csi_t *ctx,
 	}
 
 	case CSI_OBJECT_TYPE_INTEGER:
-	case CSI_OBJECT_TYPE_REAL: /* dx */
-	    x = csi_number_get_value (obj);
-	    if (++i == array->stack.len)
+	case CSI_OBJECT_TYPE_REAL: /* dx or x*/
+	    dx = csi_number_get_value (obj);
+	    if (i+1 == array->stack.len)
 		break;
-	    y = csi_number_get_value (&array->stack.objects[i]);
-	    break;
+
+	    switch ((int) csi_object_get_type (&array->stack.objects[i+1])) {
+	    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;
+	    }
 	}
     }
 
commit cbee97f0e3e784b4482429790fc3f42c81908557
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 21:26:11 2009 +0100

    [script] Preserve '{}' whilst translating

diff --git a/util/cairo-script/cairo-script-private.h b/util/cairo-script/cairo-script-private.h
index 1fd8125..d63b639 100644
--- a/util/cairo-script/cairo-script-private.h
+++ b/util/cairo-script/cairo-script-private.h
@@ -428,6 +428,7 @@ 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;
diff --git a/util/cairo-script/cairo-script-scanner.c b/util/cairo-script/cairo-script-scanner.c
index 587e765..a1a2df9 100644
--- a/util/cairo-script/cairo-script-scanner.c
+++ b/util/cairo-script/cairo-script-scanner.c
@@ -411,44 +411,46 @@ token_end (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
     s = scan->buffer.base;
     len = scan->buffer.ptr - scan->buffer.base;
 
-    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_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);
-	}
 
-	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));
+	    }
 
-	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;
 
-	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);
 
-	    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;
+	    return;
 	}
-	if (_csi_unlikely (status))
-	    longjmp (scan->jmpbuf, status);
-
-	return;
     }
 
     if (s[0] == '/') {
@@ -1238,6 +1240,7 @@ _csi_scanner_init (csi_t *ctx, csi_scanner_t *scanner)
     if (status)
 	return status;
 
+    scanner->bind = 0;
     scanner->push = _scan_push;
     scanner->execute = _scan_execute;
 
@@ -1663,11 +1666,13 @@ _csi_translate_file (csi_t *ctx,
     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;
 
commit 24b23200025166e8343425f77a53bedd08790367
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 21:12:59 2009 +0100

    [script] Fix list handling during font destruction
    
    Use cairo_list to unhook the font correctly during the fini callback.

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index fb1a4f8..565ce07 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -88,7 +88,7 @@ struct _cairo_script_vmcontext {
     cairo_list_t operands;
     cairo_list_t deferred;
 
-    cairo_script_surface_font_private_t *fonts;
+    cairo_list_t fonts;
     cairo_list_t defines;
 };
 
@@ -97,7 +97,7 @@ struct _cairo_script_surface_font_private {
     cairo_bool_t has_sfnt;
     unsigned long id;
     unsigned long subset_glyph_index;
-    cairo_script_surface_font_private_t *prev, *next;
+    cairo_list_t link;
     cairo_scaled_font_t *parent;
 };
 
@@ -1070,7 +1070,7 @@ _undef (void *data)
     struct def *def = data;
 
     cairo_list_del (&def->link);
-    _cairo_output_stream_printf (def->ctx->stream, "/s%u undef ", def->tag);
+    _cairo_output_stream_printf (def->ctx->stream, "/s%u undef\n", def->tag);
     free (def);
 }
 
@@ -1629,9 +1629,13 @@ _vmcontext_destroy (cairo_script_vmcontext_t *ctx)
     if (--ctx->ref)
 	return _cairo_output_stream_flush (ctx->stream);
 
-    while (ctx->fonts != NULL ){
-	cairo_script_surface_font_private_t *font = ctx->fonts;
-	ctx->fonts = font->next;
+    while (! cairo_list_is_empty (&ctx->fonts)) {
+	cairo_script_surface_font_private_t *font;
+
+	font = cairo_list_first_entry (&ctx->fonts,
+				       cairo_script_surface_font_private_t,
+				       link);
+	cairo_list_del (&font->link);
 	_cairo_script_surface_scaled_font_fini (font->parent);
     }
 
@@ -2231,15 +2235,7 @@ _cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
 				     font_private->id);
 
 	_bitmap_release_id (&font_private->ctx->font_id, font_private->id);
-
-	if (font_private->prev != NULL)
-	    font_private->prev = font_private->next;
-	else
-	    font_private->ctx->fonts = font_private->next;
-
-	if (font_private->next != NULL)
-	    font_private->next = font_private->prev;
-
+	cairo_list_del (&font_private->link);
 	free (font_private);
 
 	scaled_font->surface_private = NULL;
@@ -2329,11 +2325,7 @@ _emit_scaled_font_init (cairo_script_surface_t *surface,
     font_private->subset_glyph_index = 0;
     font_private->has_sfnt = TRUE;
 
-    font_private->next = font_private->ctx->fonts;
-    font_private->prev = NULL;
-    if (font_private->ctx->fonts != NULL)
-	font_private->ctx->fonts->prev = font_private;
-    font_private->ctx->fonts = font_private;
+    cairo_list_add (&font_private->link, &surface->ctx->fonts);
 
     status = _bitmap_next_id (&surface->ctx->font_id,
 			      &font_private->id);
@@ -2998,6 +2990,7 @@ _cairo_script_vmcontext_create (cairo_output_stream_t *stream)
     ctx->stream = stream;
     ctx->mode = CAIRO_SCRIPT_MODE_ASCII;
 
+    cairo_list_init (&ctx->fonts);
     cairo_list_init (&ctx->defines);
 
     return ctx;
commit 4032438625819cfa5d1928d653f404364529a2e1
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 17:02:41 2009 +0100

    [path] Eliminate redundant line-to before a close
    
    As the close implicitly issues a line-to to the initial point, remove an
    identical line-to if present.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index f1146d3..9950368 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -574,6 +574,28 @@ _cairo_path_fixed_close_path (cairo_path_fixed_t *path)
     if (! path->has_current_point)
 	return CAIRO_STATUS_SUCCESS;
 
+    /* If the previous op was also a LINE_TO back to the start, discard it */
+    if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
+	if (path->current_point.x == path->last_move_point.x &&
+	    path->current_point.y == path->last_move_point.y)
+	{
+	    cairo_path_buf_t *buf;
+	    cairo_point_t *p;
+
+	    buf = cairo_path_tail (path);
+	    if (likely (buf->num_points >= 2)) {
+		p = &buf->points[buf->num_points-2];
+	    } else {
+		cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
+		p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
+	    }
+
+	    path->current_point = *p;
+	    buf->num_ops--;
+	    buf->num_points--;
+	}
+    }
+
     status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
     if (unlikely (status))
 	return status;
commit 111f2be71b1e51fac551fd9214d13899a8ec7909
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 17:01:50 2009 +0100

    [path] Discard redundant line-to
    
    Eliminate repeated line-to to the current point.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 7ade8b2..f1146d3 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -390,7 +390,7 @@ _cairo_path_fixed_move_to (cairo_path_fixed_t  *path,
 	if (path->has_current_point && path->is_rectilinear) {
 	    /* a move-to is first an implicit close */
 	    path->is_rectilinear = path->current_point.x == path->last_move_point.x ||
-		                   path->current_point.y == path->last_move_point.y;
+				   path->current_point.y == path->last_move_point.y;
 	    path->maybe_fill_region &= path->is_rectilinear;
 	}
 	if (path->maybe_fill_region) {
@@ -450,23 +450,32 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
 	 * then just change its end-point rather than adding a new op.
 	 */
 	if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
-	    cairo_path_buf_t *buf;
-	    cairo_point_t *p;
-	    cairo_slope_t prev, self;
-
-	    buf = cairo_path_tail (path);
-	    if (likely (buf->num_points >= 2)) {
-		p = &buf->points[buf->num_points-2];
-	    } else {
-		cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
-		p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
+	    if (x == path->current_point.x &&
+		y == path->current_point.y)
+	    {
+		return CAIRO_STATUS_SUCCESS;
 	    }
-	    _cairo_slope_init (&prev, p, &path->current_point);
-	    _cairo_slope_init (&self, &path->current_point, &point);
-	    if (_cairo_slope_equal (&prev, &self)) {
-		buf->points[buf->num_points - 1] = point;
-		status = CAIRO_STATUS_SUCCESS;
-		goto DONE;
+	    else
+	    {
+		cairo_path_buf_t *buf;
+		cairo_point_t *p;
+		cairo_slope_t prev, self;
+
+		buf = cairo_path_tail (path);
+		if (likely (buf->num_points >= 2)) {
+		    p = &buf->points[buf->num_points-2];
+		} else {
+		    cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
+		    p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
+		}
+
+		_cairo_slope_init (&prev, p, &path->current_point);
+		_cairo_slope_init (&self, &path->current_point, &point);
+		if (_cairo_slope_equal (&prev, &self)) {
+		    buf->points[buf->num_points - 1] = point;
+		    path->current_point = point;
+		    return CAIRO_STATUS_SUCCESS;
+		}
 	    }
 	}
 
@@ -484,11 +493,9 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
 	    path->is_empty_fill = path->current_point.x == x &&
 		path->current_point.y == y;
 	}
-    }
 
-DONE:
-    path->current_point = point;
-    path->has_current_point = TRUE;
+	path->current_point = point;
+    }
 
     return status;
 }
commit a2d5f59e2158651ac85dcc8a2b8f49cd2861044e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 17:01:07 2009 +0100

    [debug] Path printer

diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index 4409e6e..9160728 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -165,3 +165,70 @@ _cairo_image_surface_write_to_ppm (cairo_image_surface_t *isurf, const char *fn)
     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;
+
+    status = _cairo_path_fixed_interpret (path,
+					  CAIRO_DIRECTION_FORWARD,
+					  _print_move_to,
+					  _print_line_to,
+					  _print_curve_to,
+					  _print_close,
+					  stream);
+    assert (status == CAIRO_STATUS_SUCCESS);
+
+    printf ("\n");
+}
diff --git a/src/cairoint.h b/src/cairoint.h
index 5ac8b6a..c4b22f2 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2768,4 +2768,7 @@ _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface);
 
 #endif
 
+void
+_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path);
+
 #endif
commit 4bf96bad9697cbe67907df69d40f46d8d7f24325
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 15:37:35 2009 +0100

    [fill] Use trivial rectilinear_to_traps
    
    Avoid a small amount of unnecessary overhead by performing a simple
    conversion of the path to traps when it consists solely of simple boxes.

diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index f967944..3daf246 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -347,3 +347,77 @@ TESSELLATE:
 
     return region;
 }
+
+cairo_int_status_t
+_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
+					     cairo_fill_rule_t fill_rule,
+					     cairo_traps_t *traps)
+{
+    cairo_box_t box;
+    cairo_status_t status;
+
+    if (_cairo_path_fixed_is_box (path, &box)) {
+	if (box.p1.x > box.p2.x) {
+	    cairo_fixed_t t;
+
+	    t = box.p1.x;
+	    box.p1.x = box.p2.x;
+	    box.p2.x = t;
+	}
+
+	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;
+	}
+
+	return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2);
+    } else if (fill_rule == CAIRO_FILL_RULE_WINDING) {
+	cairo_path_fixed_iter_t iter;
+	int last_cw = -1;
+
+	_cairo_path_fixed_iter_init (&iter, path);
+	while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
+	    int cw = 0;
+
+	    if (box.p1.x > box.p2.x) {
+		cairo_fixed_t t;
+
+		t = box.p1.x;
+		box.p1.x = box.p2.x;
+		box.p2.x = t;
+
+		cw = ! cw;
+	    }
+
+	    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;
+
+		cw = ! cw;
+	    }
+
+	    if (last_cw < 0)
+		last_cw = cw;
+	    else if (last_cw != cw)
+		goto out;
+
+	    status = _cairo_traps_tessellate_rectangle (traps,
+							&box.p1, &box.p2);
+	    if (unlikely (status))
+		return status;
+	}
+
+	if (_cairo_path_fixed_iter_at_end (&iter))
+	    return CAIRO_STATUS_SUCCESS;
+    }
+
+  out:
+    _cairo_traps_clear (traps);
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 32db7fb..4dc8ebb 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -1193,6 +1193,20 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
     _cairo_polygon_init (&polygon);
     _cairo_polygon_limit (&polygon, boxes, num_boxes);
 
+    if (path->is_empty_fill)
+	goto DO_TRAPS;
+
+    if (path->is_rectilinear) {
+	status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+							      fill_rule,
+							      &traps);
+	if (likely (status == CAIRO_STATUS_SUCCESS))
+	    goto DO_TRAPS;
+
+	if (_cairo_status_is_error (status))
+	    goto CLEANUP;
+    }
+
     status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
     if (unlikely (status))
 	goto CLEANUP;
diff --git a/src/cairoint.h b/src/cairoint.h
index 2afb690..5ac8b6a 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1617,6 +1617,11 @@ _cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
 				   double              tolerance,
 				   cairo_polygon_t      *polygon);
 
+cairo_private cairo_int_status_t
+_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
+					     cairo_fill_rule_t fill_rule,
+					     cairo_traps_t *traps);
+
 cairo_private cairo_region_t *
 _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
 					      cairo_fill_rule_t	 fill_rule,
commit 30e5fa0ce06b2572f09f3d47ee10b692f18ba0ae
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 15:36:11 2009 +0100

    [polygon] Return status from path ops
    
    This tidies the common case which was to call, for example,
    _cairo_polygon_line_to(); _cairo_polygon_status();

diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index d46917d..f967944 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -40,7 +40,6 @@
 
 typedef struct cairo_filler {
     double tolerance;
-    cairo_point_t current_point;
     cairo_polygon_t *polygon;
 } cairo_filler_t;
 
@@ -51,9 +50,6 @@ _cairo_filler_init (cairo_filler_t *filler,
 {
     filler->tolerance = tolerance;
     filler->polygon = polygon;
-
-    filler->current_point.x = 0;
-    filler->current_point.y = 0;
 }
 
 static void
@@ -69,11 +65,7 @@ _cairo_filler_move_to (void *closure,
     cairo_polygon_t *polygon = filler->polygon;
 
     _cairo_polygon_close (polygon);
-    _cairo_polygon_move_to (polygon, point);
-
-    filler->current_point = *point;
-
-    return _cairo_polygon_status (filler->polygon);
+    return _cairo_polygon_move_to (polygon, point);
 }
 
 static cairo_status_t
@@ -81,13 +73,7 @@ _cairo_filler_line_to (void *closure,
 		       const cairo_point_t *point)
 {
     cairo_filler_t *filler = closure;
-    cairo_polygon_t *polygon = filler->polygon;
-
-    _cairo_polygon_line_to (polygon, point);
-
-    filler->current_point = *point;
-
-    return _cairo_polygon_status (filler->polygon);
+    return _cairo_polygon_line_to (filler->polygon, point);
 }
 
 static cairo_status_t
@@ -101,7 +87,7 @@ _cairo_filler_curve_to (void *closure,
 
     if (! _cairo_spline_init (&spline,
 			      _cairo_filler_line_to, filler,
-			      &filler->current_point, b, c, d))
+			      &filler->polygon->current_point, b, c, d))
     {
 	return _cairo_filler_line_to (closure, d);
     }
@@ -113,11 +99,7 @@ static cairo_status_t
 _cairo_filler_close_path (void *closure)
 {
     cairo_filler_t *filler = closure;
-    cairo_polygon_t *polygon = filler->polygon;
-
-    _cairo_polygon_close (polygon);
-
-    return _cairo_polygon_status (polygon);
+    return _cairo_polygon_close (filler->polygon);
 }
 
 cairo_status_t
diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
index 9de92bf..f3e8e4d 100644
--- a/src/cairo-polygon.c
+++ b/src/cairo-polygon.c
@@ -386,12 +386,12 @@ _cairo_polygon_add_line (cairo_polygon_t *polygon,
     } else
 	_add_edge (polygon, &line->p1, &line->p2, top, bottom, dir);
 
-    return _cairo_polygon_status (polygon);
+    return polygon->status;
 }
 
 /* flattened path operations */
 
-void
+cairo_status_t
 _cairo_polygon_move_to (cairo_polygon_t *polygon,
 			const cairo_point_t *point)
 {
@@ -408,9 +408,10 @@ _cairo_polygon_move_to (cairo_polygon_t *polygon,
     }
 
     polygon->current_point = *point;
+    return polygon->status;
 }
 
-void
+cairo_status_t
 _cairo_polygon_line_to (cairo_polygon_t *polygon,
 			const cairo_point_t *point)
 {
@@ -424,7 +425,7 @@ _cairo_polygon_line_to (cairo_polygon_t *polygon,
 	    _cairo_slope_init (&this, &polygon->current_point, point);
 	    if (_cairo_slope_equal (&polygon->current_edge, &this)) {
 		polygon->current_point = *point;
-		return;
+		return CAIRO_STATUS_SUCCESS;
 	    }
 
 	    _cairo_polygon_add_edge (polygon,
@@ -450,13 +451,16 @@ _cairo_polygon_line_to (cairo_polygon_t *polygon,
     }
 
     polygon->current_point = *point;
+    return polygon->status;
 }
 
-void
+cairo_status_t
 _cairo_polygon_close (cairo_polygon_t *polygon)
 {
+    cairo_status_t status;
+
     if (polygon->has_current_point) {
-	_cairo_polygon_line_to (polygon, &polygon->first_point);
+	status = _cairo_polygon_line_to (polygon, &polygon->first_point);
 	polygon->has_current_point = FALSE;
     }
 
@@ -466,4 +470,6 @@ _cairo_polygon_close (cairo_polygon_t *polygon)
 				 &polygon->current_point);
 	polygon->has_current_edge = FALSE;
     }
+
+    return polygon->status;
 }
diff --git a/src/cairoint.h b/src/cairoint.h
index 9fd789d..2afb690 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2267,15 +2267,15 @@ _cairo_polygon_add_external_edge (void *polygon,
 				  const cairo_point_t *p1,
 				  const cairo_point_t *p2);
 
-cairo_private void
+cairo_private cairo_status_t
 _cairo_polygon_move_to (cairo_polygon_t *polygon,
 			const cairo_point_t *point);
 
-cairo_private void
+cairo_private cairo_status_t
 _cairo_polygon_line_to (cairo_polygon_t *polygon,
 			const cairo_point_t *point);
 
-cairo_private void
+cairo_private cairo_status_t
 _cairo_polygon_close (cairo_polygon_t *polygon);
 
 #define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status
commit 3fcac1ef21de9526bc1abca902db5755abe463ae
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 13:43:32 2009 +0100

    [slope] Inline _cairo_slope_init()
    
    Move the definition to a separate header file and allow callers to inline
    the simple function.

diff --git a/src/Makefile.sources b/src/Makefile.sources
index 0e7b54b..156bae2 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -82,6 +82,7 @@ cairo_private = \
 	cairo-region-private.h \
 	cairo-rtree-private.h \
 	cairo-scaled-font-private.h \
+	cairo-slope-private.h \
 	cairo-spans-private.h \
 	cairo-surface-fallback-private.h \
 	cairo-surface-private.h \
diff --git a/src/cairo-hull.c b/src/cairo-hull.c
index 1fa919b..8a1a31e 100644
--- a/src/cairo-hull.c
+++ b/src/cairo-hull.c
@@ -36,6 +36,8 @@
 
 #include "cairoint.h"
 
+#include "cairo-slope-private.h"
+
 typedef struct cairo_hull {
     cairo_point_t point;
     cairo_slope_t slope;
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index b716030..7ade8b2 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -39,6 +39,7 @@
 #include "cairoint.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,
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 33fed4f..ba169ae 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -38,7 +38,9 @@
 
 #define _BSD_SOURCE /* for hypot() */
 #include "cairoint.h"
+
 #include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
 
 typedef struct _cairo_stroker_dash {
     cairo_bool_t dashed;
diff --git a/src/cairo-pen.c b/src/cairo-pen.c
index 8db8fcb..cfb67ca 100644
--- a/src/cairo-pen.c
+++ b/src/cairo-pen.c
@@ -38,6 +38,8 @@
 
 #include "cairoint.h"
 
+#include "cairo-slope-private.h"
+
 static int
 _cairo_pen_vertices_needed (double tolerance,
 			    double radius,
diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
index fdc5c92..9de92bf 100644
--- a/src/cairo-polygon.c
+++ b/src/cairo-polygon.c
@@ -36,6 +36,8 @@
 
 #include "cairoint.h"
 
+#include "cairo-slope-private.h"
+
 void
 _cairo_polygon_init (cairo_polygon_t *polygon)
 {
diff --git a/src/cairo-slope-private.h b/src/cairo-slope-private.h
new file mode 100644
index 0000000..4148c68
--- /dev/null
+++ b/src/cairo-slope-private.h
@@ -0,0 +1,65 @@
+/* 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 at 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));
+}
+
+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
index 15685b7..bb3b411 100644
--- a/src/cairo-slope.c
+++ b/src/cairo-slope.c
@@ -36,14 +36,7 @@
 
 #include "cairoint.h"
 
-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;
-}
+#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
diff --git a/src/cairo-spline.c b/src/cairo-spline.c
index 45eedbd..780c21f 100644
--- a/src/cairo-spline.c
+++ b/src/cairo-spline.c
@@ -36,6 +36,8 @@
 
 #include "cairoint.h"
 
+#include "cairo-slope-private.h"
+
 cairo_bool_t
 _cairo_spline_init (cairo_spline_t *spline,
 		    cairo_spline_add_point_func_t add_point_func,
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index c6709de..d5b41e5 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -40,6 +40,7 @@
 #include "cairoint.h"
 
 #include "cairo-region-private.h"
+#include "cairo-slope-private.h"
 
 /* private functions */
 
diff --git a/src/cairoint.h b/src/cairoint.h
index bbeb8e0..9fd789d 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2427,16 +2427,6 @@ _cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps,
 					    double tx, double ty,
 					    double sx, double sy);
 
-/* cairo-slope.c */
-cairo_private void
-_cairo_slope_init (cairo_slope_t *slope,
-		   const cairo_point_t *a,
-		   const cairo_point_t *b);
-
-cairo_private int
-_cairo_slope_compare (const cairo_slope_t *a,
-	              const cairo_slope_t *b) cairo_pure;
-
 /* cairo-pattern.c */
 
 cairo_private cairo_pattern_t *
@@ -2758,13 +2748,6 @@ CAIRO_END_DECLS
 #include "cairo-malloc-private.h"
 #include "cairo-hash-private.h"
 
-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));
-}
-
 #if HAVE_VALGRIND
 #include <memcheck.h>
 
commit a1e0c4b30980d624bb3e015b7dcf39b4a2ef8c56
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 11 13:34:29 2009 +0100

    [clip] Combine directly onto target
    
    Where it is unlikely that we will reuse the temporary clip surface,
    combine the clip directly with the mask.

diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
index 6e40492..a7e06a9 100644
--- a/src/cairo-clip-private.h
+++ b/src/cairo-clip-private.h
@@ -113,6 +113,9 @@ _cairo_clip_get_extents (const cairo_clip_t *clip);
 cairo_private cairo_surface_t *
 _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *dst);
 
+cairo_private cairo_status_t
+_cairo_clip_combine_with_surface (cairo_clip_t *clip, cairo_surface_t *dst);
+
 cairo_private cairo_int_status_t
 _cairo_clip_get_region (cairo_clip_t *clip,
 			cairo_region_t **region);
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 09b26ad..67079c4 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -1127,6 +1127,106 @@ BAIL:
     return _cairo_surface_create_in_error (status);
 }
 
+cairo_status_t
+_cairo_clip_combine_with_surface (cairo_clip_t *clip, cairo_surface_t *dst)
+{
+    cairo_pattern_union_t pattern;
+    cairo_clip_path_t *clip_path = clip->path;
+    const cairo_rectangle_int_t *clip_extents = &clip_path->extents;
+    cairo_bool_t need_translate;
+    cairo_status_t status;
+
+    assert (clip_path != NULL);
+
+    if (clip_path->surface != NULL &&
+	clip_path->surface->backend == dst->backend)
+    {
+	_cairo_pattern_init_for_surface (&pattern.surface,
+					 clip_path->surface);
+	cairo_matrix_init_translate (&pattern.base.matrix,
+				     -clip_path->extents.x + clip_extents->x,
+				     -clip_path->extents.y + clip_extents->y);
+	status = _cairo_surface_paint (dst,
+				       CAIRO_OPERATOR_IN,
+				       &pattern.base,
+				       NULL);
+
+	_cairo_pattern_fini (&pattern.base);
+
+	return status;
+    }
+
+    _cairo_pattern_init_solid (&pattern.solid,
+			       CAIRO_COLOR_WHITE,
+			       CAIRO_CONTENT_COLOR);
+
+    need_translate = clip_extents->x || clip_extents->y;
+    do {
+	status = _cairo_clip_path_to_region (clip_path);
+	if (unlikely (_cairo_status_is_error (status)))
+	    return status;
+
+	if (status == CAIRO_STATUS_SUCCESS) {
+	    if (need_translate) {
+		cairo_region_translate (clip_path->region,
+					-clip_extents->x, -clip_extents->y);
+	    }
+	    status = _cairo_surface_fill_region (dst,
+						 CAIRO_OPERATOR_IN,
+						 CAIRO_COLOR_WHITE,
+						 clip_path->region);
+	    if (need_translate) {
+		cairo_region_translate (clip_path->region,
+					clip_extents->x, clip_extents->y);
+	    }
+
+	    return status;
+	}
+
+	if (clip_path->surface != NULL &&
+	    clip_path->surface->backend == dst->backend)
+	{
+	    _cairo_pattern_init_for_surface (&pattern.surface,
+					     clip_path->surface);
+	    cairo_matrix_init_translate (&pattern.base.matrix,
+					 -clip_path->extents.x + clip_extents->x,
+					 -clip_path->extents.y + clip_extents->y);
+	    status = _cairo_surface_paint (dst,
+					   CAIRO_OPERATOR_IN,
+					   &pattern.base,
+					   NULL);
+
+	    _cairo_pattern_fini (&pattern.base);
+
+	    return status;
+	}
+
+	if (need_translate) {
+	    _cairo_path_fixed_translate (&clip_path->path,
+					 _cairo_fixed_from_int (-clip_extents->x),
+					 _cairo_fixed_from_int (-clip_extents->y));
+	}
+	status = _cairo_surface_fill (dst,
+				      CAIRO_OPERATOR_IN,
+				      &pattern.base,
+				      &clip_path->path,
+				      clip_path->fill_rule,
+				      clip_path->tolerance,
+				      clip_path->antialias,
+				      NULL);
+	if (need_translate) {
+	    _cairo_path_fixed_translate (&clip_path->path,
+					 _cairo_fixed_from_int (clip_extents->x),
+					 _cairo_fixed_from_int (clip_extents->y));
+	}
+
+	if (unlikely (status))
+	    return status;
+    } while ((clip_path = clip_path->prev) != NULL);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 const cairo_rectangle_int_t *
 _cairo_clip_get_extents (const cairo_clip_t *clip)
 {
@@ -1273,18 +1373,20 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
 
     cairo_rectangle_list_t *list;
     cairo_rectangle_t *rectangles = NULL;
+    cairo_region_t *region = NULL;
     cairo_int_status_t status;
-    cairo_region_t *region;
     int n_rects = 0;
     int i;
 
-    status = _cairo_clip_get_region (clip, &region);
-    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
-	goto DONE;
-    } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
-	return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
-    } else if (unlikely (status)) {
-	return ERROR_LIST (status);
+    if (clip != NULL && clip->path != NULL) {
+	status = _cairo_clip_get_region (clip, &region);
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+	    goto DONE;
+	} else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	    return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
+	} else if (unlikely (status)) {
+	    return ERROR_LIST (status);
+	}
     }
 
     if (region != NULL) {
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 14d94fd..32db7fb 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -163,30 +163,8 @@ _create_composite_mask_pattern (cairo_surface_pattern_t       *mask_pattern,
     if (unlikely (status))
 	goto CLEANUP_SURFACE;
 
-    if (clip_surface) {
-	cairo_surface_pattern_t pattern;
-	cairo_surface_t *surface;
-
-	surface = _cairo_clip_get_surface (clip, mask);
-	_cairo_pattern_init_for_surface (&pattern, surface);
-	cairo_surface_destroy (surface);
-
-	status = _cairo_surface_composite (CAIRO_OPERATOR_IN,
-					   &pattern.base,
-					   NULL,
-					   mask,
-					   extents->x - clip->path->extents.x,
-					   extents->y - clip->path->extents.y,
-					   0, 0,
-					   0, 0,
-					   extents->width, extents->height,
-					   NULL);
-
-	_cairo_pattern_fini (&pattern.base);
-
-	if (unlikely (status))
-	    goto CLEANUP_SURFACE;
-    }
+    if (clip_surface)
+	status = _cairo_clip_combine_with_surface (clip, mask);
 
     _cairo_pattern_init_for_surface (mask_pattern, mask);
 
commit 3f12d9ec5db1ac372742c3c03408bdaeaffdc1e4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 28 10:05:52 2009 +0100

    [clip] Use geometric clipping for unaligned clips
    
    For the simple cases where the clip is an unaligned box (or boxes), apply
    the clip directly to the geometry and avoid having to use an intermediate
    clip-mask.

diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c
index 99f4655..cb508f5 100644
--- a/src/cairo-bentley-ottmann.c
+++ b/src/cairo-bentley-ottmann.c
@@ -216,28 +216,6 @@ _line_compute_intersection_x_for_y (const cairo_line_t *line,
     return x;
 }
 
-static cairo_fixed_t
-_line_compute_intersection_y_for_x (const cairo_line_t *line,
-				    cairo_fixed_t x)
-{
-    cairo_fixed_t y, dx;
-
-    if (x == line->p1.x)
-	return line->p1.y;
-    if (x == line->p2.x)
-	return line->p2.y;
-
-    y = line->p1.y;
-    dx = line->p2.x - line->p1.x;
-    if (dx != 0) {
-	y += _cairo_fixed_mul_div_floor (x - line->p1.x,
-					 line->p2.y - line->p1.y,
-					 dx);
-    }
-
-    return y;
-}
-
 static inline int
 _cairo_bo_point32_compare (cairo_bo_point32_t const *a,
 			   cairo_bo_point32_t const *b)
@@ -1801,217 +1779,12 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t	 *traps,
     return status;
 }
 
-static cairo_bool_t
-_points_outside (const cairo_fixed_t *x, const cairo_box_t *limits)
-{
-    if (x[0] < limits->p1.x || x[0] > limits->p2.x)
-	return TRUE;
-    if (x[1] < limits->p1.x || x[1] > limits->p2.x)
-	return TRUE;
-    if (x[2] < limits->p1.x || x[2] > limits->p2.x)
-	return TRUE;
-    if (x[3] < limits->p1.x || x[3] > limits->p2.x)
-	return TRUE;
-
-    return FALSE;
-}
-
-static int
-_add_event (const cairo_line_t *line,
-	   int32_t top, int32_t bottom,
-	   int dir,
-	   cairo_bo_start_event_t *event)
-{
-    if (top == bottom)
-	return 0;
-
-    event->type = CAIRO_BO_EVENT_TYPE_START;
-    event->point.y = top;
-    event->point.x = _line_compute_intersection_x_for_y (line, top);
-
-    event->edge.edge.line = *line;
-    event->edge.edge.top = top;
-    event->edge.edge.bottom = bottom;
-    event->edge.edge.dir = dir;
-
-    event->edge.deferred_trap.right = NULL;
-    event->edge.prev = NULL;
-    event->edge.next = NULL;
-
-    return 1;
-}
-
-static int
-_compute_clipped_trapezoid_edges (const cairo_traps_t *traps,
-				  const cairo_trapezoid_t *t,
-				  cairo_bo_start_event_t *events)
-{
-    cairo_fixed_t x[4];
-    int num_events = 0;
-    int top, bot;
-
-    /* compute points in clockwise orientation */
-    top = t->top;
-    bot = t->bottom;
-
-    x[0] = _line_compute_intersection_x_for_y (&t->left, top);
-    x[1] = _line_compute_intersection_x_for_y (&t->right, top);
-    x[2] = _line_compute_intersection_x_for_y (&t->right, bot);
-    x[3] = _line_compute_intersection_x_for_y (&t->left, bot);
-
-    if (traps->has_limits && _points_outside (x, &traps->limits)) {
-	cairo_line_t limits[2];
-	cairo_fixed_t ly[2], ry[2];
-	cairo_fixed_t ymin, ymax;
-
-	limits[0].p1.x = traps->limits.p1.x;
-	limits[0].p1.y = traps->limits.p1.y;
-	limits[0].p2.x = traps->limits.p1.x;
-	limits[0].p2.y = traps->limits.p2.y;
-
-	limits[1].p1.x = traps->limits.p2.x;
-	limits[1].p1.y = traps->limits.p1.y;
-	limits[1].p2.x = traps->limits.p2.x;
-	limits[1].p2.y = traps->limits.p2.y;
-
-	ly[0] = _line_compute_intersection_y_for_x (&t->left,
-						    traps->limits.p1.x);
-	ymin = ymax = ly[0];
-
-	ly[1] = _line_compute_intersection_y_for_x (&t->left,
-						    traps->limits.p2.x);
-	if (ly[1] < ymin)
-	    ymin = ly[1];
-	if (ly[1] > ymax)
-	    ymax = ly[1];
-
-	ry[0] = _line_compute_intersection_y_for_x (&t->right,
-						    traps->limits.p1.x);
-	if (ry[0] < ymin)
-	    ymin = ry[0];
-	if (ry[0] > ymax)
-	    ymax = ry[0];
-
-	ry[1] = _line_compute_intersection_y_for_x (&t->right,
-						    traps->limits.p2.x);
-	if (ry[1] < ymin)
-	    ymin = ry[1];
-	if (ry[1] > ymax)
-	    ymax = ry[1];
-
-	if (ymin > top)
-	    top = ymin;
-	if (ymax < bot)
-	    bot = ymax;
-	if (top >= bot)
-	    return 0;
-
-	/* left hand side intersects */
-
-	if (x[0] <= traps->limits.p1.x && x[3] <= traps->limits.p1.x)
-	{
-	    num_events += _add_event (&limits[0], top, bot, 1,
-				      events + num_events);
-	}
-	else if (x[0] >= traps->limits.p1.x && x[3] >= traps->limits.p1.x &&
-		 x[0] <= traps->limits.p2.x && x[3] <= traps->limits.p2.x)
-	{
-	    num_events += _add_event (&t->left, top, bot, 1,
-				      events + num_events);
-	}
-	else
-	{
-	    if (ly[0] < ly[1]) {
-		if (ly[1] >= top) {
-		    if (ly[0] < top)
-			ly[0] = top;
-		    if (ly[1] > bot)
-			ly[1] = bot;
-		    num_events += _add_event (&limits[0], top, ly[0], 1,
-					    events + num_events);
-		    num_events += _add_event (&t->left, ly[0], ly[1], 1,
-					    events + num_events);
-		    num_events += _add_event (&limits[1], ly[1], bot, 1,
-					    events + num_events);
-		}
-	    } else {
-		if (ly[1] <= bot) {
-		    if (ly[1] < top)
-			ly[1] = top;
-		    if (ly[0] > bot)
-			ly[0] = bot;
-		    num_events += _add_event (&limits[1], top, ly[1], 1,
-					    events + num_events);
-		    num_events += _add_event (&t->left, ly[1], ly[0], 1,
-					    events + num_events);
-		    num_events += _add_event (&limits[0], ly[0], bot, 1,
-					    events + num_events);
-		}
-	    }
-	}
-
-	/* right hand side intersects */
-
-	if (x[1] >= traps->limits.p2.x && x[2] >= traps->limits.p2.x)
-	{
-	    num_events += _add_event (&limits[1], top, bot, -1,
-				      events + num_events);
-	}
-	else if (x[1] <= traps->limits.p2.x && x[2] <= traps->limits.p2.x &&
-		 x[1] >= traps->limits.p1.x && x[2] >= traps->limits.p1.x)
-	{
-	    num_events += _add_event (&t->right, top, bot, -1,
-				      events + num_events);
-	}
-	else
-	{
-	    if (ry[0] < ry[1]) {
-		if (ry[0] <= bot) {
-		    if (ry[0] < top)
-			ry[0] = top;
-		    if (ry[1] > bot)
-			ry[1] = bot;
-		    num_events += _add_event (&limits[0], top, ry[0], -1,
-					    events + num_events);
-		    num_events += _add_event (&t->right, ry[0], ry[1], -1,
-					    events + num_events);
-		    num_events += _add_event (&limits[1], ry[1], bot, -1,
-					    events + num_events);
-		}
-	    } else {
-		if (ry[0] >= top) {
-		    if (ry[0] > bot)
-			ry[0] = bot;
-		    if (ry[1] < top)
-			ry[1] = top;
-		    num_events += _add_event (&limits[1], top, ry[1], -1,
-					    events + num_events);
-		    num_events += _add_event (&t->right, ry[1], ry[0], -1,
-					    events + num_events);
-		    num_events += _add_event (&limits[0], ry[0], bot, -1,
-					    events + num_events);
-		}
-	    }
-	}
-    } else {
-	num_events += _add_event (&t->left, top, bot, 1, events + num_events);
-	num_events += _add_event (&t->right, top, bot, -1, events + num_events);
-    }
-
-    return num_events;
-}
-
 cairo_status_t
 _cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps,
 					 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;
-    int num_events;
+    cairo_polygon_t polygon;
     int i;
 
     if (unlikely (0 == traps->num_traps))
@@ -2021,50 +1794,38 @@ _cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps,
     dump_traps (traps, "bo-traps-in.txt");
 #endif
 
-    /* we need at most 6 vertical edges to describe each clipped trapezoid */
-    i = 6 * traps->num_traps;
+    _cairo_polygon_init (&polygon);
+    _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits);
 
-    events = stack_events;
-    event_ptrs = stack_event_ptrs;
-    if (i > ARRAY_LENGTH (stack_events)) {
-	events = _cairo_malloc_ab_plus_c (i,
-					  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 + i);
-    }
-
-    num_events = 0;
     for (i = 0; i < traps->num_traps; i++) {
-	num_events += _compute_clipped_trapezoid_edges (traps,
-							&traps->traps[i],
-							&events[num_events]);
-    }
-
-    for (i = 0; i < num_events; i++)
-	event_ptrs[i] = (cairo_bo_event_t *) &events[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;
 
-#if DEBUG_TRAPS
-    dump_edges (events, num_events, "bo-traps-edges.txt");
-#endif
+	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_bo_edges (event_ptrs,
-							 num_events,
-							 fill_rule,
-							 traps,
-							 &intersections);
+    status = _cairo_bentley_ottmann_tessellate_polygon (traps,
+							&polygon,
+							fill_rule);
 
 #if DEBUG_TRAPS
     dump_traps (traps, "bo-traps-out.txt");
 #endif
 
-    if (events != stack_events)
-	free (events);
+  CLEANUP:
+    _cairo_polygon_fini (&polygon);
 
     return status;
 }
diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
index d045d12..6e40492 100644
--- a/src/cairo-clip-private.h
+++ b/src/cairo-clip-private.h
@@ -117,6 +117,11 @@ cairo_private cairo_int_status_t
 _cairo_clip_get_region (cairo_clip_t *clip,
 			cairo_region_t **region);
 
+cairo_private cairo_int_status_t
+_cairo_clip_get_boxes (cairo_clip_t *clip,
+		       cairo_box_t **boxes,
+		       int *count);
+
 cairo_private void
 _cairo_clip_translate (cairo_clip_t  *clip,
                        cairo_fixed_t  tx,
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 3200511..09b26ad 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -600,7 +600,6 @@ _cairo_clip_apply_clip (cairo_clip_t *clip,
     return status;
 }
 
-#if 0
 static inline cairo_bool_t
 _clip_paths_are_rectilinear (cairo_clip_path_t *clip_path)
 {
@@ -613,7 +612,6 @@ _clip_paths_are_rectilinear (cairo_clip_path_t *clip_path)
 
     return TRUE;
 }
-#endif
 
 static cairo_int_status_t
 _cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path)
@@ -634,7 +632,7 @@ _cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path)
 
     _cairo_traps_init (&traps);
     _cairo_box_from_rectangle (&box, &clip_path->extents);
-    _cairo_traps_limit (&traps, &box);
+    _cairo_traps_limit (&traps, &box, 1);
 
     status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path,
 					      clip_path->prev->fill_rule,
@@ -712,6 +710,260 @@ _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
     return CAIRO_STATUS_SUCCESS;
 }
 
+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;
+}
+
+/* XXX there is likely a faster method! ;-) */
+static cairo_status_t
+_region_clip_to_boxes (const cairo_region_t *region,
+		       cairo_box_t **boxes,
+		       int *num_boxes,
+		       int *size_boxes)
+{
+    cairo_traps_t traps;
+    cairo_status_t status;
+    int n, num_rects;
+
+    _cairo_traps_init (&traps);
+    _cairo_traps_limit (&traps, *boxes, *num_boxes);
+    traps.is_rectilinear = TRUE;
+
+    num_rects = cairo_region_num_rectangles (region);
+    for (n = 0; n < num_rects; n++) {
+	cairo_rectangle_int_t rect;
+	cairo_point_t p1, p2;
+
+	cairo_region_get_rectangle (region, n, &rect);
+
+	p1.x = _cairo_fixed_from_int (rect.x);
+	p1.y = _cairo_fixed_from_int (rect.y);
+	p2.x = _cairo_fixed_from_int (rect.x + rect.width);
+	p2.y = _cairo_fixed_from_int (rect.y + rect.height);
+
+	status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2);
+	if (unlikely (status))
+	    goto CLEANUP;
+    }
+
+    status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps, CAIRO_FILL_RULE_WINDING);
+    if (unlikely (status))
+	goto CLEANUP;
+
+    n = *size_boxes;
+    if (n < 0)
+	n = -n;
+
+    if (traps.num_traps > n) {
+	cairo_box_t *new_boxes;
+
+	new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
+	if (unlikely (new_boxes == NULL)) {
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    goto CLEANUP;
+	}
+
+	if (*size_boxes > 0)
+	    free (*boxes);
+
+	*boxes = new_boxes;
+	*size_boxes = traps.num_traps;
+    }
+
+    for (n = 0; n < traps.num_traps; n++) {
+	(*boxes)[n].p1.x = traps.traps[n].left.p1.x;
+	(*boxes)[n].p1.y = traps.traps[n].top;
+	(*boxes)[n].p2.x = traps.traps[n].right.p1.x;
+	(*boxes)[n].p2.y = traps.traps[n].bottom;
+    }
+    *num_boxes = n;
+
+  CLEANUP:
+    _cairo_traps_fini (&traps);
+
+    return status;
+}
+
+static cairo_status_t
+_rectilinear_clip_to_boxes (const cairo_path_fixed_t *path,
+			    cairo_fill_rule_t fill_rule,
+			    cairo_box_t **boxes,
+			    int *num_boxes,
+			    int *size_boxes)
+{
+    cairo_polygon_t polygon;
+    cairo_traps_t traps;
+    cairo_status_t status;
+
+    _cairo_polygon_init (&polygon);
+    _cairo_polygon_limit (&polygon, *boxes, *num_boxes);
+
+    /* tolerance will be ignored as the path is rectilinear */
+    status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
+    if (unlikely (status))
+	goto CLEANUP_POLYGON;
+
+    if (polygon.num_edges == 0) {
+	*num_boxes = 0;
+    } else {
+	_cairo_traps_init (&traps);
+
+	status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
+									&polygon,
+									fill_rule);
+	if (likely (status == CAIRO_STATUS_SUCCESS)) {
+	    int i;
+
+	    i = *size_boxes;
+	    if (i < 0)
+		i = -i;
+
+	    if (traps.num_traps > i) {
+		cairo_box_t *new_boxes;
+		int new_size;
+
+		new_size = pot (traps.num_traps);
+		new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t));
+		if (unlikely (new_boxes == NULL)) {
+		    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		    goto CLEANUP_TRAPS;
+		}
+
+		if (*size_boxes > 0)
+		    free (*boxes);
+
+		*boxes = new_boxes;
+		*size_boxes = new_size;
+	    }
+
+	    for (i = 0; i < traps.num_traps; i++) {
+		(*boxes)[i].p1.x = traps.traps[i].left.p1.x;
+		(*boxes)[i].p1.y = traps.traps[i].top;
+		(*boxes)[i].p2.x = traps.traps[i].right.p1.x;
+		(*boxes)[i].p2.y = traps.traps[i].bottom;
+	    }
+	    *num_boxes = i;
+	}
+
+      CLEANUP_TRAPS:
+	_cairo_traps_fini (&traps);
+    }
+
+  CLEANUP_POLYGON:
+    _cairo_polygon_fini (&polygon);
+
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path,
+			   cairo_box_t **boxes,
+			   int *count)
+{
+    int size = -*count;
+    int num_boxes = 0;
+    cairo_status_t status;
+
+    if (clip_path->region != NULL) {
+	int num_rects, n;
+
+	num_rects = cairo_region_num_rectangles (clip_path->region);
+	if (num_rects > -size) {
+	    cairo_box_t *new_boxes;
+
+	    new_boxes = _cairo_malloc_ab (num_rects, sizeof (cairo_box_t));
+	    if (unlikely (new_boxes == NULL))
+		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	    *boxes = new_boxes;
+	}
+
+	for (n = 0; n < num_rects; n++) {
+	    cairo_rectangle_int_t rect;
+
+	    cairo_region_get_rectangle (clip_path->region, n, &rect);
+	    (*boxes)[n].p1.x = _cairo_fixed_from_int (rect.x);
+	    (*boxes)[n].p1.y = _cairo_fixed_from_int (rect.y);
+	    (*boxes)[n].p2.x = _cairo_fixed_from_int (rect.x + rect.width);
+	    (*boxes)[n].p2.y = _cairo_fixed_from_int (rect.y + rect.height);
+	}
+
+	*count = num_rects;
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* keep it simple at first */
+    if (! _clip_paths_are_rectilinear (clip_path))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    assert (-size >= 1);
+    if (_cairo_path_fixed_is_box (&clip_path->path, *boxes)) {
+	num_boxes = 1;
+    } else {
+	status = _rectilinear_clip_to_boxes (&clip_path->path,
+					     clip_path->fill_rule,
+					     boxes, &num_boxes, &size);
+	if (unlikely (status))
+	    return status;
+    }
+
+    while ((clip_path = clip_path->prev) != NULL) {
+	cairo_box_t box;
+
+	if (clip_path->region != NULL) {
+	    status = _region_clip_to_boxes (clip_path->region,
+					    boxes, &num_boxes, &size);
+	    if (unlikely (status))
+		return status;
+
+	    break;
+	} else if (_cairo_path_fixed_is_box (&clip_path->path, &box)) {
+	    int i, j;
+
+	    for (i = j = 0; i < num_boxes; i++) {
+		if (j != i)
+		    (*boxes)[j] = (*boxes)[i];
+
+		if (box.p1.x > (*boxes)[j].p1.x)
+		    (*boxes)[j].p1.x = box.p1.x;
+		if (box.p2.x < (*boxes)[j].p2.x)
+		    (*boxes)[j].p2.x = box.p2.x;
+
+		if (box.p1.y > (*boxes)[j].p1.y)
+		    (*boxes)[j].p1.y = box.p1.y;
+		if (box.p2.y < (*boxes)[j].p2.y)
+		    (*boxes)[j].p2.y = box.p2.y;
+
+		j += (*boxes)[j].p2.x > (*boxes)[j].p1.x &&
+		     (*boxes)[j].p2.y > (*boxes)[j].p1.y;
+	    }
+
+	    num_boxes = j;
+	} else {
+	    status = _rectilinear_clip_to_boxes (&clip_path->path,
+						 clip_path->fill_rule,
+						 boxes, &num_boxes, &size);
+	    if (unlikely (status))
+		return status;
+	}
+
+	if (num_boxes == 0)
+	    break;
+    }
+
+    *count = num_boxes;
+    return CAIRO_STATUS_SUCCESS;
+}
+
 cairo_surface_t *
 _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
 {
@@ -943,12 +1195,10 @@ _cairo_clip_get_region (cairo_clip_t *clip,
 {
     cairo_int_status_t status;
 
-    *region = NULL;
     if (clip->all_clipped)
-	return CAIRO_INT_STATUS_NOTHING_TO_DO;
+	goto CLIPPED;
 
-    if (clip->path == NULL)
-	return CAIRO_STATUS_SUCCESS;
+    assert (clip->path != NULL);
 
     status = _cairo_clip_path_to_region (clip->path);
     if (status)
@@ -956,10 +1206,40 @@ _cairo_clip_get_region (cairo_clip_t *clip,
 
     if (cairo_region_is_empty (clip->path->region)) {
 	_cairo_clip_set_all_clipped (clip);
+	goto CLIPPED;
+    }
+
+    if (region)
+	*region = clip->path->region;
+    return CAIRO_STATUS_SUCCESS;
+
+  CLIPPED:
+    if (region)
+	*region = NULL;
+    return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+cairo_int_status_t
+_cairo_clip_get_boxes (cairo_clip_t *clip,
+		       cairo_box_t **boxes,
+		       int *count)
+{
+    cairo_int_status_t status;
+
+    if (clip->all_clipped)
+	return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    assert (clip->path != NULL);
+
+    status = _cairo_clip_path_to_boxes (clip->path, boxes, count);
+    if (status)
+	return status;
+
+    if (*count == 0) {
+	_cairo_clip_set_all_clipped (clip);
 	return CAIRO_INT_STATUS_NOTHING_TO_DO;
     }
 
-    *region = clip->path->region;
     return CAIRO_STATUS_SUCCESS;
 }
 
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 8cb52ab..902de85 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -869,27 +869,28 @@ static cairo_bool_t
 _clipped (cairo_gstate_t *gstate)
 {
     cairo_rectangle_int_t extents;
-    cairo_region_t *clip_region;
 
     if (gstate->clip.all_clipped)
 	return TRUE;
 
+    /* XXX consider applying a surface clip? */
+
+    if (gstate->clip.path == NULL)
+	return FALSE;
+
     if (_cairo_surface_get_extents (gstate->target, &extents)) {
 	if (extents.width == 0 || extents.height == 0)
 	    return TRUE;
 
-	if (gstate->clip.path != NULL &&
-	    ! _cairo_rectangle_intersect (&extents,
+	if (! _cairo_rectangle_intersect (&extents,
 					  &gstate->clip.path->extents))
 	{
 	    return TRUE;
 	}
-
-	/* XXX consider applying a surface clip? */
     }
 
     /* perform a simple query to exclude trivial all-clipped cases */
-    return _cairo_clip_get_region (&gstate->clip, &clip_region) == CAIRO_INT_STATUS_NOTHING_TO_DO;
+    return _cairo_clip_get_region (&gstate->clip, NULL) == CAIRO_INT_STATUS_NOTHING_TO_DO;
 }
 
 cairo_status_t
@@ -1018,7 +1019,7 @@ _cairo_gstate_in_stroke (cairo_gstate_t	    *gstate,
     limit.p2.y = limit.p1.y + 2;
 
     _cairo_traps_init (&traps);
-    _cairo_traps_limit (&traps, &limit);
+    _cairo_traps_limit (&traps, &limit, 1);
 
     status = _cairo_path_fixed_stroke_to_traps (path,
 						&gstate->stroke_style,
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 0cad156..d46917d 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -160,8 +160,7 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
 	return CAIRO_STATUS_SUCCESS;
 
     _cairo_polygon_init (&polygon);
-    if (traps->has_limits)
-	_cairo_polygon_limit (&polygon, &traps->limits);
+    _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits);
 
     status = _cairo_path_fixed_fill_to_polygon (path,
 						tolerance,
@@ -189,6 +188,7 @@ _cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_
 							 cairo_fill_rule_t	 fill_rule,
 							 const cairo_rectangle_int_t *extents)
 {
+    cairo_box_t box;
     cairo_polygon_t polygon;
     cairo_traps_t traps;
     cairo_status_t status;
@@ -196,10 +196,8 @@ _cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_
 
     _cairo_polygon_init (&polygon);
     if (extents != NULL) {
-	cairo_box_t box;
-
 	_cairo_box_from_rectangle (&box, extents);
-	_cairo_polygon_limit (&polygon, &box);
+	_cairo_polygon_limit (&polygon, &box, 1);
     }
 
     /* tolerance will be ignored as the path is rectilinear */
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index d37c597..33fed4f 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -186,13 +186,14 @@ _cairo_stroker_init (cairo_stroker_t		*stroker,
 
 static void
 _cairo_stroker_limit (cairo_stroker_t *stroker,
-		      const cairo_box_t *box)
+		      const cairo_box_t *boxes,
+		      int num_boxes)
 {
     double dx, dy;
     cairo_fixed_t fdx, fdy;
 
     stroker->has_bounds = TRUE;
-    stroker->bounds = *box;
+    _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
@@ -1353,8 +1354,8 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
     stroker.add_external_edge = _cairo_polygon_add_external_edge,
     stroker.closure = polygon;
 
-    if (polygon->has_limits)
-	_cairo_stroker_limit (&stroker, &polygon->limits);
+    if (polygon->num_limits)
+	_cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits);
 
     status = _cairo_path_fixed_interpret (path,
 					  CAIRO_DIRECTION_FORWARD,
@@ -1404,8 +1405,7 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
     }
 
     _cairo_polygon_init (&polygon);
-    if (traps->has_limits)
-	_cairo_polygon_limit (&polygon, &traps->limits);
+    _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits);
 
     status = _cairo_path_fixed_stroke_to_polygon (path,
 						 stroke_style,
@@ -1458,10 +1458,11 @@ typedef struct _cairo_rectilinear_stroker {
 
 static void
 _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
-				  const cairo_box_t *box)
+				  const cairo_box_t *boxes,
+				  int num_boxes)
 {
     stroker->has_bounds = TRUE;
-    stroker->bounds = *box;
+    _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;
@@ -2005,9 +2006,10 @@ _cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t	*path,
 				     stroke_style,
 				     ctm,
 				     traps);
-    if (traps->has_limits) {
+    if (traps->num_limits) {
 	_cairo_rectilinear_stroker_limit (&rectilinear_stroker,
-					  &traps->limits);
+					  traps->limits,
+					  traps->num_limits);
     }
 
     status = _cairo_path_fixed_interpret (path,
diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
index a8addff..fdc5c92 100644
--- a/src/cairo-polygon.c
+++ b/src/cairo-polygon.c
@@ -50,7 +50,7 @@ _cairo_polygon_init (cairo_polygon_t *polygon)
 
     polygon->has_current_point = FALSE;
     polygon->has_current_edge = FALSE;
-    polygon->has_limits = FALSE;
+    polygon->num_limits = 0;
 
     polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
     polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
@@ -58,10 +58,11 @@ _cairo_polygon_init (cairo_polygon_t *polygon)
 
 void
 _cairo_polygon_limit (cairo_polygon_t	*polygon,
-		      const cairo_box_t *limits)
+		      const cairo_box_t *limits,
+		      int num_limits)
 {
-    polygon->has_limits = TRUE;
-    polygon->limits = *limits;
+    polygon->limits = limits;
+    polygon->num_limits = num_limits;
 }
 
 void
@@ -159,156 +160,163 @@ static void
 _add_clipped_edge (cairo_polygon_t *polygon,
 		   const cairo_point_t *p1,
 		   const cairo_point_t *p2,
-		   int dir)
+		   const int top, const int bottom,
+		   const int dir)
 {
     cairo_point_t p[2];
     int top_y, bot_y;
+    int n;
 
-    if (p1->x <= polygon->limits.p1.x && p2->x <= polygon->limits.p1.x)
-    {
-	p[0].x = polygon->limits.p1.x;
-	p[0].y = polygon->limits.p1.y;
-	top_y = p1->y;
-	if (top_y < p[0].y)
-	    top_y = p[0].y;
-
-	p[1].x = polygon->limits.p1.x;
-	p[1].y = polygon->limits.p2.y;
-	bot_y = p2->y;
-	if (bot_y > p[1].y)
-	    bot_y = p[1].y;
-
-	_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-    }
-    else if (p1->x >= polygon->limits.p2.x && p2->x >= polygon->limits.p2.x)
-    {
-	p[0].x = polygon->limits.p2.x;
-	p[0].y = polygon->limits.p1.y;
-	top_y = p1->y;
-	if (top_y < p[0].y)
-	    top_y = p[0].y;
-
-	p[1].x = polygon->limits.p2.x;
-	p[1].y = polygon->limits.p2.y;
-	bot_y = p2->y;
-	if (bot_y > p[1].y)
-	    bot_y = p[1].y;
-
-	_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-    }
-    else if (p1->x >= polygon->limits.p1.x && p2->x >= polygon->limits.p1.x &&
-	     p1->x <= polygon->limits.p2.x && p2->x <= polygon->limits.p2.x)
-    {
-	top_y = p1->y;
-	if (top_y < polygon->limits.p1.y)
-	    top_y = polygon->limits.p1.y;
-
-	bot_y = p2->y;
-	if (bot_y > polygon->limits.p2.y)
-	    bot_y = polygon->limits.p2.y;
-
-	_add_edge (polygon, p1, p2, top_y, bot_y, dir);
-    }
-    else
-    {
-	int left_y, right_y;
-	int p1_y, p2_y;
+    for (n = 0; n < polygon->num_limits; n++) {
+	const cairo_box_t *limits = &polygon->limits[n];
 
-	left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
-						     polygon->limits.p1.x);
-	right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
-						      polygon->limits.p2.x);
+	if (top >= limits->p2.y)
+	    continue;
+	if (bottom <= limits->p1.y)
+	    continue;
 
-	if (left_y == right_y) /* horizontal within bounds */
-	    return;
+	if (p1->x <= limits->p1.x && p2->x <= limits->p1.x)
+	{
+	    p[0].x = limits->p1.x;
+	    p[0].y = limits->p1.y;
+	    top_y = top;
+	    if (top_y < p[0].y)
+		top_y = p[0].y;
+
+	    p[1].x = limits->p1.x;
+	    p[1].y = limits->p2.y;
+	    bot_y = bottom;
+	    if (bot_y > p[1].y)
+		bot_y = p[1].y;
+
+	    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+	}
+	else if (p1->x >= limits->p2.x && p2->x >= limits->p2.x)
+	{
+	    p[0].x = limits->p2.x;
+	    p[0].y = limits->p1.y;
+	    top_y = top;
+	    if (top_y < p[0].y)
+		top_y = p[0].y;
+
+	    p[1].x = limits->p2.x;
+	    p[1].y = limits->p2.y;
+	    bot_y = bottom;
+	    if (bot_y > p[1].y)
+		bot_y = p[1].y;
+
+	    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+	}
+	else if (p1->x >= limits->p1.x && p2->x >= limits->p1.x &&
+		 p1->x <= limits->p2.x && p2->x <= limits->p2.x)
+	{
+	    top_y = top;
+	    if (top_y < limits->p1.y)
+		top_y = limits->p1.y;
 
-	p1_y = p1->y;
-	p2_y = p2->y;
-
-	if (left_y < right_y) {
-	    if (p1->x < polygon->limits.p1.x && left_y > polygon->limits.p1.y)
-	    {
-		p[0].x = polygon->limits.p1.x;
-		p[0].y = polygon->limits.p1.y;
-		top_y = p1_y;
-		if (top_y < p[0].y)
-		    top_y = p[0].y;
-
-		p[1].x = polygon->limits.p1.x;
-		p[1].y = polygon->limits.p2.y;
-		bot_y = left_y;
-		if (bot_y > p[1].y)
-		    bot_y = p[1].y;
-
-		if (bot_y > top_y)
-		    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-		p1_y = bot_y;
-	    }
+	    bot_y = bottom;
+	    if (bot_y > limits->p2.y)
+		bot_y = limits->p2.y;
 
-	    if (p2->x > polygon->limits.p2.x && right_y < polygon->limits.p2.y)
-	    {
-		p[0].x = polygon->limits.p2.x;
-		p[0].y = polygon->limits.p1.y;
-		top_y = right_y;
-		if (top_y < p[0].y)
-		    top_y = p[0].y;
-
-		p[1].x = polygon->limits.p2.x;
-		p[1].y = polygon->limits.p2.y;
-		bot_y = p2_y;
-		if (bot_y > p[1].y)
-		    bot_y = p[1].y;
-
-		if (bot_y > top_y)
-		    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-		p2_y = top_y;
-	    }
-	} else {
-	    if (p1->x > polygon->limits.p2.x && right_y > polygon->limits.p1.y)
-	    {
-		p[0].x = polygon->limits.p2.x;
-		p[0].y = polygon->limits.p1.y;
-		top_y = p1_y;
-		if (top_y < p[0].y)
-		    top_y = p[0].y;
-
-		p[1].x = polygon->limits.p2.x;
-		p[1].y = polygon->limits.p2.y;
-		bot_y = right_y;
-		if (bot_y > p[1].y)
-		    bot_y = p[1].y;
-
-		if (bot_y > top_y)
-		    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-		p1_y = bot_y;
-	    }
+	    _add_edge (polygon, p1, p2, top_y, bot_y, dir);
+	}
+	else
+	{
+	    int left_y, right_y;
+	    int p1_y, p2_y;
 
-	    if (p2->x < polygon->limits.p1.x && left_y < polygon->limits.p2.y)
-	    {
-		p[0].x = polygon->limits.p1.x;
-		p[0].y = polygon->limits.p1.y;
-		top_y = left_y;
-		if (top_y < p[0].y)
-		    top_y = p[0].y;
-
-		p[1].x = polygon->limits.p1.x;
-		p[1].y = polygon->limits.p2.y;
-		bot_y = p2_y;
-		if (bot_y > p[1].y)
-		    bot_y = p[1].y;
-
-		if (bot_y > top_y)
-		    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-		p2_y = top_y;
+	    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);
+
+	    if (left_y == right_y) /* horizontal within bounds */
+		return;
+
+	    p1_y = top;
+	    p2_y = bottom;
+
+	    if (left_y < right_y) {
+		if (p1->x < limits->p1.x && left_y > limits->p1.y) {
+		    p[0].x = limits->p1.x;
+		    p[0].y = limits->p1.y;
+		    top_y = p1_y;
+		    if (top_y < p[0].y)
+			top_y = p[0].y;
+
+		    p[1].x = limits->p1.x;
+		    p[1].y = limits->p2.y;
+		    bot_y = left_y;
+		    if (bot_y > p[1].y)
+			bot_y = p[1].y;
+
+		    if (bot_y > top_y)
+			_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+		    p1_y = bot_y;
+		}
+
+		if (p2->x > limits->p2.x && right_y < limits->p2.y) {
+		    p[0].x = limits->p2.x;
+		    p[0].y = limits->p1.y;
+		    top_y = right_y;
+		    if (top_y < p[0].y)
+			top_y = p[0].y;
+
+		    p[1].x = limits->p2.x;
+		    p[1].y = limits->p2.y;
+		    bot_y = p2_y;
+		    if (bot_y > p[1].y)
+			bot_y = p[1].y;
+
+		    if (bot_y > top_y)
+			_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+		    p2_y = top_y;
+		}
+	    } else {
+		if (p1->x > limits->p2.x && right_y > limits->p1.y) {
+		    p[0].x = limits->p2.x;
+		    p[0].y = limits->p1.y;
+		    top_y = p1_y;
+		    if (top_y < p[0].y)
+			top_y = p[0].y;
+
+		    p[1].x = limits->p2.x;
+		    p[1].y = limits->p2.y;
+		    bot_y = right_y;
+		    if (bot_y > p[1].y)
+			bot_y = p[1].y;
+
+		    if (bot_y > top_y)
+			_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+		    p1_y = bot_y;
+		}
+
+		if (p2->x < limits->p1.x && left_y < limits->p2.y) {
+		    p[0].x = limits->p1.x;
+		    p[0].y = limits->p1.y;
+		    top_y = left_y;
+		    if (top_y < p[0].y)
+			top_y = p[0].y;
+
+		    p[1].x = limits->p1.x;
+		    p[1].y = limits->p2.y;
+		    bot_y = p2_y;
+		    if (bot_y > p[1].y)
+			bot_y = p[1].y;
+
+		    if (bot_y > top_y)
+			_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+		    p2_y = top_y;
+		}
 	    }
-	}
 
-	if (p1_y < polygon->limits.p1.y)
-	    p1_y = polygon->limits.p1.y;
-	if (p2_y > polygon->limits.p2.y)
-	    p2_y = polygon->limits.p2.y;
-	if (p2_y > p1_y)
-	    _add_edge (polygon, p1, p2, p1_y, p2_y, dir);
+	    if (p1_y < limits->p1.y)
+		p1_y = limits->p1.y;
+	    if (p2_y > limits->p2.y)
+		p2_y = limits->p2.y;
+	    if (p2_y > p1_y)
+		_add_edge (polygon, p1, p2, p1_y, p2_y, dir);
+	}
     }
 }
 
@@ -331,14 +339,14 @@ _cairo_polygon_add_edge (cairo_polygon_t *polygon,
 	dir = -1;
     }
 
-    if (polygon->has_limits) {
-	if (p2->y <= polygon->limits.p1.y)
+    if (polygon->num_limits) {
+	if (p2->y <= polygon->limits[0].p1.y)
 	    return;
 
-	if (p1->y >= polygon->limits.p2.y)
+	if (p1->y >= polygon->limits[polygon->num_limits-1].p2.y)
 	    return;
 
-	_add_clipped_edge (polygon, p1, p2, dir);
+	_add_clipped_edge (polygon, p1, p2, p1->y, p2->y, dir);
     } else
 	_add_edge (polygon, p1, p2, p1->y, p2->y, dir);
 }
@@ -352,6 +360,33 @@ _cairo_polygon_add_external_edge (void *polygon,
     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->limits[0].p1.y)
+	    return CAIRO_STATUS_SUCCESS;
+
+	if (line->p1.y >= polygon->limits[polygon->num_limits-1].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 _cairo_polygon_status (polygon);
+}
+
 /* flattened path operations */
 
 void
diff --git a/src/cairo-rectangle.c b/src/cairo-rectangle.c
index b139624..e887c75 100644
--- a/src/cairo-rectangle.c
+++ b/src/cairo-rectangle.c
@@ -71,6 +71,29 @@ _cairo_box_from_rectangle (cairo_box_t                 *box,
     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
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index cca6384..14d94fd 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -810,6 +810,80 @@ _rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip)
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_bool_t
+_clip_contains_rectangle (cairo_clip_t *clip,
+			  const cairo_rectangle_int_t *rect)
+{
+    cairo_clip_path_t *clip_path;
+
+    clip_path = clip->path;
+
+    if (clip_path->extents.x > rect->x ||
+	clip_path->extents.y > rect->y ||
+	clip_path->extents.x + clip_path->extents.width < rect->x + rect->width ||
+	clip_path->extents.y + clip_path->extents.height < rect->y + rect->height)
+    {
+	return FALSE;
+    }
+
+    do {
+	cairo_box_t box;
+
+	if (! _cairo_path_fixed_is_box (&clip_path->path, &box))
+	    return FALSE;
+
+	if (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 FALSE;
+	}
+    } while ((clip_path = clip_path->prev) != NULL);
+
+    return TRUE;
+}
+
+static cairo_status_t
+_clip_to_boxes (cairo_clip_t **clip,
+		const cairo_rectangle_int_t *extents,
+		cairo_box_t **boxes,
+		int *num_boxes)
+{
+    cairo_status_t status;
+
+    if (*clip == NULL) {
+	status = CAIRO_STATUS_SUCCESS;
+	goto OUT;
+    }
+
+    /* In some cases it may be preferable to always use boxes instead
+     * of a region, in particular where we can cull lots of geometry.
+     * For the time being, continue to use the old path until we can
+     * find a compelling use-case for geometry clipping.
+     */
+    status = _cairo_clip_get_region (*clip, NULL);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	goto OUT;
+
+    status = _cairo_clip_get_boxes (*clip, boxes, num_boxes);
+    switch ((int) status) {
+    case CAIRO_STATUS_SUCCESS:
+	*clip = NULL;
+	goto DONE;
+
+    case  CAIRO_INT_STATUS_UNSUPPORTED:
+	status = CAIRO_STATUS_SUCCESS;
+	goto OUT;
+    }
+
+  OUT:
+    _cairo_box_from_rectangle (&(*boxes)[0], extents);
+    *num_boxes = 1;
+  DONE:
+    return status;
+}
+
 cairo_status_t
 _cairo_surface_fallback_paint (cairo_surface_t		*surface,
 			       cairo_operator_t		 op,
@@ -819,9 +893,8 @@ _cairo_surface_fallback_paint (cairo_surface_t		*surface,
     cairo_status_t status;
     cairo_rectangle_int_t extents;
     cairo_bool_t is_bounded;
-    cairo_region_t *clip_region = NULL;
-    cairo_bool_t clip_surface = FALSE;
-    cairo_box_t box;
+    cairo_box_t boxes_stack[32], *boxes = boxes_stack;
+    int num_boxes = ARRAY_LENGTH (boxes_stack);
     cairo_traps_t traps;
 
     is_bounded = _cairo_surface_get_extents (surface, &extents);
@@ -835,71 +908,36 @@ _cairo_surface_fallback_paint (cairo_surface_t		*surface,
 	    return CAIRO_STATUS_SUCCESS;
     }
 
+    if (clip != NULL && _clip_contains_rectangle (clip, &extents))
+	clip = NULL;
+
     status = _rectangle_intersect_clip (&extents, clip);
-    if (status) {
+    if (unlikely (status)) {
 	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
 	    status = CAIRO_STATUS_SUCCESS;
 	return status;
     }
 
-    /* avoid the palaver of constructing traps for a simple region */
-    if (clip != NULL) {
-	status = _cairo_clip_get_region (clip, &clip_region);
-	if (unlikely (_cairo_status_is_error (status)))
-	    return status;
-	if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
-	    return CAIRO_STATUS_SUCCESS;
-
-	clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    if (! clip_surface ||
-       (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE))
-    {
-	cairo_region_t region, *trap_region;
-
-	/* avoid unnecessarily allocating a region */
-	if (clip_region != NULL &&
-	    cairo_region_num_rectangles (clip_region) > 1)
-	{
-	    trap_region = cairo_region_copy (clip_region);
-	    status = cairo_region_intersect_rectangle (trap_region, &extents);
-	    if (unlikely (status)) {
-		cairo_region_destroy (trap_region);
-		return status;
-	    }
-	}
-	else
-	{
-	    _cairo_region_init_rectangle (&region, &extents);
-	    trap_region = &region;
-	}
-
-	if (cairo_region_is_empty (trap_region)) {
+    status = _clip_to_boxes (&clip, &extents, &boxes, &num_boxes);
+    if (unlikely (status)) {
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
 	    status = CAIRO_STATUS_SUCCESS;
-	} else {
-	    status = _clip_and_composite_region (source, op, surface,
-						 trap_region,
-						 clip_surface ? clip : NULL,
-						 &extents);
-	}
-	if (trap_region == &region)
-	    _cairo_region_fini (trap_region);
-	else
-	    cairo_region_destroy (trap_region);
-
-	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
-	    return status;
+	return status;
     }
 
-    /* otherwise, use the trapezoid fallbacks */
-    _cairo_box_from_rectangle (&box, &extents);
-    _cairo_traps_init_box (&traps, &box);
+    status = _cairo_traps_init_boxes (&traps, boxes, num_boxes);
+    if (unlikely (status))
+	goto CLEANUP_BOXES;
+
     status = _clip_and_composite_trapezoids (source, op, surface,
-					     &traps, CAIRO_ANTIALIAS_NONE,
+					     &traps, CAIRO_ANTIALIAS_DEFAULT,
 					     clip, &extents);
     _cairo_traps_fini (&traps);
 
+CLEANUP_BOXES:
+    if (boxes != boxes_stack)
+	free (boxes);
+
     return status;
 }
 
@@ -934,7 +972,6 @@ _cairo_surface_mask_draw_func (void                          *closure,
     }
 }
 
-
 cairo_status_t
 _cairo_surface_fallback_mask (cairo_surface_t		*surface,
 			      cairo_operator_t		 op,
@@ -965,6 +1002,9 @@ _cairo_surface_fallback_mask (cairo_surface_t		*surface,
 	    return CAIRO_STATUS_SUCCESS;
     }
 
+    if (clip != NULL && _clip_contains_rectangle (clip, &extents))
+	clip = NULL;
+
     status = _rectangle_intersect_clip (&extents, clip);
     if (status) {
 	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
@@ -992,7 +1032,8 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
 {
     cairo_polygon_t polygon;
     cairo_traps_t traps;
-    cairo_box_t box;
+    cairo_box_t boxes_stack[32], *boxes = boxes_stack;
+    int num_boxes = ARRAY_LENGTH (boxes_stack);
     cairo_rectangle_int_t extents;
     cairo_bool_t is_bounded;
     cairo_status_t status;
@@ -1018,20 +1059,28 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
 	    return CAIRO_STATUS_SUCCESS;
     }
 
+    if (clip != NULL && _clip_contains_rectangle (clip, &extents))
+	clip = NULL;
+
     status = _rectangle_intersect_clip (&extents, clip);
-    if (status) {
+    if (unlikely (status)) {
 	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
 	    status = CAIRO_STATUS_SUCCESS;
 	return status;
     }
 
-    _cairo_box_from_rectangle (&box, &extents);
+    status = _clip_to_boxes (&clip, &extents, &boxes, &num_boxes);
+    if (unlikely (status)) {
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+	    status = CAIRO_STATUS_SUCCESS;
+	return status;
+    }
 
     _cairo_polygon_init (&polygon);
-    _cairo_polygon_limit (&polygon, &box);
+    _cairo_polygon_limit (&polygon, boxes, num_boxes);
 
     _cairo_traps_init (&traps);
-    _cairo_traps_limit (&traps, &box);
+    _cairo_traps_limit (&traps, boxes, num_boxes);
 
     if (path->is_rectilinear) {
 	status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
@@ -1094,6 +1143,8 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
   CLEANUP:
     _cairo_traps_fini (&traps);
     _cairo_polygon_fini (&polygon);
+    if (boxes != boxes_stack)
+	free (boxes);
 
     return status;
 }
@@ -1110,7 +1161,8 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
 {
     cairo_polygon_t polygon;
     cairo_traps_t traps;
-    cairo_box_t box;
+    cairo_box_t boxes_stack[32], *boxes = boxes_stack;
+    int num_boxes = ARRAY_LENGTH (boxes_stack);
     cairo_rectangle_int_t extents;
     cairo_bool_t is_bounded;
     cairo_status_t status;
@@ -1134,78 +1186,34 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
 	    return CAIRO_STATUS_SUCCESS;
     }
 
+    if (clip != NULL && _clip_contains_rectangle (clip, &extents))
+	clip = NULL;
+
+    if (clip != NULL && clip->path->prev == NULL &&
+	_cairo_path_fixed_equal (&clip->path->path, path))
+    {
+	clip = NULL;
+    }
+
     status = _rectangle_intersect_clip (&extents, clip);
-    if (status) {
+    if (unlikely (status)) {
 	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
 	    status = CAIRO_STATUS_SUCCESS;
 	return status;
     }
 
-    /* avoid the palaver of constructing traps for a simple region */
-    if (path->maybe_fill_region) {
-	cairo_region_t *clip_region = NULL;
-	cairo_bool_t clip_surface = FALSE;
-
-	if (clip != NULL) {
-	    status = _cairo_clip_get_region (clip, &clip_region);
-	    if (unlikely (_cairo_status_is_error (status)))
-		return status;
-	    if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
-		return CAIRO_STATUS_SUCCESS;
-
-	    clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
-	}
-
-	if (! clip_surface ||
-	    (_cairo_operator_bounded_by_mask (op) &&
-	     op != CAIRO_OPERATOR_SOURCE))
-	{
-	    cairo_region_t *trap_region;
-
-	    trap_region = _cairo_path_fixed_fill_rectilinear_to_region (path,
-									fill_rule,
-									&extents);
-	    if (trap_region != NULL) {
-		status = trap_region->status;
-		if (unlikely (status))
-		    return status;
-
-		if (clip_region != NULL &&
-		    cairo_region_num_rectangles (clip_region) > 1)
-		{
-		    status = cairo_region_intersect (trap_region, clip_region);
-		    if (unlikely (status)) {
-			cairo_region_destroy (trap_region);
-			return status;
-		    }
-		}
-
-		if (_cairo_operator_bounded_by_mask (op)) {
-		    cairo_region_get_extents (trap_region, &extents);
-		    if (extents.width == 0 || extents.height == 0)
-			goto CLEANUP_TRAP;
-		}
-
-		status = _clip_and_composite_region (source, op, surface,
-						     trap_region,
-						     clip_surface ? clip : NULL,
-						     &extents);
-             CLEANUP_TRAP:
-		cairo_region_destroy (trap_region);
-
-		if (status != CAIRO_INT_STATUS_UNSUPPORTED)
-		    return status;
-	    }
-	}
+    status = _clip_to_boxes (&clip, &extents, &boxes, &num_boxes);
+    if (unlikely (status)) {
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+	    status = CAIRO_STATUS_SUCCESS;
+	return status;
     }
 
-    _cairo_box_from_rectangle (&box, &extents);
-
     _cairo_traps_init (&traps);
-    _cairo_traps_limit (&traps, &box);
+    _cairo_traps_limit (&traps, boxes, num_boxes);
 
     _cairo_polygon_init (&polygon);
-    _cairo_polygon_limit (&polygon, &box);
+    _cairo_polygon_limit (&polygon, boxes, num_boxes);
 
     status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
     if (unlikely (status))
@@ -1264,6 +1272,8 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
   CLEANUP:
     _cairo_traps_fini (&traps);
     _cairo_polygon_fini (&polygon);
+    if (boxes != boxes_stack)
+	free (boxes);
 
     return status;
 }
@@ -1342,6 +1352,14 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t		*surface,
     is_bounded = _cairo_surface_get_extents (surface, &extents);
     assert (is_bounded || clip);
 
+    if (_cairo_operator_bounded_by_source (op)) {
+	cairo_rectangle_int_t source_extents;
+
+	_cairo_pattern_get_extents (source, &source_extents);
+	if (! _cairo_rectangle_intersect (&extents, &source_extents))
+	    return CAIRO_STATUS_SUCCESS;
+    }
+
     if (_cairo_operator_bounded_by_mask (op)) {
         cairo_rectangle_int_t glyph_extents;
 
@@ -1357,6 +1375,9 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t		*surface,
 	    return CAIRO_STATUS_SUCCESS;
     }
 
+    if (clip != NULL && _clip_contains_rectangle (clip, &extents))
+	clip = NULL;
+
     status = _rectangle_intersect_clip (&extents, clip);
     if (status) {
 	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index a5974cc..c6709de 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -58,17 +58,17 @@ _cairo_traps_init (cairo_traps_t *traps)
     traps->traps_size = ARRAY_LENGTH (traps->traps_embedded);
     traps->traps = traps->traps_embedded;
 
-    traps->has_limits = FALSE;
+    traps->num_limits = 0;
     traps->has_intersections = FALSE;
 }
 
 void
 _cairo_traps_limit (cairo_traps_t	*traps,
-		    cairo_box_t		*limits)
+		    const cairo_box_t	*limits,
+		    int			 num_limits)
 {
-    traps->has_limits = TRUE;
-
-    traps->limits = *limits;
+    traps->limits = limits;
+    traps->num_limits = num_limits;
 }
 
 void
@@ -92,35 +92,6 @@ _cairo_traps_fini (cairo_traps_t *traps)
     VG (VALGRIND_MAKE_MEM_NOACCESS (traps, sizeof (cairo_traps_t)));
 }
 
-/**
- * _cairo_traps_init_box:
- * @traps: a #cairo_traps_t
- * @box: a box that will be converted to a single trapezoid
- *       to store in @traps.
- *
- * Initializes a #cairo_traps_t to contain a single rectangular
- * trapezoid.
- **/
-void
-_cairo_traps_init_box (cairo_traps_t *traps,
-		       const cairo_box_t   *box)
-{
-    _cairo_traps_init (traps);
-
-    assert (traps->traps_size >= 1);
-
-    traps->num_traps = 1;
-
-    traps->traps[0].top = box->p1.y;
-    traps->traps[0].bottom = box->p2.y;
-    traps->traps[0].left.p1 = box->p1;
-    traps->traps[0].left.p2.x = box->p1.x;
-    traps->traps[0].left.p2.y = box->p2.y;
-    traps->traps[0].right.p1.x = box->p2.x;
-    traps->traps[0].right.p1.y = box->p1.y;
-    traps->traps[0].right.p2 = box->p2;
-}
-
 /* make room for at least one more trap */
 static cairo_bool_t
 _cairo_traps_grow (cairo_traps_t *traps)
@@ -171,6 +142,60 @@ _cairo_traps_add_trap (cairo_traps_t *traps,
     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_box_t  *boxes,
+			 int		     num_boxes)
+{
+    cairo_trapezoid_t *trap;
+
+    _cairo_traps_init (traps);
+
+    while (traps->traps_size < num_boxes) {
+	if (unlikely (! _cairo_traps_grow (traps))) {
+	    _cairo_traps_fini (traps);
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	}
+    }
+
+    traps->num_traps = num_boxes;
+    traps->is_rectilinear = TRUE;
+
+    trap = &traps->traps[0];
+    while (num_boxes--) {
+	trap->top    = boxes->p1.y;
+	trap->bottom = boxes->p2.y;
+
+	trap->left.p1   = boxes->p1;
+	trap->left.p2.x = boxes->p1.x;
+	trap->left.p2.y = boxes->p2.y;
+
+	trap->right.p1.x = boxes->p2.x;
+	trap->right.p1.y = boxes->p1.y;
+	trap->right.p2   = boxes->p2;
+
+	if (traps->maybe_region) {
+	    traps->maybe_region  = _cairo_fixed_is_integer (boxes->p1.x) &&
+		                   _cairo_fixed_is_integer (boxes->p1.y) &&
+		                   _cairo_fixed_is_integer (boxes->p2.x) &&
+		                   _cairo_fixed_is_integer (boxes->p2.y);
+	}
+
+	trap++, boxes++;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 cairo_status_t
 _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
 				   const cairo_point_t *top_left,
@@ -188,63 +213,72 @@ _cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
      top = top_left->y;
      bottom = bottom_right->y;
 
-    if (traps->has_limits) {
-	/* Trivially reject if trapezoid is entirely to the right or
-	 * to the left of the limits. */
-	if (left.p1.x >= traps->limits.p2.x &&
-	    left.p2.x >= traps->limits.p2.x)
-	{
-	    return CAIRO_STATUS_SUCCESS;
-	}
-
-	if (right.p1.x <= traps->limits.p1.x &&
-	    right.p2.x <= traps->limits.p1.x)
-	{
-	    return CAIRO_STATUS_SUCCESS;
-	}
-
-	/* And reject if the trapezoid is entirely above or below */
-	if (top > traps->limits.p2.y || bottom < traps->limits.p1.y)
-	    return CAIRO_STATUS_SUCCESS;
-
-	/* Otherwise, clip the trapezoid to the limits. We only clip
-	 * where an edge is entirely outside the limits. If we wanted
-	 * to be more clever, we could handle cases where a trapezoid
-	 * edge intersects the edge of the limits, but that would
-	 * require slicing this trapezoid into multiple trapezoids,
-	 * and I'm not sure the effort would be worth it. */
-	if (top < traps->limits.p1.y)
-	    top = traps->limits.p1.y;
-
-	if (bottom > traps->limits.p2.y)
-	    bottom = traps->limits.p2.y;
-
-	if (left.p1.x <= traps->limits.p1.x &&
-	    left.p2.x <= traps->limits.p1.x)
-	{
-	    left.p1.x = traps->limits.p1.x;
-	    left.p1.y = traps->limits.p1.y;
-	    left.p2.x = traps->limits.p1.x;
-	    left.p2.y = traps->limits.p2.y;
-	}
-
-	if (right.p1.x >= traps->limits.p2.x &&
-	    right.p2.x >= traps->limits.p2.x)
-	{
-	    right.p1.x = traps->limits.p2.x;
-	    right.p1.y = traps->limits.p1.y;
-	    right.p2.x = traps->limits.p2.x;
-	    right.p2.y = traps->limits.p2.y;
-	}
-    }
-
     if (top == bottom)
 	return CAIRO_STATUS_SUCCESS;
 
     if (left.p1.x == right.p1.x)
 	return CAIRO_STATUS_SUCCESS;
 
-    _cairo_traps_add_trap (traps, top, bottom, &left, &right);
+    if (traps->num_limits) {
+	int n;
+
+	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.p2.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.p2.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;
+
+	    _cairo_traps_add_trap (traps, _top, _bottom, &_left, &_right);
+	}
+    } else {
+	_cairo_traps_add_trap (traps, top, bottom, &left, &right);
+    }
 
     return traps->status;
 }
@@ -493,12 +527,6 @@ _cairo_traps_extract_region (cairo_traps_t   *traps,
 	int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
 	int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
 
-	/* XXX: Sometimes we get degenerate trapezoids from the tesellator;
-	 * skip these.
-	 */
-	assert (x1 != x2);
-	assert (y1 != y2);
-
 	rects[rect_count].x = x1;
 	rects[rect_count].y = y1;
 	rects[rect_count].width = x2 - x1;
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index 1e1f823..54bd2a7 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -254,8 +254,8 @@ typedef struct _cairo_polygon {
     cairo_bool_t has_current_edge;
 
     cairo_box_t extents;
-    cairo_box_t limits;
-    cairo_bool_t has_limits;
+    const cairo_box_t *limits;
+    int num_limits;
 
     int num_edges;
     int edges_size;
diff --git a/src/cairoint.h b/src/cairoint.h
index 7312f7e..bbeb8e0 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -252,6 +252,11 @@ cairo_private void
 _cairo_box_round_to_rectangle (const cairo_box_t     *box,
 			       cairo_rectangle_int_t *rectangle);
 
+cairo_private void
+_cairo_boxes_get_extents (const cairo_box_t *boxes,
+			  int num_boxes,
+			  cairo_box_t *extents);
+
 static inline void
 _cairo_unbounded_rectangle_init (cairo_rectangle_int_t *rect)
 {
@@ -951,10 +956,10 @@ typedef struct _cairo_surface_attributes {
 typedef struct _cairo_traps {
     cairo_status_t status;
 
-    cairo_box_t limits;
+    const cairo_box_t *limits;
+    int num_limits;
 
     unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
-    unsigned int has_limits : 1;
     unsigned int has_intersections : 1;
     unsigned int is_rectilinear : 1;
 
@@ -2245,12 +2250,19 @@ _cairo_polygon_init (cairo_polygon_t *polygon);
 
 cairo_private void
 _cairo_polygon_limit (cairo_polygon_t	*polygon,
-		      const cairo_box_t *limits);
+		      const cairo_box_t *boxes,
+		      int		 num_boxes);
 
 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);
@@ -2345,11 +2357,13 @@ _cairo_traps_init (cairo_traps_t *traps);
 
 cairo_private void
 _cairo_traps_limit (cairo_traps_t	*traps,
-		    cairo_box_t		*limits);
+		    const cairo_box_t	*boxes,
+		    int			 num_boxes);
 
-cairo_private void
-_cairo_traps_init_box (cairo_traps_t *traps,
-		       const cairo_box_t   *box);
+cairo_private cairo_status_t
+_cairo_traps_init_boxes (cairo_traps_t	    *traps,
+			 const cairo_box_t    *boxes,
+			 int		     num_boxes);
 
 cairo_private void
 _cairo_traps_clear (cairo_traps_t *traps);
commit 2457c4bedef0447f7bff9b54dba96126010917ac
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 10 14:48:03 2009 +0100

    traps-as-spans
    
    Add an interface to spans that accepts trapezoids. This allows backends
    that have an efficient span-line interface but lack efficient handling
    of boxes (partly due to the current poor compositor interface) to
    redirect composite_trapezoids() to composite_polygon() and the
    span-renderer.

diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index 9f6f116..e29a567 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -75,6 +75,13 @@ struct _cairo_scan_converter {
     /* Destroy this scan converter. */
     cairo_destroy_func_t	destroy;
 
+    /* Add a single edge to the converter. */
+    cairo_status_t (*add_edge) (void		    *abstract_converter,
+				const cairo_point_t *p1,
+				const cairo_point_t *p2,
+				int top, int bottom,
+				int dir);
+
     /* Add a polygon (set of edges) to the converter. */
     cairo_status_t (*add_polygon) (void		    *abstract_converter,
 				   const cairo_polygon_t  *polygon);
@@ -133,4 +140,16 @@ _cairo_surface_composite_polygon (cairo_surface_t	*surface,
 				  cairo_polygon_t	*polygon,
 				  cairo_region_t	*clip_region);
 
+cairo_private cairo_status_t
+_cairo_surface_composite_trapezoids_as_polygon (cairo_surface_t	*surface,
+						cairo_operator_t	 op,
+						const cairo_pattern_t	*pattern,
+						cairo_antialias_t	antialias,
+						int src_x, int src_y,
+						int dst_x, int dst_y,
+						int width, int height,
+						cairo_trapezoid_t	*traps,
+						int num_traps,
+						cairo_region_t	*clip_region);
+
 #endif /* CAIRO_SPANS_PRIVATE_H */
diff --git a/src/cairo-spans.c b/src/cairo-spans.c
index 369cd58..1a51634 100644
--- a/src/cairo-spans.c
+++ b/src/cairo-spans.c
@@ -83,6 +83,71 @@ _cairo_surface_composite_polygon (cairo_surface_t	*surface,
     return status;
 }
 
+cairo_status_t
+_cairo_surface_composite_trapezoids_as_polygon (cairo_surface_t	*surface,
+						cairo_operator_t	 op,
+						const cairo_pattern_t	*pattern,
+						cairo_antialias_t	antialias,
+						int src_x, int src_y,
+						int dst_x, int dst_y,
+						int width, int height,
+						cairo_trapezoid_t	*traps,
+						int num_traps,
+						cairo_region_t	*clip_region)
+{
+    cairo_span_renderer_t *renderer;
+    cairo_scan_converter_t *converter;
+    cairo_composite_rectangles_t rects;
+    cairo_status_t status;
+
+    rects.src.x = src_x;
+    rects.src.y = src_y;
+    rects.dst.x = 0;
+    rects.dst.y = 0;
+    rects.mask.x = dst_x;
+    rects.mask.y = dst_y;
+    rects.width  = width;
+    rects.height = height;
+
+    converter = _create_scan_converter (CAIRO_FILL_RULE_WINDING,
+					antialias,
+					&rects);
+    status = converter->status;
+    if (unlikely (status))
+	goto CLEANUP_CONVERTER;
+
+    while (num_traps--) {
+	status = converter->add_edge (converter,
+				      &traps->left.p1, &traps->left.p2,
+				      traps->top, traps->bottom, 1);
+	if (unlikely (status))
+	    goto CLEANUP_CONVERTER;
+
+	status = converter->add_edge (converter,
+				      &traps->right.p1, &traps->right.p2,
+				      traps->top, traps->bottom, -1);
+	if (unlikely (status))
+	    goto CLEANUP_CONVERTER;
+
+	traps++;
+    }
+
+    renderer = _cairo_surface_create_span_renderer (op, pattern, surface,
+						    antialias, &rects,
+						    clip_region);
+    status = converter->generate (converter, renderer);
+    if (unlikely (status))
+	goto CLEANUP_RENDERER;
+
+    status = renderer->finish (renderer);
+
+ CLEANUP_RENDERER:
+    renderer->destroy (renderer);
+ CLEANUP_CONVERTER:
+    converter->destroy (converter);
+    return status;
+}
+
 static void
 _cairo_nil_destroy (void *abstract)
 {
@@ -99,6 +164,22 @@ _cairo_nil_scan_converter_add_polygon (void *abstract_converter,
 }
 
 static cairo_status_t
+_cairo_nil_scan_converter_add_edge (void *abstract_converter,
+				    const cairo_point_t *p1,
+				    const cairo_point_t *p2,
+				    int top, int bottom,
+				    int dir)
+{
+    (void) abstract_converter;
+    (void) p1;
+    (void) p2;
+    (void) top;
+    (void) bottom;
+    (void) dir;
+    return _cairo_scan_converter_status (abstract_converter);
+}
+
+static cairo_status_t
 _cairo_nil_scan_converter_generate (void *abstract_converter,
 				    cairo_span_renderer_t *renderer)
 {
@@ -123,6 +204,7 @@ _cairo_scan_converter_set_error (void *abstract_converter,
 	ASSERT_NOT_REACHED;
     if (converter->status == CAIRO_STATUS_SUCCESS) {
 	converter->add_polygon = _cairo_nil_scan_converter_add_polygon;
+	converter->add_edge = _cairo_nil_scan_converter_add_edge;
 	converter->generate = _cairo_nil_scan_converter_generate;
 	converter->status = error;
     }
diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index 75cda4f..29262c2 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -1685,7 +1685,7 @@ glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
 
     INPUT_TO_GRID_Y (edge->top, e.top);
     INPUT_TO_GRID_Y (edge->bottom, e.bottom);
-    if (e.top == e.bottom)
+    if (e.top >= e.bottom)
 	return GLITTER_STATUS_SUCCESS;
 
     /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
@@ -1915,6 +1915,30 @@ _cairo_tor_scan_converter_destroy (void *converter)
 }
 
 static cairo_status_t
+_cairo_tor_scan_converter_add_edge (void		*converter,
+				    const cairo_point_t *p1,
+				    const cairo_point_t *p2,
+				    int top, int bottom,
+				    int dir)
+{
+    cairo_tor_scan_converter_t *self = converter;
+    cairo_status_t status;
+    cairo_edge_t edge;
+
+    edge.line.p1 = *p1;
+    edge.line.p2 = *p2;
+    edge.top = top;
+    edge.bottom = bottom;
+    edge.dir = dir;
+
+    status = glitter_scan_converter_add_edge (self->converter, &edge);
+    if (unlikely (status))
+	return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
 _cairo_tor_scan_converter_add_polygon (void		*converter,
 				       const cairo_polygon_t *polygon)
 {
@@ -1968,6 +1992,7 @@ _cairo_tor_scan_converter_create (int			xmin,
     }
 
     self->base.destroy = _cairo_tor_scan_converter_destroy;
+    self->base.add_edge = _cairo_tor_scan_converter_add_edge;
     self->base.add_polygon = _cairo_tor_scan_converter_add_polygon;
     self->base.generate = _cairo_tor_scan_converter_generate;
 
commit 3023330706b1237b0fbd697d015cad9a23c250b7
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 10 10:57:41 2009 +0100

    [fill] Early check for empty path/polygon

diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 50cd218..0cad156 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -156,6 +156,9 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
     cairo_polygon_t polygon;
     cairo_status_t status;
 
+    if (path->is_empty_fill)
+	return CAIRO_STATUS_SUCCESS;
+
     _cairo_polygon_init (&polygon);
     if (traps->has_limits)
 	_cairo_polygon_limit (&polygon, &traps->limits);
@@ -163,7 +166,7 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
     status = _cairo_path_fixed_fill_to_polygon (path,
 						tolerance,
 						&polygon);
-    if (unlikely (status))
+    if (unlikely (status || polygon.num_edges == 0))
 	goto CLEANUP;
 
     if (path->is_rectilinear) {
commit 9ba37a85d2f6c033d68eb547be6c63382164519e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 10 10:57:01 2009 +0100

    [gstate] Discard trivial all-clipped regions
    
    Avoid assertion failures later that we have a valid region.

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index e17df67..8cb52ab 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -866,9 +866,10 @@ _cairo_gstate_copy_transformed_mask (cairo_gstate_t   *gstate,
 #define _gstate_get_clip(G, C) _cairo_clip_init_copy ((C), &(G)->clip)
 
 static cairo_bool_t
-_clipped (const cairo_gstate_t *gstate)
+_clipped (cairo_gstate_t *gstate)
 {
     cairo_rectangle_int_t extents;
+    cairo_region_t *clip_region;
 
     if (gstate->clip.all_clipped)
 	return TRUE;
@@ -883,9 +884,12 @@ _clipped (const cairo_gstate_t *gstate)
 	{
 	    return TRUE;
 	}
+
+	/* XXX consider applying a surface clip? */
     }
 
-    return FALSE;
+    /* perform a simple query to exclude trivial all-clipped cases */
+    return _cairo_clip_get_region (&gstate->clip, &clip_region) == CAIRO_INT_STATUS_NOTHING_TO_DO;
 }
 
 cairo_status_t
commit 85094c4eee4e50ec724bf1bb54ecff6f7c1014bf
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 24 07:06:32 2009 +0100

    [clip] Eliminate redundant clips
    
    First perform a simple geometric clip to catch the majority of cases where
    an unaligned clip has been set outside the operation extents that can be
    discarded without having to use an image surface.
    
    This causes a dramatic increase of over 13x for the poppler-bug-12266
    trace and little impact elsewhere for more sensible clippers.

diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
index 8a3e867..d045d12 100644
--- a/src/cairo-clip-private.h
+++ b/src/cairo-clip-private.h
@@ -79,7 +79,7 @@ cairo_private cairo_status_t
 _cairo_clip_init_rectangle (cairo_clip_t *clip,
 			    const cairo_rectangle_int_t *rect);
 
-cairo_private void
+cairo_private_no_warn cairo_clip_t *
 _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other);
 
 cairo_private cairo_status_t
@@ -90,6 +90,12 @@ _cairo_clip_init_copy_transformed (cairo_clip_t    *clip,
 cairo_private void
 _cairo_clip_reset (cairo_clip_t *clip);
 
+#define _cairo_clip_fini(clip) _cairo_clip_reset (clip)
+
+cairo_private cairo_status_t
+_cairo_clip_rectangle (cairo_clip_t       *clip,
+		       const cairo_rectangle_int_t *rectangle);
+
 cairo_private cairo_status_t
 _cairo_clip_clip (cairo_clip_t       *clip,
 		  const cairo_path_fixed_t *path,
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 7aa4d5e..3200511 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -205,21 +205,25 @@ _cairo_clip_set_all_clipped (cairo_clip_t *clip)
     }
 }
 
-/* XXX consider accepting a matrix, no users yet. */
-cairo_status_t
-_cairo_clip_init_rectangle (cairo_clip_t *clip,
-			    const cairo_rectangle_int_t *rect)
+static cairo_status_t
+_cairo_clip_intersect_rectangle (cairo_clip_t *clip,
+				 const cairo_rectangle_int_t *rect)
 {
     cairo_clip_path_t *clip_path;
     cairo_status_t status;
 
-    _cairo_clip_init (clip);
-    if (rect == NULL)
-	return CAIRO_STATUS_SUCCESS;
+    if (clip->path != NULL) {
+	cairo_box_t box;
 
-    if (rect->width == 0 || rect->height == 0) {
-	_cairo_clip_set_all_clipped (clip);
-	return CAIRO_STATUS_SUCCESS;
+	if (_cairo_path_fixed_is_box (&clip->path->path, &box)) {
+	    if (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_STATUS_SUCCESS;
+	    }
+	}
     }
 
     clip_path = _cairo_clip_path_create (clip);
@@ -257,16 +261,40 @@ _cairo_clip_init_rectangle (cairo_clip_t *clip,
     return CAIRO_STATUS_SUCCESS;
 }
 
-void
+/* XXX consider accepting a matrix, no users yet. */
+cairo_status_t
+_cairo_clip_init_rectangle (cairo_clip_t *clip,
+			    const cairo_rectangle_int_t *rect)
+{
+    _cairo_clip_init (clip);
+
+    if (rect == NULL)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (rect->width == 0 || rect->height == 0) {
+	_cairo_clip_set_all_clipped (clip);
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    return _cairo_clip_intersect_rectangle (clip, rect);
+}
+
+cairo_clip_t *
 _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
 {
     if (other != NULL) {
-	clip->all_clipped = other->all_clipped;
-	clip->path = _cairo_clip_path_reference (other->path);
+	if (other->path == NULL) {
+	    _cairo_clip_init (clip);
+	    clip = NULL;
+	} else {
+	    clip->all_clipped = other->all_clipped;
+	    clip->path = _cairo_clip_path_reference (other->path);
+	}
     } else {
-	clip->all_clipped = FALSE;
-	clip->path = NULL;
+	_cairo_clip_init (clip);
     }
+
+    return clip;
 }
 
 void
@@ -291,15 +319,13 @@ _cairo_clip_intersect_path (cairo_clip_t       *clip,
     cairo_rectangle_int_t extents;
 
     if (clip->path != NULL) {
-	if (_cairo_path_fixed_equal (&clip->path->path, path)) {
-	    if (clip->path->fill_rule == fill_rule) {
-		if (path->is_rectilinear ||
-		    (tolerance == clip->path->tolerance &&
-		     antialias == clip->path->antialias))
-		{
-		    return CAIRO_STATUS_SUCCESS;
-		}
-	    }
+	if (clip->path->fill_rule == fill_rule &&
+	    (path->is_rectilinear ||
+	     (tolerance == clip->path->tolerance &&
+	      antialias == clip->path->antialias)) &&
+	    _cairo_path_fixed_equal (&clip->path->path, path))
+	{
+	    return CAIRO_STATUS_SUCCESS;
 	}
     }
 
@@ -369,6 +395,32 @@ _cairo_clip_clip (cairo_clip_t       *clip,
 				       antialias);
 }
 
+cairo_status_t
+_cairo_clip_rectangle (cairo_clip_t       *clip,
+		       const cairo_rectangle_int_t *rectangle)
+{
+    if (clip->all_clipped)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (rectangle->width == 0 || rectangle->height == 0) {
+	_cairo_clip_set_all_clipped (clip);
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    /* if a smaller clip has already been set, ignore the new path */
+    if (clip->path != NULL) {
+	if (rectangle->x <= clip->path->extents.x &&
+	    rectangle->y <= clip->path->extents.x &&
+	    rectangle->x + rectangle->width >= clip->path->extents.x + clip->path->extents.width &&
+	    rectangle->y + rectangle->height >= clip->path->extents.y + clip->path->extents.height)
+	{
+	    return CAIRO_STATUS_SUCCESS;
+	}
+    }
+
+    return _cairo_clip_intersect_rectangle (clip, rectangle);
+}
+
 static cairo_status_t
 _cairo_clip_path_reapply_clip_path_transform (cairo_clip_t      *clip,
 					      cairo_clip_path_t *other_path,
@@ -548,6 +600,65 @@ _cairo_clip_apply_clip (cairo_clip_t *clip,
     return status;
 }
 
+#if 0
+static inline cairo_bool_t
+_clip_paths_are_rectilinear (cairo_clip_path_t *clip_path)
+{
+    while (clip_path != NULL) {
+	if (! clip_path->path.is_rectilinear)
+	    return FALSE;
+
+	clip_path = clip_path->prev;
+    }
+
+    return TRUE;
+}
+#endif
+
+static cairo_int_status_t
+_cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path)
+{
+    cairo_traps_t traps;
+    cairo_box_t box;
+    cairo_status_t status;
+
+    /* If we have nothing to intersect with this path, then it cannot
+     * magically be reduced into a region.
+     */
+    if (clip_path->prev == NULL)
+	goto UNSUPPORTED;
+
+    /* start simple... */
+    if (! clip_path->path.maybe_fill_region)
+	goto UNSUPPORTED;
+
+    _cairo_traps_init (&traps);
+    _cairo_box_from_rectangle (&box, &clip_path->extents);
+    _cairo_traps_limit (&traps, &box);
+
+    status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path,
+					      clip_path->prev->fill_rule,
+					      clip_path->prev->tolerance,
+					      &traps);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_traps_extract_region (&traps, &clip_path->region);
+    _cairo_traps_fini (&traps);
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+	goto UNSUPPORTED;
+    if (unlikely (status))
+	return status;
+
+    clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
+    return CAIRO_STATUS_SUCCESS;
+
+UNSUPPORTED:
+    clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
 static cairo_int_status_t
 _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
 {
@@ -563,16 +674,16 @@ _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
 	    CAIRO_STATUS_SUCCESS;
     }
 
-    if (! clip_path->path.maybe_fill_region) {
-	clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
+    if (! clip_path->path.maybe_fill_region)
+	return _cairo_clip_path_to_region_geometric (clip_path);
 
     /* first retrieve the region for our antecedents */
     if (clip_path->prev != NULL) {
 	status = _cairo_clip_path_to_region (clip_path->prev);
 	if (status) {
-	    clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
+	    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+		return _cairo_clip_path_to_region_geometric (clip_path);
+
 	    return status;
 	}
 
@@ -581,7 +692,6 @@ _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
 
     /* now extract the region for ourselves */
 
-    /* XXX just use the cheap search for now */
     clip_path->region =
 	_cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path,
 						      clip_path->fill_rule,
@@ -647,7 +757,7 @@ _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
 			       CAIRO_CONTENT_COLOR);
 
     status = _cairo_clip_get_region (clip, &clip_region);
-    if (_cairo_status_is_error (status))
+    if (unlikely (_cairo_status_is_error (status)))
 	goto BAIL;
 
     need_translate = clip_extents->x || clip_extents->y;
@@ -693,18 +803,18 @@ _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
     }
 
     while ((clip_path = clip_path->prev) != NULL) {
-	status = _cairo_clip_get_region (clip, &clip_region);
-	if (_cairo_status_is_error (status))
+	status = _cairo_clip_path_to_region (clip_path);
+	if (unlikely (_cairo_status_is_error (status)))
 	    goto BAIL;
 
 	if (status == CAIRO_STATUS_SUCCESS) {
-	    cairo_region_translate (clip_region,
+	    cairo_region_translate (clip_path->region,
 				    -clip_extents->x, -clip_extents->y);
 	    status = _cairo_surface_fill_region (surface,
 						 CAIRO_OPERATOR_IN,
 						 CAIRO_COLOR_WHITE,
-						 clip_region);
-	    cairo_region_translate (clip_region,
+						 clip_path->region);
+	    cairo_region_translate (clip_path->region,
 				    clip_extents->x, clip_extents->y);
 	    if (unlikely (status))
 		goto BAIL;
@@ -844,6 +954,11 @@ _cairo_clip_get_region (cairo_clip_t *clip,
     if (status)
 	return status;
 
+    if (cairo_region_is_empty (clip->path->region)) {
+	_cairo_clip_set_all_clipped (clip);
+	return CAIRO_INT_STATUS_NOTHING_TO_DO;
+    }
+
     *region = clip->path->region;
     return CAIRO_STATUS_SUCCESS;
 }
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 344c34c..e17df67 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -860,7 +860,10 @@ _cairo_gstate_copy_transformed_mask (cairo_gstate_t   *gstate,
 					    &gstate->ctm_inverse);
 }
 
-#define _gstate_get_clip(g) ((g)->clip.path ? &(g)->clip : NULL)
+/* We need to take a copy of the clip so that the lower layers may modify it
+ * by, perhaps, intersecting it with the operation extents and other paths.
+ */
+#define _gstate_get_clip(G, C) _cairo_clip_init_copy ((C), &(G)->clip)
 
 static cairo_bool_t
 _clipped (const cairo_gstate_t *gstate)
@@ -870,11 +873,12 @@ _clipped (const cairo_gstate_t *gstate)
     if (gstate->clip.all_clipped)
 	return TRUE;
 
-    if (gstate->clip.path == NULL)
-	return FALSE;
-
     if (_cairo_surface_get_extents (gstate->target, &extents)) {
-	if (! _cairo_rectangle_intersect (&extents,
+	if (extents.width == 0 || extents.height == 0)
+	    return TRUE;
+
+	if (gstate->clip.path != NULL &&
+	    ! _cairo_rectangle_intersect (&extents,
 					  &gstate->clip.path->extents))
 	{
 	    return TRUE;
@@ -888,6 +892,8 @@ cairo_status_t
 _cairo_gstate_paint (cairo_gstate_t *gstate)
 {
     cairo_pattern_union_t pattern;
+    cairo_clip_t clip;
+    cairo_status_t status;
 
     if (unlikely (gstate->source->status))
 	return gstate->source->status;
@@ -897,10 +903,13 @@ _cairo_gstate_paint (cairo_gstate_t *gstate)
 
     _cairo_gstate_copy_transformed_source (gstate, &pattern.base);
 
-    return _cairo_surface_paint (gstate->target,
-				 gstate->op,
-				 &pattern.base,
-				 _gstate_get_clip (gstate));
+    status = _cairo_surface_paint (gstate->target,
+				   gstate->op,
+				   &pattern.base,
+				   _gstate_get_clip (gstate, &clip));
+    _cairo_clip_fini (&clip);
+
+    return status;
 }
 
 cairo_status_t
@@ -908,6 +917,8 @@ _cairo_gstate_mask (cairo_gstate_t  *gstate,
 		    cairo_pattern_t *mask)
 {
     cairo_pattern_union_t source_pattern, mask_pattern;
+    cairo_clip_t clip;
+    cairo_status_t status;
 
     if (unlikely (mask->status))
 	return mask->status;
@@ -921,17 +932,22 @@ _cairo_gstate_mask (cairo_gstate_t  *gstate,
     _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
     _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask);
 
-    return _cairo_surface_mask (gstate->target,
-				gstate->op,
-				&source_pattern.base,
-				&mask_pattern.base,
-				_gstate_get_clip (gstate));
+    status = _cairo_surface_mask (gstate->target,
+				  gstate->op,
+				  &source_pattern.base,
+				  &mask_pattern.base,
+				  _gstate_get_clip (gstate, &clip));
+    _cairo_clip_fini (&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_clip_t clip;
+    cairo_status_t status;
 
     if (unlikely (gstate->source->status))
 	return gstate->source->status;
@@ -944,16 +960,19 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
 
     _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
 
-    return _cairo_surface_stroke (gstate->target,
-				  gstate->op,
-				  &source_pattern.base,
-				  path,
-				  &gstate->stroke_style,
-				  &gstate->ctm,
-				  &gstate->ctm_inverse,
-				  gstate->tolerance,
-				  gstate->antialias,
-				  _gstate_get_clip (gstate));
+    status = _cairo_surface_stroke (gstate->target,
+				    gstate->op,
+				    &source_pattern.base,
+				    path,
+				    &gstate->stroke_style,
+				    &gstate->ctm,
+				    &gstate->ctm_inverse,
+				    gstate->tolerance,
+				    gstate->antialias,
+				    _gstate_get_clip (gstate, &clip));
+    _cairo_clip_fini (&clip);
+
+    return status;
 }
 
 cairo_status_t
@@ -1018,6 +1037,8 @@ cairo_status_t
 _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
 {
     cairo_pattern_union_t pattern;
+    cairo_clip_t clip;
+    cairo_status_t status;
 
     if (unlikely (gstate->source->status))
 	return gstate->source->status;
@@ -1032,22 +1053,26 @@ _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
 	_cairo_pattern_init_solid (&pattern.solid,
 				   CAIRO_COLOR_TRANSPARENT,
 				   CAIRO_CONTENT_COLOR_ALPHA);
-	return _cairo_surface_paint (gstate->target,
-				     CAIRO_OPERATOR_CLEAR,
-				     &pattern.base,
-				     _gstate_get_clip (gstate));
+	status = _cairo_surface_paint (gstate->target,
+				       CAIRO_OPERATOR_CLEAR,
+				       &pattern.base,
+				       _gstate_get_clip (gstate, &clip));
+    } else {
+	_cairo_gstate_copy_transformed_source (gstate, &pattern.base);
+
+	status = _cairo_surface_fill (gstate->target,
+				      gstate->op,
+				      &pattern.base,
+				      path,
+				      gstate->fill_rule,
+				      gstate->tolerance,
+				      gstate->antialias,
+				      _gstate_get_clip (gstate, &clip));
     }
 
-    _cairo_gstate_copy_transformed_source (gstate, &pattern.base);
+    _cairo_clip_fini (&clip);
 
-    return _cairo_surface_fill (gstate->target,
-				gstate->op,
-				&pattern.base,
-				path,
-				gstate->fill_rule,
-				gstate->tolerance,
-				gstate->antialias,
-				_gstate_get_clip (gstate));
+    return status;
 }
 
 cairo_bool_t
@@ -1642,6 +1667,7 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
     cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
     cairo_text_cluster_t *transformed_clusters;
     cairo_status_t status;
+    cairo_clip_t clip;
 
     if (unlikely (gstate->source->status))
 	return gstate->source->status;
@@ -1711,7 +1737,7 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
 						  transformed_clusters, num_clusters,
 						  cluster_flags,
 						  gstate->scaled_font,
-						  _gstate_get_clip (gstate));
+						  _gstate_get_clip (gstate, &clip));
     }
     else
     {
@@ -1731,12 +1757,14 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
 					  CAIRO_FILL_RULE_WINDING,
 					  gstate->tolerance,
 					  gstate->scaled_font->options.antialias,
-					  _gstate_get_clip (gstate));
+					  _gstate_get_clip (gstate, &clip));
 	}
 
 	_cairo_path_fixed_fini (&path);
     }
 
+    _cairo_clip_fini (&clip);
+
 CLEANUP_GLYPHS:
     if (transformed_glyphs != stack_transformed_glyphs)
       cairo_glyph_free (transformed_glyphs);
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index b74bf0a..50cd218 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -157,6 +157,9 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
     cairo_status_t status;
 
     _cairo_polygon_init (&polygon);
+    if (traps->has_limits)
+	_cairo_polygon_limit (&polygon, &traps->limits);
+
     status = _cairo_path_fixed_fill_to_polygon (path,
 						tolerance,
 						&polygon);
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index bba26a7..cca6384 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -133,6 +133,9 @@ _create_composite_mask_pattern (cairo_surface_pattern_t       *mask_pattern,
 	status = _cairo_clip_get_region (clip, &clip_region);
 	assert (! _cairo_status_is_error (status));
 
+	/* The all-clipped state should never propagate this far. */
+	assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+
 	clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
 
 	if (clip_region && cairo_region_num_rectangles (clip_region) == 1)
@@ -349,23 +352,26 @@ _clip_and_composite_source (cairo_clip_t                  *clip,
     cairo_region_t *clip_region = NULL;
     cairo_status_t status;
 
-    /* Create a surface that is mask IN clip */
-    status = _create_composite_mask_pattern (&mask_pattern,
-					     clip,
-					     draw_func, draw_closure,
-					     dst, extents);
-    if (unlikely (status))
-	return status;
-
     if (clip != NULL) {
 	status = _cairo_clip_get_region (clip, &clip_region);
 	assert (! _cairo_status_is_error (status));
+	if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+	    return CAIRO_STATUS_SUCCESS;
 
 	/* a solitary clip rectangle is already accommodated by extents */
 	if (clip_region && cairo_region_num_rectangles (clip_region) == 1)
 	    clip_region = NULL;
     }
 
+
+    /* Create a surface that is mask IN clip */
+    status = _create_composite_mask_pattern (&mask_pattern,
+					     clip,
+					     draw_func, draw_closure,
+					     dst, extents);
+    if (unlikely (status))
+	return status;
+
     /* Compute dest' = dest OUT (mask IN clip) */
     status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT,
 				       &mask_pattern.base, NULL, dst,
@@ -454,6 +460,8 @@ _clip_and_composite (cairo_clip_t                  *clip,
 	if (clip != NULL) {
 	    status = _cairo_clip_get_region (clip, &clip_region);
 	    assert (! _cairo_status_is_error (status));
+	    if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+		return CAIRO_STATUS_SUCCESS;
 
 	    clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
 	}
@@ -670,9 +678,9 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
 	status = _cairo_clip_get_region (clip, &clip_region);
 	if (unlikely (_cairo_status_is_error (status)))
 	    return status;
+	if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+	    return CAIRO_STATUS_SUCCESS;
 
-	/* The all-clipped state should not have been propagated this far. */
-	assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
 	clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
@@ -785,18 +793,21 @@ _composite_spans_draw_func (void                          *closure,
 					     clip_region);
 }
 
-static cairo_bool_t
+static cairo_status_t
 _rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip)
 {
     if (clip != NULL) {
-	const cairo_rectangle_int_t *clip_extents;
+	if (! _cairo_rectangle_intersect (extents,
+					  _cairo_clip_get_extents (clip)))
+	{
+	    return CAIRO_INT_STATUS_NOTHING_TO_DO;
+	}
 
-	clip_extents = _cairo_clip_get_extents (clip);
-	if (clip_extents != NULL)
-	    return _cairo_rectangle_intersect (extents, clip_extents);
-    }
+	return _cairo_clip_rectangle (clip, extents);
+    } else if (_cairo_rectangle_empty (extents))
+	return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
-    return ! _cairo_rectangle_empty (extents);
+    return CAIRO_STATUS_SUCCESS;
 }
 
 cairo_status_t
@@ -824,17 +835,21 @@ _cairo_surface_fallback_paint (cairo_surface_t		*surface,
 	    return CAIRO_STATUS_SUCCESS;
     }
 
-    if (! _rectangle_intersect_clip (&extents, clip))
-	return CAIRO_STATUS_SUCCESS;
+    status = _rectangle_intersect_clip (&extents, clip);
+    if (status) {
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+	    status = CAIRO_STATUS_SUCCESS;
+	return status;
+    }
 
     /* avoid the palaver of constructing traps for a simple region */
     if (clip != NULL) {
 	status = _cairo_clip_get_region (clip, &clip_region);
 	if (unlikely (_cairo_status_is_error (status)))
 	    return status;
+	if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+	    return CAIRO_STATUS_SUCCESS;
 
-	/* The all-clipped state should not have been propagated this far. */
-	assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
 	clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
@@ -929,6 +944,7 @@ _cairo_surface_fallback_mask (cairo_surface_t		*surface,
 {
     cairo_rectangle_int_t extents;
     cairo_bool_t is_bounded;
+    cairo_status_t status;
 
     is_bounded = _cairo_surface_get_extents (surface, &extents);
     assert (is_bounded || clip);
@@ -949,8 +965,12 @@ _cairo_surface_fallback_mask (cairo_surface_t		*surface,
 	    return CAIRO_STATUS_SUCCESS;
     }
 
-    if (! _rectangle_intersect_clip (&extents, clip))
-	return CAIRO_STATUS_SUCCESS;
+    status = _rectangle_intersect_clip (&extents, clip);
+    if (status) {
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+	    status = CAIRO_STATUS_SUCCESS;
+	return status;
+    }
 
     return _clip_and_composite (clip, op, source,
 				_cairo_surface_mask_draw_func,
@@ -988,8 +1008,22 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
 	    return CAIRO_STATUS_SUCCESS;
     }
 
-    if (! _rectangle_intersect_clip (&extents, clip))
-	return CAIRO_STATUS_SUCCESS;
+    if (_cairo_operator_bounded_by_mask (op)) {
+	cairo_rectangle_int_t path_extents;
+
+	_cairo_path_fixed_approximate_stroke_extents (path,
+						      stroke_style, ctm,
+						      &path_extents);
+	if (! _cairo_rectangle_intersect (&extents, &path_extents))
+	    return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _rectangle_intersect_clip (&extents, clip);
+    if (status) {
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+	    status = CAIRO_STATUS_SUCCESS;
+	return status;
+    }
 
     _cairo_box_from_rectangle (&box, &extents);
 
@@ -1092,8 +1126,20 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
 	    return CAIRO_STATUS_SUCCESS;
     }
 
-    if (! _rectangle_intersect_clip (&extents, clip))
-	return CAIRO_STATUS_SUCCESS;
+    if (_cairo_operator_bounded_by_mask (op)) {
+	cairo_rectangle_int_t path_extents;
+
+	_cairo_path_fixed_approximate_fill_extents (path, &path_extents);
+	if (! _cairo_rectangle_intersect (&extents, &path_extents))
+	    return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _rectangle_intersect_clip (&extents, clip);
+    if (status) {
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+	    status = CAIRO_STATUS_SUCCESS;
+	return status;
+    }
 
     /* avoid the palaver of constructing traps for a simple region */
     if (path->maybe_fill_region) {
@@ -1104,8 +1150,9 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
 	    status = _cairo_clip_get_region (clip, &clip_region);
 	    if (unlikely (_cairo_status_is_error (status)))
 		return status;
+	    if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+		return CAIRO_STATUS_SUCCESS;
 
-	    assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
 	    clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
 	}
 
@@ -1310,8 +1357,12 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t		*surface,
 	    return CAIRO_STATUS_SUCCESS;
     }
 
-    if (! _rectangle_intersect_clip (&extents, clip))
-	return CAIRO_STATUS_SUCCESS;
+    status = _rectangle_intersect_clip (&extents, clip);
+    if (status) {
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+	    status = CAIRO_STATUS_SUCCESS;
+	return status;
+    }
 
     glyph_info.font = scaled_font;
     glyph_info.glyphs = glyphs;
commit ff0ca6d02a2e8901e9cfca31326c3fdc16e77e2f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 5 17:54:48 2009 +0100

    [test] Stress the intersection handling by feeding spans/traps random curves
    
    Another stress test for the fill/stroke and intersection finders.

diff --git a/test/Makefile.am b/test/Makefile.am
index 9b5ee2a..ed72fc6 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -742,6 +742,16 @@ REFERENCE_IMAGES = \
 	random-intersections-nonzero.ref.png \
 	random-intersections-nonzero.ps.ref.png \
 	random-intersections-nonzero.xlib.ref.png \
+	random-intersections-curves-eo.ps.ref.png \
+	random-intersections-curves-eo.pdf.ref.png \
+	random-intersections-curves-eo.ref.png \
+	random-intersections-curves-eo.xlib.ref.png \
+	random-intersections-curves-eo.xlib-fallback.ref.png \
+	random-intersections-curves-nz.ps.ref.png \
+	random-intersections-curves-nz.pdf.ref.png \
+	random-intersections-curves-nz.ref.png \
+	random-intersections-curves-nz.xlib.ref.png \
+	random-intersections-curves-nz.xlib-fallback.ref.png \
 	rectangle-rounding-error.ref.png \
 	rectilinear-dash.ref.png \
 	rectilinear-fill.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index 0a58ac0..5494fe9 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -152,6 +152,8 @@ test_sources = \
 	radial-gradient.c				\
 	random-intersections-eo.c			\
 	random-intersections-nonzero.c			\
+	random-intersections-curves-eo.c		\
+	random-intersections-curves-nz.c		\
 	rectangle-rounding-error.c			\
 	rectilinear-fill.c				\
 	rectilinear-miter-limit.c			\
diff --git a/test/random-intersections-curves-eo.c b/test/random-intersections-curves-eo.c
new file mode 100644
index 0000000..3fa12b2
--- /dev/null
+++ b/test/random-intersections-curves-eo.c
@@ -0,0 +1,81 @@
+/*
+ * 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 at 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++) {
+	cairo_curve_to (cr,
+			uniform_random (-SIZE, SIZE),
+			uniform_random (-SIZE, SIZE),
+			uniform_random (-SIZE, SIZE),
+			uniform_random (-SIZE, SIZE),
+			uniform_random (0, SIZE),
+			uniform_random (0, SIZE));
+    }
+    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-eo.pdf.ref.png b/test/random-intersections-curves-eo.pdf.ref.png
new file mode 100644
index 0000000..befa3c8
Binary files /dev/null and b/test/random-intersections-curves-eo.pdf.ref.png differ
diff --git a/test/random-intersections-curves-eo.ps.ref.png b/test/random-intersections-curves-eo.ps.ref.png
new file mode 100644
index 0000000..3acd736
Binary files /dev/null and b/test/random-intersections-curves-eo.ps.ref.png differ
diff --git a/test/random-intersections-curves-eo.ref.png b/test/random-intersections-curves-eo.ref.png
new file mode 100644
index 0000000..ed24e8a
Binary files /dev/null and b/test/random-intersections-curves-eo.ref.png differ
diff --git a/test/random-intersections-curves-eo.xlib-fallback.ref.png b/test/random-intersections-curves-eo.xlib-fallback.ref.png
new file mode 100644
index 0000000..d5f48f1
Binary files /dev/null and b/test/random-intersections-curves-eo.xlib-fallback.ref.png differ
diff --git a/test/random-intersections-curves-eo.xlib.ref.png b/test/random-intersections-curves-eo.xlib.ref.png
new file mode 100644
index 0000000..05fc922
Binary files /dev/null and b/test/random-intersections-curves-eo.xlib.ref.png differ
diff --git a/test/random-intersections-curves-nz.c b/test/random-intersections-curves-nz.c
new file mode 100644
index 0000000..0109817
--- /dev/null
+++ b/test/random-intersections-curves-nz.c
@@ -0,0 +1,82 @@
+/*
+ * 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 at 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++) {
+	cairo_curve_to (cr,
+			uniform_random (-SIZE, SIZE),
+			uniform_random (-SIZE, SIZE),
+			uniform_random (-SIZE, SIZE),
+			uniform_random (-SIZE, SIZE),
+			uniform_random (0, SIZE),
+			uniform_random (0, SIZE));
+    }
+    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-curves-nz.pdf.ref.png b/test/random-intersections-curves-nz.pdf.ref.png
new file mode 100644
index 0000000..a374934
Binary files /dev/null and b/test/random-intersections-curves-nz.pdf.ref.png differ
diff --git a/test/random-intersections-curves-nz.ps.ref.png b/test/random-intersections-curves-nz.ps.ref.png
new file mode 100644
index 0000000..14b7c0d
Binary files /dev/null and b/test/random-intersections-curves-nz.ps.ref.png differ
diff --git a/test/random-intersections-curves-nz.ref.png b/test/random-intersections-curves-nz.ref.png
new file mode 100644
index 0000000..3b36e5e
Binary files /dev/null and b/test/random-intersections-curves-nz.ref.png differ
diff --git a/test/random-intersections-curves-nz.xlib-fallback.ref.png b/test/random-intersections-curves-nz.xlib-fallback.ref.png
new file mode 100644
index 0000000..499eb9e
Binary files /dev/null and b/test/random-intersections-curves-nz.xlib-fallback.ref.png differ
diff --git a/test/random-intersections-curves-nz.xlib.ref.png b/test/random-intersections-curves-nz.xlib.ref.png
new file mode 100644
index 0000000..729bf65
Binary files /dev/null and b/test/random-intersections-curves-nz.xlib.ref.png differ
commit 6dfe050d6360409161418d853df3d7d7dfb216af
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 4 15:32:25 2009 +0100

    [polygon] Amalgamate collinear edges
    
    Combine sequential collinear edges into a single edge, this benefits
    immensely by feeding fewer edges into either the tessellator or spans.

diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
index ac4d094..a8addff 100644
--- a/src/cairo-polygon.c
+++ b/src/cairo-polygon.c
@@ -49,6 +49,7 @@ _cairo_polygon_init (cairo_polygon_t *polygon)
     polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
 
     polygon->has_current_point = FALSE;
+    polygon->has_current_edge = FALSE;
     polygon->has_limits = FALSE;
 
     polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
@@ -357,6 +358,13 @@ void
 _cairo_polygon_move_to (cairo_polygon_t *polygon,
 			const cairo_point_t *point)
 {
+    if (polygon->has_current_edge) {
+	_cairo_polygon_add_edge (polygon,
+				 &polygon->last_point,
+				 &polygon->current_point);
+	polygon->has_current_edge = FALSE;
+    }
+
     if (! polygon->has_current_point) {
 	polygon->first_point = *point;
 	polygon->has_current_point = TRUE;
@@ -369,20 +377,56 @@ void
 _cairo_polygon_line_to (cairo_polygon_t *polygon,
 			const cairo_point_t *point)
 {
-    if (polygon->has_current_point)
-	_cairo_polygon_add_edge (polygon, &polygon->current_point, point);
+    /* squash collinear edges */
+    if (polygon->has_current_edge) {
+	if (polygon->current_point.x != point->x ||
+	    polygon->current_point.y != point->y)
+	{
+	    cairo_slope_t this;
+
+	    _cairo_slope_init (&this, &polygon->current_point, point);
+	    if (_cairo_slope_equal (&polygon->current_edge, &this)) {
+		polygon->current_point = *point;
+		return;
+	    }
+
+	    _cairo_polygon_add_edge (polygon,
+				     &polygon->last_point,
+				     &polygon->current_point);
+
+	    polygon->last_point = polygon->current_point;
+	    polygon->current_edge = this;
+	}
+    } else if (polygon->has_current_point) {
+	if (polygon->current_point.x != point->x ||
+	    polygon->current_point.y != point->y)
+	{
+	    polygon->last_point = polygon->current_point;
+	    _cairo_slope_init (&polygon->current_edge,
+			       &polygon->last_point,
+			       point);
+	    polygon->has_current_edge = TRUE;
+	}
+    } else {
+	polygon->first_point = *point;
+	polygon->has_current_point = TRUE;
+    }
 
-    _cairo_polygon_move_to (polygon, point);
+    polygon->current_point = *point;
 }
 
 void
 _cairo_polygon_close (cairo_polygon_t *polygon)
 {
     if (polygon->has_current_point) {
-	_cairo_polygon_add_edge (polygon,
-				 &polygon->current_point,
-				 &polygon->first_point);
-
+	_cairo_polygon_line_to (polygon, &polygon->first_point);
 	polygon->has_current_point = FALSE;
     }
+
+    if (polygon->has_current_edge) {
+	_cairo_polygon_add_edge (polygon,
+				 &polygon->last_point,
+				 &polygon->current_point);
+	polygon->has_current_edge = FALSE;
+    }
 }
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index fa70678..1e1f823 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -247,8 +247,11 @@ typedef struct _cairo_polygon {
     cairo_status_t status;
 
     cairo_point_t first_point;
+    cairo_point_t last_point;
     cairo_point_t current_point;
+    cairo_slope_t current_edge;
     cairo_bool_t has_current_point;
+    cairo_bool_t has_current_edge;
 
     cairo_box_t extents;
     cairo_box_t limits;
commit dc886450ac7c04252cff77729e0653c6ea2768db
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 4 13:14:38 2009 +0100

    [util] Show total edge length in show-edges

diff --git a/util/show-edges.c b/util/show-edges.c
index d3b94bc..a85ad5f 100644
--- a/util/show-edges.c
+++ b/util/show-edges.c
@@ -586,6 +586,23 @@ trap_view_draw (TrapView *self, cairo_t *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)
 {
@@ -646,26 +663,41 @@ trap_view_draw_labels (TrapView *self, cairo_t *cr)
 {
     PangoLayout *layout;
     gint width, height;
-    gdouble total_area;
+    GString *string;
     gchar *str;
     traps_t *traps;
+    edges_t *edges;
+
+    string = g_string_new (NULL);
 
     traps = self->current_traps;
-    if (traps == NULL)
-	return;
+    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);
+    }
 
-    /* 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);
+    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, 10 + height);
+    cairo_move_to (cr, 10, 40);
     pango_cairo_show_layout (cr, layout);
     g_object_unref (layout);
 }
@@ -1179,7 +1211,7 @@ main (int argc, char **argv)
     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_widget_set_size_request (window, 800, 800);
     gtk_container_add (GTK_CONTAINER (window), hbox);
     gtk_widget_show (hbox);
     gtk_widget_show (window);
commit 0f8af054841c2d6dfe4bfeb3d13a7bab0cfbe2f3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 3 08:27:01 2009 +0100

    [fallback] Avoid tessellating empty polygons
    
    I added an assert inside the tessellator to ensure that empty polygon were
    not being propagated that far...

diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index b97eb19..bba26a7 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -663,8 +663,8 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
     cairo_bool_t clip_surface = FALSE;
     cairo_status_t status;
 
-    if (_cairo_operator_bounded_by_mask (op) && traps->num_traps == 0)
-        return CAIRO_STATUS_SUCCESS;
+    if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op))
+	return CAIRO_STATUS_SUCCESS;
 
     if (clip != NULL) {
 	status = _cairo_clip_get_region (clip, &clip_region);
@@ -1019,6 +1019,9 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
     if (unlikely (status))
 	goto CLEANUP;
 
+    if (polygon.num_edges == 0)
+	goto DO_TRAPS;
+
     if (_cairo_operator_bounded_by_mask (op)) {
 	cairo_rectangle_int_t polygon_extents;
 
@@ -1161,6 +1164,9 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
     if (unlikely (status))
 	goto CLEANUP;
 
+    if (polygon.num_edges == 0)
+	goto DO_TRAPS;
+
     if (_cairo_operator_bounded_by_mask (op)) {
 	cairo_rectangle_int_t polygon_extents;
 
commit 09377a716334df3683912747067cd396768cfab6
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 2 13:09:30 2009 +0100

    [freelist] Lazy initialisation of pools

diff --git a/src/cairo-freelist-private.h b/src/cairo-freelist-private.h
index 8f9f153..d48a720 100644
--- a/src/cairo-freelist-private.h
+++ b/src/cairo-freelist-private.h
@@ -44,11 +44,19 @@ typedef struct _cairo_freelist {
     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_node_t *pools;
+    cairo_freelist_pool_t *pools;
     unsigned nodesize;
-    char embedded_pool[1000];
+    cairo_freelist_pool_t embedded_pool;
+    uint8_t embedded_data[1000];
 } cairo_freepool_t;
 
 
@@ -92,13 +100,30 @@ 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 (unlikely (node == NULL))
-	return _cairo_freepool_alloc_from_new_pool (freepool);
+	return _cairo_freepool_alloc_from_pool (freepool);
 
     VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
     freepool->first_free_node = node->next;
diff --git a/src/cairo-freelist.c b/src/cairo-freelist.c
index 6bbee84..6277f90 100644
--- a/src/cairo-freelist.c
+++ b/src/cairo-freelist.c
@@ -87,40 +87,27 @@ _cairo_freelist_free (cairo_freelist_t *freelist, void *voidnode)
 void
 _cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize)
 {
-    int poolsize;
-    char *ptr;
-
     freepool->first_free_node = NULL;
-    freepool->pools = NULL;
+    freepool->pools = &freepool->embedded_pool;
     freepool->nodesize = nodesize;
 
-    poolsize = sizeof (freepool->embedded_pool);
-    ptr = freepool->embedded_pool + poolsize - freepool->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;
 
-    poolsize /= freepool->nodesize;
-    while (poolsize--) {
-	cairo_freelist_node_t *node = (cairo_freelist_node_t *) ptr;
-	ptr -= freepool->nodesize;
-
-	node->next = freepool->first_free_node;
-	freepool->first_free_node = node;
-    }
-    VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->embedded_pool,
-				    sizeof (freepool->embedded_pool)));
+    VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->embedded_data,
+				    sizeof (freepool->embedded_data)));
 }
 
 void
 _cairo_freepool_fini (cairo_freepool_t *freepool)
 {
-    cairo_freelist_node_t *node = freepool->pools;
-    while (node != NULL) {
-	cairo_freelist_node_t *next;
-
-	VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
-	next = node->next;
-
-	free (node);
-	node = next;
+    cairo_freelist_pool_t *pool = freepool->pools;
+    while (pool != &freepool->embedded_pool) {
+	cairo_freelist_pool_t *next = pool->next;
+	free (pool);
+	pool = next;
     }
     VG (VALGRIND_MAKE_MEM_NOACCESS (freepool, sizeof (freepool)));
 }
@@ -128,32 +115,26 @@ _cairo_freepool_fini (cairo_freepool_t *freepool)
 void *
 _cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool)
 {
-    cairo_freelist_node_t *node;
-    char *ptr;
+    cairo_freelist_pool_t *pool;
     int poolsize;
 
-    poolsize = (128 * freepool->nodesize + 8191) & -8192;
-    node = malloc (poolsize);
-    if (unlikely (node == NULL))
-	return node;
+    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;
 
-    node->next = freepool->pools;
-    freepool->pools = node;
+    pool->next = freepool->pools;
+    freepool->pools = pool;
 
-    ptr = (char *) node + poolsize - freepool->nodesize;
+    pool->size = poolsize;
+    pool->rem = poolsize - freepool->nodesize;
+    pool->data = (uint8_t *) (pool + 1) + freepool->nodesize;
 
-    poolsize -= sizeof (cairo_freelist_node_t);
-    poolsize /= freepool->nodesize;
-
-    while (--poolsize) {
-	node = (cairo_freelist_node_t *) ptr;
-	ptr -= freepool->nodesize;
-
-	node->next = freepool->first_free_node;
-	freepool->first_free_node = node;
-    }
-    VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->pools,
-				    (128 * freepool->nodesize + 8191) & -8192));
+    VG (VALGRIND_MAKE_MEM_NOACCESS (pool->data, poolsize));
+    VG (VALGRIND_MAKE_MEM_UNDEFINED (pool->data, freepool->nodesize));
 
-    return ptr;
+    return pool + 1;
 }
commit 6f0340e2e5079eba597c0a3a7d39da21cf2b5e7a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jul 29 21:47:11 2009 +0100

    [clip] Use the rectilinear tessellator
    
    We can ensure that we always produce a clip region when possible by using
    the rectilinear tessellator to convert complex, device-aligned polygons to
    regions. Prior to using the tessellator, we relied on pixman's region code
    which could only handle a union of rectangles.

diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 0d85612..7aa4d5e 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -586,16 +586,11 @@ _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
 	_cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path,
 						      clip_path->fill_rule,
 						      &clip_path->extents);
-    if (clip_path->region == NULL) {
-	clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
+    assert (clip_path->region != NULL);
 
     status = clip_path->region->status;
-    if (unlikely (status)) {
-	clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
+    if (unlikely (status))
 	return status;
-    }
 
     if (prev != NULL) {
 	status = cairo_region_intersect (clip_path->region, prev);
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 5b82a40..b74bf0a 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -36,6 +36,7 @@
 
 #include "cairoint.h"
 #include "cairo-path-fixed-private.h"
+#include "cairo-region-private.h"
 
 typedef struct cairo_filler {
     double tolerance;
@@ -177,12 +178,61 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
     return status;
 }
 
+static cairo_region_t *
+_cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_t	*path,
+							 cairo_fill_rule_t	 fill_rule,
+							 const cairo_rectangle_int_t *extents)
+{
+    cairo_polygon_t polygon;
+    cairo_traps_t traps;
+    cairo_status_t status;
+    cairo_region_t *region;
+
+    _cairo_polygon_init (&polygon);
+    if (extents != NULL) {
+	cairo_box_t box;
+
+	_cairo_box_from_rectangle (&box, extents);
+	_cairo_polygon_limit (&polygon, &box);
+    }
+
+    /* tolerance will be ignored as the path is rectilinear */
+    status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
+    if (unlikely (status))
+	goto CLEANUP_POLYGON;
+
+    if (polygon.num_edges == 0) {
+	region = cairo_region_create ();
+    } else {
+	_cairo_traps_init (&traps);
+
+	status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
+									&polygon,
+									fill_rule);
+	if (likely (status == CAIRO_STATUS_SUCCESS))
+	    status = _cairo_traps_extract_region (&traps, &region);
+
+	_cairo_traps_fini (&traps);
+    }
+
+  CLEANUP_POLYGON:
+    _cairo_polygon_fini (&polygon);
+
+    if (unlikely (status)) { /* XXX _cairo_region_create_in_error() */
+	region = cairo_region_create ();
+	if (likely (region->status) == CAIRO_STATUS_SUCCESS)
+	    region->status = status;
+    }
+
+    return region;
+}
+
 /* This special-case filler supports only a path that describes a
  * device-axis aligned rectangle. It exists to avoid the overhead of
  * the general tessellator when drawing very common rectangles.
  *
  * If the path described anything but a device-axis aligned rectangle,
- * this function will return %CAIRO_INT_STATUS_UNSUPPORTED.
+ * this function will abort.
  */
 cairo_region_t *
 _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
@@ -212,10 +262,6 @@ _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
 	    box.p2.y = t;
 	}
 
-	assert (_cairo_fixed_is_integer (box.p1.x) &&
-		_cairo_fixed_is_integer (box.p1.y));
-	assert (_cairo_fixed_is_integer (box.p2.x) &&
-		_cairo_fixed_is_integer (box.p2.y));
 	rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x);
 	rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y);
 	rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) -
@@ -260,13 +306,10 @@ _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
 		cw = ! cw;
 	    }
 
-	    if (last_cw < 0) {
+	    if (last_cw < 0)
 		last_cw = cw;
-	    } else if (last_cw != cw) {
-		if (rects != rectangle_stack)
-		    free (rects);
-		return NULL;
-	    }
+	    else if (last_cw != cw)
+		goto TESSELLATE;
 
 	    if (count == size) {
 		cairo_rectangle_int_t *new_rects;
@@ -291,10 +334,6 @@ _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
 		rects = new_rects;
 	    }
 
-	    assert (_cairo_fixed_is_integer (box.p1.x) &&
-		    _cairo_fixed_is_integer (box.p1.y));
-	    assert (_cairo_fixed_is_integer (box.p2.x) &&
-		    _cairo_fixed_is_integer (box.p2.y));
 	    rects[count].x = _cairo_fixed_integer_part (box.p1.x);
 	    rects[count].y = _cairo_fixed_integer_part (box.p1.y);
 	    rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x;
@@ -306,9 +345,19 @@ _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
 	if (_cairo_path_fixed_iter_at_end (&iter))
 	    region = cairo_region_create_rectangles (rects, count);
 
+TESSELLATE:
 	if (rects != rectangle_stack)
 	    free (rects);
     }
 
+    if (region == NULL) {
+	/* Hmm, complex polygon */
+	region = _cairo_path_fixed_fill_rectilinear_tessellate_to_region (path,
+									  fill_rule,
+									  extents);
+
+
+    }
+
     return region;
 }
commit e3820bef20fdd77ab804b9832f47dc286e4887c4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 2 15:17:51 2009 +0100

    [fill] Short-circuit extents on an empty path.
    
    If the path is empty, avoid redundant polygonisation and tessellation by
    simply returning the empty extents.

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 3508b5e..344c34c 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -1203,6 +1203,18 @@ _cairo_gstate_fill_extents (cairo_gstate_t     *gstate,
     cairo_status_t status;
     cairo_traps_t traps;
 
+    if (path->is_empty_fill) {
+	if (x1)
+	    *x1 = 0.0;
+	if (y1)
+	    *y1 = 0.0;
+	if (x2)
+	    *x2 = 0.0;
+	if (y2)
+	    *y2 = 0.0;
+	return CAIRO_STATUS_SUCCESS;
+    }
+
     _cairo_traps_init (&traps);
 
     status = _cairo_path_fixed_fill_to_traps (path,
commit 4051ed328b618e28cf1df276899eefa225225c76
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 25 20:51:06 2009 +0100

    [tessellator] Special case rectilinear tessellation
    
    For the frequent cases where we know in advance that we are dealing with a
    rectilinear path, but can not use the simple region code, implement a
    variant of the Bentley-Ottmann tessellator. The advantages here are that
    edge comparison is very simple (we only have vertical edges) and there are
    no intersection, though possible overlaps. The idea is the same, maintain
    a y-x sorted queue of start/stop events that demarcate traps and sweep
    through the active edges at each event, looking for completed traps.
    
    The motivation for this was noticing a performance regression in
    box-fill-outline with the self-intersection work:
    
      1.9.2 to HEAD^: 3.66x slowdown
      HEAD^ to HEAD:  5.38x speedup
      1.9.2 to HEAD:  1.57x speedup
    
    The cause of which was choosing to use spans instead of the region handling
    code, as the complex polygon was no longer being tessellated.

diff --git a/src/Makefile.sources b/src/Makefile.sources
index b8404e3..0e7b54b 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -99,6 +99,7 @@ cairo_sources = \
 	cairo-atomic.c \
 	cairo-base85-stream.c \
 	cairo-bentley-ottmann.c \
+	cairo-bentley-ottmann-rectilinear.c \
 	cairo.c \
 	cairo-cache.c \
 	cairo-clip.c \
diff --git a/src/cairo-bentley-ottmann-rectilinear.c b/src/cairo-bentley-ottmann-rectilinear.c
new file mode 100644
index 0000000..c7e738b
--- /dev/null
+++ b/src/cairo-bentley-ottmann-rectilinear.c
@@ -0,0 +1,582 @@
+/*
+ * 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., 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 Carl Worth
+ *
+ * Contributor(s):
+ *	Carl D. Worth <cworth at cworth.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-combsort-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_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);
+    }
+
+    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;
+
+    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, traps);
+	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_traps_t	        *traps)
+{
+    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, traps);
+		    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, traps);
+	    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, traps);
+		    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, traps);
+	    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_traps_t	*traps)
+{
+    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, traps);
+	    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,
+						  traps);
+		if (unlikely (status))
+		    return status;
+	    }
+
+	    break;
+	}
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+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_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, traps);
+    if (events != stack_events)
+	free (events);
+
+    traps->is_rectilinear = TRUE;
+
+    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,
+							    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
index 3d3a3d6..99f4655 100644
--- a/src/cairo-bentley-ottmann.c
+++ b/src/cairo-bentley-ottmann.c
@@ -2002,7 +2002,8 @@ _compute_clipped_trapezoid_edges (const cairo_traps_t *traps,
 }
 
 cairo_status_t
-_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps)
+_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps,
+					 cairo_fill_rule_t fill_rule)
 {
     int intersections;
     cairo_status_t status;
@@ -2054,7 +2055,7 @@ _cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps)
     _cairo_traps_clear (traps);
     status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs,
 							 num_events,
-							 CAIRO_FILL_RULE_WINDING,
+							 fill_rule,
 							 traps,
 							 &intersections);
 
diff --git a/src/cairo-combsort-private.h b/src/cairo-combsort-private.h
index d2fbb72..ce31257 100644
--- a/src/cairo-combsort-private.h
+++ b/src/cairo-combsort-private.h
@@ -56,7 +56,7 @@ NAME (TYPE *base, unsigned int nmemb) \
   int swapped; \
   do { \
       gap = _cairo_combsort_newgap (gap); \
-      swapped = 0; \
+      swapped = gap > 1; \
       for (i = 0; i < nmemb-gap ; i++) { \
 	  j = i + gap; \
 	  if (CMP (base[i], base[j]) > 0 ) { \
@@ -67,5 +67,5 @@ NAME (TYPE *base, unsigned int nmemb) \
 	      swapped = 1; \
 	  } \
       } \
-  } while (gap > 1 || swapped); \
+  } while (swapped); \
 }
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 46148cf..5b82a40 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -155,14 +155,6 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
     cairo_polygon_t polygon;
     cairo_status_t status;
 
-    if (path->is_rectilinear) {
-	status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
-							      fill_rule,
-							      traps);
-	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
-	    return status;
-    }
-
     _cairo_polygon_init (&polygon);
     status = _cairo_path_fixed_fill_to_polygon (path,
 						tolerance,
@@ -170,15 +162,21 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
     if (unlikely (status))
 	goto CLEANUP;
 
-    status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
-							fill_rule);
+    if (path->is_rectilinear) {
+	status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (traps,
+									&polygon,
+									fill_rule);
+    } else {
+	status = _cairo_bentley_ottmann_tessellate_polygon (traps,
+							    &polygon,
+							    fill_rule);
+    }
 
   CLEANUP:
     _cairo_polygon_fini (&polygon);
     return status;
 }
 
-
 /* This special-case filler supports only a path that describes a
  * device-axis aligned rectangle. It exists to avoid the overhead of
  * the general tessellator when drawing very common rectangles.
@@ -186,86 +184,6 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
  * If the path described anything but a device-axis aligned rectangle,
  * this function will return %CAIRO_INT_STATUS_UNSUPPORTED.
  */
-cairo_int_status_t
-_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t	*path,
-					     cairo_fill_rule_t	 fill_rule,
-					     cairo_traps_t		*traps)
-{
-    cairo_box_t box;
-
-    assert (path->is_rectilinear);
-
-    if (_cairo_path_fixed_is_box (path, &box)) {
-	if (box.p1.x > box.p2.x) {
-	    cairo_fixed_t t;
-
-	    t = box.p1.x;
-	    box.p1.x = box.p2.x;
-	    box.p2.x = t;
-	}
-
-	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;
-	}
-
-	return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2);
-    } else if (fill_rule == CAIRO_FILL_RULE_WINDING) {
-	cairo_path_fixed_iter_t iter;
-	int last_cw = -1;
-
-	/* Support a series of rectangles as can be expected to describe a
-	 * GdkRegion clip region during exposes.
-	 */
-	_cairo_path_fixed_iter_init (&iter, path);
-	while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
-	    cairo_status_t status;
-	    int cw = 0;
-
-	    if (box.p1.x > box.p2.x) {
-		cairo_fixed_t t;
-
-		t = box.p1.x;
-		box.p1.x = box.p2.x;
-		box.p2.x = t;
-
-		cw = ! cw;
-	    }
-
-	    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;
-
-		cw = ! cw;
-	    }
-
-	    if (last_cw < 0) {
-		last_cw = cw;
-	    } else if (last_cw != cw) {
-		_cairo_traps_clear (traps);
-		return CAIRO_INT_STATUS_UNSUPPORTED;
-	    }
-
-	    status = _cairo_traps_tessellate_rectangle (traps,
-							&box.p1, &box.p2);
-	    if (unlikely (status))
-		return status;
-	}
-	if (_cairo_path_fixed_iter_at_end (&iter))
-	    return CAIRO_STATUS_SUCCESS;
-
-	_cairo_traps_clear (traps);
-    }
-
-    return CAIRO_INT_STATUS_UNSUPPORTED;
-}
-
 cairo_region_t *
 _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
 					      cairo_fill_rule_t	 fill_rule,
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 208e390..d37c597 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -2027,6 +2027,7 @@ _cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t	*path,
     else
 	status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
 
+    traps->is_rectilinear = 1;
 BAIL:
     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
 
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 8f13bfb..b97eb19 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -727,7 +727,12 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
 
     /* No fast path, exclude self-intersections and clip trapezoids. */
     if (traps->has_intersections) {
-	status = _cairo_bentley_ottmann_tessellate_traps (traps);
+	if (traps->is_rectilinear)
+	    status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps,
+									  CAIRO_FILL_RULE_WINDING);
+	else
+	    status = _cairo_bentley_ottmann_tessellate_traps (traps,
+							      CAIRO_FILL_RULE_WINDING);
 	if (unlikely (status))
 	    return status;
     }
@@ -1152,20 +1157,7 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
     _cairo_polygon_init (&polygon);
     _cairo_polygon_limit (&polygon, &box);
 
-    if (path->is_rectilinear) {
-	status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
-							      fill_rule,
-							      &traps);
-	if (likely (status == CAIRO_STATUS_SUCCESS))
-	    goto DO_TRAPS;
-
-	if (unlikely (_cairo_status_is_error (status)))
-	    goto CLEANUP;
-    }
-
-    status = _cairo_path_fixed_fill_to_polygon (path,
-						tolerance,
-						&polygon);
+    status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
     if (unlikely (status))
 	goto CLEANUP;
 
@@ -1177,6 +1169,18 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
 	    goto CLEANUP;
     }
 
+    if (path->is_rectilinear) {
+	status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
+									&polygon,
+									fill_rule);
+	if (likely (status == CAIRO_STATUS_SUCCESS))
+	    goto DO_TRAPS;
+
+	if (unlikely (_cairo_status_is_error (status)))
+	    goto CLEANUP;
+    }
+
+
     if (antialias != CAIRO_ANTIALIAS_NONE &&
 	_cairo_surface_check_span_renderer (op, source, surface,
 					    antialias, NULL))
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index 5f04ce1..a5974cc 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -51,6 +51,7 @@ _cairo_traps_init (cairo_traps_t *traps)
     traps->status = CAIRO_STATUS_SUCCESS;
 
     traps->maybe_region = 1;
+    traps->is_rectilinear = 0;
 
     traps->num_traps = 0;
 
@@ -76,6 +77,7 @@ _cairo_traps_clear (cairo_traps_t *traps)
     traps->status = CAIRO_STATUS_SUCCESS;
 
     traps->maybe_region = 1;
+    traps->is_rectilinear = 0;
 
     traps->num_traps = 0;
     traps->has_intersections = FALSE;
diff --git a/src/cairoint.h b/src/cairoint.h
index 439acfb..7312f7e 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -956,6 +956,7 @@ typedef struct _cairo_traps {
     unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
     unsigned int has_limits : 1;
     unsigned int has_intersections : 1;
+    unsigned int is_rectilinear : 1;
 
     int num_traps;
     int traps_size;
@@ -1616,11 +1617,6 @@ _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_int_status_t
-_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
-					     cairo_fill_rule_t fill_rule,
-					     cairo_traps_t    *traps);
-
 cairo_private cairo_status_t
 _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t   *path,
 				 cairo_fill_rule_t	     fill_rule,
@@ -2377,12 +2373,22 @@ _cairo_traps_add_trap (cairo_traps_t *traps,
 		       cairo_line_t *left, cairo_line_t *right);
 
 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_bentley_ottmann_tessellate_traps (cairo_traps_t *traps,
+					 cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps,
+						     cairo_fill_rule_t fill_rule);
 
 cairo_private int
 _cairo_traps_contain (const cairo_traps_t *traps,
diff --git a/test/ft-text-vertical-layout-type1.ref.png b/test/ft-text-vertical-layout-type1.ref.png
index 6f0df7b..f1c12a9 100644
Binary files a/test/ft-text-vertical-layout-type1.ref.png and b/test/ft-text-vertical-layout-type1.ref.png differ
diff --git a/test/ft-text-vertical-layout-type3.ref.png b/test/ft-text-vertical-layout-type3.ref.png
index 94048f1..1bda421 100644
Binary files a/test/ft-text-vertical-layout-type3.ref.png and b/test/ft-text-vertical-layout-type3.ref.png differ
commit 82ccb4c70cbf28167c280e590017b221a406b5c3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jul 29 18:47:29 2009 +0100

    [clip] Use special-purpose fill_to_region()
    
    Avoid the creation of temporary traps when generating a region, by calling
    the to_region() directly.

diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index c26f4fa..0d85612 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -42,6 +42,7 @@
 #include "cairoint.h"
 #include "cairo-clip-private.h"
 #include "cairo-path-fixed-private.h"
+#include "cairo-region-private.h"
 
 /* Keep a stash of recently freed clip_paths, since we need to
  * reallocate them frequently.
@@ -551,9 +552,7 @@ static cairo_int_status_t
 _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
 {
     cairo_int_status_t status;
-    cairo_traps_t traps;
     cairo_region_t *prev = NULL;
-    cairo_box_t box;
 
     if (clip_path->flags &
 	(CAIRO_CLIP_PATH_HAS_REGION |
@@ -582,24 +581,17 @@ _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
 
     /* now extract the region for ourselves */
 
-    /* XXX fixed fill to region */
-    _cairo_traps_init (&traps);
-
-    _cairo_box_from_rectangle (&box, &clip_path->extents);
-    _cairo_traps_limit (&traps, &box);
-
-    status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
-							  clip_path->fill_rule,
-							  &traps);
-    if (status) {
-	_cairo_traps_fini (&traps);
+    /* XXX just use the cheap search for now */
+    clip_path->region =
+	_cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path,
+						      clip_path->fill_rule,
+						      &clip_path->extents);
+    if (clip_path->region == NULL) {
 	clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
-	return status;
+	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
-    status = _cairo_traps_extract_region (&traps, &clip_path->region);
-    _cairo_traps_fini (&traps);
-
+    status = clip_path->region->status;
     if (unlikely (status)) {
 	clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
 	return status;
commit 41adeac9880d011278f083c620a42b849471a92f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Jul 25 21:25:07 2009 +0100

    [fallback] Avoid going through traps for trivial regions.

diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 2b5c4c8..46148cf 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -265,3 +265,132 @@ _cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t	*path,
 
     return CAIRO_INT_STATUS_UNSUPPORTED;
 }
+
+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_rectangle_int_t rectangle_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
+    cairo_box_t box;
+    cairo_region_t *region = NULL;
+
+    assert (path->maybe_fill_region);
+
+    if (_cairo_path_fixed_is_box (path, &box)) {
+	if (box.p1.x > box.p2.x) {
+	    cairo_fixed_t t;
+
+	    t = box.p1.x;
+	    box.p1.x = box.p2.x;
+	    box.p2.x = t;
+	}
+
+	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;
+	}
+
+	assert (_cairo_fixed_is_integer (box.p1.x) &&
+		_cairo_fixed_is_integer (box.p1.y));
+	assert (_cairo_fixed_is_integer (box.p2.x) &&
+		_cairo_fixed_is_integer (box.p2.y));
+	rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x);
+	rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y);
+	rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) -
+	                            rectangle_stack[0].x;
+	rectangle_stack[0].height = _cairo_fixed_integer_part (box.p2.y) -
+	                            rectangle_stack[0].y;
+	if (! _cairo_rectangle_intersect (&rectangle_stack[0], extents))
+	    region = cairo_region_create ();
+	else
+	    region = cairo_region_create_rectangle (&rectangle_stack[0]);
+    } else if (fill_rule == CAIRO_FILL_RULE_WINDING) {
+	cairo_rectangle_int_t *rects = rectangle_stack;
+	cairo_path_fixed_iter_t iter;
+	int last_cw = -1;
+	int size = ARRAY_LENGTH (rectangle_stack);
+	int count = 0;
+
+	/* Support a series of rectangles as can be expected to describe a
+	 * GdkRegion clip region during exposes.
+	 */
+	_cairo_path_fixed_iter_init (&iter, path);
+	while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
+	    int cw = 0;
+
+	    if (box.p1.x > box.p2.x) {
+		cairo_fixed_t t;
+
+		t = box.p1.x;
+		box.p1.x = box.p2.x;
+		box.p2.x = t;
+
+		cw = ! cw;
+	    }
+
+	    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;
+
+		cw = ! cw;
+	    }
+
+	    if (last_cw < 0) {
+		last_cw = cw;
+	    } else if (last_cw != cw) {
+		if (rects != rectangle_stack)
+		    free (rects);
+		return NULL;
+	    }
+
+	    if (count == size) {
+		cairo_rectangle_int_t *new_rects;
+
+		size *= 4;
+		if (rects == rectangle_stack) {
+		    new_rects = _cairo_malloc_ab (size,
+						  sizeof (cairo_rectangle_int_t));
+		    if (unlikely (new_rects == NULL)) {
+			/* XXX _cairo_region_nil */
+			break;
+		    }
+		    memcpy (new_rects, rects, sizeof (rectangle_stack));
+		} else {
+		    new_rects = _cairo_realloc_ab (rects, size,
+						   sizeof (cairo_rectangle_int_t));
+		    if (unlikely (new_rects == NULL)) {
+			/* XXX _cairo_region_nil */
+			break;
+		    }
+		}
+		rects = new_rects;
+	    }
+
+	    assert (_cairo_fixed_is_integer (box.p1.x) &&
+		    _cairo_fixed_is_integer (box.p1.y));
+	    assert (_cairo_fixed_is_integer (box.p2.x) &&
+		    _cairo_fixed_is_integer (box.p2.y));
+	    rects[count].x = _cairo_fixed_integer_part (box.p1.x);
+	    rects[count].y = _cairo_fixed_integer_part (box.p1.y);
+	    rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x;
+	    rects[count].height = _cairo_fixed_integer_part (box.p2.y) - rects[count].y;
+	    if (_cairo_rectangle_intersect (&rects[count], extents))
+		count++;
+	}
+
+	if (_cairo_path_fixed_iter_at_end (&iter))
+	    region = cairo_region_create_rectangles (rects, count);
+
+	if (rects != rectangle_stack)
+	    free (rects);
+    }
+
+    return region;
+}
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index c23481c..8f13bfb 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -582,48 +582,29 @@ _clip_and_composite_region (const cairo_pattern_t *src,
 			    cairo_operator_t op,
 			    cairo_surface_t *dst,
 			    cairo_region_t *trap_region,
-			    cairo_antialias_t antialias,
 			    cairo_clip_t *clip,
 			    cairo_rectangle_int_t *extents)
 {
     cairo_region_t clear_region;
-    cairo_bool_t clip_surface = FALSE;
     unsigned int has_region = 0;
     cairo_status_t status;
 
-    if (clip != NULL) {
-	cairo_region_t *clip_region;
-
-	status = _cairo_clip_get_region (clip, &clip_region);
-	assert (! _cairo_status_is_error (status));
-
-	clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    if (_cairo_operator_bounded_by_mask (op)) {
-        cairo_rectangle_int_t trap_extents;
-
-	cairo_region_get_extents (trap_region, &trap_extents);
-	if (! _cairo_rectangle_intersect (extents, &trap_extents))
-	    return CAIRO_STATUS_SUCCESS;
-    } else {
-        if (! clip_surface) {
-            /* If we optimize drawing with an unbounded operator to
-             * _cairo_surface_fill_rectangles() or to drawing with a
-             * clip region, then we have an additional region to clear.
-             */
-            _cairo_region_init_rectangle (&clear_region, extents);
-            status = cairo_region_subtract (&clear_region, trap_region);
-            if (unlikely (status))
-		return status;
+    if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) {
+	/* If we optimize drawing with an unbounded operator to
+	 * _cairo_surface_fill_rectangles() or to drawing with a
+	 * clip region, then we have an additional region to clear.
+	 */
+	_cairo_region_init_rectangle (&clear_region, extents);
+	status = cairo_region_subtract (&clear_region, trap_region);
+	if (unlikely (status))
+	    return status;
 
-            if (! cairo_region_is_empty (&clear_region))
-		has_region |= HAS_CLEAR_REGION;
-        }
+	if (! cairo_region_is_empty (&clear_region))
+	    has_region |= HAS_CLEAR_REGION;
     }
 
     if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) &&
-	! clip_surface)
+	clip == NULL)
     {
 	const cairo_color_t *color;
 
@@ -650,8 +631,7 @@ _clip_and_composite_region (const cairo_pattern_t *src,
 	 * more than rectangle and the destination doesn't support clip
 	 * regions. In that case, we fall through.
 	 */
-	status = _composite_trap_region (clip_surface ? clip : NULL,
-					 src, op, dst,
+	status = _composite_trap_region (clip, src, op, dst,
 					 trap_region, extents);
     }
 
@@ -724,17 +704,20 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
 		}
 	    }
 
-	    if (cairo_region_is_empty (trap_region) &&
-		_cairo_operator_bounded_by_mask (op))
-	    {
-		cairo_region_destroy (trap_region);
-		return CAIRO_STATUS_SUCCESS;
+	    if (_cairo_operator_bounded_by_mask (op)) {
+		cairo_rectangle_int_t trap_extents;
+
+		cairo_region_get_extents (trap_region, &trap_extents);
+		if (! _cairo_rectangle_intersect (extents, &trap_extents)) {
+		    cairo_region_destroy (trap_region);
+		    return CAIRO_STATUS_SUCCESS;
+		}
 	    }
 
 	    status = _clip_and_composite_region (src, op, dst,
 						 trap_region,
-						 antialias,
-						 clip, extents);
+						 clip_surface ? clip : NULL,
+						 extents);
 	    cairo_region_destroy (trap_region);
 
 	    if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
@@ -819,9 +802,11 @@ _cairo_surface_fallback_paint (cairo_surface_t		*surface,
 {
     cairo_status_t status;
     cairo_rectangle_int_t extents;
+    cairo_bool_t is_bounded;
+    cairo_region_t *clip_region = NULL;
+    cairo_bool_t clip_surface = FALSE;
     cairo_box_t box;
     cairo_traps_t traps;
-    cairo_bool_t is_bounded;
 
     is_bounded = _cairo_surface_get_extents (surface, &extents);
     assert (is_bounded || clip);
@@ -837,8 +822,58 @@ _cairo_surface_fallback_paint (cairo_surface_t		*surface,
     if (! _rectangle_intersect_clip (&extents, clip))
 	return CAIRO_STATUS_SUCCESS;
 
-    _cairo_box_from_rectangle (&box, &extents);
+    /* avoid the palaver of constructing traps for a simple region */
+    if (clip != NULL) {
+	status = _cairo_clip_get_region (clip, &clip_region);
+	if (unlikely (_cairo_status_is_error (status)))
+	    return status;
+
+	/* The all-clipped state should not have been propagated this far. */
+	assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+	clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (! clip_surface ||
+       (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE))
+    {
+	cairo_region_t region, *trap_region;
+
+	/* avoid unnecessarily allocating a region */
+	if (clip_region != NULL &&
+	    cairo_region_num_rectangles (clip_region) > 1)
+	{
+	    trap_region = cairo_region_copy (clip_region);
+	    status = cairo_region_intersect_rectangle (trap_region, &extents);
+	    if (unlikely (status)) {
+		cairo_region_destroy (trap_region);
+		return status;
+	    }
+	}
+	else
+	{
+	    _cairo_region_init_rectangle (&region, &extents);
+	    trap_region = &region;
+	}
+
+	if (cairo_region_is_empty (trap_region)) {
+	    status = CAIRO_STATUS_SUCCESS;
+	} else {
+	    status = _clip_and_composite_region (source, op, surface,
+						 trap_region,
+						 clip_surface ? clip : NULL,
+						 &extents);
+	}
+	if (trap_region == &region)
+	    _cairo_region_fini (trap_region);
+	else
+	    cairo_region_destroy (trap_region);
+
+	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	    return status;
+    }
 
+    /* otherwise, use the trapezoid fallbacks */
+    _cairo_box_from_rectangle (&box, &extents);
     _cairo_traps_init_box (&traps, &box);
     status = _clip_and_composite_trapezoids (source, op, surface,
 					     &traps, CAIRO_ANTIALIAS_NONE,
@@ -1052,14 +1087,71 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
     if (! _rectangle_intersect_clip (&extents, clip))
 	return CAIRO_STATUS_SUCCESS;
 
-    _cairo_box_from_rectangle (&box, &extents);
+    /* avoid the palaver of constructing traps for a simple region */
+    if (path->maybe_fill_region) {
+	cairo_region_t *clip_region = NULL;
+	cairo_bool_t clip_surface = FALSE;
 
-    _cairo_polygon_init (&polygon);
-    _cairo_polygon_limit (&polygon, &box);
+	if (clip != NULL) {
+	    status = _cairo_clip_get_region (clip, &clip_region);
+	    if (unlikely (_cairo_status_is_error (status)))
+		return status;
+
+	    assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+	    clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+	}
+
+	if (! clip_surface ||
+	    (_cairo_operator_bounded_by_mask (op) &&
+	     op != CAIRO_OPERATOR_SOURCE))
+	{
+	    cairo_region_t *trap_region;
+
+	    trap_region = _cairo_path_fixed_fill_rectilinear_to_region (path,
+									fill_rule,
+									&extents);
+	    if (trap_region != NULL) {
+		status = trap_region->status;
+		if (unlikely (status))
+		    return status;
+
+		if (clip_region != NULL &&
+		    cairo_region_num_rectangles (clip_region) > 1)
+		{
+		    status = cairo_region_intersect (trap_region, clip_region);
+		    if (unlikely (status)) {
+			cairo_region_destroy (trap_region);
+			return status;
+		    }
+		}
+
+		if (_cairo_operator_bounded_by_mask (op)) {
+		    cairo_region_get_extents (trap_region, &extents);
+		    if (extents.width == 0 || extents.height == 0)
+			goto CLEANUP_TRAP;
+		}
+
+		status = _clip_and_composite_region (source, op, surface,
+						     trap_region,
+						     clip_surface ? clip : NULL,
+						     &extents);
+             CLEANUP_TRAP:
+		cairo_region_destroy (trap_region);
+
+		if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+		    return status;
+	    }
+	}
+    }
+
+    _cairo_box_from_rectangle (&box, &extents);
 
     _cairo_traps_init (&traps);
     _cairo_traps_limit (&traps, &box);
 
+    _cairo_polygon_init (&polygon);
+    _cairo_polygon_limit (&polygon, &box);
+
     if (path->is_rectilinear) {
 	status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
 							      fill_rule,
@@ -1067,7 +1159,7 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
 	if (likely (status == CAIRO_STATUS_SUCCESS))
 	    goto DO_TRAPS;
 
-	if (_cairo_status_is_error (status))
+	if (unlikely (_cairo_status_is_error (status)))
 	    goto CLEANUP;
     }
 
diff --git a/src/cairoint.h b/src/cairoint.h
index 6c15895..439acfb 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1611,6 +1611,11 @@ _cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
 				   double              tolerance,
 				   cairo_polygon_t      *polygon);
 
+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_int_status_t
 _cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
 					     cairo_fill_rule_t fill_rule,
commit 55bd590561880136c54da0db1f7f095a426d96a9
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 24 20:45:56 2009 +0100

    [tessellator] Use a priority queue for the events
    
    The skip list was suffering from severe overhead, so though the search was
    quick, the extra copies during insertion and deletion were slow.

diff --git a/src/Makefile.am b/src/Makefile.am
index e461fbd..dce2a08 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -84,12 +84,9 @@ TESTS += check-link$(EXEEXT)
 endif
 
 EXTRA_DIST += $(TESTS_SH) check-has-hidden-symbols.c
-check_PROGRAMS += check-link check-skiplist
+check_PROGRAMS += check-link
 check_link_LDADD = libcairo.la
 
-check_skiplist_SOURCES = cairo-skiplist.c
-check_skiplist_CPPFLAGS = -DMAIN $(AM_CPPFLAGS)
-
 check: headers-standalone
 
 PREPROCESS_ARGS = $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS)
diff --git a/src/Makefile.sources b/src/Makefile.sources
index ca8d51d..b8404e3 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -82,7 +82,6 @@ cairo_private = \
 	cairo-region-private.h \
 	cairo-rtree-private.h \
 	cairo-scaled-font-private.h \
-	cairo-skiplist-private.h \
 	cairo-spans-private.h \
 	cairo-surface-fallback-private.h \
 	cairo-surface-private.h \
@@ -136,7 +135,6 @@ cairo_sources = \
 	cairo-region.c \
 	cairo-rtree.c \
 	cairo-scaled-font.c \
-	cairo-skiplist.c \
 	cairo-slope.c \
 	cairo-spans.c \
 	cairo-spline.c \
diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c
index 66d1a19..3d3a3d6 100644
--- a/src/cairo-bentley-ottmann.c
+++ b/src/cairo-bentley-ottmann.c
@@ -38,7 +38,6 @@
 /* Provide definitions for standalone compilation */
 #include "cairoint.h"
 
-#include "cairo-skiplist-private.h"
 #include "cairo-freelist-private.h"
 #include "cairo-combsort-private.h"
 
@@ -59,7 +58,6 @@ typedef struct _cairo_bo_intersect_point {
 } cairo_bo_intersect_point_t;
 
 typedef struct _cairo_bo_edge cairo_bo_edge_t;
-typedef struct _sweep_line_elt sweep_line_elt_t;
 typedef struct _cairo_bo_trap cairo_bo_trap_t;
 
 /* A deferred trapezoid of an edge */
@@ -73,16 +71,14 @@ struct _cairo_bo_edge {
     cairo_bo_edge_t *prev;
     cairo_bo_edge_t *next;
     cairo_bo_trap_t deferred_trap;
-    sweep_line_elt_t *sweep_line_elt;
 };
 
-struct _sweep_line_elt {
-    cairo_bo_edge_t *edge;
-    skip_elt_t elt;
-};
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i) >> 1)
+#define PQ_FIRST_ENTRY 1
 
-#define SKIP_ELT_TO_EDGE_ELT(elt)	SKIP_LIST_ELT_TO_DATA (sweep_line_elt_t, (elt))
-#define SKIP_ELT_TO_EDGE(elt)		(SKIP_ELT_TO_EDGE_ELT (elt)->edge)
+/* 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,
@@ -101,23 +97,26 @@ typedef struct _cairo_bo_start_event {
     cairo_bo_edge_t edge;
 } cairo_bo_start_event_t;
 
-typedef struct _cairo_bo_skiplist_event {
+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;
-    skip_elt_t elt;
-} cairo_bo_skiplist_event_t;
+} cairo_bo_queue_event_t;
 
-#define SKIP_ELT_TO_EVENT(elt) ((cairo_bo_event_t *) SKIP_LIST_ELT_TO_DATA (cairo_bo_skiplist_event_t, (elt)))
+typedef struct _pqueue {
+    int size, max_size;
 
-typedef struct _cairo_bo_event_queue {
-    cairo_skip_list_t event_queue;
+    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;
 
-/* This structure extends #cairo_skip_list_t, which must come first. */
 typedef struct _cairo_bo_sweep_line {
     cairo_bo_edge_t *head;
     cairo_bo_edge_t *stopped;
@@ -599,44 +598,10 @@ _cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t	*sweep_line,
 	cmp = _slope_compare (a, b);
 	if (cmp)
 	    return cmp;
-
-	/* We've got two collinear edges now. */
-
-	/* Since we're dealing with start events, prefer comparing top
-	 * edges before bottom edges. */
-	cmp = a->edge.top - b->edge.top;
-	if (cmp)
-	    return cmp;
-
-	cmp = a->edge.bottom - b->edge.bottom;
-	if (cmp)
-	    return cmp;
     }
 
-    return 0;
-}
-
-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 int
-cairo_bo_event_compare_skiplist (void *list, const void *a, const void *b)
-{
-    return cairo_bo_event_compare (a, b);
+    /* We've got two collinear edges now. */
+    return b->edge.bottom - a->edge.bottom;
 }
 
 static inline cairo_int64_t
@@ -687,8 +652,6 @@ intersect_lines (cairo_bo_edge_t		*a,
     cairo_quorem64_t qr;
 
     den_det = det32_64 (dx1, dy1, dx2, dy2);
-    if (_cairo_int64_is_zero (den_det))
-	return FALSE;
 
      /* Q: Can we determine that the lines do not intersect (within range)
       * much more cheaply than computing the intersection point i.e. by
@@ -712,10 +675,6 @@ intersect_lines (cairo_bo_edge_t		*a,
     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_is_zero (R))
-	return FALSE;
-    if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (R))
-	return FALSE;
     if (_cairo_int64_negative (den_det)) {
 	if (_cairo_int64_ge (den_det, R))
 	    return FALSE;
@@ -727,10 +686,6 @@ intersect_lines (cairo_bo_edge_t		*a,
     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_is_zero (R))
-	return FALSE;
-    if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (R))
-	return FALSE;
     if (_cairo_int64_negative (den_det)) {
 	if (_cairo_int64_ge (den_det, R))
 	    return FALSE;
@@ -923,49 +878,161 @@ _cairo_bo_edge_intersect (cairo_bo_edge_t	*a,
     return TRUE;
 }
 
-static void
-_cairo_bo_skiplist_event_init (cairo_bo_skiplist_event_t *event,
-			       cairo_bo_event_type_t	 type,
-			       cairo_bo_edge_t		*e1,
-			       cairo_bo_edge_t		*e2,
-			       const cairo_point_t	 *point)
+static inline int
+cairo_bo_event_compare (const cairo_bo_event_t *a,
+			const cairo_bo_event_t *b)
 {
-    event->type = type;
-    event->e1 = e1;
-    event->e2 = e2;
-    event->point = *point;
+    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
-_cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue,
-			      cairo_bo_skiplist_event_t *event)
+_pqueue_grow (pqueue_t *pq)
 {
-    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_bo_event_t **new_elements;
+    pq->max_size *= 2;
 
-    /* Only insert if there is no prior identical intersection event. */
-    if (unlikely (_cairo_skip_list_insert (&queue->event_queue, event) == NULL))
-	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    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);
 
-    return status;
+	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_skip_list_delete_given (&queue->event_queue,
-				   &((cairo_bo_skiplist_event_t *) event)->elt);
+    _cairo_freepool_free (&queue->pool, event);
 }
 
-#define NEXT_EVENT(Q) \
-    ((Q)->chains[0] ?  SKIP_ELT_TO_EVENT ((Q)->chains[0]) : NULL)
 static cairo_bo_event_t *
 _cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue)
 {
     cairo_bo_event_t *event, *cmp;
 
-    event = NEXT_EVENT (&event_queue->event_queue);
-
+    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))
@@ -973,6 +1040,10 @@ _cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue)
 	event = cmp;
 	event_queue->start_events++;
     }
+    else
+    {
+	_pqueue_pop (&event_queue->pqueue);
+    }
 
     return event;
 }
@@ -991,9 +1062,10 @@ _cairo_bo_event_queue_init (cairo_bo_event_queue_t	 *event_queue,
 
     event_queue->start_events = start_events;
 
-    _cairo_skip_list_init (&event_queue->event_queue,
-			   cairo_bo_event_compare_skiplist,
-			   sizeof (cairo_bo_skiplist_event_t));
+    _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
@@ -1001,23 +1073,21 @@ _cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t	*event_queue,
 				   cairo_bo_edge_t		*edge)
 {
     cairo_bo_point32_t point;
-    cairo_bo_skiplist_event_t event;
 
     point.y = edge->edge.bottom;
     point.x = _line_compute_intersection_x_for_y (&edge->edge.line,
 						  point.y);
-    _cairo_bo_skiplist_event_init (&event,
-				   CAIRO_BO_EVENT_TYPE_STOP,
-				   edge, NULL,
-				   &point);
-
-    return _cairo_bo_event_queue_insert (event_queue, &event);
+    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)
 {
-    _cairo_skip_list_fini (&event_queue->event_queue);
+    _pqueue_fini (&event_queue->pqueue);
+    _cairo_freepool_fini (&event_queue->pool);
 }
 
 static inline cairo_status_t
@@ -1026,10 +1096,6 @@ _cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_
 							   cairo_bo_edge_t *right)
 {
     cairo_bo_point32_t intersection;
-    cairo_bo_skiplist_event_t event;
-
-    if (left == NULL || right == NULL)
-	return CAIRO_STATUS_SUCCESS;
 
     if (_line_equal (&left->edge.line, &right->edge.line))
 	return CAIRO_STATUS_SUCCESS;
@@ -1045,12 +1111,10 @@ _cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_
     if (! _cairo_bo_edge_intersect (left, right, &intersection))
 	return CAIRO_STATUS_SUCCESS;
 
-    _cairo_bo_skiplist_event_init (&event,
-				   CAIRO_BO_EVENT_TYPE_INTERSECTION,
-				   left, right,
-				   &intersection);
-
-    return _cairo_bo_event_queue_insert (event_queue, &event);
+    return _cairo_bo_event_queue_insert (event_queue,
+					 CAIRO_BO_EVENT_TYPE_INTERSECTION,
+					 left, right,
+					 &intersection);
 }
 
 static void
@@ -1088,7 +1152,7 @@ _cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t	*sweep_line,
 	    edge->next = next;
 	    if (next != NULL)
 		next->prev = edge;
-	} else {
+	} else if (cmp > 0) {
 	    next = sweep_line->current_edge;
 	    prev = next->prev;
 	    while (prev != NULL &&
@@ -1105,6 +1169,13 @@ _cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t	*sweep_line,
 		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;
@@ -1186,22 +1257,14 @@ _cairo_bo_event_print (cairo_bo_event_t *event)
 static void
 _cairo_bo_event_queue_print (cairo_bo_event_queue_t *event_queue)
 {
-    skip_elt_t *elt;
-
     /* XXX: fixme to print the start/stop array too. */
-    cairo_skip_list_t *queue = &event_queue->event_queue;
-
     printf ("Event queue:\n");
-
-    for (elt = queue->chains[0]; elt; elt = elt->next[0])
-	_cairo_bo_event_print (SKIP_ELT_TO_EVENT (elt));
 }
 
 static void
 _cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line)
 {
     cairo_bool_t first = TRUE;
-    skip_elt_t *elt;
     cairo_bo_edge_t *edge;
 
     printf ("Sweep line from edge list: ");
@@ -1578,18 +1641,22 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t   **start_events,
 	    left = e1->prev;
 	    right = e1->next;
 
-	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
-	    if (unlikely (status))
-		goto unwind;
+	    if (left != NULL) {
+		status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
+		if (unlikely (status))
+		    goto unwind;
+	    }
 
-	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
-	    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_skiplist_event_t *) event)->e1;
+	    e1 = ((cairo_bo_queue_event_t *) event)->e1;
 	    _cairo_bo_event_queue_delete (&event_queue, event);
 
 	    left = e1->prev;
@@ -1606,15 +1673,17 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t   **start_events,
 		e1->prev = NULL;
 	    }
 
-	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
-	    if (unlikely (status))
-		goto unwind;
+	    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_skiplist_event_t *) event)->e1;
-	    e2 = ((cairo_bo_skiplist_event_t *) event)->e2;
+	    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 */
@@ -1630,13 +1699,17 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t   **start_events,
 
 	    /* after the swap e2 is left of e1 */
 
-	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
-	    if (unlikely (status))
-		goto unwind;
+	    if (left != NULL) {
+		status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
+		if (unlikely (status))
+		    goto unwind;
+	    }
 
-	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
-	    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;
 	}
@@ -1704,7 +1777,6 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t	 *traps,
 	events[i].edge.deferred_trap.right = NULL;
 	events[i].edge.prev = NULL;
 	events[i].edge.next = NULL;
-	events[i].edge.sweep_line_elt = NULL;
     }
 
 #if DEBUG_TRAPS
@@ -1765,7 +1837,6 @@ _add_event (const cairo_line_t *line,
     event->edge.deferred_trap.right = NULL;
     event->edge.prev = NULL;
     event->edge.next = NULL;
-    event->edge.sweep_line_elt = NULL;
 
     return 1;
 }
diff --git a/src/cairo-freelist.c b/src/cairo-freelist.c
index 83647f2..6bbee84 100644
--- a/src/cairo-freelist.c
+++ b/src/cairo-freelist.c
@@ -104,8 +104,9 @@ _cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize)
 
 	node->next = freepool->first_free_node;
 	freepool->first_free_node = node;
-	VG (VALGRIND_MAKE_MEM_NOACCESS (node, freepool->nodesize));
     }
+    VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->embedded_pool,
+				    sizeof (freepool->embedded_pool)));
 }
 
 void
@@ -133,7 +134,7 @@ _cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool)
 
     poolsize = (128 * freepool->nodesize + 8191) & -8192;
     node = malloc (poolsize);
-    if (node == NULL)
+    if (unlikely (node == NULL))
 	return node;
 
     node->next = freepool->pools;
@@ -150,8 +151,9 @@ _cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool)
 
 	node->next = freepool->first_free_node;
 	freepool->first_free_node = node;
-	VG (VALGRIND_MAKE_MEM_NOACCESS (node, freepool->nodesize));
     }
+    VG (VALGRIND_MAKE_MEM_NOACCESS (freepool->pools,
+				    (128 * freepool->nodesize + 8191) & -8192));
 
     return ptr;
 }
diff --git a/src/cairo-skiplist-private.h b/src/cairo-skiplist-private.h
deleted file mode 100644
index c0e5bce..0000000
--- a/src/cairo-skiplist-private.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright © 2006 Keith Packard
- * Copyright © 2006 Carl 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 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 SKIPLIST_H
-#define SKIPLIST_H
-
-#include "cairoint.h"
-
-/*
- * Skip lists are described in detail here:
- *
- *   http://citeseer.ist.psu.edu/pugh90skip.html
- */
-
-/* Note that random_level() called from alloc_node_for_level() depends on
- * this being not more than 16.
- */
-#define MAX_LEVEL   15
-
-/* Returns the index of the free-list to use for a node at level 'level' */
-#define FREELIST_FOR_LEVEL(level) (((level) - 1) / 2)
-
-/* Returns the maximum level that uses the same free-list as 'level' does */
-#define FREELIST_MAX_LEVEL_FOR(level) (((level) + 1) & ~1)
-
-#define MAX_FREELIST_LEVEL (FREELIST_FOR_LEVEL (MAX_LEVEL - 1) + 1)
-
-/*
- * Skip list element. In order to use the skip list, the caller must
- * generate a structure for list elements that has as its final member
- * a skip_elt_t, (which will be allocated with variable size).
- *
- * The caller must also pass the size of the structure to
- * _cairo_skip_list_init.
- */
-typedef struct _skip_elt {
-    int prev_index;
-    struct _skip_elt *prev;
-    struct _skip_elt *next[1];
-} skip_elt_t;
-
-#define SKIP_LIST_ELT_TO_DATA(type, elt) ((type *) ((char *) (elt) - (sizeof (type) - sizeof (skip_elt_t))))
-
-typedef int
-(*cairo_skip_list_compare_t) (void *list, const void *a, const void *b);
-
-typedef struct _skip_list {
-    cairo_skip_list_compare_t compare;
-    size_t elt_size;
-    size_t data_size;
-    skip_elt_t *chains[MAX_LEVEL];
-    skip_elt_t *freelists[MAX_FREELIST_LEVEL];
-    int		max_level;
-    struct pool *pool;
-    char pool_embedded[1024];
-} cairo_skip_list_t;
-
-/* Initialize a new skip list. The compare function accepts a pointer
- * to the list as well as pointers to two elements. The function must
- * return a value greater than zero, zero, or less then 0 if the first
- * element is considered respectively greater than, equal to, or less
- * than the second element. The size of each object, (as computed by
- * sizeof) is passed for elt_size. Note that the structure used for
- * list elements must have as its final member a skip_elt_t
- */
-cairo_private void
-_cairo_skip_list_init (cairo_skip_list_t		*list,
-		       cairo_skip_list_compare_t	 compare,
-		       size_t			 elt_size);
-
-
-/* Deallocate resources associated with a skip list and all elements
- * in it. (XXX: currently this simply deletes all elements.)
- */
-cairo_private void
-_cairo_skip_list_fini (cairo_skip_list_t		*list);
-
-/* Insert a new element into the list at the correct sort order as
- * determined by compare. If unique is true, then duplicate elements
- * are ignored and the already inserted element is returned.
- * Otherwise data will be copied (elt_size bytes from <data> via
- * memcpy) and the new element is returned. */
-cairo_private void *
-_cairo_skip_list_insert (cairo_skip_list_t *list, void *data);
-
-/* Find an element which compare considers equal to <data> */
-cairo_private void *
-_cairo_skip_list_find (cairo_skip_list_t *list, void *data);
-
-/* Delete an element which compare considers equal to <data> */
-cairo_private void
-_cairo_skip_list_delete (cairo_skip_list_t *list, void *data);
-
-/* Delete the given element from the list. */
-cairo_private void
-_cairo_skip_list_delete_given (cairo_skip_list_t *list, skip_elt_t *given);
-
-#endif
diff --git a/src/cairo-skiplist.c b/src/cairo-skiplist.c
deleted file mode 100644
index 8d8c8d1..0000000
--- a/src/cairo-skiplist.c
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * Copyright © 2006 Keith Packard
- * Copyright © 2006 Carl 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 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-skiplist-private.h"
-
-#if HAVE_FFS
-#include <strings.h> /* ffs() */
-#endif
-
-#define ELT_DATA(elt) (void *)	((char*) (elt) - list->data_size)
-#define NEXT_TO_ELT(next)	(skip_elt_t *) ((char *) (next) - offsetof (skip_elt_t, next))
-
-static uint32_t
-hars_petruska_f54_1_random (void)
-{
-#  define rol(x,k) ((x << k) | (x >> (32-k)))
-    static uint32_t x = 0;
-    x = (x ^ rol(x, 5) ^ rol(x, 24)) + 0x37798849;
-    return x;
-#  undef rol
-}
-
-struct pool {
-    struct pool *next;
-    char *ptr;
-    unsigned int rem;
-};
-
-static struct pool *
-pool_new (void)
-{
-    struct pool *pool;
-
-    pool = malloc (8192 - 8);
-    if (unlikely (pool == NULL))
-	return NULL;
-
-    pool->next = NULL;
-    pool->rem = 8192 - 8 - sizeof (struct pool);
-    pool->ptr = (char *) (pool + 1);
-
-    return pool;
-}
-
-static void
-pools_destroy (struct pool *pool)
-{
-    while (pool->next != NULL) {
-	struct pool *next = pool->next;
-	free (pool);
-	pool = next;
-    }
-}
-
-/*
- * Initialize an empty skip list
- */
-void
-_cairo_skip_list_init (cairo_skip_list_t	*list,
-                       cairo_skip_list_compare_t compare,
-                       size_t			 elt_size)
-{
-    int i;
-
-    list->compare = compare;
-    list->elt_size = elt_size;
-    list->data_size = elt_size - sizeof (skip_elt_t);
-    list->pool = (struct pool *) list->pool_embedded;
-    list->pool->next = NULL;
-    list->pool->rem = sizeof (list->pool_embedded) - sizeof (struct pool);
-    list->pool->ptr = list->pool_embedded + sizeof (struct pool);
-
-    for (i = 0; i < MAX_LEVEL; i++) {
-	list->chains[i] = NULL;
-    }
-
-    for (i = 0; i < MAX_FREELIST_LEVEL; i++) {
-	list->freelists[i] = NULL;
-    }
-
-    list->max_level = 0;
-}
-
-void
-_cairo_skip_list_fini (cairo_skip_list_t *list)
-{
-    pools_destroy (list->pool);
-}
-
-/*
- * Generate a random level number, distributed
- * so that each level is 1/4 as likely as the one before
- *
- * Note that level numbers run 1 <= level < MAX_LEVEL
- */
-static int
-random_level (void)
-{
-    /* tricky bit -- each bit is '1' 75% of the time.
-     * This works because we only use the lower MAX_LEVEL
-     * bits, and MAX_LEVEL < 16 */
-    uint32_t bits = hars_petruska_f54_1_random ();
-#if HAVE_FFS
-    return ffs (-(1<<MAX_LEVEL) | bits | bits >> 16);
-#else
-    int level = 1;
-
-    bits |= -(1<<MAX_LEVEL) | bits >> 16;
-    while ((bits & 1) == 0) {
-	level++;
-	bits >>= 1;
-    }
-    return level;
-#endif
-}
-
-static void *
-pool_alloc (cairo_skip_list_t *list,
-	    unsigned int level)
-{
-    unsigned int size;
-    struct pool *pool;
-    void *ptr;
-
-    size = list->elt_size +
-	(FREELIST_MAX_LEVEL_FOR (level) - 1) * sizeof (skip_elt_t *);
-
-    pool = list->pool;
-    if (size > pool->rem) {
-	pool = pool_new ();
-	if (unlikely (pool == NULL))
-	    return NULL;
-
-	pool->next = list->pool;
-	list->pool = pool;
-    }
-
-    ptr = pool->ptr;
-    pool->ptr += size;
-    pool->rem -= size;
-
-    return ptr;
-}
-
-static void *
-alloc_node_for_level (cairo_skip_list_t *list, unsigned level)
-{
-    int freelist_level = FREELIST_FOR_LEVEL (level);
-    if (list->freelists[freelist_level]) {
-	skip_elt_t *elt = list->freelists[freelist_level];
-	list->freelists[freelist_level] = elt->prev;
-	return ELT_DATA(elt);
-    }
-    return pool_alloc (list, level);
-}
-
-static void
-free_elt (cairo_skip_list_t *list, skip_elt_t *elt)
-{
-    int level = elt->prev_index + 1;
-    int freelist_level = FREELIST_FOR_LEVEL (level);
-    elt->prev = list->freelists[freelist_level];
-    list->freelists[freelist_level] = elt;
-}
-
-/*
- * Insert 'data' into the list
- */
-void *
-_cairo_skip_list_insert (cairo_skip_list_t *list, void *data)
-{
-    skip_elt_t **update[MAX_LEVEL];
-    skip_elt_t *prev[MAX_LEVEL];
-    char *data_and_elt;
-    skip_elt_t *elt, **next;
-    int	    i, level, prev_index;
-
-    /*
-     * Find links along each chain
-     */
-    elt = NULL;
-    next = list->chains;
-    for (i = list->max_level; --i >= 0; )
-    {
-	if (elt != next[i])
-	{
-	    for (; (elt = next[i]); next = elt->next)
-	    {
-		int cmp = list->compare (list, ELT_DATA(elt), data);
-		if (0 == cmp)
-		    return ELT_DATA(elt);
-		if (cmp > 0)
-		    break;
-	    }
-	}
-        update[i] = next;
-	if (next != list->chains)
-	    prev[i] = NEXT_TO_ELT (next);
-	else
-	    prev[i] = NULL;
-    }
-    level = random_level ();
-    prev_index = level - 1;
-
-    /*
-     * Create new list element
-     */
-    if (level > list->max_level)
-    {
-	level = list->max_level + 1;
-	prev_index = level - 1;
-	prev[prev_index] = NULL;
-	update[list->max_level] = list->chains;
-	list->max_level = level;
-    }
-
-    data_and_elt = alloc_node_for_level (list, level);
-    if (unlikely (data_and_elt == NULL)) {
-	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
-	return NULL;
-    }
-
-    memcpy (data_and_elt, data, list->data_size);
-    elt = (skip_elt_t *) (data_and_elt + list->data_size);
-
-    elt->prev_index = prev_index;
-    elt->prev = prev[prev_index];
-
-    /*
-     * Insert into all chains
-     */
-    for (i = 0; i < level; i++)
-    {
-	elt->next[i] = update[i][i];
-	if (elt->next[i] && elt->next[i]->prev_index == i)
-	    elt->next[i]->prev = elt;
-	update[i][i] = elt;
-    }
-
-    return data_and_elt;
-}
-
-void *
-_cairo_skip_list_find (cairo_skip_list_t *list, void *data)
-{
-    int i;
-    skip_elt_t **next = list->chains;
-    skip_elt_t *elt;
-
-    /*
-     * Walk chain pointers one level at a time
-     */
-    for (i = list->max_level; --i >= 0;)
-	while (next[i] && list->compare (list, data, ELT_DATA(next[i])) > 0)
-	{
-	    next = next[i]->next;
-	}
-    /*
-     * Here we are
-     */
-    elt = next[0];
-    if (elt && list->compare (list, data, ELT_DATA (elt)) == 0)
-	return ELT_DATA (elt);
-
-    return NULL;
-}
-
-void
-_cairo_skip_list_delete (cairo_skip_list_t *list, void *data)
-{
-    skip_elt_t **update[MAX_LEVEL], *prev[MAX_LEVEL];
-    skip_elt_t *elt, **next;
-    int	i;
-
-    /*
-     * Find links along each chain
-     */
-    next = list->chains;
-    for (i = list->max_level; --i >= 0; )
-    {
-	for (; (elt = next[i]); next = elt->next)
-	{
-	    if (list->compare (list, ELT_DATA (elt), data) >= 0)
-		break;
-	}
-        update[i] = &next[i];
-	if (next == list->chains)
-	    prev[i] = NULL;
-	else
-	    prev[i] = NEXT_TO_ELT (next);
-    }
-    elt = next[0];
-    assert (list->compare (list, ELT_DATA (elt), data) == 0);
-    for (i = 0; i < list->max_level && *update[i] == elt; i++) {
-	*update[i] = elt->next[i];
-	if (elt->next[i] && elt->next[i]->prev_index == i)
-	    elt->next[i]->prev = prev[i];
-    }
-    while (list->max_level > 0 && list->chains[list->max_level - 1] == NULL)
-	list->max_level--;
-    free_elt (list, elt);
-}
-
-void
-_cairo_skip_list_delete_given (cairo_skip_list_t *list, skip_elt_t *given)
-{
-    skip_elt_t **update[MAX_LEVEL], *prev[MAX_LEVEL];
-    skip_elt_t *elt, **next;
-    int	i;
-
-    /*
-     * Find links along each chain
-     */
-    if (given->prev)
-	next = given->prev->next;
-    else
-	next = list->chains;
-    for (i = given->prev_index + 1; --i >= 0; )
-    {
-	for (; (elt = next[i]); next = elt->next)
-	{
-	    if (elt == given)
-		break;
-	}
-        update[i] = &next[i];
-	if (next == list->chains)
-	    prev[i] = NULL;
-	else
-	    prev[i] = NEXT_TO_ELT (next);
-    }
-    elt = next[0];
-    assert (elt == given);
-    for (i = 0; i < (given->prev_index + 1) && *update[i] == elt; i++) {
-	*update[i] = elt->next[i];
-	if (elt->next[i] && elt->next[i]->prev_index == i)
-	    elt->next[i]->prev = prev[i];
-    }
-    while (list->max_level > 0 && list->chains[list->max_level - 1] == NULL)
-	list->max_level--;
-    free_elt (list, elt);
-}
-
-#if MAIN
-typedef struct {
-    int n;
-    skip_elt_t elt;
-} test_elt_t;
-
-static int
-test_cmp (void *list, const void *A, const void *B)
-{
-    const test_elt_t *a = A, *b = B;
-    return a->n - b->n;
-}
-
-int
-main (void)
-{
-    cairo_skip_list_t list;
-    test_elt_t elt;
-    int n;
-
-    _cairo_skip_list_init (&list, test_cmp, sizeof (test_elt_t));
-    for (n = 0; n < 10000000; n++) {
-	void *elt_and_data;
-	elt.n = n;
-	elt_and_data = _cairo_skip_list_insert (&list, &elt);
-	assert (elt_and_data != NULL);
-    }
-    _cairo_skip_list_fini (&list);
-
-    return 0;
-}
-
-/* required supporting stubs */
-cairo_status_t _cairo_error (cairo_status_t status) { return status; }
-#endif
commit ebfcc2ce8fb6fcaf28d1c59cf7a5b13168cbeb70
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 24 16:50:33 2009 +0100

    [tessellator] Remove the skiplist for the active edges
    
    The active edge list is typically short, and the skiplist adds significant
    overhead that far outweigh the benefit of the O(n lg n) sort. Instead we
    track the position of the last insertion edge, knowing that the start
    events are lexicographically sorted, and begin a linear search from there.

diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c
index f64b7a3..66d1a19 100644
--- a/src/cairo-bentley-ottmann.c
+++ b/src/cairo-bentley-ottmann.c
@@ -42,7 +42,6 @@
 #include "cairo-freelist-private.h"
 #include "cairo-combsort-private.h"
 
-#define DEBUG_VALIDATE 0
 #define DEBUG_PRINT_STATE 0
 #define DEBUG_EVENTS 0
 #define DEBUG_TRAPS 0
@@ -120,11 +119,10 @@ typedef struct _cairo_bo_event_queue {
 
 /* This structure extends #cairo_skip_list_t, which must come first. */
 typedef struct _cairo_bo_sweep_line {
-    cairo_skip_list_t active_edges;
     cairo_bo_edge_t *head;
-    cairo_bo_edge_t *tail;
     cairo_bo_edge_t *stopped;
     int32_t current_y;
+    cairo_bo_edge_t *current_edge;
 } cairo_bo_sweep_line_t;
 
 #if DEBUG_TRAPS
@@ -615,21 +613,7 @@ _cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t	*sweep_line,
 	    return cmp;
     }
 
-    return a - b;
-}
-
-static int
-_sweep_line_elt_compare (void	*list,
-			 const void	*a,
-			 const void	*b)
-{
-    cairo_bo_sweep_line_t *sweep_line = list;
-    const sweep_line_elt_t *edge_elt_a = a;
-    const sweep_line_elt_t *edge_elt_b = b;
-
-    return _cairo_bo_sweep_line_compare_edges (sweep_line,
-					       edge_elt_a->edge,
-					       edge_elt_b->edge);
+    return 0;
 }
 
 static inline int
@@ -1072,51 +1056,61 @@ _cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_
 static void
 _cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line)
 {
-    _cairo_skip_list_init (&sweep_line->active_edges,
-			   _sweep_line_elt_compare,
-			   sizeof (sweep_line_elt_t));
-
     sweep_line->head = NULL;
-    sweep_line->tail = NULL;
     sweep_line->stopped = NULL;
     sweep_line->current_y = INT32_MIN;
-}
-
-static void
-_cairo_bo_sweep_line_fini (cairo_bo_sweep_line_t *sweep_line)
-{
-    _cairo_skip_list_fini (&sweep_line->active_edges);
+    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)
 {
-    skip_elt_t *next_elt;
-    sweep_line_elt_t *sweep_line_elt;
-    cairo_bo_edge_t **prev_of_next, **next_of_prev;
-
-    sweep_line_elt = _cairo_skip_list_insert (&sweep_line->active_edges, &edge);
-    if (unlikely (sweep_line_elt == NULL))
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
-    next_elt = sweep_line_elt->elt.next[0];
-    if (next_elt)
-	prev_of_next = &SKIP_ELT_TO_EDGE (next_elt)->prev;
-    else
-	prev_of_next = &sweep_line->tail;
+    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;
+	    }
 
-    if (*prev_of_next)
-	next_of_prev = &(*prev_of_next)->next;
-    else
-	next_of_prev = &sweep_line->head;
+	    prev->next = edge;
+	    edge->prev = prev;
+	    edge->next = next;
+	    if (next != NULL)
+		next->prev = edge;
+	} else {
+	    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;
+	    }
 
-    edge->prev = *prev_of_next;
-    edge->next = *next_of_prev;
-    *prev_of_next = edge;
-    *next_of_prev = edge;
+	    next->prev = edge;
+	    edge->next = next;
+	    edge->prev = prev;
+	    if (prev != NULL)
+		prev->next = edge;
+	    else
+		sweep_line->head = edge;
+	}
+    } else {
+	sweep_line->head = edge;
+    }
 
-    edge->sweep_line_elt = sweep_line_elt;
+    sweep_line->current_edge = edge;
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1125,21 +1119,16 @@ static void
 _cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t	*sweep_line,
 			     cairo_bo_edge_t	*edge)
 {
-    cairo_bo_edge_t **left_next, **right_prev;
-
-    _cairo_skip_list_delete_given (&sweep_line->active_edges,
-				   &edge->sweep_line_elt->elt);
-
-    left_next = &sweep_line->head;
-    if (edge->prev)
-	left_next = &edge->prev->next;
+    if (edge->prev != NULL)
+	edge->prev->next = edge->next;
+    else
+	sweep_line->head = edge->next;
 
-    right_prev = &sweep_line->tail;
-    if (edge->next)
-	right_prev = &edge->next->prev;
+    if (edge->next != NULL)
+	edge->next->prev = edge->prev;
 
-    *left_next = edge->next;
-    *right_prev = edge->prev;
+    if (sweep_line->current_edge == edge)
+	sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
 }
 
 static void
@@ -1147,32 +1136,13 @@ _cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t	*sweep_line,
 			   cairo_bo_edge_t		*left,
 			   cairo_bo_edge_t		*right)
 {
-    sweep_line_elt_t *left_elt, *right_elt;
-    cairo_bo_edge_t **before_left, **after_right;
-
-    /* Within the skip list we can do the swap simply by swapping the
-     * pointers to the edge elements and leaving all of the skip list
-     * elements and pointers unchanged. */
-    left_elt = left->sweep_line_elt;
-    right_elt = SKIP_ELT_TO_EDGE_ELT (left_elt->elt.next[0]);
-
-    left_elt->edge = right;
-    right->sweep_line_elt = left_elt;
-
-    right_elt->edge = left;
-    left->sweep_line_elt = right_elt;
-
-    /* Within the doubly-linked list of edges, there's a bit more
-     * bookkeeping involved with the swap. */
-    before_left = &sweep_line->head;
-    if (left->prev)
-	before_left = &left->prev->next;
-    *before_left = right;
+    if (left->prev != NULL)
+	left->prev->next = right;
+    else
+	sweep_line->head = right;
 
-    after_right = &sweep_line->tail;
-    if (right->next)
-	after_right = &right->next->prev;
-    *after_right = left;
+    if (right->next != NULL)
+	right->next->prev = left;
 
     left->next = right->next;
     right->next = left;
@@ -1234,20 +1204,6 @@ _cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line)
     skip_elt_t *elt;
     cairo_bo_edge_t *edge;
 
-    printf ("Sweep line (reversed):     ");
-
-    for (edge = sweep_line->tail;
-	 edge;
-	 edge = edge->prev)
-    {
-	if (!first)
-	    printf (", ");
-	_cairo_bo_edge_print (edge);
-	first = FALSE;
-    }
-    printf ("\n");
-
-
     printf ("Sweep line from edge list: ");
     first = TRUE;
     for (edge = sweep_line->head;
@@ -1260,19 +1216,6 @@ _cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line)
 	first = FALSE;
     }
     printf ("\n");
-
-    printf ("Sweep line from skip list: ");
-    first = TRUE;
-    for (elt = sweep_line->active_edges.chains[0];
-	 elt;
-	 elt = elt->next[0])
-    {
-	if (!first)
-	    printf (", ");
-	_cairo_bo_edge_print (SKIP_ELT_TO_EDGE (elt));
-	first = FALSE;
-    }
-    printf ("\n");
 }
 
 static void
@@ -1289,34 +1232,6 @@ print_state (const char			*msg,
 }
 #endif
 
-#if DEBUG_VALIDATE
-static void
-_cairo_bo_sweep_line_validate (cairo_bo_sweep_line_t *sweep_line)
-{
-    cairo_bo_edge_t *edge;
-    skip_elt_t *elt;
-
-    /* March through both the skip list's singly-linked list and the
-     * sweep line's own list through pointers in the edges themselves
-     * and make sure they agree at every point. */
-
-    for (edge = sweep_line->head, elt = sweep_line->active_edges.chains[0];
-	 edge && elt;
-	 edge = edge->next, elt = elt->next[0])
-    {
-	if (SKIP_ELT_TO_EDGE (elt) != edge) {
-	    fprintf (stderr, "*** Error: Sweep line fails to validate: Inconsistent data in the two lists.\n");
-	    abort ();
-	}
-    }
-
-    if (edge || elt) {
-	fprintf (stderr, "*** Error: Sweep line fails to validate: One list ran out before the other.\n");
-	abort ();
-    }
-}
-#endif
-
 #if DEBUG_EVENTS
 static void CAIRO_PRINTF_FORMAT (1, 2)
 event_log (const char *fmt, ...)
@@ -1345,13 +1260,23 @@ 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.
      */
-    return _slope_compare (a, b) == 0 &&
-	edges_compare_x_for_y (a, b,
-				  MAX (a->edge.line.p1.y,
-				       b->edge.line.p1.y)) == 0;
+    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;
+    }
 }
 
 /* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */
@@ -1363,8 +1288,7 @@ _cairo_bo_edge_end_trap (cairo_bo_edge_t	*left,
     cairo_bo_trap_t *trap = &left->deferred_trap;
 
     /* Only emit (trivial) non-degenerate trapezoids with positive height. */
-    if (likely (trap->top < bot && ! edges_colinear (left, trap->right))) {
-	assert (trap->right->edge.bottom >= bot);
+    if (likely (trap->top < bot)) {
 	_cairo_traps_add_trap (traps,
 			       trap->top, bot,
 			       &left->edge.line, &trap->right->edge.line);
@@ -1422,7 +1346,7 @@ _cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t	*left,
 	    return status;
     }
 
-    if (right != NULL) {
+    if (right != NULL && ! edges_colinear (left, right)) {
 	left->deferred_trap.top = top;
 	left->deferred_trap.right = right;
 
@@ -1716,9 +1640,6 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t   **start_events,
 
 	    break;
 	}
-#if DEBUG_VALIDATE
-	_cairo_bo_sweep_line_validate (&sweep_line);
-#endif
     }
 
     *num_intersections = intersection_count;
@@ -1730,7 +1651,6 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t   **start_events,
 	}
     }
  unwind:
-    _cairo_bo_sweep_line_fini (&sweep_line);
     _cairo_bo_event_queue_fini (&event_queue);
 
 #if DEBUG_EVENTS
commit 36480fe531f19d9c692ee1f8cf09accd4b2c0ad8
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Jul 25 07:38:04 2009 +0100

    [traps] Increase exponential expansion factor.
    
    Grow the traps more rapidly, as the allocations are very short-lived so
    the over-allocation is less of an issue.

diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index b9f5ba7..5f04ce1 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -124,7 +124,7 @@ static cairo_bool_t
 _cairo_traps_grow (cairo_traps_t *traps)
 {
     cairo_trapezoid_t *new_traps;
-    int new_size = 2 * MAX (traps->traps_size, 16);
+    int new_size = 4 * traps->traps_size;
 
     if (CAIRO_INJECT_FAULT ()) {
 	traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -157,10 +157,8 @@ _cairo_traps_add_trap (cairo_traps_t *traps,
 {
     cairo_trapezoid_t *trap;
 
-    assert (top < bottom);
-
-    if (traps->num_traps == traps->traps_size) {
-	if (! _cairo_traps_grow (traps))
+    if (unlikely (traps->num_traps == traps->traps_size)) {
+	if (unlikely (! _cairo_traps_grow (traps)))
 	    return;
     }
 
diff --git a/src/cairoint.h b/src/cairoint.h
index 2a829ea..6c15895 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -954,15 +954,13 @@ typedef struct _cairo_traps {
     cairo_box_t limits;
 
     unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
+    unsigned int has_limits : 1;
+    unsigned int has_intersections : 1;
 
     int num_traps;
     int traps_size;
     cairo_trapezoid_t *traps;
-    /* embed enough storage for a stroked rectangle */
-    cairo_trapezoid_t  traps_embedded[4];
-
-    cairo_bool_t has_limits;
-    cairo_bool_t has_intersections;
+    cairo_trapezoid_t  traps_embedded[16];
 } cairo_traps_t;
 
 #define CAIRO_FONT_SLANT_DEFAULT   CAIRO_FONT_SLANT_NORMAL
commit 9d51c03bad5f10257e248f43375062902482c0c4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 24 13:51:23 2009 +0100

    [traps] Compute extents on demand.

diff --git a/src/cairo-region-private.h b/src/cairo-region-private.h
index 8a10115..507f72e 100644
--- a/src/cairo-region-private.h
+++ b/src/cairo-region-private.h
@@ -60,11 +60,6 @@ cairo_private void
 _cairo_region_init_rectangle (cairo_region_t *region,
 			      const cairo_rectangle_int_t *rectangle);
 
-cairo_private cairo_status_t
-_cairo_region_init_rectangles (cairo_region_t *region,
-			       const cairo_rectangle_int_t *rects,
-			       int count);
-
 cairo_private void
 _cairo_region_fini (cairo_region_t *region);
 
diff --git a/src/cairo-region.c b/src/cairo-region.c
index d6fff7f..9983481 100644
--- a/src/cairo-region.c
+++ b/src/cairo-region.c
@@ -144,23 +144,28 @@ cairo_region_create (void)
 }
 slim_hidden_def (cairo_region_create);
 
-cairo_status_t
-_cairo_region_init_rectangles (cairo_region_t *region,
-			       const cairo_rectangle_int_t *rects,
-			       int count)
+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_status_t status;
+    cairo_region_t *region;
     int i;
 
-    status = CAIRO_STATUS_SUCCESS;
-    CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 0);
+    region = _cairo_malloc (sizeof (cairo_region_t));
+    if (unlikely (region == NULL)) {
+	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+	return (cairo_region_t *) &_cairo_region_nil;
+    }
 
     if (count > ARRAY_LENGTH (stack_pboxes)) {
 	pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t));
-	if (unlikely (pboxes == NULL))
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	if (unlikely (pboxes == NULL)) {
+	    free (region);
+	    _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+	    return (cairo_region_t *) &_cairo_region_nil;
+	}
     }
 
     for (i = 0; i < count; i++) {
@@ -170,35 +175,19 @@ _cairo_region_init_rectangles (cairo_region_t *region,
 	pboxes[i].y2 = rects[i].y + rects[i].height;
     }
 
-    if (! pixman_region32_init_rects (&region->rgn, pboxes, count))
-	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    i = pixman_region32_init_rects (&region->rgn, pboxes, count);
 
     if (pboxes != stack_pboxes)
 	free (pboxes);
 
-    return region->status = status;
-}
-
-cairo_region_t *
-cairo_region_create_rectangles (const cairo_rectangle_int_t *rects,
-				int count)
-{
-    cairo_region_t *region;
-    cairo_status_t status;
-
-    region = _cairo_malloc (sizeof (cairo_region_t));
-    if (unlikely (region == NULL)) {
+    if (unlikely (i == 0)) {
+	free (region);
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return (cairo_region_t *) &_cairo_region_nil;
     }
 
-    status = _cairo_region_init_rectangles (region, rects, count);
-    if (unlikely (status)) {
-	cairo_region_destroy (region);
-	return (cairo_region_t *) &_cairo_region_nil;
-    }
-
     CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 1);
+    region->status = CAIRO_STATUS_SUCCESS;
     return region;
 }
 slim_hidden_def (cairo_region_create_rectangles);
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 7ab12b4..c23481c 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -752,16 +752,6 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
     /* Otherwise render the trapezoids to a mask and composite in the usual
      * fashion.
      */
-    if (_cairo_operator_bounded_by_mask (op)) {
-        cairo_rectangle_int_t trap_extents;
-	cairo_box_t trap_box;
-
-	_cairo_traps_extents (traps, &trap_box);
-	_cairo_box_round_to_rectangle (&trap_box, &trap_extents);
-	if (! _cairo_rectangle_intersect (extents, &trap_extents))
-	    return CAIRO_STATUS_SUCCESS;
-    }
-
     traps_info.traps = traps;
     traps_info.antialias = antialias;
 
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index 057e950..b9f5ba7 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -43,9 +43,6 @@
 
 /* private functions */
 
-static int
-_compare_point_fixed_by_y (const void *av, const void *bv);
-
 void
 _cairo_traps_init (cairo_traps_t *traps)
 {
@@ -59,8 +56,6 @@ _cairo_traps_init (cairo_traps_t *traps)
 
     traps->traps_size = ARRAY_LENGTH (traps->traps_embedded);
     traps->traps = traps->traps_embedded;
-    traps->extents.p1.x = traps->extents.p1.y = INT32_MAX;
-    traps->extents.p2.x = traps->extents.p2.y = INT32_MIN;
 
     traps->has_limits = FALSE;
     traps->has_intersections = FALSE;
@@ -84,8 +79,6 @@ _cairo_traps_clear (cairo_traps_t *traps)
 
     traps->num_traps = 0;
     traps->has_intersections = FALSE;
-    traps->extents.p1.x = traps->extents.p1.y = INT32_MAX;
-    traps->extents.p2.x = traps->extents.p2.y = INT32_MIN;
 }
 
 void
@@ -124,8 +117,6 @@ _cairo_traps_init_box (cairo_traps_t *traps,
     traps->traps[0].right.p1.x = box->p2.x;
     traps->traps[0].right.p1.y = box->p1.y;
     traps->traps[0].right.p2 = box->p2;
-
-    traps->extents = *box;
 }
 
 /* make room for at least one more trap */
@@ -159,12 +150,6 @@ _cairo_traps_grow (cairo_traps_t *traps)
     return TRUE;
 }
 
-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_add_trap (cairo_traps_t *traps,
 		       cairo_fixed_t top, cairo_fixed_t bottom,
@@ -172,37 +157,55 @@ _cairo_traps_add_trap (cairo_traps_t *traps,
 {
     cairo_trapezoid_t *trap;
 
-    /* Note: With the goofy trapezoid specification, (where an
-     * arbitrary two points on the lines can specified for the left
-     * and right edges), these limit checks would not work in
-     * general. For example, one can imagine a trapezoid entirely
-     * within the limits, but with two points used to specify the left
-     * edge entirely to the right of the limits.  Fortunately, for our
-     * purposes, cairo will never generate such a crazy
-     * trapezoid. Instead, cairo always uses for its points the
-     * extreme positions of the edge that are visible on at least some
-     * trapezoid. With this constraint, it's impossible for both
-     * points to be outside the limits while the relevant edge is
-     * entirely inside the limits.
-     */
+    assert (top < bottom);
+
+    if (traps->num_traps == traps->traps_size) {
+	if (! _cairo_traps_grow (traps))
+	    return;
+    }
+
+    trap = &traps->traps[traps->num_traps++];
+    trap->top = top;
+    trap->bottom = bottom;
+    trap->left = *left;
+    trap->right = *right;
+}
+
+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;
+
+     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->has_limits) {
 	/* Trivially reject if trapezoid is entirely to the right or
 	 * to the left of the limits. */
-	if (left->p1.x >= traps->limits.p2.x &&
-	    left->p2.x >= traps->limits.p2.x)
+	if (left.p1.x >= traps->limits.p2.x &&
+	    left.p2.x >= traps->limits.p2.x)
 	{
-	    return;
+	    return CAIRO_STATUS_SUCCESS;
 	}
 
-	if (right->p1.x <= traps->limits.p1.x &&
-	    right->p2.x <= traps->limits.p1.x)
+	if (right.p1.x <= traps->limits.p1.x &&
+	    right.p2.x <= traps->limits.p1.x)
 	{
-	    return;
+	    return CAIRO_STATUS_SUCCESS;
 	}
 
 	/* And reject if the trapezoid is entirely above or below */
 	if (top > traps->limits.p2.y || bottom < traps->limits.p1.y)
-	    return;
+	    return CAIRO_STATUS_SUCCESS;
 
 	/* Otherwise, clip the trapezoid to the limits. We only clip
 	 * where an edge is entirely outside the limits. If we wanted
@@ -216,98 +219,34 @@ _cairo_traps_add_trap (cairo_traps_t *traps,
 	if (bottom > traps->limits.p2.y)
 	    bottom = traps->limits.p2.y;
 
-	if (left->p1.x <= traps->limits.p1.x &&
-	    left->p2.x <= traps->limits.p1.x)
+	if (left.p1.x <= traps->limits.p1.x &&
+	    left.p2.x <= traps->limits.p1.x)
 	{
-	    left->p1.x = traps->limits.p1.x;
-	    left->p2.x = traps->limits.p1.x;
+	    left.p1.x = traps->limits.p1.x;
+	    left.p1.y = traps->limits.p1.y;
+	    left.p2.x = traps->limits.p1.x;
+	    left.p2.y = traps->limits.p2.y;
 	}
 
-	if (right->p1.x >= traps->limits.p2.x &&
-	    right->p2.x >= traps->limits.p2.x)
+	if (right.p1.x >= traps->limits.p2.x &&
+	    right.p2.x >= traps->limits.p2.x)
 	{
-	    right->p1.x = traps->limits.p2.x;
-	    right->p2.x = traps->limits.p2.x;
+	    right.p1.x = traps->limits.p2.x;
+	    right.p1.y = traps->limits.p1.y;
+	    right.p2.x = traps->limits.p2.x;
+	    right.p2.y = traps->limits.p2.y;
 	}
     }
 
-    /* Trivial discards for empty trapezoids that are likely to be produced
-     * by our tessellators (most notably convex_quad when given a simple
-     * rectangle).
-     */
-    if (top >= bottom)
-	return;
-    /* cheap colinearity check */
-    if (right->p1.x <= left->p1.x && right->p1.y == left->p1.y &&
-	right->p2.x <= left->p2.x && right->p2.y == left->p2.y)
-	return;
+    if (top == bottom)
+	return CAIRO_STATUS_SUCCESS;
 
-    if (traps->num_traps == traps->traps_size) {
-	if (! _cairo_traps_grow (traps))
-	    return;
-    }
+    if (left.p1.x == right.p1.x)
+	return CAIRO_STATUS_SUCCESS;
 
-    trap = &traps->traps[traps->num_traps];
-    trap->top = top;
-    trap->bottom = bottom;
-    trap->left = *left;
-    trap->right = *right;
-
-    if (top < traps->extents.p1.y)
-	traps->extents.p1.y = top;
-    if (bottom > traps->extents.p2.y)
-	traps->extents.p2.y = bottom;
-
-    if (left->p1.x < traps->extents.p1.x) {
-	cairo_fixed_t x = left->p1.x;
-	if (top != left->p1.y) {
-	    x = _line_compute_intersection_x_for_y (left, top);
-	    if (x < traps->extents.p1.x)
-		traps->extents.p1.x = x;
-	} else
-	    traps->extents.p1.x = x;
-    }
-    if (left->p2.x < traps->extents.p1.x) {
-	cairo_fixed_t x = left->p2.x;
-	if (bottom != left->p2.y) {
-	    x = _line_compute_intersection_x_for_y (left, bottom);
-	    if (x < traps->extents.p1.x)
-		traps->extents.p1.x = x;
-	} else
-	    traps->extents.p1.x = x;
-    }
-
-    if (right->p1.x > traps->extents.p2.x) {
-	cairo_fixed_t x = right->p1.x;
-	if (top != right->p1.y) {
-	    x = _line_compute_intersection_x_for_y (right, top);
-	    if (x > traps->extents.p2.x)
-		traps->extents.p2.x = x;
-	} else
-	    traps->extents.p2.x = x;
-    }
-    if (right->p2.x > traps->extents.p2.x) {
-	cairo_fixed_t x = right->p2.x;
-	if (bottom != right->p2.y) {
-	    x = _line_compute_intersection_x_for_y (right, bottom);
-	    if (x > traps->extents.p2.x)
-		traps->extents.p2.x = x;
-	} else
-	    traps->extents.p2.x = x;
-    }
-
-    traps->num_traps++;
-}
+    _cairo_traps_add_trap (traps, top, bottom, &left, &right);
 
-static int
-_compare_point_fixed_by_y (const void *av, const void *bv)
-{
-    const cairo_point_t	*a = av, *b = bv;
-
-    int ret = a->y - b->y;
-    if (ret == 0)
-	ret = a->x - b->x;
-    return ret;
+    return traps->status;
 }
 
 void
@@ -382,184 +321,6 @@ _cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps,
     }
 }
 
-/* A triangle is simply a degenerate case of a convex
- * quadrilateral. We would not benefit from having any distinct
- * implementation of triangle vs. quadrilateral tessellation here. */
-cairo_status_t
-_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
-				  const cairo_point_t t[3])
-{
-    cairo_point_t quad[4];
-
-    quad[0] = t[0];
-    quad[1] = t[0];
-    quad[2] = t[1];
-    quad[3] = t[2];
-
-    return _cairo_traps_tessellate_convex_quad (traps, quad);
-}
-
-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;
-
-     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;
-
-    _cairo_traps_add_trap (traps, top_left->y, bottom_right->y, &left, &right);
-
-    return traps->status;
-}
-
-cairo_status_t
-_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
-				     const cairo_point_t q[4])
-{
-    int a, b, c, d;
-    int i;
-    cairo_slope_t ab, ad;
-    cairo_bool_t b_left_of_d;
-    cairo_line_t left;
-    cairo_line_t right;
-
-    /* Choose a as a point with minimal y */
-    a = 0;
-    for (i = 1; i < 4; i++)
-	if (_compare_point_fixed_by_y (&q[i], &q[a]) < 0)
-	    a = i;
-
-    /* b and d are adjacent to a, while c is opposite */
-    b = (a + 1) % 4;
-    c = (a + 2) % 4;
-    d = (a + 3) % 4;
-
-    /* Choose between b and d so that b.y is less than d.y */
-    if (_compare_point_fixed_by_y (&q[d], &q[b]) < 0) {
-	b = (a + 3) % 4;
-	d = (a + 1) % 4;
-    }
-
-    /* Without freedom left to choose anything else, we have four
-     * cases to tessellate.
-     *
-     * First, we have to determine the Y-axis sort of the four
-     * vertices, (either abcd or abdc). After that we need to detemine
-     * which edges will be "left" and which will be "right" in the
-     * resulting trapezoids. This can be determined by computing a
-     * slope comparison of ab and ad to determine if b is left of d or
-     * not.
-     *
-     * Note that "left of" here is in the sense of which edges should
-     * be the left vs. right edges of the trapezoid. In particular, b
-     * left of d does *not* mean that b.x is less than d.x.
-     *
-     * This should hopefully be made clear in the lame ASCII art
-     * below. Since the same slope comparison is used in all cases, we
-     * compute it before testing for the Y-value sort. */
-
-    /* Note: If a == b then the ab slope doesn't give us any
-     * information. In that case, we can replace it with the ac (or
-     * equivalenly the bc) slope which gives us exactly the same
-     * information we need. At worst the names of the identifiers ab
-     * and b_left_of_d are inaccurate in this case, (would be ac, and
-     * c_left_of_d). */
-    if (q[a].x == q[b].x && q[a].y == q[b].y)
-	_cairo_slope_init (&ab, &q[a], &q[c]);
-    else
-	_cairo_slope_init (&ab, &q[a], &q[b]);
-
-    _cairo_slope_init (&ad, &q[a], &q[d]);
-
-    b_left_of_d = (_cairo_slope_compare (&ab, &ad) > 0);
-
-    if (q[c].y <= q[d].y) {
-	if (b_left_of_d) {
-	    /* Y-sort is abcd and b is left of d, (slope(ab) > slope (ad))
-	     *
-	     *                      top bot left right
-	     *        _a  a  a
-	     *      / /  /|  |\      a.y b.y  ab   ad
-	     *     b /  b |  b \
-	     *    / /   | |   \ \    b.y c.y  bc   ad
-	     *   c /    c |    c \
-	     *  | /      \|     \ \  c.y d.y  cd   ad
-	     *  d         d       d
-	     */
-	    left.p1  = q[a]; left.p2  = q[b];
-	    right.p1 = q[a]; right.p2 = q[d];
-	    _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
-	    left.p1  = q[b]; left.p2  = q[c];
-	    _cairo_traps_add_trap (traps, q[b].y, q[c].y, &left, &right);
-	    left.p1  = q[c]; left.p2  = q[d];
-	    _cairo_traps_add_trap (traps, q[c].y, q[d].y, &left, &right);
-	} else {
-	    /* Y-sort is abcd and b is right of d, (slope(ab) <= slope (ad))
-	     *
-	     *       a  a  a_
-	     *      /|  |\  \ \     a.y b.y  ad  ab
-	     *     / b  | b  \ b
-	     *    / /   | |   \ \   b.y c.y  ad  bc
-	     *   / c    | c    \ c
-	     *  / /     |/      \ | c.y d.y  ad  cd
-	     *  d       d         d
-	     */
-	    left.p1  = q[a]; left.p2  = q[d];
-	    right.p1 = q[a]; right.p2 = q[b];
-	    _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
-	    right.p1 = q[b]; right.p2 = q[c];
-	    _cairo_traps_add_trap (traps, q[b].y, q[c].y, &left, &right);
-	    right.p1 = q[c]; right.p2 = q[d];
-	    _cairo_traps_add_trap (traps, q[c].y, q[d].y, &left, &right);
-	}
-    } else {
-	if (b_left_of_d) {
-	    /* Y-sort is abdc and b is left of d, (slope (ab) > slope (ad))
-	     *
-	     *        a   a     a
-	     *       //  / \    |\     a.y b.y  ab  ad
-	     *     /b/  b   \   b \
-	     *    / /    \   \   \ \   b.y d.y  bc  ad
-	     *   /d/      \   d   \ d
-	     *  //         \ /     \|  d.y c.y  bc  dc
-	     *  c           c       c
-	     */
-	    left.p1  = q[a]; left.p2  = q[b];
-	    right.p1 = q[a]; right.p2 = q[d];
-	    _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
-	    left.p1  = q[b]; left.p2  = q[c];
-	    _cairo_traps_add_trap (traps, q[b].y, q[d].y, &left, &right);
-	    right.p1 = q[d]; right.p2 = q[c];
-	    _cairo_traps_add_trap (traps, q[d].y, q[c].y, &left, &right);
-	} else {
-	    /* Y-sort is abdc and b is right of d, (slope (ab) <= slope (ad))
-	     *
-	     *      a     a   a
-	     *     /|    / \  \\       a.y b.y  ad  ab
-	     *    / b   /   b  \b\
-	     *   / /   /   /    \ \    b.y d.y  ad  bc
-	     *  d /   d   /	 \d\
-	     *  |/     \ /         \\  d.y c.y  dc  bc
-	     *  c       c	   c
-	     */
-	    left.p1  = q[a]; left.p2  = q[d];
-	    right.p1 = q[a]; right.p2 = q[b];
-	    _cairo_traps_add_trap (traps, q[a].y, q[b].y, &left, &right);
-	    right.p1 = q[b]; right.p2 = q[c];
-	    _cairo_traps_add_trap (traps, q[b].y, q[d].y, &left, &right);
-	    left.p1  = q[d]; left.p2  = q[c];
-	    _cairo_traps_add_trap (traps, q[d].y, q[c].y, &left, &right);
-	}
-    }
-
-    return traps->status;
-}
-
 static cairo_bool_t
 _cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt)
 {
@@ -603,30 +364,81 @@ _cairo_traps_contain (const cairo_traps_t *traps,
     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)
+		      cairo_box_t *extents)
 {
+    int i;
+
     if (traps->num_traps == 0) {
-	extents->p1.x = extents->p1.y = _cairo_fixed_from_int (0);
-	extents->p2.x = extents->p2.y = _cairo_fixed_from_int (0);
-    } else {
-	*extents = traps->extents;
-	if (traps->has_limits) {
-	    /* clip the traps to the imposed limits */
-	    if (extents->p1.x < traps->limits.p1.x)
-		extents->p1.x = traps->limits.p1.x;
-	    if (extents->p2.x > traps->limits.p2.x)
-		extents->p2.x = traps->limits.p2.x;
-
-	    if (extents->p1.y < traps->limits.p1.y)
-		extents->p1.y = traps->limits.p1.y;
-	    if (extents->p2.y > traps->limits.p2.y)
-		extents->p2.y = traps->limits.p2.y;
+	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;
 	}
     }
 }
 
+
 /**
  * _cairo_traps_extract_region:
  * @traps: a #cairo_traps_t
@@ -684,8 +496,8 @@ _cairo_traps_extract_region (cairo_traps_t   *traps,
 	/* XXX: Sometimes we get degenerate trapezoids from the tesellator;
 	 * skip these.
 	 */
-	if (x1 == x2 || y1 == y2)
-	    continue;
+	assert (x1 != x2);
+	assert (y1 != y2);
 
 	rects[rect_count].x = x1;
 	rects[rect_count].y = y1;
@@ -712,7 +524,7 @@ _sanitize_trap (cairo_trapezoid_t *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 (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.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);
diff --git a/src/cairoint.h b/src/cairoint.h
index 30b0c4c..2a829ea 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -951,7 +951,6 @@ typedef struct _cairo_surface_attributes {
 typedef struct _cairo_traps {
     cairo_status_t status;
 
-    cairo_box_t extents;
     cairo_box_t limits;
 
     unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
@@ -2365,14 +2364,6 @@ cairo_private void
 _cairo_traps_translate (cairo_traps_t *traps, int x, int y);
 
 cairo_private cairo_status_t
-_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
-				  const cairo_point_t t[3]);
-
-cairo_private cairo_status_t
-_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
-				     const cairo_point_t q[4]);
-
-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);
commit f8bb3617c3a7ec598c42eff1f8562e3ccc95127f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Dec 17 09:32:16 2008 +0000

    Eliminate self-intersecting strokes.
    
    We refactor the surface fallbacks to convert full strokes and fills to the
    intermediate polygon representation (as opposed to before where we
    returned the trapezoidal representation). This allow greater flexibility
    to choose how then to rasterize the polygon. Where possible we use the
    local spans rasteriser for its increased performance, but still have the
    option to use the tessellator instead (for example, with the current
    Render protocol which does not yet have a polygon image).
    
    In order to accommodate this, the spans interface is tweaked to accept
    whole polygons instead of a path and the tessellator is tweaked for speed.
    
    Performance Impact
    ==================
    
    ...
    Still measuring, expecting some severe regressions.
    ...

diff --git a/NEWS b/NEWS
index 9c798dd..a4a2c83 100644
--- a/NEWS
+++ b/NEWS
@@ -69,6 +69,24 @@ New experimental backends:
 	    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.
+
 
 Snapshot 1.9.2 (2009-06-12)
 ===========================
diff --git a/src/Makefile.am b/src/Makefile.am
index f34d79c..e461fbd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -99,5 +99,7 @@ COMPILE_ARGS = $(PREPROCESS_ARGS) $(AM_CFLAGS) $(CFLAGS)
 # 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/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c
index 1d59d70..f64b7a3 100644
--- a/src/cairo-bentley-ottmann.c
+++ b/src/cairo-bentley-ottmann.c
@@ -1,6 +1,7 @@
 /*
  * 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
@@ -31,6 +32,7 @@
  *
  * Contributor(s):
  *	Carl D. Worth <cworth at cworth.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
  */
 
 /* Provide definitions for standalone compilation */
@@ -42,14 +44,11 @@
 
 #define DEBUG_VALIDATE 0
 #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_point128 {
-    cairo_int128_t x;
-    cairo_int128_t y;
-} cairo_bo_point128_t;
-
 typedef struct _cairo_bo_intersect_ordinate {
     int32_t ordinate;
     enum { EXACT, INEXACT } exactness;
@@ -63,34 +62,18 @@ typedef struct _cairo_bo_intersect_point {
 typedef struct _cairo_bo_edge cairo_bo_edge_t;
 typedef struct _sweep_line_elt sweep_line_elt_t;
 typedef struct _cairo_bo_trap cairo_bo_trap_t;
-typedef struct _cairo_bo_traps cairo_bo_traps_t;
 
-/* A deferred trapezoid of an edge. */
+/* A deferred trapezoid of an edge */
 struct _cairo_bo_trap {
     cairo_bo_edge_t *right;
     int32_t top;
 };
 
-struct _cairo_bo_traps {
-    cairo_traps_t *traps;
-    cairo_freelist_t freelist;
-
-    /* These form the closed bounding box of the original input
-     * points. */
-    cairo_fixed_t xmin;
-    cairo_fixed_t ymin;
-    cairo_fixed_t xmax;
-    cairo_fixed_t ymax;
-};
-
 struct _cairo_bo_edge {
-    cairo_bo_point32_t top;
-    cairo_bo_point32_t middle;
-    cairo_bo_point32_t bottom;
-    int dir;
+    cairo_edge_t edge;
     cairo_bo_edge_t *prev;
     cairo_bo_edge_t *next;
-    cairo_bo_trap_t *deferred_trap;
+    cairo_bo_trap_t deferred_trap;
     sweep_line_elt_t *sweep_line_elt;
 };
 
@@ -100,35 +83,39 @@ struct _sweep_line_elt {
 };
 
 #define SKIP_ELT_TO_EDGE_ELT(elt)	SKIP_LIST_ELT_TO_DATA (sweep_line_elt_t, (elt))
-#define SKIP_ELT_TO_EDGE(elt) 		(SKIP_ELT_TO_EDGE_ELT (elt)->edge)
+#define SKIP_ELT_TO_EDGE(elt)		(SKIP_ELT_TO_EDGE_ELT (elt)->edge)
 
 typedef enum {
-    CAIRO_BO_STATUS_INTERSECTION,
-    CAIRO_BO_STATUS_PARALLEL,
-    CAIRO_BO_STATUS_NO_INTERSECTION
-} cairo_bo_status_t;
-
-typedef enum {
-    CAIRO_BO_EVENT_TYPE_START,
     CAIRO_BO_EVENT_TYPE_STOP,
-    CAIRO_BO_EVENT_TYPE_INTERSECTION
+    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_skiplist_event {
+    cairo_bo_event_type_t type;
+    cairo_point_t point;
     cairo_bo_edge_t *e1;
     cairo_bo_edge_t *e2;
-    cairo_bo_point32_t point;
     skip_elt_t elt;
-} cairo_bo_event_t;
+} cairo_bo_skiplist_event_t;
 
-#define SKIP_ELT_TO_EVENT(elt) SKIP_LIST_ELT_TO_DATA (cairo_bo_event_t, (elt))
+#define SKIP_ELT_TO_EVENT(elt) ((cairo_bo_event_t *) SKIP_LIST_ELT_TO_DATA (cairo_bo_skiplist_event_t, (elt)))
 
 typedef struct _cairo_bo_event_queue {
-    cairo_skip_list_t intersection_queue;
+    cairo_skip_list_t event_queue;
 
-    cairo_bo_event_t *startstop_events;
-    cairo_bo_event_t **sorted_startstop_event_ptrs;
+    cairo_bo_event_t **start_events;
 } cairo_bo_event_queue_t;
 
 /* This structure extends #cairo_skip_list_t, which must come first. */
@@ -136,16 +123,134 @@ typedef struct _cairo_bo_sweep_line {
     cairo_skip_list_t active_edges;
     cairo_bo_edge_t *head;
     cairo_bo_edge_t *tail;
+    cairo_bo_edge_t *stopped;
     int32_t current_y;
 } cairo_bo_sweep_line_t;
 
+#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;
+
+    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);
+    }
+    printf ("%s: extents=(%d, %d, %d, %d)\n",
+	    filename,
+	    traps->extents.p1.x, traps->extents.p1.y,
+	    traps->extents.p2.x, traps->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 cairo_fixed_t
+_line_compute_intersection_y_for_x (const cairo_line_t *line,
+				    cairo_fixed_t x)
+{
+    cairo_fixed_t y, dx;
+
+    if (x == line->p1.x)
+	return line->p1.y;
+    if (x == line->p2.x)
+	return line->p2.y;
+
+    y = line->p1.y;
+    dx = line->p2.x - line->p1.x;
+    if (dx != 0) {
+	y += _cairo_fixed_mul_div_floor (x - line->p1.x,
+					 line->p2.y - line->p1.y,
+					 dx);
+    }
+
+    return y;
+}
 
 static inline int
 _cairo_bo_point32_compare (cairo_bo_point32_t const *a,
 			   cairo_bo_point32_t const *b)
 {
-    int cmp = a->y - b->y;
-    if (cmp) return cmp;
+    int cmp;
+
+    cmp = a->y - b->y;
+    if (cmp)
+	return cmp;
+
     return a->x - b->x;
 }
 
@@ -159,7 +264,7 @@ _cairo_bo_point32_compare (cairo_bo_point32_t const *a,
  *
  * which is:
  *
- *	(dx, dy) = (bottom.x - top.x, bottom.y - top.y)
+ *	(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
@@ -183,17 +288,17 @@ _cairo_bo_point32_compare (cairo_bo_point32_t const *a,
  * the sense of the result will be exactly reversed for two edges that
  * have a common stop point.
  */
-static int
-_slope_compare (cairo_bo_edge_t *a,
-		cairo_bo_edge_t *b)
+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->bottom.x - a->top.x;
-    int32_t bdx = b->bottom.x - b->top.x;
+    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.
@@ -211,8 +316,8 @@ _slope_compare (cairo_bo_edge_t *a,
 
     /* Finally we actually need to do the general comparison. */
     {
-	int32_t ady = a->bottom.y - a->top.y;
-	int32_t bdy = b->bottom.y - b->top.y;
+	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);
 
@@ -270,23 +375,46 @@ edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
        HAVE_ALL     = HAVE_DX | HAVE_ADX | HAVE_BDX
     } have_dx_adx_bdx = HAVE_ALL;
 
-    ady = a->bottom.y - a->top.y;
-    adx = a->bottom.x - a->top.x;
+    /* 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->bottom.y - b->top.y;
-    bdx = b->bottom.x - b->top.x;
+    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->top.x - b->top.x;
+    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->top.y)
-#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->top.y)
+#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:
@@ -304,7 +432,7 @@ edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
 	/*  0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */
 	if ((adx ^ bdx) < 0) {
 	    return adx;
-	} else if (a->top.y == b->top.y) { /* common origin */
+	} 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 */
@@ -323,7 +451,7 @@ edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
 	    cairo_int64_t ady_dx, dy_adx;
 
 	    ady_dx = _cairo_int32x32_64_mul (ady, dx);
-	    dy_adx = _cairo_int32x32_64_mul (a->top.y - y, adx);
+	    dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx);
 
 	    return _cairo_int64_cmp (ady_dx, dy_adx);
 	}
@@ -335,11 +463,12 @@ edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
 	    cairo_int64_t bdy_dx, dy_bdx;
 
 	    bdy_dx = _cairo_int32x32_64_mul (bdy, dx);
-	    dy_bdx = _cairo_int32x32_64_mul (y - b->top.y, bdx);
+	    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
@@ -377,16 +506,21 @@ edge_compare_for_y_against_x (const cairo_bo_edge_t *a,
     int32_t dx, dy;
     cairo_int64_t L, R;
 
-    adx = a->bottom.x - a->top.x;
-    dx = x - a->top.x;
+    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 ((adx ^ dx) < 0)
+    if (dx == 0 || (adx ^ dx) < 0)
 	return adx;
 
-    dy = y - a->top.y;
-    ady = a->bottom.y - a->top.y;
+    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);
@@ -412,17 +546,17 @@ edges_compare_x_for_y (const cairo_bo_edge_t *a,
     } have_ax_bx = HAVE_BOTH;
     int32_t ax, bx;
 
-    if (y == a->top.y)
-	ax = a->top.x;
-    else if (y == a->bottom.y)
-	ax = a->bottom.x;
+    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->top.y)
-	bx = b->top.x;
-    else if (y == b->bottom.y)
-	bx = b->bottom.x;
+    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;
 
@@ -439,82 +573,59 @@ edges_compare_x_for_y (const cairo_bo_edge_t *a,
     }
 }
 
+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,
-				    cairo_bo_edge_t		*a,
-				    cairo_bo_edge_t		*b)
+				    const cairo_bo_edge_t	*a,
+				    const cairo_bo_edge_t	*b)
 {
     int cmp;
 
-    if (a == b)
-	return 0;
-
-    /* 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->middle.x < a->bottom.x) {
-                   amin = a->middle.x;
-                   amax = a->bottom.x;
-           } else {
-                   amin = a->bottom.x;
-                   amax = a->middle.x;
-           }
-           if (b->middle.x < b->bottom.x) {
-                   bmin = b->middle.x;
-                   bmax = b->bottom.x;
-           } else {
-                   bmin = b->bottom.x;
-                   bmax = b->middle.x;
-           }
-           if (amax < bmin) return -1;
-           if (amin > bmax) return +1;
-    }
-
-    cmp = edges_compare_x_for_y (a, b, sweep_line->current_y);
-    if (cmp)
-	return 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;
+	/* 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. */
+	/* We've got two collinear edges now. */
 
-    /* Since we're dealing with start events, prefer comparing top
-     * edges before bottom edges. */
-    cmp = _cairo_bo_point32_compare (&a->top, &b->top);
-    if (cmp)
-	return cmp;
+	/* Since we're dealing with start events, prefer comparing top
+	 * edges before bottom edges. */
+	cmp = a->edge.top - b->edge.top;
+	if (cmp)
+	    return cmp;
 
-    cmp = _cairo_bo_point32_compare (&a->bottom, &b->bottom);
-    if (cmp)
-	return cmp;
+	cmp = a->edge.bottom - b->edge.bottom;
+	if (cmp)
+	    return cmp;
+    }
 
-    /* Finally, we've got two identical edges. Let's finally
-     * discriminate by a simple pointer comparison, (which works only
-     * because we "know" the edges are all in a single array and don't
-     * move. */
-    if (a > b)
-	return 1;
-    else
-	return -1;
+    return a - b;
 }
 
 static int
 _sweep_line_elt_compare (void	*list,
-			 void	*a,
-			 void	*b)
+			 const void	*a,
+			 const void	*b)
 {
     cairo_bo_sweep_line_t *sweep_line = list;
-    sweep_line_elt_t *edge_elt_a = a;
-    sweep_line_elt_t *edge_elt_b = b;
+    const sweep_line_elt_t *edge_elt_a = a;
+    const sweep_line_elt_t *edge_elt_b = b;
 
     return _cairo_bo_sweep_line_compare_edges (sweep_line,
 					       edge_elt_a->edge,
@@ -522,157 +633,44 @@ _sweep_line_elt_compare (void	*list,
 }
 
 static inline int
-cairo_bo_event_compare (cairo_bo_event_t const *a,
-			cairo_bo_event_t const *b)
+cairo_bo_event_compare (const cairo_bo_event_t *a,
+			const cairo_bo_event_t *b)
 {
     int cmp;
 
-    /* The major motion of the sweep line is vertical (top-to-bottom),
-     * and the minor motion is horizontal (left-to-right), dues to the
-     * infinitesimal tilt rule.
-     *
-     * Our point comparison function respects these rules.
-     */
     cmp = _cairo_bo_point32_compare (&a->point, &b->point);
     if (cmp)
 	return cmp;
 
-    /* The events share a common point, so further discrimination is
-     * determined by the event type. Due to the infinitesimal
-     * shortening rule, stop events come first, then intersection
-     * events, then start events.
-     */
-    if (a->type != b->type) {
-	if (a->type == CAIRO_BO_EVENT_TYPE_STOP)
-	    return -1;
-	if (a->type == CAIRO_BO_EVENT_TYPE_START)
-	    return 1;
-
-	if (b->type == CAIRO_BO_EVENT_TYPE_STOP)
-	    return 1;
-	if (b->type == CAIRO_BO_EVENT_TYPE_START)
-	    return -1;
-    }
-
-    /* At this stage we are looking at two events of the same type at
-     * the same point. The final sort key is a slope comparison. We
-     * need a different sense for start and stop events based on the
-     * shortening rule.
-     *
-     * Note: Fortunately, we get to ignore errors in the relative
-     * ordering of intersection events. This means we don't even have
-     * to look at e2 here, nor worry about which sense of the slope
-     * comparison test is used for intersection events.
-     */
-    cmp = _slope_compare (a->e1, b->e1);
-    if (cmp) {
-	if (a->type == CAIRO_BO_EVENT_TYPE_START)
-	    return cmp;
-	else
-	    return - cmp;
-    }
-
-    /* Next look at the opposite point. This leaves ambiguities only
-     * for identical edges. */
-    if (a->type == CAIRO_BO_EVENT_TYPE_START) {
-	cmp = _cairo_bo_point32_compare (&b->e1->bottom,
-					 &a->e1->bottom);
-	if (cmp)
-	    return cmp;
-    }
-    else if (a->type == CAIRO_BO_EVENT_TYPE_STOP) {
-	cmp = _cairo_bo_point32_compare (&a->e1->top,
-					 &b->e1->top);
-	if (cmp)
-	    return cmp;
-    }
-    else { /* CAIRO_BO_EVENT_TYPE_INTERSECT */
-	/* For two intersection events at the identical point, we
-	 * don't care what order they sort in, but we do care that we
-	 * have a stable sort. In particular intersections between
-	 * different pairs of edges must never return 0. */
-	cmp = _cairo_bo_point32_compare (&a->e2->top, &b->e2->top);
-	if (cmp)
-	    return cmp;
-	cmp = _cairo_bo_point32_compare (&a->e2->bottom, &b->e2->bottom);
-	if (cmp)
-	    return cmp;
-	cmp = _cairo_bo_point32_compare (&a->e1->top, &b->e1->top);
-	if (cmp)
-	    return cmp;
-	cmp = _cairo_bo_point32_compare (&a->e1->bottom, &b->e1->bottom);
-	if (cmp)
-	    return cmp;
-     }
-
-    /* Discrimination based on the edge pointers. */
-    if (a->e1 < b->e1)
-	return -1;
-    if (a->e1 > b->e1)
-	return +1;
-    if (a->e2 < b->e2)
-	return -1;
-    if (a->e2 > b->e2)
-	return +1;
-    return 0;
-}
-
-static int
-cairo_bo_event_compare_abstract (void		*list,
-				 void	*a,
-				 void	*b)
-{
-    cairo_bo_event_t *event_a = a;
-    cairo_bo_event_t *event_b = b;
+    cmp = a->type - b->type;
+    if (cmp)
+	return cmp;
 
-    return cairo_bo_event_compare (event_a, event_b);
+    return a - b;
 }
 
 static int
-cairo_bo_event_compare_pointers (const cairo_bo_event_t *a,
-				 const cairo_bo_event_t *b)
+cairo_bo_event_compare_skiplist (void *list, const void *a, const void *b)
 {
-    int cmp;
-
-    if (a == b)
-	return 0;
-    cmp = cairo_bo_event_compare (a, b);
-    if (cmp)
-	return cmp;
-
-    return a - b;
+    return cairo_bo_event_compare (a, b);
 }
 
 static inline cairo_int64_t
-det32_64 (int32_t a,
-	  int32_t b,
-	  int32_t c,
-	  int32_t d)
+det32_64 (int32_t a, int32_t b,
+	  int32_t c, int32_t d)
 {
-    cairo_int64_t ad;
-    cairo_int64_t bc;
-
     /* det = a * d - b * c */
-    ad = _cairo_int32x32_64_mul (a, d);
-    bc = _cairo_int32x32_64_mul (b, c);
-
-    return _cairo_int64_sub (ad, bc);
+    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)
+det64x32_128 (cairo_int64_t a, int32_t       b,
+	      cairo_int64_t c, int32_t       d)
 {
-    cairo_int128_t ad;
-    cairo_int128_t bc;
-
     /* det = a * d - b * c */
-    ad = _cairo_int64x32_128_mul (a, d);
-    bc = _cairo_int64x32_128_mul (c, b);
-
-    return _cairo_int128_sub (ad, bc);
+    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
@@ -681,7 +679,7 @@ det64x32_128 (cairo_int64_t a,
  * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or
  * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel.
  */
-static cairo_bo_status_t
+static cairo_bool_t
 intersect_lines (cairo_bo_edge_t		*a,
 		 cairo_bo_edge_t		*b,
 		 cairo_bo_intersect_point_t	*intersection)
@@ -694,11 +692,11 @@ intersect_lines (cairo_bo_edge_t		*a,
      * What we're doing to mitigate this is to perform clamping in
      * cairo_bo_tessellate_polygon().
      */
-    int32_t dx1 = a->top.x - a->bottom.x;
-    int32_t dy1 = a->top.y - a->bottom.y;
+    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->top.x - b->bottom.x;
-    int32_t dy2 = b->top.y - b->bottom.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;
@@ -706,7 +704,7 @@ intersect_lines (cairo_bo_edge_t		*a,
 
     den_det = det32_64 (dx1, dy1, dx2, dy2);
     if (_cairo_int64_is_zero (den_det))
-	return CAIRO_BO_STATUS_PARALLEL;
+	return FALSE;
 
      /* Q: Can we determine that the lines do not intersect (within range)
       * much more cheaply than computing the intersection point i.e. by
@@ -727,58 +725,90 @@ intersect_lines (cairo_bo_edge_t		*a,
       * 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->top.x - a->top.x, b->top.y - a->top.y);
+    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_is_zero (R))
-	return CAIRO_BO_STATUS_NO_INTERSECTION;
+	return FALSE;
     if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (R))
-	return CAIRO_BO_STATUS_NO_INTERSECTION;
+	return FALSE;
     if (_cairo_int64_negative (den_det)) {
 	if (_cairo_int64_ge (den_det, R))
-	    return CAIRO_BO_STATUS_NO_INTERSECTION;
+	    return FALSE;
     } else {
 	if (_cairo_int64_le (den_det, R))
-	    return CAIRO_BO_STATUS_NO_INTERSECTION;
+	    return FALSE;
     }
 
-    R = det32_64 (dy1, dx1, a->top.y - b->top.y, a->top.x - b->top.x);
+    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_is_zero (R))
-	return CAIRO_BO_STATUS_NO_INTERSECTION;
+	return FALSE;
     if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (R))
-	return CAIRO_BO_STATUS_NO_INTERSECTION;
+	return FALSE;
     if (_cairo_int64_negative (den_det)) {
 	if (_cairo_int64_ge (den_det, R))
-	    return CAIRO_BO_STATUS_NO_INTERSECTION;
+	    return FALSE;
     } else {
 	if (_cairo_int64_le (den_det, R))
-	    return CAIRO_BO_STATUS_NO_INTERSECTION;
+	    return FALSE;
     }
 
     /* We now know that the two lines should intersect within range. */
 
-    a_det = det32_64 (a->top.x, a->top.y,
-		      a->bottom.x, a->bottom.y);
-    b_det = det32_64 (b->top.x, b->top.y,
-		      b->bottom.x, b->bottom.y);
+    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 CAIRO_BO_STATUS_NO_INTERSECTION;
-    intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo);
+	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 CAIRO_BO_STATUS_NO_INTERSECTION;
-    intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo);
+	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 CAIRO_BO_STATUS_INTERSECTION;
+    return TRUE;
 }
 
 static int
@@ -825,8 +855,10 @@ _cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t		*edge,
      * finder which needs them.
      */
 
-    cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y, edge->top.y);
-    cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y, edge->bottom.y);
+    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)
     {
@@ -849,10 +881,19 @@ _cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t		*edge,
      * edge, then the x value of the point must be less to be
      * considered as inside. */
 
-    if (cmp_top == 0)
-	return (_cairo_bo_intersect_ordinate_32_compare (point->x, edge->top.x) > 0);
-    else /* cmp_bottom == 0 */
-	return (_cairo_bo_intersect_ordinate_32_compare (point->x, edge->bottom.x) < 0);
+    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
@@ -871,23 +912,21 @@ _cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t		*edge,
  * effectively outside (no intersection is returned). Also, if the
  * intersection point has the same
  */
-static cairo_bo_status_t
+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_status_t status;
     cairo_bo_intersect_point_t quorem;
 
-    status = intersect_lines (a, b, &quorem);
-    if (status)
-	return status;
+    if (! intersect_lines (a, b, &quorem))
+	return FALSE;
 
     if (! _cairo_bo_edge_contains_intersect_point (a, &quorem))
-	return CAIRO_BO_STATUS_NO_INTERSECTION;
+	return FALSE;
 
     if (! _cairo_bo_edge_contains_intersect_point (b, &quorem))
-	return CAIRO_BO_STATUS_NO_INTERSECTION;
+	return FALSE;
 
     /* Now that we've correctly compared the intersection point and
      * determined that it lies within the edge, then we know that we
@@ -897,31 +936,32 @@ _cairo_bo_edge_intersect (cairo_bo_edge_t	*a,
     intersection->x = quorem.x.ordinate;
     intersection->y = quorem.y.ordinate;
 
-    return CAIRO_BO_STATUS_INTERSECTION;
+    return TRUE;
 }
 
 static void
-_cairo_bo_event_init (cairo_bo_event_t		*event,
-		      cairo_bo_event_type_t	 type,
-		      cairo_bo_edge_t	*e1,
-		      cairo_bo_edge_t	*e2,
-		      cairo_bo_point32_t	 point)
+_cairo_bo_skiplist_event_init (cairo_bo_skiplist_event_t *event,
+			       cairo_bo_event_type_t	 type,
+			       cairo_bo_edge_t		*e1,
+			       cairo_bo_edge_t		*e2,
+			       const cairo_point_t	 *point)
 {
     event->type = type;
     event->e1 = e1;
     event->e2 = e2;
-    event->point = point;
+    event->point = *point;
 }
 
 static cairo_status_t
 _cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue,
-			      cairo_bo_event_t	     *event)
+			      cairo_bo_skiplist_event_t *event)
 {
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
-    /* Don't insert if there's already an equivalent intersection event in the queue. */
-    if (_cairo_skip_list_insert (&queue->intersection_queue, event,
-		      event->type == CAIRO_BO_EVENT_TYPE_INTERSECTION) == NULL)
+
+    /* Only insert if there is no prior identical intersection event. */
+    if (unlikely (_cairo_skip_list_insert (&queue->event_queue, event) == NULL))
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
     return status;
 }
 
@@ -929,126 +969,102 @@ static void
 _cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue,
 			      cairo_bo_event_t	     *event)
 {
-    if (CAIRO_BO_EVENT_TYPE_INTERSECTION == event->type)
-	_cairo_skip_list_delete_given ( &queue->intersection_queue, &event->elt );
+    _cairo_skip_list_delete_given (&queue->event_queue,
+				   &((cairo_bo_skiplist_event_t *) event)->elt);
 }
 
+#define NEXT_EVENT(Q) \
+    ((Q)->chains[0] ?  SKIP_ELT_TO_EVENT ((Q)->chains[0]) : NULL)
 static cairo_bo_event_t *
 _cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue)
 {
-    skip_elt_t *elt = event_queue->intersection_queue.chains[0];
-    cairo_bo_event_t *intersection = elt ? SKIP_ELT_TO_EVENT (elt) : NULL;
-    cairo_bo_event_t *startstop;
+    cairo_bo_event_t *event, *cmp;
 
-    startstop = *event_queue->sorted_startstop_event_ptrs;
-    if (startstop == NULL)
-	return intersection;
+    event = NEXT_EVENT (&event_queue->event_queue);
 
-    if (intersection == NULL ||
-	cairo_bo_event_compare (startstop, intersection) <= 0)
+    cmp = *event_queue->start_events;
+    if (event == NULL ||
+	(cmp != NULL && cairo_bo_event_compare (cmp, event) < 0))
     {
-	event_queue->sorted_startstop_event_ptrs++;
-	return startstop;
+	event = cmp;
+	event_queue->start_events++;
     }
 
-    return intersection;
+    return event;
 }
 
 CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort,
 			cairo_bo_event_t *,
-			cairo_bo_event_compare_pointers)
+			cairo_bo_event_compare)
 
-static cairo_status_t
-_cairo_bo_event_queue_init (cairo_bo_event_queue_t	*event_queue,
-			    cairo_bo_edge_t	*edges,
-			    int				 num_edges)
+static void
+_cairo_bo_event_queue_init (cairo_bo_event_queue_t	 *event_queue,
+			    cairo_bo_event_t		**start_events,
+			    int				  num_events)
 {
-    int i;
-    cairo_bo_event_t *events, **sorted_event_ptrs;
-    unsigned num_events = 2*num_edges;
-
-    /* The skip_elt_t field of a cairo_bo_event_t isn't used for start
-     * or stop events, so this allocation is safe.  XXX: make the
-     * event type a union so it doesn't always contain the skip
-     * elt? */
-    events = _cairo_malloc_ab_plus_c (num_events,
-				      sizeof (cairo_bo_event_t) +
-				      sizeof (cairo_bo_event_t *),
-				      sizeof (cairo_bo_event_t *));
-    if (unlikely (events == NULL))
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    _cairo_bo_event_queue_sort (start_events, num_events);
+    start_events[num_events] = NULL;
 
-    sorted_event_ptrs = (cairo_bo_event_t **) (events + num_events);
-    event_queue->startstop_events = events;
-    event_queue->sorted_startstop_event_ptrs = sorted_event_ptrs;
+    event_queue->start_events = start_events;
 
-    for (i = 0; i < num_edges; i++) {
-	sorted_event_ptrs[i] = &events[2*i];
-	sorted_event_ptrs[i+num_edges] = &events[2*i+1];
-
-	/* Initialize "middle" to top */
-	edges[i].middle = edges[i].top;
-
-	_cairo_bo_event_init (&events[2*i],
-			      CAIRO_BO_EVENT_TYPE_START,
-			      &edges[i], NULL,
-			      edges[i].top);
-
-	_cairo_bo_event_init (&events[2*i+1],
-			      CAIRO_BO_EVENT_TYPE_STOP,
-			      &edges[i], NULL,
-			      edges[i].bottom);
-    }
+    _cairo_skip_list_init (&event_queue->event_queue,
+			   cairo_bo_event_compare_skiplist,
+			   sizeof (cairo_bo_skiplist_event_t));
+}
 
-    _cairo_bo_event_queue_sort (sorted_event_ptrs, num_events);
-    event_queue->sorted_startstop_event_ptrs[num_events] = 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;
+    cairo_bo_skiplist_event_t event;
 
-    _cairo_skip_list_init (&event_queue->intersection_queue,
-			   cairo_bo_event_compare_abstract,
-			   sizeof (cairo_bo_event_t));
+    point.y = edge->edge.bottom;
+    point.x = _line_compute_intersection_x_for_y (&edge->edge.line,
+						  point.y);
+    _cairo_bo_skiplist_event_init (&event,
+				   CAIRO_BO_EVENT_TYPE_STOP,
+				   edge, NULL,
+				   &point);
 
-    return CAIRO_STATUS_SUCCESS;
+    return _cairo_bo_event_queue_insert (event_queue, &event);
 }
 
 static void
 _cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue)
 {
-    _cairo_skip_list_fini (&event_queue->intersection_queue);
-    if (event_queue->startstop_events)
-	free (event_queue->startstop_events);
+    _cairo_skip_list_fini (&event_queue->event_queue);
 }
 
-static cairo_status_t
+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_edge_t *right)
 {
-    cairo_bo_status_t status;
     cairo_bo_point32_t intersection;
-    cairo_bo_event_t event;
+    cairo_bo_skiplist_event_t event;
 
     if (left == NULL || right == NULL)
 	return CAIRO_STATUS_SUCCESS;
 
+    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 oalready
+     * that the intersection of these two segments has already
      * occurred before the current sweep line position. */
-    if (_slope_compare (left, right) < 0)
+    if (_slope_compare (left, right) <= 0)
 	return CAIRO_STATUS_SUCCESS;
 
-    status = _cairo_bo_edge_intersect (left, right, &intersection);
-    if (status == CAIRO_BO_STATUS_PARALLEL ||
-	status == CAIRO_BO_STATUS_NO_INTERSECTION)
-    {
+    if (! _cairo_bo_edge_intersect (left, right, &intersection))
 	return CAIRO_STATUS_SUCCESS;
-    }
 
-    _cairo_bo_event_init (&event,
-			  CAIRO_BO_EVENT_TYPE_INTERSECTION,
-			  left, right,
-			  intersection);
+    _cairo_bo_skiplist_event_init (&event,
+				   CAIRO_BO_EVENT_TYPE_INTERSECTION,
+				   left, right,
+				   &intersection);
 
     return _cairo_bo_event_queue_insert (event_queue, &event);
 }
@@ -1062,7 +1078,8 @@ _cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line)
 
     sweep_line->head = NULL;
     sweep_line->tail = NULL;
-    sweep_line->current_y = 0;
+    sweep_line->stopped = NULL;
+    sweep_line->current_y = INT32_MIN;
 }
 
 static void
@@ -1079,14 +1096,13 @@ _cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t	*sweep_line,
     sweep_line_elt_t *sweep_line_elt;
     cairo_bo_edge_t **prev_of_next, **next_of_prev;
 
-    sweep_line_elt = _cairo_skip_list_insert (&sweep_line->active_edges, &edge,
-					      1 /* unique inserts*/);
+    sweep_line_elt = _cairo_skip_list_insert (&sweep_line->active_edges, &edge);
     if (unlikely (sweep_line_elt == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     next_elt = sweep_line_elt->elt.next[0];
     if (next_elt)
-	prev_of_next = & (SKIP_ELT_TO_EDGE (next_elt)->prev);
+	prev_of_next = &SKIP_ELT_TO_EDGE (next_elt)->prev;
     else
 	prev_of_next = &sweep_line->tail;
 
@@ -1170,8 +1186,8 @@ static void
 _cairo_bo_edge_print (cairo_bo_edge_t *edge)
 {
     printf ("(0x%x, 0x%x)-(0x%x, 0x%x)",
-	    edge->top.x, edge->top.y,
-	    edge->bottom.x, edge->bottom.y);
+	    edge->edge.line.p1.x, edge->edge.line.p1.y,
+	    edge->edge.line.p2.x, edge->edge.line.p2.y);
 }
 
 static void
@@ -1201,19 +1217,14 @@ static void
 _cairo_bo_event_queue_print (cairo_bo_event_queue_t *event_queue)
 {
     skip_elt_t *elt;
+
     /* XXX: fixme to print the start/stop array too. */
-    cairo_skip_list_t *queue = &event_queue->intersection_queue;
-    cairo_bo_event_t *event;
+    cairo_skip_list_t *queue = &event_queue->event_queue;
 
     printf ("Event queue:\n");
 
-    for (elt = queue->chains[0];
-	 elt;
-	 elt = elt->next[0])
-    {
-	event = SKIP_ELT_TO_EVENT (elt);
-	_cairo_bo_event_print (event);
-    }
+    for (elt = queue->chains[0]; elt; elt = elt->next[0])
+	_cairo_bo_event_print (SKIP_ELT_TO_EVENT (elt));
 }
 
 static void
@@ -1266,140 +1277,18 @@ _cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line)
 
 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\n", msg);
+    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
 
-/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t
- * of bo_traps. */
-static cairo_status_t
-_cairo_bo_edge_end_trap (cairo_bo_edge_t	*left,
-			 int32_t		bot,
-			 cairo_bo_traps_t	*bo_traps)
-{
-    cairo_fixed_t fixed_top, fixed_bot;
-    cairo_bo_trap_t *trap = left->deferred_trap;
-    cairo_bo_edge_t *right;
-
-    if (!trap)
-	return CAIRO_STATUS_SUCCESS;
-
-     /* If the right edge of the trapezoid stopped earlier than the
-      * left edge, then cut the trapezoid bottom early. */
-    right = trap->right;
-    if (right->bottom.y < bot)
-	bot = right->bottom.y;
-
-    fixed_top = trap->top;
-    fixed_bot = bot;
-
-    /* Only emit trapezoids with positive height. */
-    if (fixed_top < fixed_bot) {
-	cairo_line_t left_line;
-	cairo_line_t right_line;
-	cairo_fixed_t xmin = bo_traps->xmin;
-	cairo_fixed_t ymin = bo_traps->ymin;
-	fixed_top += ymin;
-	fixed_bot += ymin;
-
-	left_line.p1.x  = left->top.x + xmin;
-	left_line.p1.y  = left->top.y + ymin;
-	right_line.p1.x = right->top.x + xmin;
-	right_line.p1.y = right->top.y + ymin;
-
-	left_line.p2.x  = left->bottom.x + xmin;
-	left_line.p2.y  = left->bottom.y + ymin;
-	right_line.p2.x = right->bottom.x + xmin;
-	right_line.p2.y = right->bottom.y + ymin;
-
-	/* Avoid emitting the trapezoid if it is obviously degenerate.
-	 * TODO: need a real collinearity test here for the cases
-	 * where the trapezoid is degenerate, yet the top and bottom
-	 * coordinates aren't equal.  */
-	if (left_line.p1.x != right_line.p1.x ||
-	    left_line.p1.y != right_line.p1.y ||
-	    left_line.p2.x != right_line.p2.x ||
-	    left_line.p2.y != right_line.p2.y)
-	{
-	    _cairo_traps_add_trap (bo_traps->traps,
-				   fixed_top, fixed_bot,
-				   &left_line, &right_line);
-
-#if DEBUG_PRINT_STATE
-	    printf ("Deferred trap: left=(%08x, %08x)-(%08x,%08x) "
-		    "right=(%08x,%08x)-(%08x,%08x) top=%08x, bot=%08x\n",
-		    left->top.x, left->top.y, left->bottom.x, left->bottom.y,
-		    right->top.x, right->top.y, right->bottom.x, right->bottom.y,
-		    trap->top, bot);
-#endif
-	}
-    }
-
-    _cairo_freelist_free (&bo_traps->freelist, trap);
-    left->deferred_trap = NULL;
-
-    return _cairo_traps_status (bo_traps->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 `bo_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 cairo_status_t
-_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t	*edge,
-				       int32_t		top,
-				       cairo_bo_traps_t	*bo_traps)
-{
-    cairo_status_t status;
-    cairo_bo_trap_t *trap = edge->deferred_trap;
-
-    if (trap) {
-	if (trap->right == edge->next) return CAIRO_STATUS_SUCCESS;
-	status = _cairo_bo_edge_end_trap (edge, top, bo_traps);
-	if (status)
-	    return status;
-    }
-
-    if (edge->next) {
-	trap = edge->deferred_trap = _cairo_freelist_alloc (&bo_traps->freelist);
-	if (!edge->deferred_trap)
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
-	trap->right = edge->next;
-	trap->top = top;
-    }
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static void
-_cairo_bo_traps_init (cairo_bo_traps_t	*bo_traps,
-		      cairo_traps_t	*traps,
-		      cairo_fixed_t	 xmin,
-		      cairo_fixed_t	 ymin,
-		      cairo_fixed_t	 xmax,
-		      cairo_fixed_t	 ymax)
-{
-    bo_traps->traps = traps;
-    _cairo_freelist_init (&bo_traps->freelist, sizeof(cairo_bo_trap_t));
-    bo_traps->xmin = xmin;
-    bo_traps->ymin = ymin;
-    bo_traps->xmax = xmax;
-    bo_traps->ymax = ymax;
-}
-
-static void
-_cairo_bo_traps_fini (cairo_bo_traps_t *bo_traps)
-{
-    _cairo_freelist_fini (&bo_traps->freelist);
-}
-
 #if DEBUG_VALIDATE
 static void
 _cairo_bo_sweep_line_validate (cairo_bo_sweep_line_t *sweep_line)
@@ -1428,184 +1317,403 @@ _cairo_bo_sweep_line_validate (cairo_bo_sweep_line_t *sweep_line)
 }
 #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)
+{
+    if (_line_equal (&a->edge.line, &b->edge.line))
+	return TRUE;
+
+    /* The choice of y is not truly arbitrary since we must guarantee that it
+     * is greater than the start of either line.
+     */
+    return _slope_compare (a, b) == 0 &&
+	edges_compare_x_for_y (a, b,
+				  MAX (a->edge.line.p1.y,
+				       b->edge.line.p1.y)) == 0;
+}
 
+/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */
 static cairo_status_t
-_active_edges_to_traps (cairo_bo_edge_t		*head,
+_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 && ! edges_colinear (left, trap->right))) {
+	assert (trap->right->edge.bottom >= 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;
+
+    if (left->deferred_trap.right != NULL) {
+	if (right != NULL && 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 (right != NULL) {
+	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		*left,
 			int32_t			 top,
 			cairo_fill_rule_t	 fill_rule,
-			cairo_bo_traps_t	*bo_traps)
+			cairo_traps_t	        *traps)
 {
+    cairo_bo_edge_t *right;
     cairo_status_t status;
-    int in_out = 0;
-    cairo_bo_edge_t *edge;
 
-    for (edge = head; edge; edge = edge->next) {
-	if (fill_rule == CAIRO_FILL_RULE_WINDING) {
-	    in_out += edge->dir;
-	    if (in_out == 0) {
-		status = _cairo_bo_edge_end_trap (edge, top, bo_traps);
-		if (status)
-		    return status;
-		continue;
+#if DEBUG_PRINT_STATE
+    printf ("Processing active edges for %x\n", top);
+#endif
+
+    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_colinear (left, right)) {
+		    /* continuation on left */
+		    left->deferred_trap = right->deferred_trap;
+		    right->deferred_trap.right = NULL;
+		}
 	    }
-	} else {
-	    in_out++;
-	    if ((in_out & 1) == 0) {
-		status = _cairo_bo_edge_end_trap (edge, top, bo_traps);
-		if (status)
-		    return status;
-		continue;
+
+	    /* End all subsumed traps */
+	    right = left->next;
+	    while (right != NULL) {
+		if (right->deferred_trap.right != NULL) {
+		    status = _cairo_bo_edge_end_trap (right, top, traps);
+		    if (unlikely (status))
+			return status;
+		}
+
+		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;
 	    }
+
+	    status = _cairo_bo_edge_start_or_continue_trap (left, right,
+							    top, traps);
+	    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, traps);
+		    if (unlikely (status))
+			return status;
+		}
 
-	status = _cairo_bo_edge_start_or_continue_trap (edge, top, bo_traps);
-	if (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_colinear (right, next);
+
+		    if (! skip)
+			break;
+		}
+
+		right = right->next;
+	    }
+
+	    status = _cairo_bo_edge_start_or_continue_trap (left, right,
+							    top, traps);
+	    if (unlikely (status))
+		return status;
+
+	    left = right;
+	    if (left != NULL)
+		left = left->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_edge_t	*edges,
-					    int			 num_edges,
+_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t   **start_events,
+					    int			 num_events,
 					    cairo_fill_rule_t	 fill_rule,
 					    cairo_traps_t	*traps,
-					    cairo_fixed_t	xmin,
-					    cairo_fixed_t	ymin,
-					    cairo_fixed_t	xmax,
-					    cairo_fixed_t	ymax,
 					    int			*num_intersections)
 {
-    cairo_status_t status;
+    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_traps_t bo_traps;
-    cairo_bo_event_t *event, event_saved;
-    cairo_bo_edge_t *edge;
+    cairo_bo_event_t *event;
     cairo_bo_edge_t *left, *right;
-    cairo_bo_edge_t *edge1, *edge2;
-
-    if (num_edges == 0)
-	return CAIRO_STATUS_SUCCESS;
-
-    status = _cairo_bo_event_queue_init (&event_queue, edges, num_edges);
-    if (status)
-	return status;
-
-    _cairo_bo_sweep_line_init (&sweep_line);
-
-    _cairo_bo_traps_init (&bo_traps, traps, xmin, ymin, xmax, ymax);
+    cairo_bo_edge_t *e1, *e2;
 
-#if DEBUG_PRINT_STATE
-    print_state ("After initializing", &event_queue, &sweep_line);
+#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
 
-    while (1)
-    {
-	event = _cairo_bo_event_dequeue (&event_queue);
-	if (!event)
-	    break;
+    _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, &bo_traps);
-	    if (status)
+					     fill_rule, traps);
+	    if (unlikely (status))
 		goto unwind;
 
 	    sweep_line.current_y = event->point.y;
 	}
 
-	event_saved = *event;
-	_cairo_bo_event_queue_delete (&event_queue, event);
-	event = &event_saved;
+#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:
-	    edge = event->e1;
+	    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_sweep_line_insert (&sweep_line, edge);
-	    if (status)
+	    status = _cairo_bo_event_queue_insert_stop (&event_queue, e1);
+	    if (unlikely (status))
 		goto unwind;
-	    /* Cache the insert position for use in pass 2.
-	    event->e2 = Sortlist::prev (sweep_line, edge);
-	    */
 
-	    left = edge->prev;
-	    right = edge->next;
+	    /* 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;
 
-	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, edge);
-	    if (status)
+	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
+	    if (unlikely (status))
 		goto unwind;
 
-	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, edge, right);
-	    if (status)
+	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+	    if (unlikely (status))
 		goto unwind;
 
-#if DEBUG_PRINT_STATE
-	    print_state ("After processing start", &event_queue, &sweep_line);
-#endif
 	    break;
 
 	case CAIRO_BO_EVENT_TYPE_STOP:
-	    edge = event->e1;
+	    e1 = ((cairo_bo_skiplist_event_t *) event)->e1;
+	    _cairo_bo_event_queue_delete (&event_queue, event);
 
-	    left = edge->prev;
-	    right = edge->next;
+	    left = e1->prev;
+	    right = e1->next;
 
-	    _cairo_bo_sweep_line_delete (&sweep_line, edge);
+	    _cairo_bo_sweep_line_delete (&sweep_line, e1);
 
-	    status = _cairo_bo_edge_end_trap (edge, edge->bottom.y, &bo_traps);
-	    if (status)
-		goto unwind;
+	    /* 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;
+	    }
 
 	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
-	    if (status)
+	    if (unlikely (status))
 		goto unwind;
 
-#if DEBUG_PRINT_STATE
-	    print_state ("After processing stop", &event_queue, &sweep_line);
-#endif
 	    break;
 
 	case CAIRO_BO_EVENT_TYPE_INTERSECTION:
-	    edge1 = event->e1;
-	    edge2 = event->e2;
+	    e1 = ((cairo_bo_skiplist_event_t *) event)->e1;
+	    e2 = ((cairo_bo_skiplist_event_t *) event)->e2;
+	    _cairo_bo_event_queue_delete (&event_queue, event);
 
 	    /* skip this intersection if its edges are not adjacent */
-	    if (edge2 != edge1->next)
+	    if (e2 != e1->next)
 		break;
 
 	    intersection_count++;
 
-	    edge1->middle = event->point;
-	    edge2->middle = event->point;
+	    left = e1->prev;
+	    right = e2->next;
 
-	    left = edge1->prev;
-	    right = edge2->next;
-
-	    _cairo_bo_sweep_line_swap (&sweep_line, edge1, edge2);
+	    _cairo_bo_sweep_line_swap (&sweep_line, e1, e2);
 
 	    /* after the swap e2 is left of e1 */
 
-	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue,
-								       left, edge2);
-	    if (status)
+	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
+	    if (unlikely (status))
 		goto unwind;
 
-	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue,
-								       edge1, right);
-	    if (status)
+	    status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
+	    if (unlikely (status))
 		goto unwind;
 
-#if DEBUG_PRINT_STATE
-	    print_state ("After processing intersection", &event_queue, &sweep_line);
-#endif
 	    break;
 	}
 #if DEBUG_VALIDATE
@@ -1614,29 +1722,22 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t	*edges,
     }
 
     *num_intersections = intersection_count;
- unwind:
-    for (edge = sweep_line.head; edge; edge = edge->next) {
-	cairo_status_t status2 = _cairo_bo_edge_end_trap (edge,
-							  sweep_line.current_y,
-							  &bo_traps);
-	if (!status)
-	    status = status2;
+    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;
+	}
     }
-    _cairo_bo_traps_fini (&bo_traps);
+ unwind:
     _cairo_bo_sweep_line_fini (&sweep_line);
     _cairo_bo_event_queue_fini (&event_queue);
-    return status;
-}
 
-static void
-update_minmax(cairo_fixed_t *inout_min,
-	      cairo_fixed_t *inout_max,
-	      cairo_fixed_t v)
-{
-    if (v < *inout_min)
-	*inout_min = v;
-    if (v > *inout_max)
-	*inout_max = v;
+#if DEBUG_EVENTS
+    event_log ("\n");
+#endif
+
+    return status;
 }
 
 cairo_status_t
@@ -1646,116 +1747,332 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t	 *traps,
 {
     int intersections;
     cairo_status_t status;
-    cairo_bo_edge_t stack_edges[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_edge_t)];
-    cairo_bo_edge_t *edges;
-    cairo_fixed_t xmin = 0x7FFFFFFF;
-    cairo_fixed_t ymin = 0x7FFFFFFF;
-    cairo_fixed_t xmax = -0x80000000;
-    cairo_fixed_t ymax = -0x80000000;
-    cairo_box_t limit;
-    cairo_bool_t has_limits;
-    int num_bo_edges;
+    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;
 
-    if (0 == polygon->num_edges)
+    num_events = polygon->num_edges;
+    if (unlikely (0 == num_events))
 	return CAIRO_STATUS_SUCCESS;
 
-    if (CAIRO_INJECT_FAULT ())
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
-    has_limits = _cairo_traps_get_limit (traps, &limit);
-
-    edges = stack_edges;
-    if (polygon->num_edges > ARRAY_LENGTH (stack_edges)) {
-	edges = _cairo_malloc_ab (polygon->num_edges, sizeof (cairo_bo_edge_t));
-	if (unlikely (edges == NULL))
+    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);
-    }
 
-    /* Figure out the bounding box of the input coordinates and
-     * validate that we're not given invalid polygon edges. */
-    for (i = 0; i < polygon->num_edges; i++) {
-	update_minmax (&xmin, &xmax, polygon->edges[i].edge.p1.x);
-	update_minmax (&ymin, &ymax, polygon->edges[i].edge.p1.y);
-	update_minmax (&xmin, &xmax, polygon->edges[i].edge.p2.x);
-	update_minmax (&ymin, &ymax, polygon->edges[i].edge.p2.y);
-	assert (polygon->edges[i].edge.p1.y <= polygon->edges[i].edge.p2.y &&
-		"BUG: tessellator given upside down or horizontal edges");
+	event_ptrs = (cairo_bo_event_t **) (events + num_events);
     }
 
-    /* The tessellation functions currently assume that no line
-     * segment extends more than 2^31-1 in either dimension.  We
-     * guarantee this by offsetting the internal coordinates to the
-     * range [0,2^31-1], and clamping to 2^31-1 if a coordinate
-     * exceeds the range (and yes, this generates an incorrect
-     * result).  First we have to clamp the bounding box itself. */
-    /* XXX: Rather than changing the input values, a better approach
-     * would be to detect out-of-bounds input and return a
-     * CAIRO_STATUS_OVERFLOW value to the user. */
-    if (xmax - xmin < 0)
-	xmax = xmin + 0x7FFFFFFF;
-    if (ymax - ymin < 0)
-	ymax = ymin + 0x7FFFFFFF;
-
-    for (i = 0, num_bo_edges = 0; i < polygon->num_edges; i++) {
-	cairo_bo_edge_t *edge = &edges[num_bo_edges];
-	cairo_point_t top = polygon->edges[i].edge.p1;
-	cairo_point_t bot = polygon->edges[i].edge.p2;
-
-	/* Discard the edge if it lies outside the limits of traps. */
-	if (has_limits) {
-	    /* Strictly above or below the limits? */
-	    if (bot.y <= limit.p1.y || top.y >= limit.p2.y)
-		continue;
-	}
+    for (i = 0; i < num_events; i++) {
+	event_ptrs[i] = (cairo_bo_event_t *) &events[i];
 
-	/* Offset coordinates into the non-negative range. */
-	top.x -= xmin;
-	top.y -= ymin;
-	bot.x -= xmin;
-	bot.y -= ymin;
-
-	/* If the coordinates are still negative, then their extent is
-	 * overflowing 2^31-1.  We're going to kludge it and clamp the
-	 * coordinates into the clamped bounding box.  */
-	if (top.x < 0) top.x = xmax - xmin;
-	if (top.y < 0) top.y = ymax - ymin;
-	if (bot.x < 0) bot.x = xmax - xmin;
-	if (bot.y < 0) bot.y = ymax - ymin;
-
-	if (top.y == bot.y) {
-	    /* Clamping might have produced horizontal edges.  Ignore
-	     * those. */
-	    continue;
-	}
-	assert (top.y < bot.y &&
-		"BUG: clamping the input range flipped the "
-		"orientation of an edge");
-
-	edge->top.x = top.x;
-	edge->top.y = top.y;
-	edge->bottom.x = bot.x;
-	edge->bottom.y = bot.y;
-	edge->dir = polygon->edges[i].dir;
-	edge->deferred_trap = NULL;
-	edge->prev = NULL;
-	edge->next = NULL;
-	edge->sweep_line_elt = NULL;
-
-	num_bo_edges++;
+	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;
+	events[i].edge.sweep_line_elt = 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 (edges, num_bo_edges,
+    status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs,
+							 num_events,
 							 fill_rule, traps,
-							 xmin, ymin, xmax, ymax,
 							 &intersections);
+#if DEBUG_TRAPS
+    dump_traps (traps, "bo-polygon-out.txt");
+#endif
 
-    if (edges != stack_edges)
-	free (edges);
+    if (events != stack_events)
+	free (events);
+
+    return status;
+}
+
+static cairo_bool_t
+_points_outside (const cairo_fixed_t *x, const cairo_box_t *limits)
+{
+    if (x[0] < limits->p1.x || x[0] > limits->p2.x)
+	return TRUE;
+    if (x[1] < limits->p1.x || x[1] > limits->p2.x)
+	return TRUE;
+    if (x[2] < limits->p1.x || x[2] > limits->p2.x)
+	return TRUE;
+    if (x[3] < limits->p1.x || x[3] > limits->p2.x)
+	return TRUE;
+
+    return FALSE;
+}
+
+static int
+_add_event (const cairo_line_t *line,
+	   int32_t top, int32_t bottom,
+	   int dir,
+	   cairo_bo_start_event_t *event)
+{
+    if (top == bottom)
+	return 0;
+
+    event->type = CAIRO_BO_EVENT_TYPE_START;
+    event->point.y = top;
+    event->point.x = _line_compute_intersection_x_for_y (line, top);
+
+    event->edge.edge.line = *line;
+    event->edge.edge.top = top;
+    event->edge.edge.bottom = bottom;
+    event->edge.edge.dir = dir;
+
+    event->edge.deferred_trap.right = NULL;
+    event->edge.prev = NULL;
+    event->edge.next = NULL;
+    event->edge.sweep_line_elt = NULL;
+
+    return 1;
+}
+
+static int
+_compute_clipped_trapezoid_edges (const cairo_traps_t *traps,
+				  const cairo_trapezoid_t *t,
+				  cairo_bo_start_event_t *events)
+{
+    cairo_fixed_t x[4];
+    int num_events = 0;
+    int top, bot;
+
+    /* compute points in clockwise orientation */
+    top = t->top;
+    bot = t->bottom;
+
+    x[0] = _line_compute_intersection_x_for_y (&t->left, top);
+    x[1] = _line_compute_intersection_x_for_y (&t->right, top);
+    x[2] = _line_compute_intersection_x_for_y (&t->right, bot);
+    x[3] = _line_compute_intersection_x_for_y (&t->left, bot);
+
+    if (traps->has_limits && _points_outside (x, &traps->limits)) {
+	cairo_line_t limits[2];
+	cairo_fixed_t ly[2], ry[2];
+	cairo_fixed_t ymin, ymax;
+
+	limits[0].p1.x = traps->limits.p1.x;
+	limits[0].p1.y = traps->limits.p1.y;
+	limits[0].p2.x = traps->limits.p1.x;
+	limits[0].p2.y = traps->limits.p2.y;
+
+	limits[1].p1.x = traps->limits.p2.x;
+	limits[1].p1.y = traps->limits.p1.y;
+	limits[1].p2.x = traps->limits.p2.x;
+	limits[1].p2.y = traps->limits.p2.y;
+
+	ly[0] = _line_compute_intersection_y_for_x (&t->left,
+						    traps->limits.p1.x);
+	ymin = ymax = ly[0];
+
+	ly[1] = _line_compute_intersection_y_for_x (&t->left,
+						    traps->limits.p2.x);
+	if (ly[1] < ymin)
+	    ymin = ly[1];
+	if (ly[1] > ymax)
+	    ymax = ly[1];
+
+	ry[0] = _line_compute_intersection_y_for_x (&t->right,
+						    traps->limits.p1.x);
+	if (ry[0] < ymin)
+	    ymin = ry[0];
+	if (ry[0] > ymax)
+	    ymax = ry[0];
+
+	ry[1] = _line_compute_intersection_y_for_x (&t->right,
+						    traps->limits.p2.x);
+	if (ry[1] < ymin)
+	    ymin = ry[1];
+	if (ry[1] > ymax)
+	    ymax = ry[1];
+
+	if (ymin > top)
+	    top = ymin;
+	if (ymax < bot)
+	    bot = ymax;
+	if (top >= bot)
+	    return 0;
+
+	/* left hand side intersects */
+
+	if (x[0] <= traps->limits.p1.x && x[3] <= traps->limits.p1.x)
+	{
+	    num_events += _add_event (&limits[0], top, bot, 1,
+				      events + num_events);
+	}
+	else if (x[0] >= traps->limits.p1.x && x[3] >= traps->limits.p1.x &&
+		 x[0] <= traps->limits.p2.x && x[3] <= traps->limits.p2.x)
+	{
+	    num_events += _add_event (&t->left, top, bot, 1,
+				      events + num_events);
+	}
+	else
+	{
+	    if (ly[0] < ly[1]) {
+		if (ly[1] >= top) {
+		    if (ly[0] < top)
+			ly[0] = top;
+		    if (ly[1] > bot)
+			ly[1] = bot;
+		    num_events += _add_event (&limits[0], top, ly[0], 1,
+					    events + num_events);
+		    num_events += _add_event (&t->left, ly[0], ly[1], 1,
+					    events + num_events);
+		    num_events += _add_event (&limits[1], ly[1], bot, 1,
+					    events + num_events);
+		}
+	    } else {
+		if (ly[1] <= bot) {
+		    if (ly[1] < top)
+			ly[1] = top;
+		    if (ly[0] > bot)
+			ly[0] = bot;
+		    num_events += _add_event (&limits[1], top, ly[1], 1,
+					    events + num_events);
+		    num_events += _add_event (&t->left, ly[1], ly[0], 1,
+					    events + num_events);
+		    num_events += _add_event (&limits[0], ly[0], bot, 1,
+					    events + num_events);
+		}
+	    }
+	}
+
+	/* right hand side intersects */
+
+	if (x[1] >= traps->limits.p2.x && x[2] >= traps->limits.p2.x)
+	{
+	    num_events += _add_event (&limits[1], top, bot, -1,
+				      events + num_events);
+	}
+	else if (x[1] <= traps->limits.p2.x && x[2] <= traps->limits.p2.x &&
+		 x[1] >= traps->limits.p1.x && x[2] >= traps->limits.p1.x)
+	{
+	    num_events += _add_event (&t->right, top, bot, -1,
+				      events + num_events);
+	}
+	else
+	{
+	    if (ry[0] < ry[1]) {
+		if (ry[0] <= bot) {
+		    if (ry[0] < top)
+			ry[0] = top;
+		    if (ry[1] > bot)
+			ry[1] = bot;
+		    num_events += _add_event (&limits[0], top, ry[0], -1,
+					    events + num_events);
+		    num_events += _add_event (&t->right, ry[0], ry[1], -1,
+					    events + num_events);
+		    num_events += _add_event (&limits[1], ry[1], bot, -1,
+					    events + num_events);
+		}
+	    } else {
+		if (ry[0] >= top) {
+		    if (ry[0] > bot)
+			ry[0] = bot;
+		    if (ry[1] < top)
+			ry[1] = top;
+		    num_events += _add_event (&limits[1], top, ry[1], -1,
+					    events + num_events);
+		    num_events += _add_event (&t->right, ry[1], ry[0], -1,
+					    events + num_events);
+		    num_events += _add_event (&limits[0], ry[0], bot, -1,
+					    events + num_events);
+		}
+	    }
+	}
+    } else {
+	num_events += _add_event (&t->left, top, bot, 1, events + num_events);
+	num_events += _add_event (&t->right, top, bot, -1, events + num_events);
+    }
+
+    return num_events;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps)
+{
+    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;
+    int num_events;
+    int i;
+
+    if (unlikely (0 == traps->num_traps))
+	return CAIRO_STATUS_SUCCESS;
+
+#if DEBUG_TRAPS
+    dump_traps (traps, "bo-traps-in.txt");
+#endif
+
+    /* we need at most 6 vertical edges to describe each clipped trapezoid */
+    i = 6 * traps->num_traps;
+
+    events = stack_events;
+    event_ptrs = stack_event_ptrs;
+    if (i > ARRAY_LENGTH (stack_events)) {
+	events = _cairo_malloc_ab_plus_c (i,
+					  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 + i);
+    }
+
+    num_events = 0;
+    for (i = 0; i < traps->num_traps; i++) {
+	num_events += _compute_clipped_trapezoid_edges (traps,
+							&traps->traps[i],
+							&events[num_events]);
+    }
+
+    for (i = 0; i < num_events; i++)
+	event_ptrs[i] = (cairo_bo_event_t *) &events[i];
+
+
+#if DEBUG_TRAPS
+    dump_edges (events, num_events, "bo-traps-edges.txt");
+#endif
+
+    _cairo_traps_clear (traps);
+    status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs,
+							 num_events,
+							 CAIRO_FILL_RULE_WINDING,
+							 traps,
+							 &intersections);
+
+#if DEBUG_TRAPS
+    dump_traps (traps, "bo-traps-out.txt");
+#endif
+
+    if (events != stack_events)
+	free (events);
 
     return status;
 }
@@ -1769,15 +2086,14 @@ edges_have_an_intersection_quadratic (cairo_bo_edge_t	*edges,
     int i, j;
     cairo_bo_edge_t *a, *b;
     cairo_bo_point32_t intersection;
-    cairo_bo_status_t status;
 
     /* 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].top.x <<= CAIRO_BO_GUARD_BITS;
-	edges[i].top.y <<= CAIRO_BO_GUARD_BITS;
-	edges[i].bottom.x <<= CAIRO_BO_GUARD_BITS;
-	edges[i].bottom.y <<= CAIRO_BO_GUARD_BITS;
+	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++) {
@@ -1788,20 +2104,16 @@ edges_have_an_intersection_quadratic (cairo_bo_edge_t	*edges,
 	    a = &edges[i];
 	    b = &edges[j];
 
-	    status = _cairo_bo_edge_intersect (a, b, &intersection);
-	    if (status == CAIRO_BO_STATUS_PARALLEL ||
-		status == CAIRO_BO_STATUS_NO_INTERSECTION)
-	    {
+	    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->top.x, a->top.y,
-		    a->bottom.x, a->bottom.y,
-		    b->top.x, b->top.y,
-		    b->bottom.x, b->bottom.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;
 	}
@@ -2041,16 +2353,16 @@ main (void)
 	for (i = 0; i < num_random; i++) {
 	    do {
 		edge = &random_edges[i];
-		edge->top.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
-		edge->top.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
-		edge->bottom.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
-		edge->bottom.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0)));
-		if (edge->top.y > edge->bottom.y) {
-		    int32_t tmp = edge->top.y;
-		    edge->top.y = edge->bottom.y;
-		    edge->bottom.y = tmp;
+		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->top.y == edge->bottom.y);
+	    } while (edge->line.p1.y == edge->line.p2.y);
 	}
 
 	sprintf (random_name, "random-%02d", num_random);
@@ -2061,4 +2373,3 @@ main (void)
     return 0;
 }
 #endif
-
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 9334894..c26f4fa 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -588,17 +588,19 @@ _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
     _cairo_box_from_rectangle (&box, &clip_path->extents);
     _cairo_traps_limit (&traps, &box);
 
-    status = _cairo_path_fixed_fill_to_traps (&clip_path->path,
-					      clip_path->fill_rule,
-					      clip_path->tolerance,
-					      &traps);
-    if (unlikely (status))
+    status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
+							  clip_path->fill_rule,
+							  &traps);
+    if (status) {
+	_cairo_traps_fini (&traps);
+	clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
 	return status;
+    }
 
     status = _cairo_traps_extract_region (&traps, &clip_path->region);
     _cairo_traps_fini (&traps);
 
-    if (status) {
+    if (unlikely (status)) {
 	clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
 	return status;
     }
diff --git a/src/cairo-fixed-private.h b/src/cairo-fixed-private.h
index 630f032..e3add4a 100644
--- a/src/cairo-fixed-private.h
+++ b/src/cairo-fixed-private.h
@@ -226,15 +226,61 @@ _cairo_fixed_mul (cairo_fixed_t a, cairo_fixed_t b)
     return _cairo_int64_to_int32(_cairo_int64_rsl (temp, CAIRO_FIXED_FRAC_BITS));
 }
 
-/* computes a * b / c */
+/* 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);
-    cairo_int64_t quo = _cairo_int64_divrem (ab, c64).quo;
+    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;
 
-    return _cairo_int64_to_int32(quo);
+    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
diff --git a/src/cairo-fixed-type-private.h b/src/cairo-fixed-type-private.h
index 7dd5797..730ed3e 100644
--- a/src/cairo-fixed-type-private.h
+++ b/src/cairo-fixed-type-private.h
@@ -67,4 +67,9 @@ 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-gstate.c b/src/cairo-gstate.c
index 2257db0..3508b5e 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -1184,7 +1184,7 @@ _cairo_gstate_stroke_extents (cairo_gstate_t	 *gstate,
 						&gstate->ctm_inverse,
 						gstate->tolerance,
 						&traps);
-    if (status == CAIRO_STATUS_SUCCESS) {
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
 	_cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps,
 						       x1, y1, x2, y2);
     }
@@ -1209,7 +1209,7 @@ _cairo_gstate_fill_extents (cairo_gstate_t     *gstate,
 					      gstate->fill_rule,
 					      gstate->tolerance,
 					      &traps);
-    if (status == CAIRO_STATUS_SUCCESS) {
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
 	_cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps,
 						       x1, y1, x2, y2);
     }
diff --git a/src/cairo-hash.c b/src/cairo-hash.c
index 51303f5..15159d9 100644
--- a/src/cairo-hash.c
+++ b/src/cairo-hash.c
@@ -191,9 +191,6 @@ _cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal)
 void
 _cairo_hash_table_destroy (cairo_hash_table_t *hash_table)
 {
-    if (hash_table == NULL)
-	return;
-
     /* The hash table must be empty. Otherwise, halt. */
     assert (hash_table->live_entries == 0);
     /* No iterators can be running. Otherwise, halt. */
@@ -525,9 +522,6 @@ _cairo_hash_table_foreach (cairo_hash_table_t	      *hash_table,
     unsigned long i;
     cairo_hash_entry_t *entry;
 
-    if (hash_table == NULL)
-	return;
-
     /* Mark the table for iteration */
     ++hash_table->iterating;
     for (i = 0; i < hash_table->arrangement->size; i++) {
diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
index 6c83388..94ed0e9 100644
--- a/src/cairo-matrix.c
+++ b/src/cairo-matrix.c
@@ -688,16 +688,9 @@ _cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix,
     return FALSE;
 }
 
-/* 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_private cairo_bool_t
-_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix)
+cairo_bool_t
+_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix)
 {
-    cairo_fixed_t x0_fixed, y0_fixed;
-
     if (matrix->xy == 0.0 && matrix->yx == 0.0) {
 	if (! (matrix->xx == 1.0 || matrix->xx == -1.0))
 	    return FALSE;
@@ -711,6 +704,22 @@ _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix)
     } else
 	return FALSE;
 
+    return TRUE;
+}
+
+/* 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);
 
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 123da96..2b5c4c8 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -39,29 +39,25 @@
 
 typedef struct cairo_filler {
     double tolerance;
-    cairo_traps_t *traps;
-
     cairo_point_t current_point;
-
-    cairo_polygon_t polygon;
+    cairo_polygon_t *polygon;
 } cairo_filler_t;
 
 static void
-_cairo_filler_init (cairo_filler_t *filler, double tolerance, cairo_traps_t *traps)
+_cairo_filler_init (cairo_filler_t *filler,
+		    double tolerance,
+		    cairo_polygon_t *polygon)
 {
     filler->tolerance = tolerance;
-    filler->traps = traps;
+    filler->polygon = polygon;
 
     filler->current_point.x = 0;
     filler->current_point.y = 0;
-
-    _cairo_polygon_init (&filler->polygon);
 }
 
 static void
 _cairo_filler_fini (cairo_filler_t *filler)
 {
-    _cairo_polygon_fini (&filler->polygon);
 }
 
 static cairo_status_t
@@ -69,14 +65,14 @@ _cairo_filler_move_to (void *closure,
 		       const cairo_point_t *point)
 {
     cairo_filler_t *filler = closure;
-    cairo_polygon_t *polygon = &filler->polygon;
+    cairo_polygon_t *polygon = filler->polygon;
 
     _cairo_polygon_close (polygon);
     _cairo_polygon_move_to (polygon, point);
 
     filler->current_point = *point;
 
-    return _cairo_polygon_status (&filler->polygon);
+    return _cairo_polygon_status (filler->polygon);
 }
 
 static cairo_status_t
@@ -84,13 +80,13 @@ _cairo_filler_line_to (void *closure,
 		       const cairo_point_t *point)
 {
     cairo_filler_t *filler = closure;
-    cairo_polygon_t *polygon = &filler->polygon;
+    cairo_polygon_t *polygon = filler->polygon;
 
     _cairo_polygon_line_to (polygon, point);
 
     filler->current_point = *point;
 
-    return _cairo_polygon_status (&filler->polygon);
+    return _cairo_polygon_status (filler->polygon);
 }
 
 static cairo_status_t
@@ -103,11 +99,10 @@ _cairo_filler_curve_to (void *closure,
     cairo_spline_t spline;
 
     if (! _cairo_spline_init (&spline,
-			      _cairo_filler_line_to,
-			      filler,
+			      _cairo_filler_line_to, filler,
 			      &filler->current_point, b, c, d))
     {
-	return CAIRO_STATUS_SUCCESS;
+	return _cairo_filler_line_to (closure, d);
     }
 
     return _cairo_spline_decompose (&spline, filler->tolerance);
@@ -117,36 +112,22 @@ static cairo_status_t
 _cairo_filler_close_path (void *closure)
 {
     cairo_filler_t *filler = closure;
-    cairo_polygon_t *polygon = &filler->polygon;
+    cairo_polygon_t *polygon = filler->polygon;
 
     _cairo_polygon_close (polygon);
 
     return _cairo_polygon_status (polygon);
 }
 
-static cairo_int_status_t
-_cairo_path_fixed_fill_rectangle (const cairo_path_fixed_t	*path,
-				  cairo_fill_rule_t	 fill_rule,
-				  cairo_traps_t		*traps);
-
 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_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
+				   double tolerance,
+				   cairo_polygon_t *polygon)
 {
-    cairo_status_t status = CAIRO_STATUS_SUCCESS;
     cairo_filler_t filler;
+    cairo_status_t status;
 
-    traps->maybe_region = path->maybe_fill_region;
-
-    /* Before we do anything else, we use a special-case filler for
-     * a device-axis aligned rectangle if possible. */
-    status = _cairo_path_fixed_fill_rectangle (path, fill_rule, traps);
-    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
-	return status;
-
-    _cairo_filler_init (&filler, tolerance, traps);
+    _cairo_filler_init (&filler, tolerance, polygon);
 
     status = _cairo_path_fixed_interpret (path,
 					  CAIRO_DIRECTION_FORWARD,
@@ -156,25 +137,48 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
 					  _cairo_filler_close_path,
 					  &filler);
     if (unlikely (status))
-	goto BAIL;
+	return status;
 
-    _cairo_polygon_close (&filler.polygon);
-    status = _cairo_polygon_status (&filler.polygon);
-    if (unlikely (status))
-	goto BAIL;
+    _cairo_polygon_close (polygon);
+    status = _cairo_polygon_status (polygon);
+    _cairo_filler_fini (&filler);
 
-    status = _cairo_bentley_ottmann_tessellate_polygon (filler.traps,
-							&filler.polygon,
-							fill_rule);
+    return status;
+}
+
+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 (path->is_rectilinear) {
+	status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+							      fill_rule,
+							      traps);
+	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	    return status;
+    }
+
+    _cairo_polygon_init (&polygon);
+    status = _cairo_path_fixed_fill_to_polygon (path,
+						tolerance,
+						&polygon);
     if (unlikely (status))
-	goto BAIL;
+	goto CLEANUP;
 
-BAIL:
-    _cairo_filler_fini (&filler);
+    status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
+							fill_rule);
 
+  CLEANUP:
+    _cairo_polygon_fini (&polygon);
     return status;
 }
 
+
 /* This special-case filler supports only a path that describes a
  * device-axis aligned rectangle. It exists to avoid the overhead of
  * the general tessellator when drawing very common rectangles.
@@ -182,15 +186,14 @@ BAIL:
  * If the path described anything but a device-axis aligned rectangle,
  * this function will return %CAIRO_INT_STATUS_UNSUPPORTED.
  */
-static cairo_int_status_t
-_cairo_path_fixed_fill_rectangle (const cairo_path_fixed_t	*path,
-				  cairo_fill_rule_t	 fill_rule,
-				  cairo_traps_t		*traps)
+cairo_int_status_t
+_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t	*path,
+					     cairo_fill_rule_t	 fill_rule,
+					     cairo_traps_t		*traps)
 {
     cairo_box_t box;
 
-    if (! path->is_rectilinear)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    assert (path->is_rectilinear);
 
     if (_cairo_path_fixed_is_box (path, &box)) {
 	if (box.p1.x > box.p2.x) {
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 4fa4ce5..208e390 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -33,6 +33,7 @@
  *
  * Contributor(s):
  *	Carl D. Worth <cworth at cworth.org>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
  */
 
 #define _BSD_SOURCE /* for hypot() */
@@ -60,7 +61,18 @@ typedef struct cairo_stroker {
     double ctm_determinant;
     cairo_bool_t ctm_det_positive;
 
-    cairo_traps_t *traps;
+    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;
 
@@ -141,8 +153,7 @@ _cairo_stroker_init (cairo_stroker_t		*stroker,
 		     cairo_stroke_style_t	*stroke_style,
 		     const cairo_matrix_t	*ctm,
 		     const cairo_matrix_t	*ctm_inverse,
-		     double			 tolerance,
-		     cairo_traps_t		*traps)
+		     double			 tolerance)
 {
     cairo_status_t status;
 
@@ -150,7 +161,6 @@ _cairo_stroker_init (cairo_stroker_t		*stroker,
     stroker->ctm = ctm;
     stroker->ctm_inverse = ctm_inverse;
     stroker->tolerance = tolerance;
-    stroker->traps = traps;
 
     stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
     stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
@@ -161,35 +171,45 @@ _cairo_stroker_init (cairo_stroker_t		*stroker,
     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->has_bounds = _cairo_traps_get_limit (traps, &stroker->bounds);
-    if (stroker->has_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.
-	 */
-	double dx, dy;
-	cairo_fixed_t fdx, fdy;
+    stroker->add_external_edge = NULL;
 
-	_cairo_stroke_style_max_distance_from_path (stroker->style,
-						    stroker->ctm,
-						    &dx, &dy);
+    return CAIRO_STATUS_SUCCESS;
+}
 
-	fdx = _cairo_fixed_from_double (dx);
-	stroker->bounds.p1.x -= fdx;
-	stroker->bounds.p2.x += fdx;
+static void
+_cairo_stroker_limit (cairo_stroker_t *stroker,
+		      const cairo_box_t *box)
+{
+    double dx, dy;
+    cairo_fixed_t fdx, fdy;
 
-	fdy = _cairo_fixed_from_double (dy);
-	stroker->bounds.p1.y -= fdy;
-	stroker->bounds.p2.y += fdy;
-    }
+    stroker->has_bounds = TRUE;
+    stroker->bounds = *box;
 
-    return CAIRO_STATUS_SUCCESS;
+    /* 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, 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
@@ -199,14 +219,15 @@ _cairo_stroker_fini (cairo_stroker_t *stroker)
 }
 
 static void
-_translate_point (cairo_point_t *point, cairo_point_t *offset)
+_translate_point (cairo_point_t *point, const cairo_point_t *offset)
 {
     point->x += offset->x;
     point->y += offset->y;
 }
 
 static int
-_cairo_stroker_face_clockwise (cairo_stroke_face_t *in, cairo_stroke_face_t *out)
+_cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in,
+				  const cairo_stroke_face_t *out)
 {
     cairo_slope_t in_slope, out_slope;
 
@@ -232,76 +253,209 @@ _cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
     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
-_cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out)
+_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)
 {
-    int			clockwise = _cairo_stroker_face_clockwise (out, in);
-    cairo_point_t	*inpt, *outpt;
+    cairo_point_t stack_points[64], *points = stack_points;
+    int start, stop, step, i, npoints;
     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)
+    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 += 2;
+
+    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 {
-	inpt = &in->cw;
-	outpt = &out->cw;
-    }
-
-    switch (stroker->style->line_join) {
-    case CAIRO_LINE_JOIN_ROUND: {
-	int i;
-	int start, step, stop;
-	cairo_point_t tri[3];
-	cairo_pen_t *pen = &stroker->pen;
-
-	tri[0] = in->point;
-	if (clockwise) {
-	    start =
-		_cairo_pen_find_active_ccw_vertex_index (pen, &in->dev_vector);
-	    stop =
-		_cairo_pen_find_active_ccw_vertex_index (pen, &out->dev_vector);
-	    step = -1;
-	} else {
-	    start =
-		_cairo_pen_find_active_cw_vertex_index (pen, &in->dev_vector);
-	    stop =
-		_cairo_pen_find_active_cw_vertex_index (pen, &out->dev_vector);
-	    step = +1;
-	}
+	if (stroker->add_external_edge != NULL) {
+	    status = stroker->add_external_edge (stroker->closure,
+						 &in->ccw, &in->point);
+	    if (unlikely (status))
+		return status;
 
-	i = start;
-	tri[1] = *inpt;
-	while (i != stop) {
-	    tri[2] = in->point;
-	    _translate_point (&tri[2], &pen->vertices[i].point);
-	    status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
+	    status = stroker->add_external_edge (stroker->closure,
+						 &in->point, &out->ccw);
 	    if (unlikely (status))
 		return status;
-	    tri[1] = tri[2];
-	    i += step;
-	    if (i < 0)
-		i = pen->num_vertices - 1;
-	    if (i >= pen->num_vertices)
-		i = 0;
 	}
 
-	tri[2] = *outpt;
-
-	return _cairo_traps_tessellate_triangle (stroker->traps, tri);
+	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	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
@@ -361,13 +515,10 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
 	 *	2 <= ml² (1 - in · out)
 	 *
 	 */
-	if (2 <= ml * ml * (1 - in_dot_out))
-	{
+	if (2 <= ml * ml * (1 - in_dot_out)) {
 	    double		x1, y1, x2, y2;
 	    double		mx, my;
 	    double		dx1, dx2, dy1, dy2;
-	    cairo_point_t	outer;
-	    cairo_point_t	quad[4];
 	    double		ix, iy;
 	    double		fdx1, fdy1, fdx2, fdy2;
 	    double		mdx, mdy;
@@ -435,74 +586,90 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
 	    if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
 		_cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
 	    {
-		/*
-		 * Draw the quadrilateral
-		 */
-		outer.x = _cairo_fixed_from_double (mx);
-		outer.y = _cairo_fixed_from_double (my);
-
-		quad[0] = in->point;
-		quad[1] = *inpt;
-		quad[2] = outer;
-		quad[3] = *outpt;
-
-		return _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+		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: {
-	cairo_point_t tri[3];
-	tri[0] = in->point;
-	tri[1] = *inpt;
-	tri[2] = *outpt;
 
-	return _cairo_traps_tessellate_triangle (stroker->traps, tri);
-    }
+    /* 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, cairo_stroke_face_t *f)
+_cairo_stroker_add_cap (cairo_stroker_t *stroker,
+			const cairo_stroke_face_t *f)
 {
-    cairo_status_t	    status;
-
-    if (stroker->style->line_cap == CAIRO_LINE_CAP_BUTT)
-	return CAIRO_STATUS_SUCCESS;
-
     switch (stroker->style->line_cap) {
     case CAIRO_LINE_CAP_ROUND: {
-	int i;
-	int start, stop;
 	cairo_slope_t slope;
-	cairo_point_t tri[3];
-	cairo_pen_t *pen = &stroker->pen;
-
-	slope = f->dev_vector;
-	start = _cairo_pen_find_active_cw_vertex_index (pen, &slope);
-	slope.dx = -slope.dx;
-	slope.dy = -slope.dy;
-	stop = _cairo_pen_find_active_cw_vertex_index (pen, &slope);
-
-	tri[0] = f->point;
-	tri[1] = f->cw;
-	for (i=start; i != stop; i = (i+1) % pen->num_vertices) {
-	    tri[2] = f->point;
-	    _translate_point (&tri[2], &pen->vertices[i].point);
-	    status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
-	    if (unlikely (status))
-		return status;
-	    tri[1] = tri[2];
-	}
-	tri[2] = f->ccw;
 
-	return _cairo_traps_tessellate_triangle (stroker->traps, tri);
+	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	occw, ocw;
-	cairo_polygon_t	polygon;
+	cairo_point_t	quad[4];
 
 	dx = f->usr_vector.x;
 	dy = f->usr_vector.y;
@@ -511,38 +678,52 @@ _cairo_stroker_add_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f)
 	cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
 	fvector.dx = _cairo_fixed_from_double (dx);
 	fvector.dy = _cairo_fixed_from_double (dy);
-	occw.x = f->ccw.x + fvector.dx;
-	occw.y = f->ccw.y + fvector.dy;
-	ocw.x = f->cw.x + fvector.dx;
-	ocw.y = f->cw.y + fvector.dy;
-
-	_cairo_polygon_init (&polygon);
-	_cairo_polygon_move_to (&polygon, &f->cw);
-	_cairo_polygon_line_to (&polygon, &ocw);
-	_cairo_polygon_line_to (&polygon, &occw);
-	_cairo_polygon_line_to (&polygon, &f->ccw);
-	_cairo_polygon_close (&polygon);
-	status = _cairo_polygon_status (&polygon);
-
-	if (status == CAIRO_STATUS_SUCCESS) {
-	    status = _cairo_bentley_ottmann_tessellate_polygon (stroker->traps,
-								&polygon,
-								CAIRO_FILL_RULE_WINDING);
-	}
 
-	_cairo_polygon_fini (&polygon);
+	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;
 
-	return status;
+	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:
-	return CAIRO_STATUS_SUCCESS;
+	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,
-				cairo_stroke_face_t *face)
+				const cairo_stroke_face_t *face)
 {
     cairo_stroke_face_t reversed;
     cairo_point_t t;
@@ -563,7 +744,7 @@ _cairo_stroker_add_leading_cap (cairo_stroker_t     *stroker,
 
 static cairo_status_t
 _cairo_stroker_add_trailing_cap (cairo_stroker_t     *stroker,
-				 cairo_stroke_face_t *face)
+				 const cairo_stroke_face_t *face)
 {
     return _cairo_stroker_add_cap (stroker, face);
 }
@@ -617,55 +798,6 @@ _compute_normalized_device_slope (double *dx, double *dy,
 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);
-
-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_JOIN_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 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;
@@ -712,6 +844,55 @@ _compute_face (const cairo_point_t *point, cairo_slope_t *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_JOIN_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,
@@ -720,24 +901,42 @@ _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker,
 			     cairo_stroke_face_t *start,
 			     cairo_stroke_face_t *end)
 {
-    cairo_point_t rectangle[4];
-
     _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
-
-    /* XXX: This could be optimized slightly by not calling
-       _compute_face again but rather  translating the relevant
-       fields from start. */
-    _compute_face (p2, dev_slope, slope_dx, slope_dy, stroker, end);
+    *end = *start;
 
     if (p1->x == p2->x && p1->y == p2->y)
 	return CAIRO_STATUS_SUCCESS;
 
-    rectangle[0] = start->cw;
-    rectangle[1] = start->ccw;
-    rectangle[2] = end->ccw;
-    rectangle[3] = end->cw;
+    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;
 
-    return _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
+	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
@@ -747,6 +946,9 @@ _cairo_stroker_move_to (void *closure,
     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))
@@ -763,40 +965,29 @@ _cairo_stroker_move_to (void *closure,
 }
 
 static cairo_status_t
-_cairo_stroker_move_to_dashed (void *closure,
-			       const cairo_point_t *point)
-{
-    cairo_stroker_t *stroker = closure;
-
-    /* reset the dash pattern for new sub paths */
-    _cairo_stroker_dash_start (&stroker->dash);
-
-    return _cairo_stroker_move_to (closure, point);
-}
-
-static cairo_status_t
 _cairo_stroker_line_to (void *closure,
-			const cairo_point_t *p2)
+			const cairo_point_t *point)
 {
-    cairo_status_t status;
     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 == p2->x && p1->y == p2->y)
+    if (p1->x == point->x && p1->y == point->y)
 	return CAIRO_STATUS_SUCCESS;
 
-    _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);
-    _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL);
+    _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, p2,
+					  p1, point,
 					  &dev_slope,
 					  slope_dx, slope_dy,
 					  &start, &end);
@@ -805,10 +996,12 @@ _cairo_stroker_line_to (void *closure,
 
     if (stroker->has_current_face) {
 	/* Join with final face from previous segment */
-	status = _cairo_stroker_join (stroker, &stroker->current_face, &start);
+	status = _cairo_stroker_join (stroker,
+				      &stroker->current_face,
+				      &start);
 	if (unlikely (status))
 	    return status;
-    } else if (!stroker->has_first_face) {
+    } 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;
@@ -816,7 +1009,7 @@ _cairo_stroker_line_to (void *closure,
     stroker->current_face = end;
     stroker->has_current_face = TRUE;
 
-    stroker->current_point = *p2;
+    stroker->current_point = *point;
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -858,7 +1051,7 @@ _cairo_stroker_line_to_dashed (void *closure,
     slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
 
     if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
-					   stroker->ctm_inverse, &mag))
+					    stroker->ctm_inverse, &mag))
     {
 	return CAIRO_STATUS_SUCCESS;
     }
@@ -973,121 +1166,22 @@ _cairo_stroker_curve_to (void *closure,
 			 const cairo_point_t *d)
 {
     cairo_stroker_t *stroker = closure;
-    cairo_pen_stroke_spline_t spline_pen;
-    cairo_stroke_face_t start, end;
-    cairo_point_t extra_points[4];
-    cairo_point_t *a = &stroker->current_point;
-    double initial_slope_dx, initial_slope_dy;
-    double final_slope_dx, final_slope_dy;
-    cairo_status_t status;
-
-    status = _cairo_pen_stroke_spline_init (&spline_pen,
-					    &stroker->pen,
-					    a, b, c, d);
-    if (status == CAIRO_INT_STATUS_DEGENERATE)
-	return _cairo_stroker_line_to (closure, d);
-    else if (unlikely (status))
-	return status;
-
-    initial_slope_dx = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dx);
-    initial_slope_dy = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dy);
-    final_slope_dx = _cairo_fixed_to_double (spline_pen.spline.final_slope.dx);
-    final_slope_dy = _cairo_fixed_to_double (spline_pen.spline.final_slope.dy);
-
-    if (_compute_normalized_device_slope (&initial_slope_dx, &initial_slope_dy,
-					  stroker->ctm_inverse, NULL))
-    {
-	_compute_face (a,
-		       &spline_pen.spline.initial_slope,
-		       initial_slope_dx, initial_slope_dy,
-		       stroker, &start);
-    }
-
-    if (_compute_normalized_device_slope (&final_slope_dx, &final_slope_dy,
-					  stroker->ctm_inverse, NULL))
-    {
-	_compute_face (d,
-		       &spline_pen.spline.final_slope,
-		       final_slope_dx, final_slope_dy,
-		       stroker, &end);
-    }
-
-    if (stroker->has_current_face) {
-	status = _cairo_stroker_join (stroker, &stroker->current_face, &start);
-	if (unlikely (status))
-	    goto CLEANUP_PEN;
-    } else if (! stroker->has_first_face) {
-	stroker->first_face = start;
-	stroker->has_first_face = TRUE;
-    }
-    stroker->current_face = end;
-    stroker->has_current_face = TRUE;
-
-    extra_points[0] = start.cw;
-    extra_points[0].x -= start.point.x;
-    extra_points[0].y -= start.point.y;
-    extra_points[1] = start.ccw;
-    extra_points[1].x -= start.point.x;
-    extra_points[1].y -= start.point.y;
-    extra_points[2] = end.cw;
-    extra_points[2].x -= end.point.x;
-    extra_points[2].y -= end.point.y;
-    extra_points[3] = end.ccw;
-    extra_points[3].x -= end.point.x;
-    extra_points[3].y -= end.point.y;
-
-    status = _cairo_pen_add_points (&spline_pen.pen, extra_points, 4);
-    if (unlikely (status))
-	goto CLEANUP_PEN;
-
-    status = _cairo_pen_stroke_spline (&spline_pen,
-				       stroker->tolerance,
-				       stroker->traps);
-
-  CLEANUP_PEN:
-    _cairo_pen_stroke_spline_fini (&spline_pen);
-
-    stroker->current_point = *d;
-
-    return status;
-}
-
-/* We're using two different algorithms here for dashed and un-dashed
- * splines. The dashed algorithm uses the existing line dashing
- * code. It's linear in path length, but gets subtly wrong results for
- * self-intersecting paths (an outstanding but for self-intersecting
- * non-curved paths as well). The non-dashed algorithm tessellates a
- * single polygon for the whole curve. It handles the
- * self-intersecting problem, but it's (unsurprisingly) not O(n) and
- * more significantly, it doesn't yet handle dashes.
- *
- * The only reason we're doing split algorithms here is to
- * minimize the impact of fixing the splines-aren't-dashed bug for
- * 1.0.2. Long-term the right answer is to rewrite the whole pile
- * of stroking code so that the entire result is computed as a
- * single polygon that is tessellated, (that is, stroking can be
- * built on top of filling). That will solve the self-intersecting
- * problem. It will also increase the importance of implementing
- * an efficient and more robust tessellator.
- */
-static cairo_status_t
-_cairo_stroker_curve_to_dashed (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_point_t *a = &stroker->current_point;
     cairo_line_join_t line_join_save;
-    cairo_status_t status;
+    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_stroker_line_to_dashed,
-			      stroker,
-			      a, b, c, d))
+			      line_to, stroker,
+			      &stroker->current_point, b, c, d))
     {
-	return _cairo_stroker_line_to_dashed (closure, d);
+	return line_to (closure, d);
     }
 
     /* If the line width is so small that the pen is reduced to a
@@ -1095,23 +1189,71 @@ _cairo_stroker_curve_to_dashed (void *closure,
     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 status;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_status_t
 _cairo_stroker_close_path (void *closure)
 {
-    cairo_status_t status;
     cairo_stroker_t *stroker = closure;
+    cairo_status_t status;
 
     if (stroker->dash.dashed)
 	status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point);
@@ -1122,7 +1264,9 @@ _cairo_stroker_close_path (void *closure)
 
     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);
+	status = _cairo_stroker_join (stroker,
+				      &stroker->current_face,
+				      &stroker->first_face);
 	if (unlikely (status))
 	    return status;
     } else {
@@ -1139,11 +1283,100 @@ _cairo_stroker_close_path (void *closure)
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear (const cairo_path_fixed_t	*path,
-				      cairo_stroke_style_t	*stroke_style,
-				      const cairo_matrix_t	*ctm,
-				      cairo_traps_t		*traps);
+cairo_status_t
+_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t	*path,
+				    cairo_stroke_style_t	*stroke_style,
+				    cairo_matrix_t	*ctm,
+				    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_DIRECTION_FORWARD,
+					  _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_to_polygon (const cairo_path_fixed_t	*path,
+				     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->has_limits)
+	_cairo_stroker_limit (&stroker, &polygon->limits);
+
+    status = _cairo_path_fixed_interpret (path,
+					  CAIRO_DIRECTION_FORWARD,
+					  _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_to_traps (const cairo_path_fixed_t	*path,
@@ -1154,50 +1387,44 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
 				   cairo_traps_t	*traps)
 {
     cairo_status_t status;
-    cairo_stroker_t stroker;
+    cairo_polygon_t polygon;
 
     /* Before we do anything else, we attempt the rectilinear
      * stroker. It's careful to generate trapezoids that align to
      * device-pixel boundaries when possible. Many backends can render
      * those much faster than non-aligned trapezoids, (by using clip
      * regions, etc.) */
-    status = _cairo_path_fixed_stroke_rectilinear (path,
-						   stroke_style,
-						   ctm,
-						   traps);
-    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
-	return status;
+    if (path->is_rectilinear) {
+	status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
+								stroke_style,
+								ctm,
+								traps);
+	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	    return status;
+    }
 
-    status = _cairo_stroker_init (&stroker, stroke_style,
-			          ctm, ctm_inverse, tolerance,
-				  traps);
+    _cairo_polygon_init (&polygon);
+    if (traps->has_limits)
+	_cairo_polygon_limit (&polygon, &traps->limits);
+
+    status = _cairo_path_fixed_stroke_to_polygon (path,
+						 stroke_style,
+						 ctm,
+						 ctm_inverse,
+						 tolerance,
+						 &polygon);
     if (unlikely (status))
-	return status;
+	goto BAIL;
 
-    if (stroker.style->dash)
-	status = _cairo_path_fixed_interpret (path,
-					      CAIRO_DIRECTION_FORWARD,
-					      _cairo_stroker_move_to_dashed,
-					      _cairo_stroker_line_to_dashed,
-					      _cairo_stroker_curve_to_dashed,
-					      _cairo_stroker_close_path,
-					      &stroker);
-    else
-	status = _cairo_path_fixed_interpret (path,
-					      CAIRO_DIRECTION_FORWARD,
-					      _cairo_stroker_move_to,
-					      _cairo_stroker_line_to,
-					      _cairo_stroker_curve_to,
-					      _cairo_stroker_close_path,
-					      &stroker);
+    status = _cairo_polygon_status (&polygon);
     if (unlikely (status))
 	goto BAIL;
 
-    /* Cap the start and end of the final sub path as needed */
-    status = _cairo_stroker_add_caps (&stroker);
+    status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
+							CAIRO_FILL_RULE_WINDING);
 
 BAIL:
-    _cairo_stroker_fini (&stroker);
+    _cairo_polygon_fini (&polygon);
 
     return status;
 }
@@ -1263,6 +1490,9 @@ _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t	*stroker,
     _cairo_stroker_dash_init (&stroker->dash, stroke_style);
 
     stroker->has_bounds = FALSE;
+
+    /* As we incrementally tessellate, we do not eliminate self-intersections */
+    stroker->traps->has_intersections = TRUE;
 }
 
 static void
@@ -1583,8 +1813,7 @@ _cairo_rectilinear_stroker_line_to (void		*closure,
     cairo_status_t status;
 
     /* We only support horizontal or vertical elements. */
-    if (! (a->x == b->x || a->y == b->y))
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    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)
@@ -1620,8 +1849,7 @@ _cairo_rectilinear_stroker_line_to_dashed (void		*closure,
 	return CAIRO_STATUS_SUCCESS;
 
     /* We only support horizontal or vertical elements. */
-    if (! (a->x == b->x || a->y == b->y))
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    assert (a->x == b->x || a->y == b->y);
 
     fully_in_bounds = TRUE;
     if (stroker->has_bounds &&
@@ -1736,11 +1964,11 @@ _cairo_rectilinear_stroker_close_path (void *closure)
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear (const cairo_path_fixed_t	*path,
-				      cairo_stroke_style_t	*stroke_style,
-				      const cairo_matrix_t	*ctm,
-				      cairo_traps_t		*traps)
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t	*path,
+					       cairo_stroke_style_t	*stroke_style,
+					       const cairo_matrix_t	*ctm,
+					       cairo_traps_t		*traps)
 {
     cairo_rectilinear_stroker_t rectilinear_stroker;
     cairo_int_status_t status;
@@ -1755,8 +1983,8 @@ _cairo_path_fixed_stroke_rectilinear (const cairo_path_fixed_t	*path,
      * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
      * non-rectilinear line_to is encountered.
      */
-    if (! path->is_rectilinear)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    assert (path->is_rectilinear);
+
     if (stroke_style->line_join	!= CAIRO_LINE_JOIN_MITER)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     /* If the miter limit turns right angles into bevels, then we
@@ -1770,11 +1998,8 @@ _cairo_path_fixed_stroke_rectilinear (const cairo_path_fixed_t	*path,
     {
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
-    if (! (_cairo_matrix_is_identity (ctm) ||
-	   _cairo_matrix_is_translation (ctm)))
-    {
+    if (! _cairo_matrix_has_unity_scale (ctm))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
 
     _cairo_rectilinear_stroker_init (&rectilinear_stroker,
 				     stroke_style,
diff --git a/src/cairo-pen.c b/src/cairo-pen.c
index b2fd855..8db8fcb 100644
--- a/src/cairo-pen.c
+++ b/src/cairo-pen.c
@@ -393,194 +393,3 @@ _cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
 
     return i;
 }
-
-static int
-_cairo_pen_stroke_spline_add_convolved_point (cairo_pen_stroke_spline_t	*stroker,
-					      const cairo_point_t *last_point,
-					      const cairo_slope_t *slope,
-					      cairo_point_t *last_hull_point,
-					      int active,
-					      int step)
-{
-    do {
-	cairo_point_t hull_point;
-
-	hull_point.x = last_point->x + stroker->pen.vertices[active].point.x;
-	hull_point.y = last_point->y + stroker->pen.vertices[active].point.y;
-	_cairo_polygon_add_edge (&stroker->polygon,
-				 last_hull_point, &hull_point,
-				 step);
-	*last_hull_point = hull_point;
-
-	/* The strict inequalities here ensure that if a spline slope
-	 * compares identically with either of the slopes of the
-	 * active vertex, then it remains the active vertex. This is
-	 * very important since otherwise we can trigger an infinite
-	 * loop in the case of a degenerate pen, (a line), where
-	 * neither vertex considers itself active for the slope---one
-	 * will consider it as equal and reject, and the other will
-	 * consider it unequal and reject. This is due to the inherent
-	 * ambiguity when comparing slopes that differ by exactly
-	 * pi. */
-	if (_cairo_slope_compare (slope,
-				  &stroker->pen.vertices[active].slope_ccw) > 0)
-	{
-	    if (++active == stroker->pen.num_vertices)
-		active = 0;
-	}
-	else if (_cairo_slope_compare (slope,
-				       &stroker->pen.vertices[active].slope_cw) < 0)
-	{
-	    if (--active == -1)
-		active = stroker->pen.num_vertices - 1;
-	}
-	else
-	{
-	    return active;
-	}
-    } while (TRUE);
-}
-
-
-/* Compute outline of a given spline using the pen.
- * The trapezoids needed to fill that outline will be added to traps
- */
-cairo_status_t
-_cairo_pen_stroke_spline (cairo_pen_stroke_spline_t	*stroker,
-			  double			 tolerance,
-			  cairo_traps_t			*traps)
-{
-    cairo_status_t status;
-    cairo_slope_t slope;
-
-    /* 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;
-
-    /* open the polygon */
-    slope = stroker->spline.initial_slope;
-    stroker->forward_vertex =
-	_cairo_pen_find_active_cw_vertex_index (&stroker->pen, &slope);
-    stroker->forward_hull_point.x = stroker->last_point.x +
-	stroker->pen.vertices[stroker->forward_vertex].point.x;
-    stroker->forward_hull_point.y = stroker->last_point.y +
-	stroker->pen.vertices[stroker->forward_vertex].point.y;
-
-    slope.dx = -slope.dx;
-    slope.dy = -slope.dy;
-    stroker->backward_vertex =
-	_cairo_pen_find_active_cw_vertex_index (&stroker->pen, &slope);
-    stroker->backward_hull_point.x = stroker->last_point.x +
-	stroker->pen.vertices[stroker->backward_vertex].point.x;
-    stroker->backward_hull_point.y = stroker->last_point.y +
-	stroker->pen.vertices[stroker->backward_vertex].point.y;
-
-    _cairo_polygon_add_edge (&stroker->polygon,
-			     &stroker->backward_hull_point,
-			     &stroker->forward_hull_point,
-			     1);
-
-    status = _cairo_spline_decompose (&stroker->spline, tolerance);
-    if (unlikely (status))
-	return status;
-
-    /* close the polygon */
-    slope = stroker->spline.final_slope;
-    _cairo_pen_stroke_spline_add_convolved_point (stroker,
-						  &stroker->last_point,
-						  &slope,
-						  &stroker->forward_hull_point,
-						  stroker->forward_vertex,
-						  1);
-
-    slope.dx = -slope.dx;
-    slope.dy = -slope.dy;
-    _cairo_pen_stroke_spline_add_convolved_point (stroker,
-						  &stroker->last_point,
-						  &slope,
-						  &stroker->backward_hull_point,
-						  stroker->backward_vertex,
-						  -1);
-
-    _cairo_polygon_add_edge (&stroker->polygon,
-			     &stroker->forward_hull_point,
-			     &stroker->backward_hull_point,
-			     1);
-
-    status = _cairo_polygon_status (&stroker->polygon);
-    if (unlikely (status))
-	return status;
-
-    status = _cairo_bentley_ottmann_tessellate_polygon (traps,
-							&stroker->polygon,
-							CAIRO_FILL_RULE_WINDING);
-
-    return status;
-}
-
-static cairo_status_t
-_cairo_pen_stroke_spline_add_point (void		    *closure,
-				    const cairo_point_t	    *point)
-{
-    cairo_pen_stroke_spline_t	*stroker = closure;
-    cairo_slope_t slope;
-
-    _cairo_slope_init (&slope, &stroker->last_point, point);
-    stroker->forward_vertex =
-	_cairo_pen_stroke_spline_add_convolved_point (stroker,
-						      &stroker->last_point,
-						      &slope,
-						      &stroker->forward_hull_point,
-						      stroker->forward_vertex,
-						      1);
-
-    slope.dx = -slope.dx;
-    slope.dy = -slope.dy;
-    stroker->backward_vertex =
-	_cairo_pen_stroke_spline_add_convolved_point (stroker,
-						      &stroker->last_point,
-						      &slope,
-						      &stroker->backward_hull_point,
-						      stroker->backward_vertex,
-						      -1);
-    stroker->last_point = *point;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-cairo_int_status_t
-_cairo_pen_stroke_spline_init (cairo_pen_stroke_spline_t *stroker,
-			       const cairo_pen_t *pen,
-			       const cairo_point_t *a,
-			       const cairo_point_t *b,
-			       const cairo_point_t *c,
-			       const cairo_point_t *d)
-{
-    cairo_int_status_t status;
-
-    if (! _cairo_spline_init (&stroker->spline,
-			      _cairo_pen_stroke_spline_add_point,
-			      stroker,
-			      a, b, c, d))
-    {
-	return CAIRO_INT_STATUS_DEGENERATE;
-    }
-
-    status = _cairo_pen_init_copy (&stroker->pen, pen);
-    if (unlikely (status))
-	return status;
-
-    _cairo_polygon_init (&stroker->polygon);
-
-    stroker->last_point = *a;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-void
-_cairo_pen_stroke_spline_fini (cairo_pen_stroke_spline_t *stroker)
-{
-    _cairo_polygon_fini (&stroker->polygon);
-    _cairo_pen_fini (&stroker->pen);
-}
diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
index 202cb4d..ac4d094 100644
--- a/src/cairo-polygon.c
+++ b/src/cairo-polygon.c
@@ -49,6 +49,18 @@ _cairo_polygon_init (cairo_polygon_t *polygon)
     polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
 
     polygon->has_current_point = FALSE;
+    polygon->has_limits = FALSE;
+
+    polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
+    polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
+}
+
+void
+_cairo_polygon_limit (cairo_polygon_t	*polygon,
+		      const cairo_box_t *limits)
+{
+    polygon->has_limits = TRUE;
+    polygon->limits = *limits;
 }
 
 void
@@ -93,17 +105,16 @@ _cairo_polygon_grow (cairo_polygon_t *polygon)
     return TRUE;
 }
 
-void
-_cairo_polygon_add_edge (cairo_polygon_t *polygon,
-			 const cairo_point_t *p1,
-			 const cairo_point_t *p2,
-			 int		      dir)
+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;
 
-    /* drop horizontal edges */
-    if (p1->y == p2->y)
-	return;
+    assert (top < bottom);
 
     if (polygon->num_edges == polygon->edges_size) {
 	if (! _cairo_polygon_grow (polygon))
@@ -111,26 +122,247 @@ _cairo_polygon_add_edge (cairo_polygon_t *polygon,
     }
 
     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,
+		   int dir)
+{
+    cairo_point_t p[2];
+    int top_y, bot_y;
+
+    if (p1->x <= polygon->limits.p1.x && p2->x <= polygon->limits.p1.x)
+    {
+	p[0].x = polygon->limits.p1.x;
+	p[0].y = polygon->limits.p1.y;
+	top_y = p1->y;
+	if (top_y < p[0].y)
+	    top_y = p[0].y;
+
+	p[1].x = polygon->limits.p1.x;
+	p[1].y = polygon->limits.p2.y;
+	bot_y = p2->y;
+	if (bot_y > p[1].y)
+	    bot_y = p[1].y;
+
+	_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+    }
+    else if (p1->x >= polygon->limits.p2.x && p2->x >= polygon->limits.p2.x)
+    {
+	p[0].x = polygon->limits.p2.x;
+	p[0].y = polygon->limits.p1.y;
+	top_y = p1->y;
+	if (top_y < p[0].y)
+	    top_y = p[0].y;
+
+	p[1].x = polygon->limits.p2.x;
+	p[1].y = polygon->limits.p2.y;
+	bot_y = p2->y;
+	if (bot_y > p[1].y)
+	    bot_y = p[1].y;
+
+	_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+    }
+    else if (p1->x >= polygon->limits.p1.x && p2->x >= polygon->limits.p1.x &&
+	     p1->x <= polygon->limits.p2.x && p2->x <= polygon->limits.p2.x)
+    {
+	top_y = p1->y;
+	if (top_y < polygon->limits.p1.y)
+	    top_y = polygon->limits.p1.y;
+
+	bot_y = p2->y;
+	if (bot_y > polygon->limits.p2.y)
+	    bot_y = polygon->limits.p2.y;
+
+	_add_edge (polygon, p1, p2, top_y, bot_y, dir);
+    }
+    else
+    {
+	int left_y, right_y;
+	int p1_y, p2_y;
+
+	left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+						     polygon->limits.p1.x);
+	right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+						      polygon->limits.p2.x);
+
+	if (left_y == right_y) /* horizontal within bounds */
+	    return;
+
+	p1_y = p1->y;
+	p2_y = p2->y;
+
+	if (left_y < right_y) {
+	    if (p1->x < polygon->limits.p1.x && left_y > polygon->limits.p1.y)
+	    {
+		p[0].x = polygon->limits.p1.x;
+		p[0].y = polygon->limits.p1.y;
+		top_y = p1_y;
+		if (top_y < p[0].y)
+		    top_y = p[0].y;
+
+		p[1].x = polygon->limits.p1.x;
+		p[1].y = polygon->limits.p2.y;
+		bot_y = left_y;
+		if (bot_y > p[1].y)
+		    bot_y = p[1].y;
+
+		if (bot_y > top_y)
+		    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+		p1_y = bot_y;
+	    }
+
+	    if (p2->x > polygon->limits.p2.x && right_y < polygon->limits.p2.y)
+	    {
+		p[0].x = polygon->limits.p2.x;
+		p[0].y = polygon->limits.p1.y;
+		top_y = right_y;
+		if (top_y < p[0].y)
+		    top_y = p[0].y;
+
+		p[1].x = polygon->limits.p2.x;
+		p[1].y = polygon->limits.p2.y;
+		bot_y = p2_y;
+		if (bot_y > p[1].y)
+		    bot_y = p[1].y;
+
+		if (bot_y > top_y)
+		    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+		p2_y = top_y;
+	    }
+	} else {
+	    if (p1->x > polygon->limits.p2.x && right_y > polygon->limits.p1.y)
+	    {
+		p[0].x = polygon->limits.p2.x;
+		p[0].y = polygon->limits.p1.y;
+		top_y = p1_y;
+		if (top_y < p[0].y)
+		    top_y = p[0].y;
+
+		p[1].x = polygon->limits.p2.x;
+		p[1].y = polygon->limits.p2.y;
+		bot_y = right_y;
+		if (bot_y > p[1].y)
+		    bot_y = p[1].y;
+
+		if (bot_y > top_y)
+		    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+		p1_y = bot_y;
+	    }
+
+	    if (p2->x < polygon->limits.p1.x && left_y < polygon->limits.p2.y)
+	    {
+		p[0].x = polygon->limits.p1.x;
+		p[0].y = polygon->limits.p1.y;
+		top_y = left_y;
+		if (top_y < p[0].y)
+		    top_y = p[0].y;
+
+		p[1].x = polygon->limits.p1.x;
+		p[1].y = polygon->limits.p2.y;
+		bot_y = p2_y;
+		if (bot_y > p[1].y)
+		    bot_y = p[1].y;
+
+		if (bot_y > top_y)
+		    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+		p2_y = top_y;
+	    }
+	}
+
+	if (p1_y < polygon->limits.p1.y)
+	    p1_y = polygon->limits.p1.y;
+	if (p2_y > polygon->limits.p2.y)
+	    p2_y = polygon->limits.p2.y;
+	if (p2_y > p1_y)
+	    _add_edge (polygon, p1, p2, p1_y, p2_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) {
-	edge->edge.p1 = *p1;
-	edge->edge.p2 = *p2;
-	edge->dir = dir;
+	dir = 1;
     } else {
-	edge->edge.p1 = *p2;
-	edge->edge.p2 = *p1;
-	edge->dir = -dir;
+	const cairo_point_t *t;
+	t = p1, p1 = p2, p2 = t;
+	dir = -1;
     }
+
+    if (polygon->has_limits) {
+	if (p2->y <= polygon->limits.p1.y)
+	    return;
+
+	if (p1->y >= polygon->limits.p2.y)
+	    return;
+
+	_add_clipped_edge (polygon, p1, p2, 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);
+    return _cairo_polygon_status (polygon);
 }
 
+/* flattened path operations */
+
 void
 _cairo_polygon_move_to (cairo_polygon_t *polygon,
 			const cairo_point_t *point)
 {
-    if (! polygon->has_current_point)
+    if (! polygon->has_current_point) {
 	polygon->first_point = *point;
+	polygon->has_current_point = TRUE;
+    }
 
     polygon->current_point = *point;
-    polygon->has_current_point = TRUE;
 }
 
 void
@@ -138,7 +370,7 @@ _cairo_polygon_line_to (cairo_polygon_t *polygon,
 			const cairo_point_t *point)
 {
     if (polygon->has_current_point)
-	_cairo_polygon_add_edge (polygon, &polygon->current_point, point, 1);
+	_cairo_polygon_add_edge (polygon, &polygon->current_point, point);
 
     _cairo_polygon_move_to (polygon, point);
 }
@@ -149,8 +381,7 @@ _cairo_polygon_close (cairo_polygon_t *polygon)
     if (polygon->has_current_point) {
 	_cairo_polygon_add_edge (polygon,
 				 &polygon->current_point,
-				 &polygon->first_point,
-				 1);
+				 &polygon->first_point);
 
 	polygon->has_current_point = FALSE;
     }
diff --git a/src/cairo-skiplist-private.h b/src/cairo-skiplist-private.h
index 250b5a2..c0e5bce 100644
--- a/src/cairo-skiplist-private.h
+++ b/src/cairo-skiplist-private.h
@@ -62,7 +62,7 @@ typedef struct _skip_elt {
 #define SKIP_LIST_ELT_TO_DATA(type, elt) ((type *) ((char *) (elt) - (sizeof (type) - sizeof (skip_elt_t))))
 
 typedef int
-(*cairo_skip_list_compare_t) (void *list, void *a, void *b);
+(*cairo_skip_list_compare_t) (void *list, const void *a, const void *b);
 
 typedef struct _skip_list {
     cairo_skip_list_compare_t compare;
@@ -101,7 +101,7 @@ _cairo_skip_list_fini (cairo_skip_list_t		*list);
  * Otherwise data will be copied (elt_size bytes from <data> via
  * memcpy) and the new element is returned. */
 cairo_private void *
-_cairo_skip_list_insert (cairo_skip_list_t *list, void *data, int unique);
+_cairo_skip_list_insert (cairo_skip_list_t *list, void *data);
 
 /* Find an element which compare considers equal to <data> */
 cairo_private void *
diff --git a/src/cairo-skiplist.c b/src/cairo-skiplist.c
index 18d69ca..8d8c8d1 100644
--- a/src/cairo-skiplist.c
+++ b/src/cairo-skiplist.c
@@ -189,7 +189,7 @@ free_elt (cairo_skip_list_t *list, skip_elt_t *elt)
  * Insert 'data' into the list
  */
 void *
-_cairo_skip_list_insert (cairo_skip_list_t *list, void *data, int unique)
+_cairo_skip_list_insert (cairo_skip_list_t *list, void *data)
 {
     skip_elt_t **update[MAX_LEVEL];
     skip_elt_t *prev[MAX_LEVEL];
@@ -209,7 +209,7 @@ _cairo_skip_list_insert (cairo_skip_list_t *list, void *data, int unique)
 	    for (; (elt = next[i]); next = elt->next)
 	    {
 		int cmp = list->compare (list, ELT_DATA(elt), data);
-		if (unique && 0 == cmp)
+		if (0 == cmp)
 		    return ELT_DATA(elt);
 		if (cmp > 0)
 		    break;
@@ -369,7 +369,7 @@ typedef struct {
 } test_elt_t;
 
 static int
-test_cmp (void *list, void *A, void *B)
+test_cmp (void *list, const void *A, const void *B)
 {
     const test_elt_t *a = A, *b = B;
     return a->n - b->n;
@@ -386,7 +386,7 @@ main (void)
     for (n = 0; n < 10000000; n++) {
 	void *elt_and_data;
 	elt.n = n;
-	elt_and_data = _cairo_skip_list_insert (&list, &elt, TRUE);
+	elt_and_data = _cairo_skip_list_insert (&list, &elt);
 	assert (elt_and_data != NULL);
     }
     _cairo_skip_list_fini (&list);
diff --git a/src/cairo-slope.c b/src/cairo-slope.c
index 35c5372..15685b7 100644
--- a/src/cairo-slope.c
+++ b/src/cairo-slope.c
@@ -94,9 +94,7 @@ _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b)
      * of b by an infinitesimally small amount, (that is, 'a' will
      * always be considered less than 'b').
      */
-    if (((a->dx > 0) != (b->dx > 0)) ||
-	((a->dy > 0) != (b->dy > 0)))
-    {
+    if ((a->dx ^ b->dx) < 0 || (a->dy ^ b->dy) < 0) {
 	if (a->dx > 0 || (a->dx == 0 && a->dy > 0))
 	    return +1;
 	else
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index c706b22..9f6f116 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -75,22 +75,15 @@ struct _cairo_scan_converter {
     /* Destroy this scan converter. */
     cairo_destroy_func_t	destroy;
 
-    /* Add an edge to the converter. */
-    cairo_status_t
-    (*add_edge)(
-	void		*abstract_converter,
-	cairo_fixed_t	 x1,
-	cairo_fixed_t	 y1,
-	cairo_fixed_t	 x2,
-	cairo_fixed_t	 y2);
+    /* Add a polygon (set of edges) to the converter. */
+    cairo_status_t (*add_polygon) (void		    *abstract_converter,
+				   const cairo_polygon_t  *polygon);
 
     /* 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);
+    cairo_status_t (*generate) (void			*abstract_converter,
+				cairo_span_renderer_t	*renderer);
 
     /* Private status. Read with _cairo_scan_converter_status(). */
     cairo_status_t status;
@@ -99,12 +92,11 @@ struct _cairo_scan_converter {
 /* 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_tor_scan_converter_create (int			xmin,
+				  int			ymin,
+				  int			xmax,
+				  int			ymax,
+				  cairo_fill_rule_t	fill_rule);
 
 /* cairo-spans.c: */
 
@@ -132,14 +124,13 @@ _cairo_span_renderer_set_error (void *abstract_renderer,
 				cairo_status_t error);
 
 cairo_private cairo_status_t
-_cairo_path_fixed_fill_using_spans (cairo_operator_t		 op,
-				    const cairo_pattern_t	*pattern,
-				    cairo_path_fixed_t		*path,
-				    cairo_surface_t		*dst,
-				    cairo_fill_rule_t		 fill_rule,
-				    double			 tolerance,
-				    cairo_antialias_t		 antialias,
-				    const cairo_composite_rectangles_t *rects,
-				    cairo_region_t		*clip_region);
+_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
index 48396b5..369cd58 100644
--- a/src/cairo-spans.c
+++ b/src/cairo-spans.c
@@ -26,107 +26,7 @@
  */
 #include "cairoint.h"
 
-typedef struct {
-    cairo_scan_converter_t *converter;
-    cairo_point_t current_point;
-    cairo_point_t first_point;
-    cairo_bool_t has_first_point;
-} scan_converter_filler_t;
-
-static void
-scan_converter_filler_init (scan_converter_filler_t		*filler,
-			    cairo_scan_converter_t		*converter)
-{
-    filler->converter = converter;
-    filler->current_point.x = 0;
-    filler->current_point.y = 0;
-    filler->first_point = filler->current_point;
-    filler->has_first_point = FALSE;
-}
-
-static cairo_status_t
-scan_converter_filler_close_path (void *closure)
-{
-    scan_converter_filler_t *filler = closure;
-    cairo_status_t status;
-
-    filler->has_first_point = FALSE;
-
-    if (filler->first_point.x == filler->current_point.x &&
-	filler->first_point.y == filler->current_point.y)
-    {
-	return CAIRO_STATUS_SUCCESS;
-    }
-
-    status = filler->converter->add_edge (
-	filler->converter,
-	filler->current_point.x, filler->current_point.y,
-	filler->first_point.x, filler->first_point.y);
-
-    filler->current_point = filler->first_point;
-    return status;
-}
-
-static cairo_status_t
-scan_converter_filler_move_to (void *closure,
-			       const cairo_point_t *p)
-{
-    scan_converter_filler_t *filler = closure;
-    if (filler->has_first_point) {
-	cairo_status_t status = scan_converter_filler_close_path (closure);
-	if (status)
-	    return status;
-    }
-    filler->current_point.x = p->x;
-    filler->current_point.y = p->y;
-    filler->first_point = filler->current_point;
-    filler->has_first_point = TRUE;
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-scan_converter_filler_line_to (void *closure,
-			       const cairo_point_t *p)
-{
-    scan_converter_filler_t *filler = closure;
-    cairo_status_t status;
-    cairo_point_t to;
-
-    to.x = p->x;
-    to.y = p->y;
-
-    status = filler->converter->add_edge (
-	filler->converter,
-	filler->current_point.x, filler->current_point.y,
-	to.x, to.y);
-
-    filler->current_point = to;
-
-    return status;
-}
-
-static cairo_status_t
-_cairo_path_fixed_fill_to_scan_converter (
-    cairo_path_fixed_t			*path,
-    double				 tolerance,
-    cairo_scan_converter_t		*converter)
-{
-    scan_converter_filler_t filler;
-    cairo_status_t status;
-
-    scan_converter_filler_init (&filler, converter);
-
-    status = _cairo_path_fixed_interpret_flat (
-	path, CAIRO_DIRECTION_FORWARD,
-	scan_converter_filler_move_to,
-	scan_converter_filler_line_to,
-	scan_converter_filler_close_path,
-	&filler, tolerance);
-    if (status)
-	return status;
-
-    return scan_converter_filler_close_path (&filler);
-}
+#include "cairo-fixed-private.h"
 
 static cairo_scan_converter_t *
 _create_scan_converter (cairo_fill_rule_t			 fill_rule,
@@ -135,51 +35,50 @@ _create_scan_converter (cairo_fill_rule_t			 fill_rule,
 {
     if (antialias == CAIRO_ANTIALIAS_NONE) {
 	ASSERT_NOT_REACHED;
-	return _cairo_scan_converter_create_in_error (
-	    CAIRO_INT_STATUS_UNSUPPORTED);
-    }
-    else {
-	return _cairo_tor_scan_converter_create (
-	    rects->mask.x,
-	    rects->mask.y,
-	    rects->mask.x + rects->width,
-	    rects->mask.y + rects->height,
-	    fill_rule);
+	return NULL;
     }
+
+    return _cairo_tor_scan_converter_create (rects->mask.x,
+					     rects->mask.y,
+					     rects->mask.x + rects->width,
+					     rects->mask.y + rects->height,
+					     fill_rule);
 }
 
+/* XXX Add me to the compositor interface. Ok, first create the compositor
+ * interface, and then add this with associated fallback!
+ */
 cairo_status_t
-_cairo_path_fixed_fill_using_spans (cairo_operator_t		 op,
-				    const cairo_pattern_t	*pattern,
-				    cairo_path_fixed_t		*path,
-				    cairo_surface_t		*dst,
-				    cairo_fill_rule_t		 fill_rule,
-				    double			 tolerance,
-				    cairo_antialias_t		 antialias,
-				    const cairo_composite_rectangles_t *rects,
-				    cairo_region_t		*clip_region)
+_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)
 {
+    cairo_span_renderer_t *renderer;
+    cairo_scan_converter_t *converter;
     cairo_status_t status;
-    cairo_span_renderer_t *renderer = _cairo_surface_create_span_renderer (
-	op, pattern, dst, antialias, rects, clip_region);
-    cairo_scan_converter_t *converter = _create_scan_converter (
-	fill_rule, antialias, rects);
 
-    status = _cairo_path_fixed_fill_to_scan_converter (
-	path, tolerance, converter);
-    if (status)
-	goto BAIL;
+    converter = _create_scan_converter (fill_rule, antialias, rects);
+    status = converter->add_polygon (converter, polygon);
+    if (unlikely (status))
+	goto CLEANUP_CONVERTER;
 
+    renderer = _cairo_surface_create_span_renderer (op, pattern, surface,
+						    antialias, rects,
+						    clip_region);
     status = converter->generate (converter, renderer);
-    if (status)
-	goto BAIL;
+    if (unlikely (status))
+	goto CLEANUP_RENDERER;
 
     status = renderer->finish (renderer);
-    if (status)
-	goto BAIL;
 
- BAIL:
+ CLEANUP_RENDERER:
     renderer->destroy (renderer);
+ CLEANUP_CONVERTER:
     converter->destroy (converter);
     return status;
 }
@@ -191,17 +90,11 @@ _cairo_nil_destroy (void *abstract)
 }
 
 static cairo_status_t
-_cairo_nil_scan_converter_add_edge (void *abstract_converter,
-				    cairo_fixed_t x1,
-				    cairo_fixed_t y1,
-				    cairo_fixed_t x2,
-				    cairo_fixed_t y2)
+_cairo_nil_scan_converter_add_polygon (void *abstract_converter,
+				       const cairo_polygon_t *polygon)
 {
     (void) abstract_converter;
-    (void) x1;
-    (void) y1;
-    (void) x2;
-    (void) y2;
+    (void) polygon;
     return _cairo_scan_converter_status (abstract_converter);
 }
 
@@ -229,7 +122,7 @@ _cairo_scan_converter_set_error (void *abstract_converter,
     if (error == CAIRO_STATUS_SUCCESS)
 	ASSERT_NOT_REACHED;
     if (converter->status == CAIRO_STATUS_SUCCESS) {
-	converter->add_edge = _cairo_nil_scan_converter_add_edge;
+	converter->add_polygon = _cairo_nil_scan_converter_add_polygon;
 	converter->generate = _cairo_nil_scan_converter_generate;
 	converter->status = error;
     }
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 3bfb17d..7ab12b4 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -34,6 +34,8 @@
  *
  * Contributor(s):
  *	Carl D. Worth <cworth at cworth.org>
+ *      Joonas Pihlaja <jpihlaja at cc.helsinki.fi>
+ *	Chris Wilson <chris at chris-wilson.co.uk>
  */
 
 #include "cairoint.h"
@@ -41,6 +43,7 @@
 #include "cairo-surface-fallback-private.h"
 #include "cairo-clip-private.h"
 #include "cairo-region-private.h"
+#include "cairo-spans-private.h"
 
 typedef struct {
     cairo_surface_t *dst;
@@ -739,8 +742,15 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
 	}
     }
 
-    /* Otherwise we need to render the trapezoids to a mask and composite
-     * in the usual fashion.
+    /* No fast path, exclude self-intersections and clip trapezoids. */
+    if (traps->has_intersections) {
+	status = _cairo_bentley_ottmann_tessellate_traps (traps);
+	if (unlikely (status))
+	    return status;
+    }
+
+    /* Otherwise render the trapezoids to a mask and composite in the usual
+     * fashion.
      */
     if (_cairo_operator_bounded_by_mask (op)) {
         cairo_rectangle_int_t trap_extents;
@@ -754,20 +764,20 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
 
     traps_info.traps = traps;
     traps_info.antialias = antialias;
+
     return _clip_and_composite (clip, op, src,
 				_composite_traps_draw_func,
 				&traps_info, dst, extents);
 }
 
 typedef struct {
-    cairo_path_fixed_t		*path;
+    cairo_polygon_t		*polygon;
     cairo_fill_rule_t		 fill_rule;
-    double			 tolerance;
     cairo_antialias_t		 antialias;
-} cairo_composite_spans_fill_info_t;
+} cairo_composite_spans_info_t;
 
 static cairo_status_t
-_composite_spans_fill_func (void                          *closure,
+_composite_spans_draw_func (void                          *closure,
 			    cairo_operator_t               op,
 			    const cairo_pattern_t         *src,
 			    cairo_surface_t               *dst,
@@ -777,7 +787,7 @@ _composite_spans_fill_func (void                          *closure,
 			    cairo_region_t		  *clip_region)
 {
     cairo_composite_rectangles_t rects;
-    cairo_composite_spans_fill_info_t *info = closure;
+    cairo_composite_spans_info_t *info = closure;
 
     _cairo_composite_rectangles_init (
 	&rects, extents->x, extents->y,
@@ -789,12 +799,12 @@ _composite_spans_fill_func (void                          *closure,
     rects.dst.x -= dst_x;
     rects.dst.y -= dst_y;
 
-    return _cairo_path_fixed_fill_using_spans (op, src, info->path, dst,
-					       info->fill_rule,
-					       info->tolerance,
-					       info->antialias,
-					       &rects,
-					       clip_region);
+    return _cairo_surface_composite_polygon (dst, op, src,
+					     info->fill_rule,
+					     info->antialias,
+					     &rects,
+					     info->polygon,
+					     clip_region);
 }
 
 static cairo_bool_t
@@ -930,11 +940,12 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
 				cairo_antialias_t	 antialias,
 				cairo_clip_t		*clip)
 {
-    cairo_status_t status;
+    cairo_polygon_t polygon;
     cairo_traps_t traps;
     cairo_box_t box;
     cairo_rectangle_int_t extents;
     cairo_bool_t is_bounded;
+    cairo_status_t status;
 
     is_bounded = _cairo_surface_get_extents (surface, &extents);
     assert (is_bounded || clip);
@@ -952,22 +963,70 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
 
     _cairo_box_from_rectangle (&box, &extents);
 
+    _cairo_polygon_init (&polygon);
+    _cairo_polygon_limit (&polygon, &box);
+
     _cairo_traps_init (&traps);
     _cairo_traps_limit (&traps, &box);
 
-    status = _cairo_path_fixed_stroke_to_traps (path,
-						stroke_style,
-						ctm, ctm_inverse,
-						tolerance,
-						&traps);
+    if (path->is_rectilinear) {
+	status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
+								stroke_style,
+								ctm,
+								&traps);
+	if (likely (status == CAIRO_STATUS_SUCCESS))
+	    goto DO_TRAPS;
+
+	if (_cairo_status_is_error (status))
+	    goto CLEANUP;
+    }
+
+    status = _cairo_path_fixed_stroke_to_polygon (path,
+						  stroke_style,
+						  ctm, ctm_inverse,
+						  tolerance,
+						  &polygon);
     if (unlikely (status))
-	goto FAIL;
+	goto CLEANUP;
+
+    if (_cairo_operator_bounded_by_mask (op)) {
+	cairo_rectangle_int_t polygon_extents;
 
-    status = _clip_and_composite_trapezoids (source, op,
-					     surface, &traps, antialias,
+	_cairo_box_round_to_rectangle (&polygon.extents, &polygon_extents);
+	if (! _cairo_rectangle_intersect (&extents, &polygon_extents))
+	    goto CLEANUP;
+    }
+
+    if (antialias != CAIRO_ANTIALIAS_NONE &&
+	_cairo_surface_check_span_renderer (op, source, surface,
+					    antialias, NULL))
+    {
+	cairo_composite_spans_info_t info;
+
+	info.polygon = &polygon;
+	info.fill_rule = CAIRO_FILL_RULE_WINDING;
+	info.antialias = antialias;
+
+	status = _clip_and_composite (clip, op, source,
+				      _composite_spans_draw_func,
+				      &info, surface, &extents);
+	goto CLEANUP;
+    }
+
+    /* Fall back to trapezoid fills. */
+    status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+							&polygon,
+							CAIRO_FILL_RULE_WINDING);
+    if (unlikely (status))
+	goto CLEANUP;
+
+  DO_TRAPS:
+    status = _clip_and_composite_trapezoids (source, op, surface,
+					     &traps, antialias,
 					     clip, &extents);
-  FAIL:
+  CLEANUP:
     _cairo_traps_fini (&traps);
+    _cairo_polygon_fini (&polygon);
 
     return status;
 }
@@ -982,11 +1041,12 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
 			      cairo_antialias_t		 antialias,
 			      cairo_clip_t		*clip)
 {
-    cairo_status_t status;
+    cairo_polygon_t polygon;
     cairo_traps_t traps;
     cairo_box_t box;
     cairo_rectangle_int_t extents;
     cairo_bool_t is_bounded;
+    cairo_status_t status;
 
     is_bounded = _cairo_surface_get_extents (surface, &extents);
     assert (is_bounded || clip);
@@ -1002,71 +1062,69 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
     if (! _rectangle_intersect_clip (&extents, clip))
 	return CAIRO_STATUS_SUCCESS;
 
-    /* XXX future direction:
-       status = _cairo_path_fixed_fill_to_region (path, fill_rule, &region);
-       if (status != CAIRO_STATUS_INT_UNSUPPORTED) {
-	   if (unlikely (status))
-	       return status;
+    _cairo_box_from_rectangle (&box, &extents);
 
-         status = _clip_and_composite_region ();
-	 if (status !=  CAIRO_INT_STATUS_UNSUPPORTED)
-	     return status;
-	}
-      */
-
-    /* Ask if the surface would like to render this combination of
-     * op/source/dst/antialias with spans or not, but don't actually
-     * make a renderer yet.  We'll try to hit the region optimisations
-     * in _clip_and_composite_trapezoids() if it looks like the path
-     * is a region. */
-    /* TODO: Until we have a mono scan converter we won't even try
-     * to use spans for CAIRO_ANTIALIAS_NONE. */
-    /* TODO: The region filling code should be lifted from
-     * _clip_and_composite_trapezoids() and given first priority
-     * explicitly before deciding between spans and trapezoids. */
-    if (antialias != CAIRO_ANTIALIAS_NONE && ! path->is_rectilinear &&
-	_cairo_surface_check_span_renderer (
-	    op, source, surface, antialias, NULL))
+    _cairo_polygon_init (&polygon);
+    _cairo_polygon_limit (&polygon, &box);
+
+    _cairo_traps_init (&traps);
+    _cairo_traps_limit (&traps, &box);
+
+    if (path->is_rectilinear) {
+	status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+							      fill_rule,
+							      &traps);
+	if (likely (status == CAIRO_STATUS_SUCCESS))
+	    goto DO_TRAPS;
+
+	if (_cairo_status_is_error (status))
+	    goto CLEANUP;
+    }
+
+    status = _cairo_path_fixed_fill_to_polygon (path,
+						tolerance,
+						&polygon);
+    if (unlikely (status))
+	goto CLEANUP;
+
+    if (_cairo_operator_bounded_by_mask (op)) {
+	cairo_rectangle_int_t polygon_extents;
+
+	_cairo_box_round_to_rectangle (&polygon.extents, &polygon_extents);
+	if (! _cairo_rectangle_intersect (&extents, &polygon_extents))
+	    goto CLEANUP;
+    }
+
+    if (antialias != CAIRO_ANTIALIAS_NONE &&
+	_cairo_surface_check_span_renderer (op, source, surface,
+					    antialias, NULL))
     {
-	cairo_composite_spans_fill_info_t info;
-	info.path = path;
+	cairo_composite_spans_info_t info;
+
+	info.polygon = &polygon;
 	info.fill_rule = fill_rule;
-	info.tolerance = tolerance;
 	info.antialias = antialias;
 
-	if (_cairo_operator_bounded_by_mask (op)) {
-	    cairo_rectangle_int_t path_extents;
-
-	    _cairo_path_fixed_approximate_clip_extents (path,
-							&path_extents);
-	    if (! _cairo_rectangle_intersect (&extents, &path_extents))
-		return CAIRO_STATUS_SUCCESS;
-	}
-
-	return _clip_and_composite (clip, op, source,
-				    _composite_spans_fill_func,
-				    &info,
-				    surface,
-				    &extents);
+	status = _clip_and_composite (clip, op, source,
+				      _composite_spans_draw_func,
+				      &info, surface, &extents);
+	goto CLEANUP;
     }
 
     /* Fall back to trapezoid fills. */
-    _cairo_box_from_rectangle (&box, &extents);
-    _cairo_traps_init (&traps);
-    _cairo_traps_limit (&traps, &box);
-
-    status = _cairo_path_fixed_fill_to_traps (path,
-					      fill_rule,
-					      tolerance,
-					      &traps);
+    status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+							&polygon,
+							fill_rule);
     if (unlikely (status))
-	goto FAIL;
+	goto CLEANUP;
 
+  DO_TRAPS:
     status = _clip_and_composite_trapezoids (source, op, surface,
 					     &traps, antialias,
 					     clip, &extents);
-  FAIL:
+  CLEANUP:
     _cairo_traps_fini (&traps);
+    _cairo_polygon_fini (&polygon);
 
     return status;
 }
diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index f6b5b43..75cda4f 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -199,11 +199,8 @@ glitter_scan_converter_reset(
  * converter should be reset or destroyed.  Dir must be +1 or -1,
  * with the latter reversing the orientation of the edge. */
 I glitter_status_t
-glitter_scan_converter_add_edge(
-    glitter_scan_converter_t *converter,
-    glitter_input_scaled_t x1, glitter_input_scaled_t y1,
-    glitter_input_scaled_t x2, glitter_input_scaled_t y2,
-    int dir);
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+				 const cairo_edge_t *edge);
 
 /* Render the polygon in the scan converter to the given A8 format
  * image raster.  Only the pixels accessible as pixels[y*stride+x] for
@@ -623,10 +620,8 @@ _pool_alloc_from_new_chunk(
     }
 
     if (NULL == chunk) {
-	chunk = _pool_chunk_create(
-	    pool->current,
-	    capacity);
-	if (NULL == chunk)
+	chunk = _pool_chunk_create (pool->current, capacity);
+	if (unlikely (NULL == chunk))
 	    return NULL;
     }
     pool->current = chunk;
@@ -643,9 +638,7 @@ _pool_alloc_from_new_chunk(
  * allocation failures.	 The pool retains ownership of the returned
  * memory. */
 inline static void *
-pool_alloc(
-    struct pool *pool,
-    size_t size)
+pool_alloc (struct pool *pool, size_t size)
 {
     struct _pool_chunk *chunk = pool->current;
 
@@ -653,15 +646,14 @@ pool_alloc(
 	void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
 	chunk->size += size;
 	return obj;
-    }
-    else {
+    } 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)
+pool_reset (struct pool *pool)
 {
     /* Transfer all used chunks to the chunk free list. */
     struct _pool_chunk *chunk = pool->current;
@@ -680,19 +672,18 @@ pool_reset(struct pool *pool)
 /* 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)
+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)
+cell_list_maybe_rewind (struct cell_list *cells, int x)
 {
     struct cell *tail = *cells->cursor;
-    if (tail->x > x) {
-	cell_list_rewind(cells);
-    }
+    if (tail->x > x)
+	cell_list_rewind (cells);
 }
 
 static void
@@ -704,24 +695,24 @@ cell_list_init(struct cell_list *cells)
     cells->tail.next = NULL;
     cells->tail.x = INT_MAX;
     cells->head = &cells->tail;
-    cell_list_rewind(cells);
+    cell_list_rewind (cells);
 }
 
 static void
 cell_list_fini(struct cell_list *cells)
 {
-    pool_fini(cells->cell_pool.base);
-    cell_list_init(cells);
+    pool_fini (cells->cell_pool.base);
+    cell_list_init (cells);
 }
 
 /* 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_reset (struct cell_list *cells)
 {
-    cell_list_rewind(cells);
+    cell_list_rewind (cells);
     cells->head = &cells->tail;
-    pool_reset(cells->cell_pool.base);
+    pool_reset (cells->cell_pool.base);
 }
 
 /* Find a cell at the given x-coordinate.  Returns %NULL if a new cell
@@ -730,7 +721,7 @@ cell_list_reset(struct cell_list *cells)
  * 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)
+cell_list_find (struct cell_list *cells, int x)
 {
     struct cell **cursor = cells->cursor;
     struct cell *tail;
@@ -749,11 +740,12 @@ cell_list_find(struct cell_list *cells, int x)
     if (tail->x == x) {
 	return tail;
     } else {
-	struct cell *cell = pool_alloc(
-	    cells->cell_pool.base,
-	    sizeof(struct cell));
-	if (NULL == cell)
+	struct cell *cell;
+
+	cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+	if (unlikely (NULL == cell))
 	    return NULL;
+
 	*cursor = cell;
 	cell->next = tail;
 	cell->x = x;
@@ -785,17 +777,18 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2)
 	    cell1 = *cursor;
 	    if (cell1->x > x1)
 		break;
+
 	    if (cell1->x == x1)
 		goto found_first;
+
 	    cursor = &cell1->next;
 	});
     }
 
     /* New first cell at x1. */
-    newcell = pool_alloc(
-	cells->cell_pool.base,
-	sizeof(struct cell));
-    if (NULL != newcell) {
+    newcell = pool_alloc (cells->cell_pool.base,
+			  sizeof (struct cell));
+    if (likely (NULL != newcell)) {
 	*cursor = newcell;
 	newcell->next = cell1;
 	newcell->x = x1;
@@ -818,10 +811,9 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2)
     }
 
     /* New second cell at x2. */
-    newcell = pool_alloc(
-	cells->cell_pool.base,
-	sizeof(struct cell));
-    if (NULL != newcell) {
+    newcell = pool_alloc (cells->cell_pool.base,
+			 sizeof (struct cell));
+    if (likely (NULL != newcell)) {
 	*cursor = newcell;
 	newcell->next = cell2;
 	newcell->x = x2;
@@ -849,12 +841,13 @@ cell_list_add_unbounded_subspan(
 
     GRID_X_TO_INT_FRAC(x, ix, fx);
 
-    cell = cell_list_find(cells, ix);
-    if (cell) {
+    cell = cell_list_find (cells, ix);
+    if (likely (cell != NULL)) {
 	cell->uncovered_area += 2*fx;
 	cell->covered_height++;
 	return GLITTER_STATUS_SUCCESS;
     }
+
     return GLITTER_STATUS_NO_MEMORY;
 }
 
@@ -874,17 +867,16 @@ cell_list_add_subspan(
     if (ix1 != ix2) {
 	struct cell_pair p;
 	p = cell_list_find_pair(cells, ix1, ix2);
-	if (p.cell1 && p.cell2) {
+	if (likely (p.cell1 != NULL && p.cell2 != NULL)) {
 	    p.cell1->uncovered_area += 2*fx1;
 	    ++p.cell1->covered_height;
 	    p.cell2->uncovered_area -= 2*fx2;
 	    --p.cell2->covered_height;
 	    return GLITTER_STATUS_SUCCESS;
 	}
-    }
-    else {
+    } else {
 	struct cell *cell = cell_list_find(cells, ix1);
-	if (cell) {
+	if (likely (cell != NULL)) {
 	    cell->uncovered_area += 2*(fx1-fx2);
 	    return GLITTER_STATUS_SUCCESS;
 	}
@@ -938,8 +930,9 @@ cell_list_render_edge(
 	/* 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);
-	if (NULL == cell)
+	if (unlikely (NULL == cell))
 	    return GLITTER_STATUS_NO_MEMORY;
+
 	cell->covered_height += sign*GRID_Y;
 	cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
 	return GLITTER_STATUS_SUCCESS;
@@ -988,7 +981,7 @@ cell_list_render_edge(
 	cell_list_maybe_rewind(cells, ix1);
 
 	pair = cell_list_find_pair(cells, ix1, ix1+1);
-	if (!pair.cell1 || !pair.cell2)
+	if (unlikely (!pair.cell1 || !pair.cell2))
 	    return GLITTER_STATUS_NO_MEMORY;
 
 	pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
@@ -1016,7 +1009,7 @@ cell_list_render_edge(
 
 		++ix1;
 		cell = cell_list_find(cells, ix1);
-		if (NULL == cell)
+		if (unlikely (NULL == cell))
 		    return GLITTER_STATUS_NO_MEMORY;
 	    } while (ix1 != ix2);
 
@@ -1030,22 +1023,22 @@ cell_list_render_edge(
 }
 
 static void
-polygon_init(struct polygon *polygon)
+polygon_init (struct polygon *polygon)
 {
     polygon->ymin = polygon->ymax = 0;
     polygon->y_buckets = polygon->y_buckets_embedded;
-    pool_init(polygon->edge_pool.base,
-	      8192 - sizeof(struct _pool_chunk),
-	      sizeof(polygon->edge_pool.embedded));
+    pool_init (polygon->edge_pool.base,
+	       8192 - sizeof (struct _pool_chunk),
+	       sizeof (polygon->edge_pool.embedded));
 }
 
 static void
-polygon_fini(struct polygon *polygon)
+polygon_fini (struct polygon *polygon)
 {
     if (polygon->y_buckets != polygon->y_buckets_embedded)
-	free(polygon->y_buckets);
-    pool_fini(polygon->edge_pool.base);
-    polygon_init(polygon);
+	free (polygon->y_buckets);
+
+    pool_fini (polygon->edge_pool.base);
 }
 
 /* Empties the polygon of all edges. The polygon is then prepared to
@@ -1063,11 +1056,12 @@ polygon_reset(
 
     pool_reset(polygon->edge_pool.base);
 
-    if (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT)
+    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,
@@ -1099,11 +1093,8 @@ _polygon_insert_edge_into_its_y_bucket(
 }
 
 inline static glitter_status_t
-polygon_add_edge(
-    struct polygon *polygon,
-    int x0, int y0,
-    int x1, int y1,
-    int dir)
+polygon_add_edge (struct polygon *polygon,
+		  const cairo_edge_t *edge)
 {
     struct edge *e;
     grid_scaled_x_t dx;
@@ -1112,54 +1103,45 @@ polygon_add_edge(
     grid_scaled_y_t ymin = polygon->ymin;
     grid_scaled_y_t ymax = polygon->ymax;
 
-    if (y0 == y1)
-	return GLITTER_STATUS_SUCCESS;
+    assert (edge->bottom > edge->top);
 
-    if (y0 > y1) {
-	int tmp;
-	tmp = x0; x0 = x1; x1 = tmp;
-	tmp = y0; y0 = y1; y1 = tmp;
-	dir = -dir;
-    }
-
-    if (y0 >= ymax || y1 <= ymin)
+    if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
 	return GLITTER_STATUS_SUCCESS;
 
-    e = pool_alloc(polygon->edge_pool.base,
-		   sizeof(struct edge));
-    if (NULL == e)
+    e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+    if (unlikely (NULL == e))
 	return GLITTER_STATUS_NO_MEMORY;
 
-    dx = x1 - x0;
-    dy = y1 - y0;
+    dx = edge->line.p2.x - edge->line.p1.x;
+    dy = edge->line.p2.y - edge->line.p1.y;
     e->dy = dy;
-    e->dxdy = floored_divrem(dx, dy);
+    e->dxdy = floored_divrem (dx, dy);
 
-    if (ymin <= y0) {
-	ytop = y0;
-	e->x.quo = x0;
-	e->x.rem = 0;
-    }
-    else {
+    if (ymin <= edge->top)
+	ytop = edge->top;
+    else
 	ytop = ymin;
-	e->x = floored_muldivrem(ymin - y0, dx, dy);
-	e->x.quo += x0;
+    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;
     }
 
-    e->dir = dir;
+    e->dir = edge->dir;
     e->ytop = ytop;
-    ybot = y1 < ymax ? y1 : ymax;
+    ybot = edge->bottom < ymax ? edge->bottom : ymax;
     e->height_left = ybot - ytop;
 
     if (e->height_left >= GRID_Y) {
-	e->dxdy_full = floored_muldivrem(GRID_Y, dx, dy);
-    }
-    else {
+	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);
+    _polygon_insert_edge_into_its_y_bucket (polygon, e);
 
     e->x.rem -= dy;		/* Bias the remainder for faster
 				 * edge advancement. */
@@ -1167,8 +1149,7 @@ polygon_add_edge(
 }
 
 static void
-active_list_reset(
-    struct active_list *active)
+active_list_reset (struct active_list *active)
 {
     active->head = NULL;
     active->min_height = 0;
@@ -1292,8 +1273,7 @@ active_list_merge_edges_from_polygon(
 	    subrow_edges = tail;
 	    if (tail->height_left < min_height)
 		min_height = tail->height_left;
-	}
-	else {
+	} else {
 	    ptail = &tail->next;
 	}
     }
@@ -1347,9 +1327,8 @@ active_list_substep_edges(
 }
 
 inline static glitter_status_t
-apply_nonzero_fill_rule_for_subrow(
-    struct active_list *active,
-    struct cell_list *coverages)
+apply_nonzero_fill_rule_for_subrow (struct active_list *active,
+				    struct cell_list *coverages)
 {
     struct edge *edge = active->head;
     int winding = 0;
@@ -1357,25 +1336,26 @@ apply_nonzero_fill_rule_for_subrow(
     int xend;
     int status;
 
-    cell_list_rewind(coverages);
+    cell_list_rewind (coverages);
 
     while (NULL != edge) {
 	xstart = edge->x.quo;
 	winding = edge->dir;
 	while (1) {
 	    edge = edge->next;
-	    if (NULL == edge) {
-		return cell_list_add_unbounded_subspan(
-		    coverages, xstart);
-	    }
+	    if (NULL == edge)
+		return cell_list_add_unbounded_subspan (coverages, xstart);
+
 	    winding += edge->dir;
-	    if (0 == winding)
-		break;
+	    if (0 == winding) {
+		if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+		    break;
+	    }
 	}
 
 	xend = edge->x.quo;
-	status = cell_list_add_subspan(coverages, xstart, xend);
-	if (status)
+	status = cell_list_add_subspan (coverages, xstart, xend);
+	if (unlikely (status))
 	    return status;
 
 	edge = edge->next;
@@ -1385,29 +1365,33 @@ apply_nonzero_fill_rule_for_subrow(
 }
 
 static glitter_status_t
-apply_evenodd_fill_rule_for_subrow(
-    struct active_list *active,
-    struct cell_list *coverages)
+apply_evenodd_fill_rule_for_subrow (struct active_list *active,
+				    struct cell_list *coverages)
 {
     struct edge *edge = active->head;
     int xstart;
     int xend;
     int status;
 
-    cell_list_rewind(coverages);
+    cell_list_rewind (coverages);
 
     while (NULL != edge) {
 	xstart = edge->x.quo;
 
-	edge = edge->next;
-	if (NULL == edge) {
-	    return cell_list_add_unbounded_subspan(
-		coverages, xstart);
+	while (1) {
+	    edge = edge->next;
+	    if (NULL == edge)
+		return cell_list_add_unbounded_subspan (coverages, xstart);
+
+	    if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+		break;
+
+	    edge = edge->next;
 	}
 
 	xend = edge->x.quo;
-	status = cell_list_add_subspan(coverages, xstart, xend);
-	if (status)
+	status = cell_list_add_subspan (coverages, xstart, xend);
+	if (unlikely (status))
 	    return status;
 
 	edge = edge->next;
@@ -1417,9 +1401,8 @@ apply_evenodd_fill_rule_for_subrow(
 }
 
 static glitter_status_t
-apply_nonzero_fill_rule_and_step_edges(
-    struct active_list *active,
-    struct cell_list *coverages)
+apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
+					struct cell_list *coverages)
 {
     struct edge **cursor = &active->head;
     struct edge *left_edge;
@@ -1431,32 +1414,30 @@ apply_nonzero_fill_rule_and_step_edges(
 	int winding = left_edge->dir;
 
 	left_edge->height_left -= GRID_Y;
-	if (left_edge->height_left) {
+	if (left_edge->height_left)
 	    cursor = &left_edge->next;
-	}
-	else {
+	else
 	    *cursor = left_edge->next;
-	}
 
 	while (1) {
 	    right_edge = *cursor;
-
-	    if (NULL == right_edge) {
-		return cell_list_render_edge(
-		    coverages, left_edge, +1);
-	    }
+	    if (NULL == right_edge)
+		return cell_list_render_edge (coverages, left_edge, +1);
 
 	    right_edge->height_left -= GRID_Y;
-	    if (right_edge->height_left) {
+	    if (right_edge->height_left)
 		cursor = &right_edge->next;
-	    }
-	    else {
+	    else
 		*cursor = right_edge->next;
-	    }
 
 	    winding += right_edge->dir;
-	    if (0 == winding)
-		break;
+	    if (0 == winding) {
+		if (right_edge->next == NULL ||
+		    right_edge->next->x.quo != right_edge->x.quo)
+		{
+		    break;
+		}
+	    }
 
 	    right_edge->x.quo += right_edge->dxdy_full.quo;
 	    right_edge->x.rem += right_edge->dxdy_full.rem;
@@ -1466,13 +1447,12 @@ apply_nonzero_fill_rule_and_step_edges(
 	    }
 	}
 
-	status = cell_list_render_edge(
-	    coverages, left_edge, +1);
-	if (status)
+	status = cell_list_render_edge (coverages, left_edge, +1);
+	if (unlikely (status))
 	    return status;
-	status = cell_list_render_edge(
-	    coverages, right_edge, -1);
-	if (status)
+
+	status = cell_list_render_edge (coverages, right_edge, -1);
+	if (unlikely (status))
 	    return status;
 
 	left_edge = *cursor;
@@ -1482,9 +1462,8 @@ apply_nonzero_fill_rule_and_step_edges(
 }
 
 static glitter_status_t
-apply_evenodd_fill_rule_and_step_edges(
-    struct active_list *active,
-    struct cell_list *coverages)
+apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
+					struct cell_list *coverages)
 {
     struct edge **cursor = &active->head;
     struct edge *left_edge;
@@ -1495,35 +1474,42 @@ apply_evenodd_fill_rule_and_step_edges(
 	struct edge *right_edge;
 
 	left_edge->height_left -= GRID_Y;
-	if (left_edge->height_left) {
+	if (left_edge->height_left)
 	    cursor = &left_edge->next;
-	}
-	else {
+	else
 	    *cursor = left_edge->next;
-	}
 
-	right_edge = *cursor;
+	while (1) {
+	    right_edge = *cursor;
+	    if (NULL == right_edge)
+		return cell_list_render_edge (coverages, left_edge, +1);
 
-	if (NULL == right_edge) {
-	    return cell_list_render_edge(
-		coverages, left_edge, +1);
-	}
+	    right_edge->height_left -= GRID_Y;
+	    if (right_edge->height_left)
+		cursor = &right_edge->next;
+	    else
+		*cursor = right_edge->next;
 
-	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;
+	    }
+
+	    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;
+	    }
 	}
 
-	status = cell_list_render_edge(
-	    coverages, left_edge, +1);
-	if (status)
+	status = cell_list_render_edge (coverages, left_edge, +1);
+	if (unlikely (status))
 	    return status;
-	status = cell_list_render_edge(
-	    coverages, right_edge, -1);
-	if (status)
+
+	status = cell_list_render_edge (coverages, right_edge, -1);
+	if (unlikely (status))
 	    return status;
 
 	left_edge = *cursor;
@@ -1692,26 +1678,28 @@ glitter_scan_converter_reset(
 } while (0)
 
 I glitter_status_t
-glitter_scan_converter_add_edge(
-    glitter_scan_converter_t *converter,
-    glitter_input_scaled_t x1, glitter_input_scaled_t y1,
-    glitter_input_scaled_t x2, glitter_input_scaled_t y2,
-    int dir)
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+				 const cairo_edge_t *edge)
 {
-    /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
-    grid_scaled_y_t sx1, sy1;
-    grid_scaled_y_t sx2, sy2;
+    cairo_edge_t e;
 
-    INPUT_TO_GRID_Y(y1, sy1);
-    INPUT_TO_GRID_Y(y2, sy2);
-    if (sy1 == sy2)
+    INPUT_TO_GRID_Y (edge->top, e.top);
+    INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+    if (e.top == e.bottom)
+	return GLITTER_STATUS_SUCCESS;
+
+    /* 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 GLITTER_STATUS_SUCCESS;
 
-    INPUT_TO_GRID_X(x1, sx1);
-    INPUT_TO_GRID_X(x2, sx2);
+    INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+    INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
 
-    return polygon_add_edge(
-	converter->polygon, sx1, sy1, sx2, sy2, dir);
+    e.dir = edge->dir;
+
+    return polygon_add_edge (converter->polygon, &e);
 }
 
 #ifndef GLITTER_BLIT_COVERAGES_BEGIN
@@ -1756,58 +1744,54 @@ glitter_scan_converter_render(
 
 	/* 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) {
-		GLITTER_BLIT_COVERAGES_EMPTY(i+ymin_i, xmin_i, xmax_i);
+	if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
+	    if (! active->head) {
+		GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, xmin_i, xmax_i);
 		continue;
 	    }
-	    do_full_step = active_list_can_step_full_row(active);
+
+	    do_full_step = active_list_can_step_full_row (active);
 	}
 
-	cell_list_reset(coverages);
+	cell_list_reset (coverages);
 
 	if (do_full_step) {
 	    /* Step by a full pixel row's worth. */
 	    if (nonzero_fill) {
-		status = apply_nonzero_fill_rule_and_step_edges(
-		    active, coverages);
-	    }
-	    else {
-		status = apply_evenodd_fill_rule_and_step_edges(
-		    active, coverages);
+		status = apply_nonzero_fill_rule_and_step_edges (active,
+								 coverages);
+	    } else {
+		status = apply_evenodd_fill_rule_and_step_edges (active,
+								 coverages);
 	    }
-	}
-	else {
+	} else {
 	    /* Subsample this row. */
 	    grid_scaled_y_t suby;
 	    for (suby = 0; suby < GRID_Y; suby++) {
 		grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
 
-		active_list_merge_edges_from_polygon(
-		    active, y, polygon);
+		active_list_merge_edges_from_polygon (active, y, polygon);
 
-		if (nonzero_fill)
-		    status |= apply_nonzero_fill_rule_for_subrow(
-			active, coverages);
-		else
-		    status |= apply_evenodd_fill_rule_for_subrow(
-			active, coverages);
+		if (nonzero_fill) {
+		    status |= apply_nonzero_fill_rule_for_subrow (active,
+								  coverages);
+		} else {
+		    status |= apply_evenodd_fill_rule_for_subrow (active,
+								  coverages);
+		}
 
 		active_list_substep_edges(active);
 	    }
 	}
 
-	if (status)
+	if (unlikely (status))
 	    return status;
 
 	GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, xmin_i, xmax_i);
 
-	if (!active->head) {
+	if (! active->head) {
 	    active->min_height = INT_MAX;
-	}
-	else {
+	} else {
 	    active->min_height -= GRID_Y;
 	}
     }
@@ -1860,7 +1844,7 @@ blit_with_span_renderer(
     /* Allocate enough spans for the row. */
     pool_reset (span_pool);
     spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
-    if (spans == NULL)
+    if (unlikely (spans == NULL))
 	return GLITTER_STATUS_NO_MEMORY;
 
     num_spans = 0;
@@ -1906,6 +1890,7 @@ blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y)
 
 struct _cairo_tor_scan_converter {
     cairo_scan_converter_t base;
+
     glitter_scan_converter_t converter[1];
     cairo_fill_rule_t fill_rule;
 
@@ -1918,9 +1903,9 @@ struct _cairo_tor_scan_converter {
 typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t;
 
 static void
-_cairo_tor_scan_converter_destroy(void *abstract_converter)
+_cairo_tor_scan_converter_destroy (void *converter)
 {
-    cairo_tor_scan_converter_t *self = abstract_converter;
+    cairo_tor_scan_converter_t *self = converter;
     if (self == NULL) {
 	return;
     }
@@ -1930,69 +1915,70 @@ _cairo_tor_scan_converter_destroy(void *abstract_converter)
 }
 
 static cairo_status_t
-_cairo_tor_scan_converter_add_edge(
-    void		*abstract_converter,
-    cairo_fixed_t	 x1,
-    cairo_fixed_t	 y1,
-    cairo_fixed_t	 x2,
-    cairo_fixed_t	 y2)
+_cairo_tor_scan_converter_add_polygon (void		*converter,
+				       const cairo_polygon_t *polygon)
 {
-    cairo_tor_scan_converter_t *self = abstract_converter;
+    cairo_tor_scan_converter_t *self = converter;
     cairo_status_t status;
-    status = glitter_scan_converter_add_edge (
-	self->converter,
-	x1, y1, x2, y2, +1);
-    if (status) {
-	return _cairo_scan_converter_set_error (self,
-						_cairo_error (status));
+    int i;
+
+    for (i = 0; i < polygon->num_edges; i++) {
+	status = glitter_scan_converter_add_edge (self->converter,
+						  &polygon->edges[i]);
+	if (unlikely (status)) {
+	    return _cairo_scan_converter_set_error (self,
+						    _cairo_error (status));
+	}
     }
+
     return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_status_t
-_cairo_tor_scan_converter_generate(
-    void			*abstract_converter,
-    cairo_span_renderer_t	*renderer)
+_cairo_tor_scan_converter_generate (void			*converter,
+				    cairo_span_renderer_t	*renderer)
 {
-    cairo_tor_scan_converter_t *self = abstract_converter;
-    cairo_status_t status = glitter_scan_converter_render (
-	self->converter,
-	self->fill_rule == CAIRO_FILL_RULE_WINDING,
-	renderer,
-	self->span_pool.base);
-    if (status) {
-	return _cairo_scan_converter_set_error (self,
-						_cairo_error (status));
-    }
+    cairo_tor_scan_converter_t *self = converter;
+    cairo_status_t status;
+
+   status = glitter_scan_converter_render (self->converter,
+					   self->fill_rule == CAIRO_FILL_RULE_WINDING,
+					   renderer,
+					   self->span_pool.base);
+    if (unlikely (status))
+	return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
     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_tor_scan_converter_create (int			xmin,
+				  int			ymin,
+				  int			xmax,
+				  int			ymax,
+				  cairo_fill_rule_t	fill_rule)
 {
+    cairo_tor_scan_converter_t *self;
     cairo_status_t status;
-    cairo_tor_scan_converter_t *self =
-	calloc (1, sizeof(struct _cairo_tor_scan_converter));
-    if (self == NULL)
+
+    self = calloc (1, 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.add_edge = &_cairo_tor_scan_converter_add_edge;
-    self->base.generate = &_cairo_tor_scan_converter_generate;
+    self->base.destroy = _cairo_tor_scan_converter_destroy;
+    self->base.add_polygon = _cairo_tor_scan_converter_add_polygon;
+    self->base.generate = _cairo_tor_scan_converter_generate;
 
     pool_init (self->span_pool.base,
 	      250 * sizeof(self->span_pool.embedded[0]),
 	      sizeof(self->span_pool.embedded));
 
     _glitter_scan_converter_init (self->converter);
-    status = glitter_scan_converter_reset (
-	self->converter, xmin, ymin, xmax, ymax);
-    if (status != CAIRO_STATUS_SUCCESS)
+    status = glitter_scan_converter_reset (self->converter,
+					   xmin, ymin, xmax, ymax);
+    if (unlikely (status))
 	goto bail;
 
     self->fill_rule = fill_rule;
@@ -2002,5 +1988,5 @@ _cairo_tor_scan_converter_create(
  bail:
     self->base.destroy(&self->base);
  bail_nomem:
-    return _cairo_scan_converter_create_in_error (CAIRO_STATUS_NO_MEMORY);
+    return _cairo_scan_converter_create_in_error (status);
 }
diff --git a/src/cairo-toy-font-face.c b/src/cairo-toy-font-face.c
index a9b600a..e7c841a 100644
--- a/src/cairo-toy-font-face.c
+++ b/src/cairo-toy-font-face.c
@@ -520,5 +520,6 @@ _cairo_toy_font_face_reset_static_data (void)
     cairo_toy_font_face_hash_table = NULL;
     CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
 
-    _cairo_hash_table_destroy (hash_table);
+    if (hash_table != NULL)
+	_cairo_hash_table_destroy (hash_table);
 }
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index 343d77f..057e950 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -63,6 +63,7 @@ _cairo_traps_init (cairo_traps_t *traps)
     traps->extents.p2.x = traps->extents.p2.y = INT32_MIN;
 
     traps->has_limits = FALSE;
+    traps->has_intersections = FALSE;
 }
 
 void
@@ -74,14 +75,6 @@ _cairo_traps_limit (cairo_traps_t	*traps,
     traps->limits = *limits;
 }
 
-cairo_bool_t
-_cairo_traps_get_limit (cairo_traps_t *traps,
-			cairo_box_t   *limits)
-{
-    *limits = traps->limits;
-    return traps->has_limits;
-}
-
 void
 _cairo_traps_clear (cairo_traps_t *traps)
 {
@@ -90,6 +83,7 @@ _cairo_traps_clear (cairo_traps_t *traps)
     traps->maybe_region = 1;
 
     traps->num_traps = 0;
+    traps->has_intersections = FALSE;
     traps->extents.p1.x = traps->extents.p1.y = INT32_MAX;
     traps->extents.p2.x = traps->extents.p2.y = INT32_MIN;
 }
@@ -165,6 +159,12 @@ _cairo_traps_grow (cairo_traps_t *traps)
     return TRUE;
 }
 
+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_add_trap (cairo_traps_t *traps,
 		       cairo_fixed_t top, cairo_fixed_t bottom,
@@ -257,23 +257,44 @@ _cairo_traps_add_trap (cairo_traps_t *traps,
 	traps->extents.p1.y = top;
     if (bottom > traps->extents.p2.y)
 	traps->extents.p2.y = bottom;
-    /*
-     * This isn't generally accurate, but it is close enough for
-     * this purpose.  Assuming that the left and right segments always
-     * contain the trapezoid vertical extents, these compares will
-     * yield a containing box.  Assuming that the points all come from
-     * the same figure which will eventually be completely drawn, then
-     * the compares will yield the correct overall extents
-     */
-    if (left->p1.x < traps->extents.p1.x)
-	traps->extents.p1.x = left->p1.x;
-    if (left->p2.x < traps->extents.p1.x)
-	traps->extents.p1.x = left->p2.x;
 
-    if (right->p1.x > traps->extents.p2.x)
-	traps->extents.p2.x = right->p1.x;
-    if (right->p2.x > traps->extents.p2.x)
-	traps->extents.p2.x = right->p2.x;
+    if (left->p1.x < traps->extents.p1.x) {
+	cairo_fixed_t x = left->p1.x;
+	if (top != left->p1.y) {
+	    x = _line_compute_intersection_x_for_y (left, top);
+	    if (x < traps->extents.p1.x)
+		traps->extents.p1.x = x;
+	} else
+	    traps->extents.p1.x = x;
+    }
+    if (left->p2.x < traps->extents.p1.x) {
+	cairo_fixed_t x = left->p2.x;
+	if (bottom != left->p2.y) {
+	    x = _line_compute_intersection_x_for_y (left, bottom);
+	    if (x < traps->extents.p1.x)
+		traps->extents.p1.x = x;
+	} else
+	    traps->extents.p1.x = x;
+    }
+
+    if (right->p1.x > traps->extents.p2.x) {
+	cairo_fixed_t x = right->p1.x;
+	if (top != right->p1.y) {
+	    x = _line_compute_intersection_x_for_y (right, top);
+	    if (x > traps->extents.p2.x)
+		traps->extents.p2.x = x;
+	} else
+	    traps->extents.p2.x = x;
+    }
+    if (right->p2.x > traps->extents.p2.x) {
+	cairo_fixed_t x = right->p2.x;
+	if (bottom != right->p2.y) {
+	    x = _line_compute_intersection_x_for_y (right, bottom);
+	    if (x > traps->extents.p2.x)
+		traps->extents.p2.x = x;
+	} else
+	    traps->extents.p2.x = x;
+    }
 
     traps->num_traps++;
 }
@@ -284,9 +305,8 @@ _compare_point_fixed_by_y (const void *av, const void *bv)
     const cairo_point_t	*a = av, *b = bv;
 
     int ret = a->y - b->y;
-    if (ret == 0) {
+    if (ret == 0)
 	ret = a->x - b->x;
-    }
     return ret;
 }
 
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index b553620..fa70678 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -177,13 +177,7 @@ typedef enum _cairo_internal_surface_type {
 #define CAIRO_HAS_TEST_NULL_SURFACE 1
 #define CAIRO_HAS_TEST_WRAPPING_SURFACE 1
 
-typedef struct _cairo_point {
-    cairo_fixed_t x;
-    cairo_fixed_t y;
-} cairo_point_t;
-
-typedef struct _cairo_slope
-{
+typedef struct _cairo_slope {
     cairo_fixed_t dx;
     cairo_fixed_t dy;
 } cairo_slope_t, cairo_distance_t;
@@ -244,7 +238,8 @@ typedef enum _cairo_direction {
 } cairo_direction_t;
 
 typedef struct _cairo_edge {
-    cairo_line_t edge;
+    cairo_line_t line;
+    int top, bottom;
     int dir;
 } cairo_edge_t;
 
@@ -255,6 +250,10 @@ typedef struct _cairo_polygon {
     cairo_point_t current_point;
     cairo_bool_t has_current_point;
 
+    cairo_box_t extents;
+    cairo_box_t limits;
+    cairo_bool_t has_limits;
+
     int num_edges;
     int edges_size;
     cairo_edge_t *edges;
diff --git a/src/cairo-wideint-private.h b/src/cairo-wideint-private.h
index f5aac28..ec7fa1f 100644
--- a/src/cairo-wideint-private.h
+++ b/src/cairo-wideint-private.h
@@ -51,6 +51,9 @@
 
 #if !HAVE_UINT64_T
 
+cairo_uquorem64_t I
+_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den);
+
 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);
@@ -90,6 +93,16 @@ int	       I	_cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b);
 
 #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;
+}
+
 #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))
@@ -147,11 +160,35 @@ int	       I	_cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b);
  * a function which returns both for the 'native' type as well
  */
 
-cairo_uquorem64_t I
-_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den);
-
-cairo_quorem64_t I
-_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den);
+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)
+{
+    return num / den;
+}
 
 /*
  * 128-bit datatypes.  Again, provide two implementations in
diff --git a/src/cairo-wideint.c b/src/cairo-wideint.c
index 4565593..1843a60 100644
--- a/src/cairo-wideint.c
+++ b/src/cairo-wideint.c
@@ -39,16 +39,6 @@
 
 #define _cairo_uint32s_to_uint64(h,l) ((uint64_t) (h) << 32 | (l))
 
-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;
-}
-
 #else
 
 cairo_uint64_t
@@ -317,30 +307,6 @@ _cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den)
 
 #endif /* !HAVE_UINT64_T */
 
-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;
-}
-
 #if HAVE_UINT128_T
 
 cairo_uquorem128_t
diff --git a/src/cairoint.h b/src/cairoint.h
index 12e4098..30b0c4c 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -952,6 +952,7 @@ typedef struct _cairo_traps {
     cairo_status_t status;
 
     cairo_box_t extents;
+    cairo_box_t limits;
 
     unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
 
@@ -962,7 +963,7 @@ typedef struct _cairo_traps {
     cairo_trapezoid_t  traps_embedded[4];
 
     cairo_bool_t has_limits;
-    cairo_box_t limits;
+    cairo_bool_t has_intersections;
 } cairo_traps_t;
 
 #define CAIRO_FONT_SLANT_DEFAULT   CAIRO_FONT_SLANT_NORMAL
@@ -1609,13 +1610,36 @@ _cairo_path_fixed_in_fill (const cairo_path_fixed_t	*path,
 
 /* cairo-path-fill.c */
 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_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
+				   double              tolerance,
+				   cairo_polygon_t      *polygon);
+
+cairo_private cairo_int_status_t
+_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
+					     cairo_fill_rule_t fill_rule,
+					     cairo_traps_t    *traps);
+
+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,
+				     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_traps (const cairo_path_fixed_t	*path,
+					       cairo_stroke_style_t	*stroke_style,
+					       const cairo_matrix_t	*ctm,
+					       cairo_traps_t		*traps);
+cairo_private cairo_status_t
 _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
 				   cairo_stroke_style_t	*stroke_style,
 				   const cairo_matrix_t	*ctm,
@@ -1623,6 +1647,22 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
 				   double		 tolerance,
 				   cairo_traps_t	*traps);
 
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t	*path,
+				   cairo_stroke_style_t	*stroke_style,
+				   cairo_matrix_t	*ctm,
+				   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
@@ -2201,45 +2241,21 @@ cairo_private int
 _cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
 					 const cairo_slope_t *slope);
 
-typedef struct _cairo_pen_stroke_spline {
-    cairo_pen_t pen;
-    cairo_spline_t spline;
-    cairo_polygon_t polygon;
-    cairo_point_t last_point;
-    cairo_point_t forward_hull_point;
-    cairo_point_t backward_hull_point;
-    int forward_vertex;
-    int backward_vertex;
-} cairo_pen_stroke_spline_t;
-
-cairo_private cairo_int_status_t
-_cairo_pen_stroke_spline_init (cairo_pen_stroke_spline_t *stroker,
-			       const cairo_pen_t *pen,
-			       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_pen_stroke_spline (cairo_pen_stroke_spline_t	*pen,
-			  double			 tolerance,
-			  cairo_traps_t			*traps);
-
-cairo_private void
-_cairo_pen_stroke_spline_fini (cairo_pen_stroke_spline_t *stroker);
-
 /* cairo-polygon.c */
 cairo_private void
 _cairo_polygon_init (cairo_polygon_t *polygon);
 
 cairo_private void
-_cairo_polygon_fini (cairo_polygon_t *polygon);
+_cairo_polygon_limit (cairo_polygon_t	*polygon,
+		      const cairo_box_t *limits);
 
 cairo_private void
-_cairo_polygon_add_edge (cairo_polygon_t *polygon,
-			 const cairo_point_t *p1,
-			 const cairo_point_t *p2,
-			 int dir);
+_cairo_polygon_fini (cairo_polygon_t *polygon);
+
+cairo_private cairo_status_t
+_cairo_polygon_add_external_edge (void *polygon,
+				  const cairo_point_t *p1,
+				  const cairo_point_t *p2);
 
 cairo_private void
 _cairo_polygon_move_to (cairo_polygon_t *polygon,
@@ -2252,7 +2268,7 @@ _cairo_polygon_line_to (cairo_polygon_t *polygon,
 cairo_private void
 _cairo_polygon_close (cairo_polygon_t *polygon);
 
-#define _cairo_polygon_status(P) (P)->status
+#define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status
 
 /* cairo-spline.c */
 cairo_private cairo_bool_t
@@ -2310,6 +2326,9 @@ _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);
+
+cairo_private cairo_bool_t
 _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
 
 cairo_private double
@@ -2330,10 +2349,6 @@ cairo_private void
 _cairo_traps_limit (cairo_traps_t	*traps,
 		    cairo_box_t		*limits);
 
-cairo_private cairo_bool_t
-_cairo_traps_get_limit (cairo_traps_t *traps,
-                        cairo_box_t   *limits);
-
 cairo_private void
 _cairo_traps_init_box (cairo_traps_t *traps,
 		       const cairo_box_t   *box);
@@ -2372,6 +2387,9 @@ _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_private int
 _cairo_traps_contain (const cairo_traps_t *traps,
 		      double x, double y);
diff --git a/test/Makefile.am b/test/Makefile.am
index a052393..9b5ee2a 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -4,8 +4,6 @@ include $(top_srcdir)/test/Makefile.sources
 
 SUBDIRS=pdiff .
 
-CLEANFILES += have-similar.*
-
 # Then we have a collection of tests that are only run if certain
 # features are compiled into cairo
 if HAVE_PTHREAD
@@ -147,8 +145,10 @@ REFERENCE_IMAGES = \
 	bitmap-font.rgb24.ref.png \
 	caps-joins-alpha.quartz.ref.png \
 	caps-joins-alpha.ref.png \
+	caps-joins-alpha.xlib.ref.png \
 	caps-joins-curve.ps.ref.png \
 	caps-joins-curve.ref.png \
+	caps-joins-curve.xlib.ref.png \
 	caps-joins.ps.ref.png \
 	caps-joins.ref.png \
 	caps-sub-paths.ref.png \
@@ -156,6 +156,8 @@ REFERENCE_IMAGES = \
 	caps.ref.png \
 	clear.argb32.ref.png \
 	clear.rgb24.ref.png \
+	clear.pdf.argb32.ref.png \
+	clear.ps.argb32.ref.png \
 	clear.svg12.argb32.xfail.png \
 	clear.svg12.rgb24.xfail.png \
 	clip-all.ref.png \
@@ -237,6 +239,7 @@ REFERENCE_IMAGES = \
 	clipped-group.ps3.ref.png \
 	clipped-group.ref.png \
 	clipped-surface.ref.png \
+	clipped-trapezoids.ref.png \
 	close-path-current-point.ps.ref.png \
 	close-path-current-point.ref.png \
 	close-path.ps2.ref.png \
@@ -275,6 +278,7 @@ REFERENCE_IMAGES = \
 	dash-curve.ps3.ref.png \
 	dash-curve.quartz.ref.png \
 	dash-curve.ref.png \
+	dash-curve.xlib.ref.png \
 	dash-infinite-loop.ref.png \
 	dash-no-dash.ref.png \
 	dash-offset-negative.ref.png \
@@ -302,16 +306,18 @@ REFERENCE_IMAGES = \
 	degenerate-curve-to.ps.xfail.png \
 	degenerate-dash.ps.xfail.png \
 	degenerate-dash.ref.png \
+	degenerate-dash.xlib.ref.png \
 	degenerate-path.ps.argb32.xfail.png \
 	degenerate-path.ps.rgb24.xfail.png \
 	degenerate-path.quartz.ref.png \
 	degenerate-path.quartz.rgb24.ref.png \
-	degenerate-path.ref.png \
+	degenerate-path.argb32.ref.png \
 	degenerate-path.rgb24.ref.png \
 	degenerate-pen.ps2.ref.png \
 	degenerate-pen.ps3.ref.png \
 	degenerate-pen.quartz.ref.png \
 	degenerate-pen.ref.png \
+	degenerate-pen.xlib.ref.png \
 	degenerate-rel-curve-to.ref.png \
 	degenerate-rel-curve-to.ps.xfail.png \
 	device-offset-fractional.gl.xfail.png \
@@ -389,8 +395,10 @@ REFERENCE_IMAGES = \
 	fill-and-stroke.ps3.rgb24.ref.png \
 	fill-and-stroke.quartz.ref.png \
 	fill-and-stroke.quartz.rgb24.ref.png \
-	fill-and-stroke.ref.png \
+	fill-and-stroke.argb32.ref.png \
 	fill-and-stroke.rgb24.ref.png \
+	fill-and-stroke.xlib.argb32.ref.png \
+	fill-and-stroke.xlib.rgb24.ref.png \
 	fill-degenerate-sort-order.ps.argb32.xfail.png \
 	fill-degenerate-sort-order.ps.rgb24.xfail.png \
 	fill-degenerate-sort-order.quartz.ref.png \
@@ -469,14 +477,12 @@ REFERENCE_IMAGES = \
 	ft-text-antialias-none.ps3.argb32.ref.png \
 	ft-text-antialias-none.ref.png \
 	ft-text-vertical-layout-type1.pdf.ref.png \
-	ft-text-vertical-layout-type1.ps2.ref.png \
-	ft-text-vertical-layout-type1.ps3.ref.png \
+	ft-text-vertical-layout-type1.ps.ref.png \
 	ft-text-vertical-layout-type1.ref.png \
 	ft-text-vertical-layout-type1.svg.ref.png \
 	ft-text-vertical-layout-type1.xlib.ref.png \
 	ft-text-vertical-layout-type3.pdf.ref.png \
-	ft-text-vertical-layout-type3.ps2.ref.png \
-	ft-text-vertical-layout-type3.ps3.ref.png \
+	ft-text-vertical-layout-type3.ps.ref.png \
 	ft-text-vertical-layout-type3.ref.png \
 	ft-text-vertical-layout-type3.svg.ref.png \
 	ft-text-vertical-layout-type3.xlib.ref.png \
@@ -538,9 +544,11 @@ REFERENCE_IMAGES = \
 	leaky-dashed-rectangle.pdf.ref.png \
 	leaky-dashed-rectangle.ps.ref.png \
 	leaky-dashed-rectangle.ref.png \
+	leaky-dashed-rectangle.xlib.ref.png \
 	leaky-dashed-stroke.ps2.ref.png \
 	leaky-dashed-stroke.ps3.ref.png \
 	leaky-dashed-stroke.ref.png \
+	leaky-dashed-stroke.xlib.ref.png \
 	leaky-polygon.ps2.ref.png \
 	leaky-polygon.ps3.ref.png \
 	leaky-polygon.ref.png \
@@ -613,13 +621,14 @@ REFERENCE_IMAGES = \
 	miter-precision.ps3.ref.png \
 	miter-precision.ref.png \
 	move-to-show-surface.ref.png \
+	new-sub-path.pdf.argb32.ref.png \
 	new-sub-path.ps2.argb32.ref.png \
 	new-sub-path.ps2.rgb24.ref.png \
 	new-sub-path.ps3.argb32.ref.png \
 	new-sub-path.ps3.rgb24.ref.png \
 	new-sub-path.quartz.ref.png \
 	new-sub-path.quartz.rgb24.ref.png \
-	new-sub-path.ref.png \
+	new-sub-path.argb32.ref.png \
 	new-sub-path.rgb24.ref.png \
 	nil-surface.ref.png \
 	nil-surface.rgb24.ref.png \
@@ -726,11 +735,13 @@ REFERENCE_IMAGES = \
 	radial-gradient.pdf.ref.png \
 	radial-gradient.quartz.ref.png \
 	radial-gradient.ref.png \
-	random-intersections.ps2.ref.png \
-	random-intersections.ps3.ref.png \
-	random-intersections.quartz.ref.png \
-	random-intersections.ref.png \
-	random-intersections.xlib.ref.png \
+	random-intersections-eo.ps.ref.png \
+	random-intersections-eo.quartz.ref.png \
+	random-intersections-eo.ref.png \
+	random-intersections-eo.xlib.ref.png \
+	random-intersections-nonzero.ref.png \
+	random-intersections-nonzero.ps.ref.png \
+	random-intersections-nonzero.xlib.ref.png \
 	rectangle-rounding-error.ref.png \
 	rectilinear-dash.ref.png \
 	rectilinear-fill.ref.png \
@@ -783,16 +794,9 @@ REFERENCE_IMAGES = \
 	self-copy.ps2.ref.png \
 	self-copy.ps3.ref.png \
 	self-copy.ref.png \
-	self-intersecting.argb32.xfail.png \
-	self-intersecting.pdf.argb32.xfail.png \
-	self-intersecting.pdf.rgb24.xfail.png \
-	self-intersecting.ps.argb32.xfail.png \
-	self-intersecting.ps.rgb24.xfail.png \
+	self-intersecting.ps.ref.png \
 	self-intersecting.ref.png \
-	self-intersecting.rgb24.ref.png \
-	self-intersecting.rgb24.xfail.png \
-	self-intersecting.xlib.argb32.xfail.png \
-	self-intersecting.xlib.rgb24.xfail.png \
+	self-intersecting.xlib.ref.png \
 	set-source.ref.png \
 	set-source.rgb24.ref.png \
 	show-glyphs-many.ref.png \
@@ -815,6 +819,7 @@ REFERENCE_IMAGES = \
 	smask-paint.svg.ref.png \
 	smask-stroke.pdf.xfail.png \
 	smask-stroke.ref.png \
+	smask-stroke.xlib.ref.png \
 	smask-text.pdf.ref.png \
 	smask-text.ps2.ref.png \
 	smask-text.ps3.ref.png \
@@ -822,8 +827,7 @@ REFERENCE_IMAGES = \
 	smask-text.svg.ref.png \
 	smask-text.xlib.ref.png \
 	smask.pdf.xfail.png \
-	smask.ps2.ref.png \
-	smask.ps3.ref.png \
+	smask.ps.ref.png \
 	smask.ref.png \
 	smask.svg.ref.png \
 	smask.xlib.ref.png \
@@ -844,15 +848,16 @@ REFERENCE_IMAGES = \
 	spline-decomposition.ps.ref.png \
 	spline-decomposition.ref.png \
 	spline-decomposition.svg.ref.png \
+	spline-decomposition.xlib.ref.png \
 	stroke-ctm-caps.ps2.ref.png \
 	stroke-ctm-caps.ps3.ref.png \
 	stroke-ctm-caps.quartz.ref.png \
 	stroke-ctm-caps.ref.png \
 	stroke-image.pdf.ref.png \
-	stroke-image.ps2.ref.png \
-	stroke-image.ps3.ref.png \
+	stroke-image.ps.ref.png \
 	stroke-image.quartz.ref.png \
 	stroke-image.ref.png \
+	stroke-image.xlib.ref.png \
 	surface-pattern-big-scale-down.ref.png \
 	surface-pattern-big-scale-down.ps.xfail.png \
 	surface-pattern-scale-down.pdf.ref.png \
@@ -900,6 +905,7 @@ REFERENCE_IMAGES = \
 	text-rotate.quartz.ref.png \
 	text-rotate.ref.png \
 	text-rotate.svg.ref.png \
+	text-rotate.xlib.ref.png \
 	text-transform.pdf.ref.png \
 	text-transform.ps2.ref.png \
 	text-transform.ps3.ref.png \
@@ -923,6 +929,7 @@ REFERENCE_IMAGES = \
 	twin.ps.ref.png \
 	twin.ref.png \
 	twin.svg.ref.png \
+	twin.xlib.ref.png \
 	unantialiased-shapes.quartz.ref.png \
 	unantialiased-shapes.ref.png \
 	unbounded-operator.gl.argb32.xfail.png \
@@ -934,7 +941,7 @@ REFERENCE_IMAGES = \
 	unbounded-operator.quartz.rgb24.ref.png \
 	unbounded-operator.ref.png \
 	unbounded-operator.rgb24.ref.png \
-	unbounded-operator.svg12.argb32.xfail.png \
+	unbounded-operator.svg12.argb32.ref.png \
 	unbounded-operator.svg12.rgb24.xfail.png \
 	unbounded-operator.xlib.rgb24.ref.png \
 	user-font-mask.pdf.ref.png \
@@ -953,8 +960,7 @@ REFERENCE_IMAGES = \
 	user-font-rescale.ps3.ref.png \
 	user-font-rescale.ref.png \
 	user-font-rescale.svg.ref.png \
-	user-font.ps2.ref.png \
-	user-font.ps3.ref.png \
+	user-font.ps.ref.png \
 	user-font.ref.png \
 	user-font.svg.ref.png \
 	user-font.xlib.ref.png \
@@ -1132,14 +1138,22 @@ imagediff_LDADD = \
 	$(top_builddir)/src/libcairo.la
 
 png_flatten_SOURCES = png-flatten.c
-png_flatten_LDADD = $(top_builddir)/src/libcairo.la $(CAIRO_LDADD)
+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)
+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
@@ -1147,7 +1161,9 @@ 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)
+pdf2png_LDADD  = $(top_builddir)/src/libcairo.la \
+		 $(CAIRO_LDADD) \
+		 $(POPPLER_LIBS)
 endif
 
 if CAIRO_CAN_TEST_SVG_SURFACE
@@ -1155,7 +1171,9 @@ 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)
+svg2png_LDADD  = $(top_builddir)/src/libcairo.la \
+		 $(CAIRO_LDADD) \
+		 $(LIBRSVG_LIBS)
 endif
 
 if CAIRO_HAS_SPECTRE
@@ -1163,7 +1181,9 @@ 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)
+ps2png_LDADD  = $(top_builddir)/src/libcairo.la \
+		$(CAIRO_LDADD) \
+		$(LIBSPECTRE_LIBS)
 endif
 
 EXTRA_PROGRAMS += $(TESTS)
diff --git a/test/Makefile.sources b/test/Makefile.sources
index 50b4d3e..0a58ac0 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -150,7 +150,8 @@ test_sources = \
 	png.c						\
 	push-group.c					\
 	radial-gradient.c				\
-	random-intersections.c				\
+	random-intersections-eo.c			\
+	random-intersections-nonzero.c			\
 	rectangle-rounding-error.c			\
 	rectilinear-fill.c				\
 	rectilinear-miter-limit.c			\
diff --git a/test/caps-joins-alpha.ref.png b/test/caps-joins-alpha.ref.png
index 1d34c73..b28d936 100644
Binary files a/test/caps-joins-alpha.ref.png and b/test/caps-joins-alpha.ref.png differ
diff --git a/test/caps-joins-alpha.xlib.ref.png b/test/caps-joins-alpha.xlib.ref.png
new file mode 100644
index 0000000..288a500
Binary files /dev/null and b/test/caps-joins-alpha.xlib.ref.png differ
diff --git a/test/caps-joins-curve.ref.png b/test/caps-joins-curve.ref.png
index 9f76301..cb4034b 100644
Binary files a/test/caps-joins-curve.ref.png and b/test/caps-joins-curve.ref.png differ
diff --git a/test/caps-joins-curve.xlib.ref.png b/test/caps-joins-curve.xlib.ref.png
new file mode 100644
index 0000000..be7688d
Binary files /dev/null and b/test/caps-joins-curve.xlib.ref.png differ
diff --git a/test/clear.pdf.argb32.ref.png b/test/clear.pdf.argb32.ref.png
new file mode 100644
index 0000000..0960f48
Binary files /dev/null and b/test/clear.pdf.argb32.ref.png differ
diff --git a/test/clear.ps.argb32.ref.png b/test/clear.ps.argb32.ref.png
new file mode 100644
index 0000000..0960f48
Binary files /dev/null and b/test/clear.ps.argb32.ref.png differ
diff --git a/test/clip-operator.svg12.argb32.xfail.png b/test/clip-operator.svg12.argb32.xfail.png
index be0696e..1c21d15 100644
Binary files a/test/clip-operator.svg12.argb32.xfail.png and b/test/clip-operator.svg12.argb32.xfail.png differ
diff --git a/test/clip-operator.svg12.rgb24.xfail.png b/test/clip-operator.svg12.rgb24.xfail.png
index 494852d..f79de48 100644
Binary files a/test/clip-operator.svg12.rgb24.xfail.png and b/test/clip-operator.svg12.rgb24.xfail.png differ
diff --git a/test/clipped-group.pdf.ref.png b/test/clipped-group.pdf.ref.png
index 6495860..23db5a4 100644
Binary files a/test/clipped-group.pdf.ref.png and b/test/clipped-group.pdf.ref.png differ
diff --git a/test/clipped-trapezoids-ref.png b/test/clipped-trapezoids-ref.png
new file mode 100644
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
index 0000000..1f96daa
--- /dev/null
+++ b/test/clipped-trapezoids.c
@@ -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 at 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/clipped-trapezoids.ref.png b/test/clipped-trapezoids.ref.png
new file mode 100644
index 0000000..975a692
Binary files /dev/null and b/test/clipped-trapezoids.ref.png differ
diff --git a/test/close-path-current-point.ref.png b/test/close-path-current-point.ref.png
index f30002c..70f9acc 100644
Binary files a/test/close-path-current-point.ref.png and b/test/close-path-current-point.ref.png differ
diff --git a/test/dash-caps-joins.ref.png b/test/dash-caps-joins.ref.png
index 15a0679..bde3697 100644
Binary files a/test/dash-caps-joins.ref.png and b/test/dash-caps-joins.ref.png differ
diff --git a/test/dash-curve.ref.png b/test/dash-curve.ref.png
index a590fc4..b940c1b 100644
Binary files a/test/dash-curve.ref.png and b/test/dash-curve.ref.png differ
diff --git a/test/dash-curve.xlib.ref.png b/test/dash-curve.xlib.ref.png
new file mode 100644
index 0000000..7621260
Binary files /dev/null and b/test/dash-curve.xlib.ref.png differ
diff --git a/test/dash-scale.ref.png b/test/dash-scale.ref.png
index a0f004b..cbfb6bd 100644
Binary files a/test/dash-scale.ref.png and b/test/dash-scale.ref.png differ
diff --git a/test/degenerate-arc.ref.png b/test/degenerate-arc.ref.png
index 1d131b2..d83e2c7 100644
Binary files a/test/degenerate-arc.ref.png and b/test/degenerate-arc.ref.png differ
diff --git a/test/degenerate-dash.ref.png b/test/degenerate-dash.ref.png
index ec51a3c..2ddfc8b 100644
Binary files a/test/degenerate-dash.ref.png and b/test/degenerate-dash.ref.png differ
diff --git a/test/degenerate-dash.xlib.ref.png b/test/degenerate-dash.xlib.ref.png
new file mode 100644
index 0000000..a1b2e83
Binary files /dev/null and b/test/degenerate-dash.xlib.ref.png differ
diff --git a/test/degenerate-path.argb32.ref.png b/test/degenerate-path.argb32.ref.png
new file mode 100644
index 0000000..e647677
Binary files /dev/null and b/test/degenerate-path.argb32.ref.png differ
diff --git a/test/degenerate-path.ref.png b/test/degenerate-path.ref.png
deleted file mode 100644
index 102e890..0000000
Binary files a/test/degenerate-path.ref.png and /dev/null differ
diff --git a/test/degenerate-path.rgb24.ref.png b/test/degenerate-path.rgb24.ref.png
index 881c019..d8210a5 100644
Binary files a/test/degenerate-path.rgb24.ref.png and b/test/degenerate-path.rgb24.ref.png differ
diff --git a/test/degenerate-pen.ref.png b/test/degenerate-pen.ref.png
index 5961ddd..b7dcaad 100644
Binary files a/test/degenerate-pen.ref.png and b/test/degenerate-pen.ref.png differ
diff --git a/test/degenerate-pen.xlib.ref.png b/test/degenerate-pen.xlib.ref.png
new file mode 100644
index 0000000..b7dcaad
Binary files /dev/null and b/test/degenerate-pen.xlib.ref.png differ
diff --git a/test/device-offset-fractional.pdf.xfail.png b/test/device-offset-fractional.pdf.xfail.png
index 77a4963..6248b4a 100644
Binary files a/test/device-offset-fractional.pdf.xfail.png and b/test/device-offset-fractional.pdf.xfail.png differ
diff --git a/test/fill-and-stroke-alpha-add.ref.png b/test/fill-and-stroke-alpha-add.ref.png
index 412dd4f..c69b6bd 100644
Binary files a/test/fill-and-stroke-alpha-add.ref.png and b/test/fill-and-stroke-alpha-add.ref.png differ
diff --git a/test/fill-and-stroke-alpha-add.svg12.xfail.png b/test/fill-and-stroke-alpha-add.svg12.xfail.png
index 32962f7..c1d7d6f 100644
Binary files a/test/fill-and-stroke-alpha-add.svg12.xfail.png and b/test/fill-and-stroke-alpha-add.svg12.xfail.png differ
diff --git a/test/fill-and-stroke-alpha.ref.png b/test/fill-and-stroke-alpha.ref.png
index fd4b81b..ff2e156 100644
Binary files a/test/fill-and-stroke-alpha.ref.png and b/test/fill-and-stroke-alpha.ref.png differ
diff --git a/test/fill-and-stroke.argb32.ref.png b/test/fill-and-stroke.argb32.ref.png
new file mode 100644
index 0000000..01d92c8
Binary files /dev/null and b/test/fill-and-stroke.argb32.ref.png differ
diff --git a/test/fill-and-stroke.ref.png b/test/fill-and-stroke.ref.png
deleted file mode 100644
index 298f694..0000000
Binary files a/test/fill-and-stroke.ref.png and /dev/null differ
diff --git a/test/fill-and-stroke.rgb24.ref.png b/test/fill-and-stroke.rgb24.ref.png
index ff886eb..7228813 100644
Binary files a/test/fill-and-stroke.rgb24.ref.png and b/test/fill-and-stroke.rgb24.ref.png differ
diff --git a/test/fill-and-stroke.xlib.argb32.ref.png b/test/fill-and-stroke.xlib.argb32.ref.png
new file mode 100644
index 0000000..5f77c92
Binary files /dev/null and b/test/fill-and-stroke.xlib.argb32.ref.png differ
diff --git a/test/fill-and-stroke.xlib.rgb24.ref.png b/test/fill-and-stroke.xlib.rgb24.ref.png
new file mode 100644
index 0000000..f4035eb
Binary files /dev/null and b/test/fill-and-stroke.xlib.rgb24.ref.png differ
diff --git a/test/filter-nearest-offset.pdf.xfail.png b/test/filter-nearest-offset.pdf.xfail.png
index ffe2df1..4d436aa 100644
Binary files a/test/filter-nearest-offset.pdf.xfail.png and b/test/filter-nearest-offset.pdf.xfail.png differ
diff --git a/test/filter-nearest-transformed.pdf.xfail.png b/test/filter-nearest-transformed.pdf.xfail.png
index 7eb5988..5ad98a7 100644
Binary files a/test/filter-nearest-transformed.pdf.xfail.png and b/test/filter-nearest-transformed.pdf.xfail.png differ
diff --git a/test/ft-text-vertical-layout-type1.ps.ref.png b/test/ft-text-vertical-layout-type1.ps.ref.png
new file mode 100644
index 0000000..05cdf10
Binary files /dev/null and b/test/ft-text-vertical-layout-type1.ps.ref.png differ
diff --git a/test/ft-text-vertical-layout-type1.ps2.ref.png b/test/ft-text-vertical-layout-type1.ps2.ref.png
deleted file mode 100644
index 4523495..0000000
Binary files a/test/ft-text-vertical-layout-type1.ps2.ref.png and /dev/null differ
diff --git a/test/ft-text-vertical-layout-type1.ps3.ref.png b/test/ft-text-vertical-layout-type1.ps3.ref.png
deleted file mode 100644
index 4523495..0000000
Binary files a/test/ft-text-vertical-layout-type1.ps3.ref.png and /dev/null differ
diff --git a/test/ft-text-vertical-layout-type1.ref.png b/test/ft-text-vertical-layout-type1.ref.png
index 1accc0b..6f0df7b 100644
Binary files a/test/ft-text-vertical-layout-type1.ref.png and b/test/ft-text-vertical-layout-type1.ref.png differ
diff --git a/test/ft-text-vertical-layout-type1.svg.ref.png b/test/ft-text-vertical-layout-type1.svg.ref.png
index 0be400c..326a240 100644
Binary files a/test/ft-text-vertical-layout-type1.svg.ref.png and b/test/ft-text-vertical-layout-type1.svg.ref.png differ
diff --git a/test/ft-text-vertical-layout-type1.xlib.ref.png b/test/ft-text-vertical-layout-type1.xlib.ref.png
index 2b74aa6..44a1ec7 100644
Binary files a/test/ft-text-vertical-layout-type1.xlib.ref.png and b/test/ft-text-vertical-layout-type1.xlib.ref.png differ
diff --git a/test/ft-text-vertical-layout-type3.ps.ref.png b/test/ft-text-vertical-layout-type3.ps.ref.png
new file mode 100644
index 0000000..bcc208d
Binary files /dev/null and b/test/ft-text-vertical-layout-type3.ps.ref.png differ
diff --git a/test/ft-text-vertical-layout-type3.ps2.ref.png b/test/ft-text-vertical-layout-type3.ps2.ref.png
deleted file mode 100644
index e6ad8fc..0000000
Binary files a/test/ft-text-vertical-layout-type3.ps2.ref.png and /dev/null differ
diff --git a/test/ft-text-vertical-layout-type3.ps3.ref.png b/test/ft-text-vertical-layout-type3.ps3.ref.png
deleted file mode 100644
index e6ad8fc..0000000
Binary files a/test/ft-text-vertical-layout-type3.ps3.ref.png and /dev/null differ
diff --git a/test/ft-text-vertical-layout-type3.ref.png b/test/ft-text-vertical-layout-type3.ref.png
index 1bda421..94048f1 100644
Binary files a/test/ft-text-vertical-layout-type3.ref.png and b/test/ft-text-vertical-layout-type3.ref.png differ
diff --git a/test/ft-text-vertical-layout-type3.svg.ref.png b/test/ft-text-vertical-layout-type3.svg.ref.png
index cddb955..985a8de 100644
Binary files a/test/ft-text-vertical-layout-type3.svg.ref.png and b/test/ft-text-vertical-layout-type3.svg.ref.png differ
diff --git a/test/ft-text-vertical-layout-type3.xlib.ref.png b/test/ft-text-vertical-layout-type3.xlib.ref.png
index 8ec2ebe..7a7f68f 100644
Binary files a/test/ft-text-vertical-layout-type3.xlib.ref.png and b/test/ft-text-vertical-layout-type3.xlib.ref.png differ
diff --git a/test/group-unaligned.svg.argb32.xfail.png b/test/group-unaligned.svg.argb32.xfail.png
index 01c34be..3855037 100644
Binary files a/test/group-unaligned.svg.argb32.xfail.png and b/test/group-unaligned.svg.argb32.xfail.png differ
diff --git a/test/joins.ref.png b/test/joins.ref.png
index eee7627..f8e33f8 100644
Binary files a/test/joins.ref.png and b/test/joins.ref.png differ
diff --git a/test/leaky-dashed-rectangle.pdf.ref.png b/test/leaky-dashed-rectangle.pdf.ref.png
index 690cb36..c0ba7b2 100644
Binary files a/test/leaky-dashed-rectangle.pdf.ref.png and b/test/leaky-dashed-rectangle.pdf.ref.png differ
diff --git a/test/leaky-dashed-rectangle.xlib.ref.png b/test/leaky-dashed-rectangle.xlib.ref.png
new file mode 100644
index 0000000..690cb36
Binary files /dev/null and b/test/leaky-dashed-rectangle.xlib.ref.png differ
diff --git a/test/leaky-dashed-stroke.ref.png b/test/leaky-dashed-stroke.ref.png
index e79d4d1..ae64dae 100644
Binary files a/test/leaky-dashed-stroke.ref.png and b/test/leaky-dashed-stroke.ref.png differ
diff --git a/test/leaky-dashed-stroke.xlib.ref.png b/test/leaky-dashed-stroke.xlib.ref.png
new file mode 100644
index 0000000..4ebf1a7
Binary files /dev/null and b/test/leaky-dashed-stroke.xlib.ref.png differ
diff --git a/test/line-width-scale.ref.png b/test/line-width-scale.ref.png
index c40bce3..e012b6e 100644
Binary files a/test/line-width-scale.ref.png and b/test/line-width-scale.ref.png differ
diff --git a/test/long-dashed-lines.ref.png b/test/long-dashed-lines.ref.png
index caf8b5e..09829b7 100644
Binary files a/test/long-dashed-lines.ref.png and b/test/long-dashed-lines.ref.png differ
diff --git a/test/mask-glyphs.svg.ref.png b/test/mask-glyphs.svg.ref.png
index 0058afc..5d524dd 100644
Binary files a/test/mask-glyphs.svg.ref.png and b/test/mask-glyphs.svg.ref.png differ
diff --git a/test/meta-surface-pattern.pdf.argb32.ref.png b/test/meta-surface-pattern.pdf.argb32.ref.png
index 0433222..015f744 100644
Binary files a/test/meta-surface-pattern.pdf.argb32.ref.png and b/test/meta-surface-pattern.pdf.argb32.ref.png differ
diff --git a/test/meta-surface-pattern.pdf.rgb24.ref.png b/test/meta-surface-pattern.pdf.rgb24.ref.png
index b59a9ca..1762e8a 100644
Binary files a/test/meta-surface-pattern.pdf.rgb24.ref.png and b/test/meta-surface-pattern.pdf.rgb24.ref.png differ
diff --git a/test/meta-surface-pattern.svg.argb32.ref.png b/test/meta-surface-pattern.svg.argb32.ref.png
index 86f6b61..ff4154d 100644
Binary files a/test/meta-surface-pattern.svg.argb32.ref.png and b/test/meta-surface-pattern.svg.argb32.ref.png differ
diff --git a/test/meta-surface-pattern.svg.rgb24.ref.png b/test/meta-surface-pattern.svg.rgb24.ref.png
index bbd8936..d2d5372 100644
Binary files a/test/meta-surface-pattern.svg.rgb24.ref.png and b/test/meta-surface-pattern.svg.rgb24.ref.png differ
diff --git a/test/new-sub-path.argb32.ref.png b/test/new-sub-path.argb32.ref.png
new file mode 100644
index 0000000..4ecaa0f
Binary files /dev/null and b/test/new-sub-path.argb32.ref.png differ
diff --git a/test/new-sub-path.pdf.argb32.ref.png b/test/new-sub-path.pdf.argb32.ref.png
new file mode 100644
index 0000000..41fe131
Binary files /dev/null and b/test/new-sub-path.pdf.argb32.ref.png differ
diff --git a/test/new-sub-path.ref.png b/test/new-sub-path.ref.png
deleted file mode 100644
index 7319ab3..0000000
Binary files a/test/new-sub-path.ref.png and /dev/null differ
diff --git a/test/new-sub-path.rgb24.ref.png b/test/new-sub-path.rgb24.ref.png
index 8cbc731..938344a 100644
Binary files a/test/new-sub-path.rgb24.ref.png and b/test/new-sub-path.rgb24.ref.png differ
diff --git a/test/operator-source.svg12.argb32.xfail.png b/test/operator-source.svg12.argb32.xfail.png
index ccf4315..722e5ac 100644
Binary files a/test/operator-source.svg12.argb32.xfail.png and b/test/operator-source.svg12.argb32.xfail.png differ
diff --git a/test/operator-source.svg12.rgb24.xfail.png b/test/operator-source.svg12.rgb24.xfail.png
index 827521b..5f445fc 100644
Binary files a/test/operator-source.svg12.rgb24.xfail.png and b/test/operator-source.svg12.rgb24.xfail.png differ
diff --git a/test/over-around-source.pdf.argb32.ref.png b/test/over-around-source.pdf.argb32.ref.png
index 02af76a..da700af 100644
Binary files a/test/over-around-source.pdf.argb32.ref.png and b/test/over-around-source.pdf.argb32.ref.png differ
diff --git a/test/random-intersections-eo.c b/test/random-intersections-eo.c
new file mode 100644
index 0000000..d35894f
--- /dev/null
+++ b/test/random-intersections-eo.c
@@ -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 at 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-eo.ps.ref.png b/test/random-intersections-eo.ps.ref.png
new file mode 100644
index 0000000..4bb11d6
Binary files /dev/null and b/test/random-intersections-eo.ps.ref.png differ
diff --git a/test/random-intersections-eo.quartz.ref.png b/test/random-intersections-eo.quartz.ref.png
new file mode 100644
index 0000000..ef76cba
Binary files /dev/null and b/test/random-intersections-eo.quartz.ref.png differ
diff --git a/test/random-intersections-eo.ref.png b/test/random-intersections-eo.ref.png
new file mode 100644
index 0000000..ea23a87
Binary files /dev/null and b/test/random-intersections-eo.ref.png differ
diff --git a/test/random-intersections-eo.xlib.ref.png b/test/random-intersections-eo.xlib.ref.png
new file mode 100644
index 0000000..942a5fc
Binary files /dev/null and b/test/random-intersections-eo.xlib.ref.png differ
diff --git a/test/random-intersections-nonzero.c b/test/random-intersections-nonzero.c
new file mode 100644
index 0000000..cad047e
--- /dev/null
+++ b/test/random-intersections-nonzero.c
@@ -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 at 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/random-intersections-nonzero.ps.ref.png b/test/random-intersections-nonzero.ps.ref.png
new file mode 100644
index 0000000..0431091
Binary files /dev/null and b/test/random-intersections-nonzero.ps.ref.png differ
diff --git a/test/random-intersections-nonzero.ref.png b/test/random-intersections-nonzero.ref.png
new file mode 100644
index 0000000..3ea650c
Binary files /dev/null and b/test/random-intersections-nonzero.ref.png differ
diff --git a/test/random-intersections-nonzero.xlib.ref.png b/test/random-intersections-nonzero.xlib.ref.png
new file mode 100644
index 0000000..69e06f8
Binary files /dev/null and b/test/random-intersections-nonzero.xlib.ref.png differ
diff --git a/test/random-intersections.c b/test/random-intersections.c
deleted file mode 100644
index 75e6b62..0000000
--- a/test/random-intersections.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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 at 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,
-	    "Tests the tessellator trapezoid generation and intersection computation",
-	    "trap", /* keywords */
-	    NULL, /* requirements */
-	    SIZE+3, SIZE+3,
-	    NULL, draw)
-
diff --git a/test/random-intersections.ps2.ref.png b/test/random-intersections.ps2.ref.png
deleted file mode 100644
index 590c832..0000000
Binary files a/test/random-intersections.ps2.ref.png and /dev/null differ
diff --git a/test/random-intersections.ps3.ref.png b/test/random-intersections.ps3.ref.png
deleted file mode 100644
index 590c832..0000000
Binary files a/test/random-intersections.ps3.ref.png and /dev/null differ
diff --git a/test/random-intersections.quartz.ref.png b/test/random-intersections.quartz.ref.png
deleted file mode 100644
index ef76cba..0000000
Binary files a/test/random-intersections.quartz.ref.png and /dev/null differ
diff --git a/test/random-intersections.ref.png b/test/random-intersections.ref.png
deleted file mode 100644
index ace75a2..0000000
Binary files a/test/random-intersections.ref.png and /dev/null differ
diff --git a/test/random-intersections.xlib.ref.png b/test/random-intersections.xlib.ref.png
deleted file mode 100644
index 3188ede..0000000
Binary files a/test/random-intersections.xlib.ref.png and /dev/null differ
diff --git a/test/reflected-stroke.ref.png b/test/reflected-stroke.ref.png
index 20f89c8..2dd865b 100644
Binary files a/test/reflected-stroke.ref.png and b/test/reflected-stroke.ref.png differ
diff --git a/test/rel-path.ref.png b/test/rel-path.ref.png
index 7b7007f..18e67ca 100644
Binary files a/test/rel-path.ref.png and b/test/rel-path.ref.png differ
diff --git a/test/rel-path.rgb24.ref.png b/test/rel-path.rgb24.ref.png
index 0a8a873..c317d11 100644
Binary files a/test/rel-path.rgb24.ref.png and b/test/rel-path.rgb24.ref.png differ
diff --git a/test/scale-offset-image.meta.xfail.png b/test/scale-offset-image.meta.xfail.png
deleted file mode 100644
index 3e0191a..0000000
Binary files a/test/scale-offset-image.meta.xfail.png and /dev/null differ
diff --git a/test/scale-offset-image.pdf.xfail.png b/test/scale-offset-image.pdf.xfail.png
index 3eacbbc..76e6fb7 100644
Binary files a/test/scale-offset-image.pdf.xfail.png and b/test/scale-offset-image.pdf.xfail.png differ
diff --git a/test/scale-offset-image.xfail.png b/test/scale-offset-image.xfail.png
index 3e0191a..fef3a39 100644
Binary files a/test/scale-offset-image.xfail.png and b/test/scale-offset-image.xfail.png differ
diff --git a/test/scale-offset-image.xlib-fallback.xfail.png b/test/scale-offset-image.xlib-fallback.xfail.png
index 1a286cd..0dd7100 100644
Binary files a/test/scale-offset-image.xlib-fallback.xfail.png and b/test/scale-offset-image.xlib-fallback.xfail.png differ
diff --git a/test/scale-offset-image.xlib.xfail.png b/test/scale-offset-image.xlib.xfail.png
index efb0091..89611db 100644
Binary files a/test/scale-offset-image.xlib.xfail.png and b/test/scale-offset-image.xlib.xfail.png differ
diff --git a/test/scale-offset-similar.meta.xfail.png b/test/scale-offset-similar.meta.xfail.png
index 83d53e6..0f2553e 100644
Binary files a/test/scale-offset-similar.meta.xfail.png and b/test/scale-offset-similar.meta.xfail.png differ
diff --git a/test/scale-offset-similar.pdf.xfail.png b/test/scale-offset-similar.pdf.xfail.png
index 7808aeb..38b9a20 100644
Binary files a/test/scale-offset-similar.pdf.xfail.png and b/test/scale-offset-similar.pdf.xfail.png differ
diff --git a/test/scale-offset-similar.xfail.png b/test/scale-offset-similar.xfail.png
index 3e0191a..fef3a39 100644
Binary files a/test/scale-offset-similar.xfail.png and b/test/scale-offset-similar.xfail.png differ
diff --git a/test/scale-offset-similar.xlib-fallback.xfail.png b/test/scale-offset-similar.xlib-fallback.xfail.png
index 1a286cd..0dd7100 100644
Binary files a/test/scale-offset-similar.xlib-fallback.xfail.png and b/test/scale-offset-similar.xlib-fallback.xfail.png differ
diff --git a/test/scale-offset-similar.xlib.xfail.png b/test/scale-offset-similar.xlib.xfail.png
index d58aeba..09e17da 100644
Binary files a/test/scale-offset-similar.xlib.xfail.png and b/test/scale-offset-similar.xlib.xfail.png differ
diff --git a/test/self-intersecting.argb32.xfail.png b/test/self-intersecting.argb32.xfail.png
deleted file mode 100644
index f644ed4..0000000
Binary files a/test/self-intersecting.argb32.xfail.png and /dev/null differ
diff --git a/test/self-intersecting.c b/test/self-intersecting.c
index 6719e93..8053235 100644
--- a/test/self-intersecting.c
+++ b/test/self-intersecting.c
@@ -47,6 +47,9 @@
 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 */
diff --git a/test/self-intersecting.pdf.argb32.xfail.png b/test/self-intersecting.pdf.argb32.xfail.png
deleted file mode 100644
index eb38a89..0000000
Binary files a/test/self-intersecting.pdf.argb32.xfail.png and /dev/null differ
diff --git a/test/self-intersecting.pdf.rgb24.xfail.png b/test/self-intersecting.pdf.rgb24.xfail.png
deleted file mode 100644
index a79a297..0000000
Binary files a/test/self-intersecting.pdf.rgb24.xfail.png and /dev/null differ
diff --git a/test/self-intersecting.ps.argb32.xfail.png b/test/self-intersecting.ps.argb32.xfail.png
deleted file mode 100644
index 84fde01..0000000
Binary files a/test/self-intersecting.ps.argb32.xfail.png and /dev/null differ
diff --git a/test/self-intersecting.ps.ref.png b/test/self-intersecting.ps.ref.png
new file mode 100644
index 0000000..84fde01
Binary files /dev/null and b/test/self-intersecting.ps.ref.png differ
diff --git a/test/self-intersecting.ps.rgb24.xfail.png b/test/self-intersecting.ps.rgb24.xfail.png
deleted file mode 100644
index c813607..0000000
Binary files a/test/self-intersecting.ps.rgb24.xfail.png and /dev/null differ
diff --git a/test/self-intersecting.ref.png b/test/self-intersecting.ref.png
index 384b0ab..b2f4259 100644
Binary files a/test/self-intersecting.ref.png and b/test/self-intersecting.ref.png differ
diff --git a/test/self-intersecting.rgb24.ref.png b/test/self-intersecting.rgb24.ref.png
deleted file mode 100644
index 5121792..0000000
Binary files a/test/self-intersecting.rgb24.ref.png and /dev/null differ
diff --git a/test/self-intersecting.rgb24.xfail.png b/test/self-intersecting.rgb24.xfail.png
deleted file mode 100644
index 958215c..0000000
Binary files a/test/self-intersecting.rgb24.xfail.png and /dev/null differ
diff --git a/test/self-intersecting.xlib.argb32.xfail.png b/test/self-intersecting.xlib.argb32.xfail.png
deleted file mode 100644
index f644ed4..0000000
Binary files a/test/self-intersecting.xlib.argb32.xfail.png and /dev/null differ
diff --git a/test/self-intersecting.xlib.ref.png b/test/self-intersecting.xlib.ref.png
new file mode 100644
index 0000000..f8fe0c0
Binary files /dev/null and b/test/self-intersecting.xlib.ref.png differ
diff --git a/test/self-intersecting.xlib.rgb24.xfail.png b/test/self-intersecting.xlib.rgb24.xfail.png
deleted file mode 100644
index 958215c..0000000
Binary files a/test/self-intersecting.xlib.rgb24.xfail.png and /dev/null differ
diff --git a/test/smask-stroke.ref.png b/test/smask-stroke.ref.png
index 0d3f7fe..c94a09f 100644
Binary files a/test/smask-stroke.ref.png and b/test/smask-stroke.ref.png differ
diff --git a/test/smask-stroke.xlib.ref.png b/test/smask-stroke.xlib.ref.png
new file mode 100644
index 0000000..71f427b
Binary files /dev/null and b/test/smask-stroke.xlib.ref.png differ
diff --git a/test/smask.ps.ref.png b/test/smask.ps.ref.png
new file mode 100644
index 0000000..31ccc17
Binary files /dev/null and b/test/smask.ps.ref.png differ
diff --git a/test/smask.ps2.ref.png b/test/smask.ps2.ref.png
deleted file mode 100644
index c006bbd..0000000
Binary files a/test/smask.ps2.ref.png and /dev/null differ
diff --git a/test/smask.ps3.ref.png b/test/smask.ps3.ref.png
deleted file mode 100644
index c006bbd..0000000
Binary files a/test/smask.ps3.ref.png and /dev/null differ
diff --git a/test/smask.ref.png b/test/smask.ref.png
index e190454..b5919de 100644
Binary files a/test/smask.ref.png and b/test/smask.ref.png differ
diff --git a/test/smask.svg.ref.png b/test/smask.svg.ref.png
index b4ad527..b9c0308 100644
Binary files a/test/smask.svg.ref.png and b/test/smask.svg.ref.png differ
diff --git a/test/smask.xlib.ref.png b/test/smask.xlib.ref.png
index bb70abf..5ce0082 100644
Binary files a/test/smask.xlib.ref.png and b/test/smask.xlib.ref.png differ
diff --git a/test/spline-decomposition.pdf.ref.png b/test/spline-decomposition.pdf.ref.png
index 9ea094a..5afa094 100644
Binary files a/test/spline-decomposition.pdf.ref.png and b/test/spline-decomposition.pdf.ref.png differ
diff --git a/test/spline-decomposition.ps.ref.png b/test/spline-decomposition.ps.ref.png
index 2b3c07d..e632404 100644
Binary files a/test/spline-decomposition.ps.ref.png and b/test/spline-decomposition.ps.ref.png differ
diff --git a/test/spline-decomposition.ref.png b/test/spline-decomposition.ref.png
index bac35a9..426aefa 100644
Binary files a/test/spline-decomposition.ref.png and b/test/spline-decomposition.ref.png differ
diff --git a/test/spline-decomposition.svg.ref.png b/test/spline-decomposition.svg.ref.png
index 9ea094a..5afa094 100644
Binary files a/test/spline-decomposition.svg.ref.png and b/test/spline-decomposition.svg.ref.png differ
diff --git a/test/spline-decomposition.xlib.ref.png b/test/spline-decomposition.xlib.ref.png
new file mode 100644
index 0000000..30d05b3
Binary files /dev/null and b/test/spline-decomposition.xlib.ref.png differ
diff --git a/test/stroke-ctm-caps.ref.png b/test/stroke-ctm-caps.ref.png
index f364357..799ff39 100644
Binary files a/test/stroke-ctm-caps.ref.png and b/test/stroke-ctm-caps.ref.png differ
diff --git a/test/stroke-image.pdf.ref.png b/test/stroke-image.pdf.ref.png
index f3eb75d..80df100 100644
Binary files a/test/stroke-image.pdf.ref.png and b/test/stroke-image.pdf.ref.png differ
diff --git a/test/stroke-image.ps.ref.png b/test/stroke-image.ps.ref.png
new file mode 100644
index 0000000..208e008
Binary files /dev/null and b/test/stroke-image.ps.ref.png differ
diff --git a/test/stroke-image.ps2.ref.png b/test/stroke-image.ps2.ref.png
deleted file mode 100644
index a187167..0000000
Binary files a/test/stroke-image.ps2.ref.png and /dev/null differ
diff --git a/test/stroke-image.ps3.ref.png b/test/stroke-image.ps3.ref.png
deleted file mode 100644
index a187167..0000000
Binary files a/test/stroke-image.ps3.ref.png and /dev/null differ
diff --git a/test/stroke-image.ref.png b/test/stroke-image.ref.png
index c8b9417..a3c57aa 100644
Binary files a/test/stroke-image.ref.png and b/test/stroke-image.ref.png differ
diff --git a/test/stroke-image.xlib.ref.png b/test/stroke-image.xlib.ref.png
new file mode 100644
index 0000000..dbb5c5a
Binary files /dev/null and b/test/stroke-image.xlib.ref.png differ
diff --git a/test/surface-pattern.pdf.xfail.png b/test/surface-pattern.pdf.xfail.png
index a43dc4d..fadc2c2 100644
Binary files a/test/surface-pattern.pdf.xfail.png and b/test/surface-pattern.pdf.xfail.png differ
diff --git a/test/text-rotate.pdf.ref.png b/test/text-rotate.pdf.ref.png
index bdd64e6..ea82bfe 100644
Binary files a/test/text-rotate.pdf.ref.png and b/test/text-rotate.pdf.ref.png differ
diff --git a/test/text-rotate.ref.png b/test/text-rotate.ref.png
index b227398..291c6c7 100644
Binary files a/test/text-rotate.ref.png and b/test/text-rotate.ref.png differ
diff --git a/test/text-rotate.svg.ref.png b/test/text-rotate.svg.ref.png
index 7ffd356..9d887a0 100644
Binary files a/test/text-rotate.svg.ref.png and b/test/text-rotate.svg.ref.png differ
diff --git a/test/text-rotate.xlib.ref.png b/test/text-rotate.xlib.ref.png
new file mode 100644
index 0000000..3d35964
Binary files /dev/null and b/test/text-rotate.xlib.ref.png differ
diff --git a/test/twin.ps.ref.png b/test/twin.ps.ref.png
index e75062c..16b49ba 100644
Binary files a/test/twin.ps.ref.png and b/test/twin.ps.ref.png differ
diff --git a/test/twin.ref.png b/test/twin.ref.png
index 3c46b02..f5d524f 100644
Binary files a/test/twin.ref.png and b/test/twin.ref.png differ
diff --git a/test/twin.xlib.ref.png b/test/twin.xlib.ref.png
new file mode 100644
index 0000000..0442186
Binary files /dev/null and b/test/twin.xlib.ref.png differ
diff --git a/test/unantialiased-shapes.ref.png b/test/unantialiased-shapes.ref.png
index 128a2a1..c909d38 100644
Binary files a/test/unantialiased-shapes.ref.png and b/test/unantialiased-shapes.ref.png differ
diff --git a/test/unbounded-operator.svg12.argb32.ref.png b/test/unbounded-operator.svg12.argb32.ref.png
new file mode 100644
index 0000000..45b173f
Binary files /dev/null and b/test/unbounded-operator.svg12.argb32.ref.png differ
diff --git a/test/unbounded-operator.svg12.argb32.xfail.png b/test/unbounded-operator.svg12.argb32.xfail.png
deleted file mode 100644
index 15965c8..0000000
Binary files a/test/unbounded-operator.svg12.argb32.xfail.png and /dev/null differ
diff --git a/test/unbounded-operator.svg12.rgb24.xfail.png b/test/unbounded-operator.svg12.rgb24.xfail.png
index 828a9db..c369fd2 100644
Binary files a/test/unbounded-operator.svg12.rgb24.xfail.png and b/test/unbounded-operator.svg12.rgb24.xfail.png differ
diff --git a/test/user-font-proxy.svg.ref.png b/test/user-font-proxy.svg.ref.png
index 6c45848..747750a 100644
Binary files a/test/user-font-proxy.svg.ref.png and b/test/user-font-proxy.svg.ref.png differ
diff --git a/test/user-font.ps.ref.png b/test/user-font.ps.ref.png
new file mode 100644
index 0000000..63f2896
Binary files /dev/null and b/test/user-font.ps.ref.png differ
diff --git a/test/user-font.ps2.ref.png b/test/user-font.ps2.ref.png
deleted file mode 100644
index e1dd00a..0000000
Binary files a/test/user-font.ps2.ref.png and /dev/null differ
diff --git a/test/user-font.ps3.ref.png b/test/user-font.ps3.ref.png
deleted file mode 100644
index e1dd00a..0000000
Binary files a/test/user-font.ps3.ref.png and /dev/null differ
diff --git a/test/user-font.ref.png b/test/user-font.ref.png
index 753fc7b..ab18e1c 100644
Binary files a/test/user-font.ref.png and b/test/user-font.ref.png differ
diff --git a/test/user-font.xlib.ref.png b/test/user-font.xlib.ref.png
index abc3117..d81ecf1 100644
Binary files a/test/user-font.xlib.ref.png and b/test/user-font.xlib.ref.png differ
diff --git a/util/.gitignore b/util/.gitignore
index aa0e19b..01eb637 100644
--- a/util/.gitignore
+++ b/util/.gitignore
@@ -2,6 +2,9 @@
 .libs
 Makefile
 Makefile.in
+show-edges
+show-events
+show-traps
 *.so
 *.la
 *.lo
diff --git a/util/Makefile.am b/util/Makefile.am
index d90d852..79bc336 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -6,6 +6,25 @@ if BUILD_TRACE
 SUBDIRS += cairo-trace
 endif
 
+AM_CPPFLAGS = -I$(top_srcdir)/src
+
+EXTRA_PROGRAMS += show-traps show-edges show-events
+
+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_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_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:
@@ -20,12 +39,10 @@ AM_LDFLAGS = -module -avoid-version -export-dynamic -rpath /dev/null
 
 EXTRA_LTLIBRARIES += malloc-stats.la backtrace-symbols.la
 
-backtrace_symbols_la_LIBADD  = -lbfd -liberty
+backtrace_symbols_la_LIBADD = -lbfd -liberty
 
 #malloc_stats_la_LIBADD  = $(backtrace_symbols_la_LIBADD) backtrace-symbols.lo
 
-noinst_PROGRAMS =
-
 if HAVE_GTK
 EXTRA_PROGRAMS += font-view
 font_view_CFLAGS = $(gtk_CFLAGS)
diff --git a/util/show-edges.c b/util/show-edges.c
new file mode 100644
index 0000000..d3b94bc
--- /dev/null
+++ b/util/show-edges.c
@@ -0,0 +1,1189 @@
+#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
+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;
+    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, 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/show-events.c b/util/show-events.c
new file mode 100644
index 0000000..8bff3ef
--- /dev/null
+++ b/util/show-events.c
@@ -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-traps.c b/util/show-traps.c
new file mode 100644
index 0000000..9a99aba
--- /dev/null
+++ b/util/show-traps.c
@@ -0,0 +1,1239 @@
+#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;
+    if (traps == NULL)
+	return;
+
+    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;
+    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;
+
+    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);
+
+	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;
+    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, *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;
+}
commit 7c499db8afe8a7cf8c512ec166fe7dbf11a25c02
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 25 20:37:55 2009 +0100

    [test] Minor tweak of ft-text-vertical-layout
    
    Tidily destroy the font immediately after use.

diff --git a/test/ft-text-vertical-layout-type1.c b/test/ft-text-vertical-layout-type1.c
index 28e81e0..c6f26a6 100644
--- a/test/ft-text-vertical-layout-type1.c
+++ b/test/ft-text-vertical-layout-type1.c
@@ -125,6 +125,7 @@ draw (cairo_t *cr, int width, int height)
     }
 
     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 */
@@ -154,16 +155,13 @@ draw (cairo_t *cr, int width, int height)
 		     extents.height + line_width);
     cairo_stroke (cr);
 
-    cairo_scaled_font_destroy (scaled_font);
-
     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, text", /* keywords */
+	    "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
index 36a0755..9826806 100644
--- a/test/ft-text-vertical-layout-type3.c
+++ b/test/ft-text-vertical-layout-type3.c
@@ -125,6 +125,7 @@ draw (cairo_t *cr, int width, int height)
     }
 
     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 */
@@ -154,14 +155,12 @@ draw (cairo_t *cr, int width, int height)
 		     extents.height + line_width);
     cairo_stroke (cr);
 
-    cairo_scaled_font_destroy (scaled_font);
-
     return CAIRO_TEST_SUCCESS;
 }
 
 CAIRO_TEST (ft_text_vertical_layout_type3,
 	    "Tests text rendering for vertical layout with TrueType fonts",
-	    "ft, text", /* keywords */
+	    "ft, fc, text", /* keywords */
 	    NULL, /* requirements */
 	    WIDTH, HEIGHT,
 	    NULL, draw)
commit bdd3c5ba6987280b455229dd12b20c22159ce61c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 22 18:59:01 2009 +0100

    [perf] Match directory names
    
    In order to handle 'cairo-perf-trace benchmark', we need to perform the
    can_run? test on the directory name as opposed to the individual trace
    names. Make it so.

diff --git a/perf/box-outline.c b/perf/box-outline.c
index 6b97b08..e216b79 100644
--- a/perf/box-outline.c
+++ b/perf/box-outline.c
@@ -94,7 +94,7 @@ box_outline_fill (cairo_t *cr, int width, int height, int loops)
 void
 box_outline (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "box-outline"))
+    if (! cairo_perf_can_run (perf, "box-outline", NULL))
 	return;
 
     cairo_perf_run (perf, "box-outline-stroke", box_outline_stroke);
diff --git a/perf/cairo-perf-trace.c b/perf/cairo-perf-trace.c
index f594a28..52e5429 100644
--- a/perf/cairo-perf-trace.c
+++ b/perf/cairo-perf-trace.c
@@ -124,14 +124,21 @@ target_is_measurable (const cairo_boilerplate_target_t *target)
 
 cairo_bool_t
 cairo_perf_can_run (cairo_perf_t	*perf,
-		    const char		*name)
+		    const char		*name,
+		    cairo_bool_t	*is_explicit)
 {
     unsigned int i;
     char *copy, *dot;
     cairo_bool_t ret;
 
-    if (perf->exact_names)
+    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;
@@ -144,8 +151,11 @@ cairo_perf_can_run (cairo_perf_t	*perf,
     if (perf->num_names) {
 	ret = TRUE;
 	for (i = 0; i < perf->num_names; i++)
-	    if (strstr (copy, perf->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;
@@ -155,8 +165,11 @@ 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 (strstr (copy, perf->exclude_names[i])) {
+		if (is_explicit)
+		    *is_explicit = strcmp (copy, perf->exclude_names[i]) == 0;
 		goto done;
+	    }
 
 	ret = TRUE;
 	goto done;
@@ -315,8 +328,8 @@ execute (cairo_perf_t		 *perf,
 		     name);
 	    fprintf (perf->summary,
 		     "%#8.3f %#8.3f %#6.2f%% %4d/%d",
-		     stats.min_ticks / (double) cairo_perf_ticks_per_second (),
-		     stats.median_ticks / (double) cairo_perf_ticks_per_second (),
+		     (double) stats.min_ticks / cairo_perf_ticks_per_second (),
+		     (double) stats.median_ticks / cairo_perf_ticks_per_second (),
 		     stats.std_dev * 100.0,
 		     stats.iterations, i+1);
 	    fflush (perf->summary);
@@ -335,8 +348,8 @@ execute (cairo_perf_t		 *perf,
 	}
 	fprintf (perf->summary,
 		 "%#8.3f %#8.3f %#6.2f%% %4d/%d\n",
-		 stats.min_ticks / (double) cairo_perf_ticks_per_second (),
-		 stats.median_ticks / (double) cairo_perf_ticks_per_second (),
+		 (double) stats.min_ticks / cairo_perf_ticks_per_second (),
+		 (double) stats.median_ticks / cairo_perf_ticks_per_second (),
 		 stats.std_dev * 100.0,
 		 stats.iterations, i);
 	fflush (perf->summary);
@@ -623,11 +636,17 @@ cairo_perf_trace_dir (cairo_perf_t *perf,
     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;
@@ -651,7 +670,7 @@ cairo_perf_trace_dir (cairo_perf_t *perf,
 		goto next;
 
 	    num_traces++;
-	    if (! cairo_perf_can_run (perf, de->d_name))
+	    if (!force && ! cairo_perf_can_run (perf, de->d_name, NULL))
 		goto next;
 
 	    cairo_perf_trace (perf, target, trace);
diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
index 21d48f4..0d60fc6 100644
--- a/perf/cairo-perf.c
+++ b/perf/cairo-perf.c
@@ -153,16 +153,24 @@ cairo_perf_has_similar (cairo_perf_t *perf)
 
 cairo_bool_t
 cairo_perf_can_run (cairo_perf_t	*perf,
-		    const char		*name)
+		    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]))
+    for (i = 0; i < perf->num_names; i++) {
+	if (strstr (name, perf->names[i])) {
+	    if (is_explicit)
+		*is_explicit = FALSE;
 	    return TRUE;
+	}
+    }
 
     return FALSE;
 }
diff --git a/perf/cairo-perf.h b/perf/cairo-perf.h
index 139de8e..6903dbf 100644
--- a/perf/cairo-perf.h
+++ b/perf/cairo-perf.h
@@ -98,7 +98,8 @@ typedef cairo_perf_ticks_t
 
 cairo_bool_t
 cairo_perf_can_run (cairo_perf_t	*perf,
-		    const char		*name);
+		    const char		*name,
+		    cairo_bool_t	*is_explicit);
 
 void
 cairo_perf_run (cairo_perf_t		*perf,
diff --git a/perf/composite-checker.c b/perf/composite-checker.c
index 301006f..0e61ec8 100644
--- a/perf/composite-checker.c
+++ b/perf/composite-checker.c
@@ -83,7 +83,7 @@ composite_checker (cairo_perf_t *perf,
 {
     cairo_surface_t *image;
 
-    if (! cairo_perf_can_run (perf, "composite-checker"))
+    if (! cairo_perf_can_run (perf, "composite-checker", NULL))
 	return;
 
     /* Create the checker pattern. We don't actually need to draw
diff --git a/perf/dragon.c b/perf/dragon.c
index a145f78..eb8251c 100644
--- a/perf/dragon.c
+++ b/perf/dragon.c
@@ -72,6 +72,7 @@ path (cairo_t *cr, int step, int dir, int iterations)
     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;
@@ -237,7 +238,7 @@ do_dragon_solid_circle_clip (cairo_t *cr, int width, int height, int loops)
 void
 dragon (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "dragon"))
+    if (! cairo_perf_can_run (perf, "dragon", NULL))
 	return;
 
     cairo_perf_run (perf, "dragon-solid", do_dragon_solid);
diff --git a/perf/fill.c b/perf/fill.c
index 453f9a4..c65a649 100644
--- a/perf/fill.c
+++ b/perf/fill.c
@@ -110,7 +110,7 @@ do_fill_eo_noaa (cairo_t *cr, int width, int height, int loops)
 void
 fill (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "fill"))
+    if (! cairo_perf_can_run (perf, "fill", NULL))
 	return;
 
     cairo_perf_cover_sources_and_operators (perf, "fill", do_fill);
diff --git a/perf/glyphs.c b/perf/glyphs.c
index bead68c..25175d5 100644
--- a/perf/glyphs.c
+++ b/perf/glyphs.c
@@ -92,7 +92,7 @@ out:
 void
 glyphs (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "glyphs"))
+    if (! cairo_perf_can_run (perf, "glyphs", NULL))
 	return;
 
     cairo_perf_cover_sources_and_operators (perf, "glyphs", do_glyphs);
diff --git a/perf/intersections.c b/perf/intersections.c
index 1a00b1d..0418ee3 100644
--- a/perf/intersections.c
+++ b/perf/intersections.c
@@ -146,7 +146,7 @@ random_curve_nz (cairo_t *cr, int width, int height, int loops)
 void
 intersections (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "intersections"))
+    if (! cairo_perf_can_run (perf, "intersections", NULL))
 	return;
 
     cairo_perf_run (perf, "intersections-nz-fill", random_nz);
diff --git a/perf/long-dashed-lines.c b/perf/long-dashed-lines.c
index 96e6486..c4de24f 100644
--- a/perf/long-dashed-lines.c
+++ b/perf/long-dashed-lines.c
@@ -64,7 +64,7 @@ do_long_dashed_lines (cairo_t *cr, int width, int height, int loops)
 void
 long_dashed_lines (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "long-dashed-lines"))
+    if (! cairo_perf_can_run (perf, "long-dashed-lines", NULL))
 	return;
 
     cairo_perf_run (perf, "long-dashed-lines", do_long_dashed_lines);
diff --git a/perf/long-lines.c b/perf/long-lines.c
index 124c4f2..2b72879 100644
--- a/perf/long-lines.c
+++ b/perf/long-lines.c
@@ -135,7 +135,7 @@ long_lines_cropped_once (cairo_t *cr, int width, int height, int loops)
 void
 long_lines (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "long-lines"))
+    if (! cairo_perf_can_run (perf, "long-lines", NULL))
 	return;
 
     cairo_perf_run (perf, "long-lines-uncropped", long_lines_uncropped);
diff --git a/perf/mask.c b/perf/mask.c
index 55dc20a..3050b44 100644
--- a/perf/mask.c
+++ b/perf/mask.c
@@ -275,7 +275,7 @@ do_mask_radial (cairo_t *cr, int width, int height, int loops)
 void
 mask (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "mask"))
+    if (! cairo_perf_can_run (perf, "mask", NULL))
 	return;
 
     cairo_perf_cover_sources_and_operators (perf, "mask-solid",
diff --git a/perf/mosaic.c b/perf/mosaic.c
index 715dffb..b762105 100644
--- a/perf/mosaic.c
+++ b/perf/mosaic.c
@@ -163,7 +163,7 @@ mosaic_tessellate_curves (cairo_t *cr, int width, int height, int loops)
 void
 mosaic (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "mosaic"))
+    if (! cairo_perf_can_run (perf, "mosaic", NULL))
 	return;
 
     cairo_perf_run (perf, "mosaic-fill-curves", mosaic_fill_curves);
diff --git a/perf/paint-with-alpha.c b/perf/paint-with-alpha.c
index cef353d..5c23fe4 100644
--- a/perf/paint-with-alpha.c
+++ b/perf/paint-with-alpha.c
@@ -41,7 +41,7 @@ do_paint_with_alpha (cairo_t *cr, int width, int height, int loops)
 void
 paint_with_alpha (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "paint-with-alpha"))
+    if (! cairo_perf_can_run (perf, "paint-with-alpha", NULL))
 	return;
 
     cairo_perf_cover_sources_and_operators (perf, "paint-with-alpha",
diff --git a/perf/paint.c b/perf/paint.c
index ac7c724..bdc014c 100644
--- a/perf/paint.c
+++ b/perf/paint.c
@@ -41,7 +41,7 @@ do_paint (cairo_t *cr, int width, int height, int loops)
 void
 paint (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "paint"))
+    if (! cairo_perf_can_run (perf, "paint", NULL))
 	return;
 
     cairo_perf_cover_sources_and_operators (perf, "paint", do_paint);
diff --git a/perf/pattern_create_radial.c b/perf/pattern_create_radial.c
index 26dc713..2959e74 100644
--- a/perf/pattern_create_radial.c
+++ b/perf/pattern_create_radial.c
@@ -84,7 +84,7 @@ pattern_create_radial (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
     int i;
 
-    if (! cairo_perf_can_run (perf, "pattern-create-radial"))
+    if (! cairo_perf_can_run (perf, "pattern-create-radial", NULL))
 	return;
 
     srand (time (0));
diff --git a/perf/pythagoras-tree.c b/perf/pythagoras-tree.c
index bf37f5f..f2200c9 100644
--- a/perf/pythagoras-tree.c
+++ b/perf/pythagoras-tree.c
@@ -84,8 +84,8 @@ do_pythagoras_tree (cairo_t *cr, int width, int height, int loops)
 void
 pythagoras_tree (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "pythagoras-tree"))
+    if (! cairo_perf_can_run (perf, "pythagoras-tree", NULL))
 	return;
 
-    cairo_perf_run (perf, "pythagoras_tree", do_pythagoras_tree);
+    cairo_perf_run (perf, "pythagoras-tree", do_pythagoras_tree);
 }
diff --git a/perf/rectangles.c b/perf/rectangles.c
index 53b908e..601a0c5 100644
--- a/perf/rectangles.c
+++ b/perf/rectangles.c
@@ -100,7 +100,7 @@ rectangles (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
     int i;
 
-    if (! cairo_perf_can_run (perf, "rectangles"))
+    if (! cairo_perf_can_run (perf, "rectangles", NULL))
 	return;
 
     srand (8478232);
diff --git a/perf/rounded-rectangles.c b/perf/rounded-rectangles.c
index 2cd89a8..477abf2 100644
--- a/perf/rounded-rectangles.c
+++ b/perf/rounded-rectangles.c
@@ -124,7 +124,7 @@ rounded_rectangles (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
     int i;
 
-    if (! cairo_perf_can_run (perf, "rounded-rectangles"))
+    if (! cairo_perf_can_run (perf, "rounded-rectangles", NULL))
 	return;
 
     srand (8478232);
diff --git a/perf/spiral.c b/perf/spiral.c
index 123ad57..046351c 100644
--- a/perf/spiral.c
+++ b/perf/spiral.c
@@ -329,7 +329,7 @@ draw_spiral_stroke_na (cairo_t *cr, int width, int height, int loops)
 void
 spiral (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "spiral"))
+    if (! cairo_perf_can_run (perf, "spiral", NULL))
 	return;
 
     cairo_perf_run (perf, "spiral-box-nonalign-evenodd-fill", draw_spiral_eo_na_box);
diff --git a/perf/stroke.c b/perf/stroke.c
index 81ba8f2..660dce5 100644
--- a/perf/stroke.c
+++ b/perf/stroke.c
@@ -89,7 +89,7 @@ do_strokes (cairo_t *cr, int width, int height, int loops)
 void
 stroke (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "stroke"))
+    if (! cairo_perf_can_run (perf, "stroke", NULL))
 	return;
 
     cairo_perf_cover_sources_and_operators (perf, "stroke", do_stroke);
diff --git a/perf/subimage_copy.c b/perf/subimage_copy.c
index 9bec2fb..0bfad80 100644
--- a/perf/subimage_copy.c
+++ b/perf/subimage_copy.c
@@ -58,7 +58,7 @@ subimage_copy (cairo_perf_t *perf, cairo_t *cr, int width, int height)
     cairo_surface_t *image;
     cairo_t *cr2;
 
-    if (! cairo_perf_can_run (perf, "subimage-copy"))
+    if (! cairo_perf_can_run (perf, "subimage-copy", NULL))
 	return;
 
     cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
diff --git a/perf/tessellate.c b/perf/tessellate.c
index 1a4d978..9debc53 100644
--- a/perf/tessellate.c
+++ b/perf/tessellate.c
@@ -144,7 +144,7 @@ tessellate_256 (cairo_t *cr, int width, int height, int loops)
 void
 tessellate (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "tessellate"))
+    if (! cairo_perf_can_run (perf, "tessellate", NULL))
 	return;
 
     cairo_perf_run (perf, "tessellate-16", tessellate_16);
diff --git a/perf/text.c b/perf/text.c
index 9c51274..827bb88 100644
--- a/perf/text.c
+++ b/perf/text.c
@@ -59,7 +59,7 @@ do_text (cairo_t *cr, int width, int height, int loops)
 void
 text (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "text"))
+    if (! cairo_perf_can_run (perf, "text", NULL))
 	return;
 
     cairo_perf_cover_sources_and_operators (perf, "text", do_text);
diff --git a/perf/twin.c b/perf/twin.c
index 4dd06dd..b2c37a2 100644
--- a/perf/twin.c
+++ b/perf/twin.c
@@ -49,7 +49,7 @@ twin (cairo_perf_t *perf,
       int           width,
       int           height)
 {
-    if (! cairo_perf_can_run (perf, "twin"))
+    if (! cairo_perf_can_run (perf, "twin", NULL))
 	return;
 
     cairo_perf_run (perf, "twin", do_twin);
diff --git a/perf/unaligned-clip.c b/perf/unaligned-clip.c
index 0bff258..284c832 100644
--- a/perf/unaligned-clip.c
+++ b/perf/unaligned-clip.c
@@ -63,7 +63,7 @@ do_unaligned_clip (cairo_t *cr, int width, int height, int loops)
 void
 unaligned_clip (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "unaligned-clip"))
+    if (! cairo_perf_can_run (perf, "unaligned-clip", NULL))
 	return;
 
     cairo_perf_run (perf, "unaligned-clip", do_unaligned_clip);
diff --git a/perf/world-map.c b/perf/world-map.c
index d9a267d..2a45500 100644
--- a/perf/world-map.c
+++ b/perf/world-map.c
@@ -109,7 +109,7 @@ do_world_map (cairo_t *cr, int width, int height, int loops)
 void
 world_map (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "world-map"))
+    if (! cairo_perf_can_run (perf, "world-map", NULL))
 	return;
 
     cairo_perf_run (perf, "world-map", do_world_map);
diff --git a/perf/zrusin.c b/perf/zrusin.c
index d57c7c4..24aff14 100644
--- a/perf/zrusin.c
+++ b/perf/zrusin.c
@@ -87,7 +87,7 @@ zrusin_another_fill (cairo_t *cr, int width, int height, int loops)
 void
 zrusin (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "zrusin"))
+    if (! cairo_perf_can_run (perf, "zrusin", NULL))
 	return;
 
     cairo_perf_run (perf, "zrusin-another-tessellate", zrusin_another_tessellate);
commit 77c11096169bc8af6aa08241a800a51a2292a27a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 22 12:23:03 2009 +0100

    [perf] Reset global caches after every trace
    
    I'd disabled this to look at cairo-qt performance, then forgot about it.
    Be clean, cleanup globals -- this should fix the huge performance loss
    when running in series multiple backends that need separate font caches.

diff --git a/perf/cairo-perf-trace.c b/perf/cairo-perf-trace.c
index 99caa0a..f594a28 100644
--- a/perf/cairo-perf-trace.c
+++ b/perf/cairo-perf-trace.c
@@ -596,6 +596,11 @@ cairo_perf_trace (cairo_perf_t *perf,
 
     if (target->cleanup)
 	target->cleanup (closure);
+
+    cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+    FcFini ();
+#endif
 }
 
 static void
commit 9c8e60f7718b29051268541d2cfec58e51d8e7b8
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 22 12:09:25 2009 +0100

    [perf] Remove cpuset warning for cairo-perf-trace
    
    These traces run for much longer than the original synthetic benchmarks
    and seek to replicate 'real-world' applications, so the warning that the
    xserver and cairo-perf are not bound to any cpu is false.

diff --git a/perf/cairo-perf-trace.c b/perf/cairo-perf-trace.c
index c6f7526..99caa0a 100644
--- a/perf/cairo-perf-trace.c
+++ b/perf/cairo-perf-trace.c
@@ -55,10 +55,6 @@
 #include <fontconfig/fontconfig.h>
 #endif
 
-#ifdef HAVE_SCHED_H
-#include <sched.h>
-#endif
-
 #define CAIRO_PERF_ITERATIONS_DEFAULT	15
 #define CAIRO_PERF_LOW_STD_DEV		0.05
 #define CAIRO_PERF_MIN_STD_DEV_COUNT	3
@@ -544,37 +540,6 @@ parse_options (cairo_perf_t *perf, int argc, char *argv[])
     }
 }
 
-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)
 {
@@ -705,18 +670,6 @@ main (int argc, char *argv[])
 
     parse_options (&perf, argc, argv);
 
-    if (! perf.list_only && 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\n",
-	       stderr);
-    }
-
     signal (SIGINT, interrupt);
 
     if (getenv ("CAIRO_TRACE_DIR") != NULL)
commit d07ed5d990634dc09d0d1b6ce45aa8ad1994b75a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 19 12:36:56 2009 +0100

    [perf] Add charting utility
    
    cairo-perf-chart takes multiple runs (currently it is limited to
    prefiltered data sets) and pretty-prints a chart showing performace
    improvements/regressions (in either ASCII or HTML) along with a
    cairo-perf-chart.png

diff --git a/perf/Makefile.am b/perf/Makefile.am
index a62ad95..4f43372 100644
--- a/perf/Makefile.am
+++ b/perf/Makefile.am
@@ -14,6 +14,7 @@ EXTRA_PROGRAMS += cairo-perf \
 		  cairo-perf-trace \
 		  cairo-perf-diff-files \
 		  cairo-perf-print \
+		  cairo-perf-chart \
 		  cairo-perf-compare-backends \
 		  cairo-perf-graph-files
 EXTRA_DIST += cairo-perf-diff COPYING
@@ -103,6 +104,9 @@ cairo_perf_diff_files_SOURCES =	\
 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
 
diff --git a/perf/cairo-perf-chart.c b/perf/cairo-perf-chart.c
new file mode 100644
index 0000000..401fa59
--- /dev/null
+++ b/perf/cairo-perf-chart.c
@@ -0,0 +1,751 @@
+/*
+ * 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 at cworth.org>
+ *          Chris Wilson <chris at 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 void
+find_ranges (struct chart *chart)
+{
+    test_report_t **tests, *min_test;
+    double min = 0, max = 0;
+    double test_time;
+    int seen_non_null;
+    int num_tests = 0;
+    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;
+
+    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;
+
+		    v = to_factor (test_time / report_time);
+		    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;
+		}
+	    }
+	}
+    }
+
+    free (tests);
+
+    chart->min_value = min;
+    chart->max_value = max;
+    chart->num_tests = num_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) {
+	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.);
+    } 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 - (extents.height / 2. + extents.y_bearing));
+    cairo_rotate (c->cr, -M_PI/2);
+    cairo_translate (c->cr, -extents.width, 0);
+
+    cairo_set_source_rgba (c->cr, 1, 1, 1, .5);
+    cairo_move_to (c->cr, 0, 0);
+    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_source_rgba (c->cr, .95, .95, .95, .5);
+	cairo_move_to (c->cr, 0, floor (y) + .5);
+	cairo_line_to (c->cr, c->width, floor (y) + .5);
+	cairo_stroke (c->cr);
+
+	cairo_set_font_size (c->cr, 8);
+
+	cairo_set_source_rgba (c->cr, 0, .75, 0, .95);
+	sprintf (buf, "%.0fs", i*v/1000);
+	cairo_text_extents (c->cr, buf, &extents);
+
+	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);
+    } while (1);
+
+    cairo_restore (c->cr);
+}
+
+static void
+add_relative_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, mid;
+    unsigned int i;
+    char buf[80];
+    cairo_text_extents_t extents;
+
+    v = MAX (-c->min_value, 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:
+
+    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);
+
+    i = 0;
+    do {
+	y = ++i * v * dy;
+	if (y > mid)
+	    break;
+
+	cairo_set_source_rgba (c->cr, .95, .95, .95, .5);
+	cairo_move_to (c->cr, 0, floor (mid + y) + .5);
+	cairo_line_to (c->cr, c->width, floor (mid + y) + .5);
+
+	cairo_move_to (c->cr, 0, ceil (mid - y) + .5);
+	cairo_line_to (c->cr, c->width, ceil (mid - y) + .5);
+	cairo_stroke (c->cr);
+
+	cairo_set_font_size (c->cr, 8);
+
+	cairo_set_source_rgba (c->cr, .75, 0, 0, .95);
+	sprintf (buf, "%.0fx", i*v/100 + 1);
+	cairo_text_extents (c->cr, buf, &extents);
+
+	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);
+    } 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, 3 * FONT_SIZE / 2);
+
+    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/1000);
+		}
+	    }
+	}
+
+	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 = 480;
+    chart.height = 300;
+
+    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);
+
+	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_perf_reports_compare (&chart, !chart.relative);
+
+	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;
+}
commit 015eccadcb8b22f302bdaa315b88981a38294686
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 16 09:57:07 2009 +0100

    [perf] Fix unaligned-clip
    
    We failed to reset the scale after each loop, eventually generating a vast
    clip-mask that exceeded memory capacity.

diff --git a/perf/unaligned-clip.c b/perf/unaligned-clip.c
index f532b55..0bff258 100644
--- a/perf/unaligned-clip.c
+++ b/perf/unaligned-clip.c
@@ -34,6 +34,8 @@ 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);
@@ -51,7 +53,7 @@ do_unaligned_clip (cairo_t *cr, int width, int height, int loops)
 	/* And paint something to force the clip to be evaluated. */
 	cairo_paint (cr);
 
-	cairo_reset_clip (cr);
+	cairo_restore (cr);
     }
     cairo_perf_timer_stop ();
 
commit 6f8fae21e6fee07a8f6561f77bcb0bfaf64f5882
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 15 10:16:41 2009 +0100

    [perf] Add a box mode to spiral
    
    Add a very similar 'spiral' path (really just concentric boxes) that hit
    the rectangular optimisations so that we can compare how effective they
    are.

diff --git a/perf/spiral.c b/perf/spiral.c
index 0e17e93..123ad57 100644
--- a/perf/spiral.c
+++ b/perf/spiral.c
@@ -107,6 +107,42 @@ draw_spiral (cairo_t *cr,
 }
 
 static cairo_perf_ticks_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_perf_ticks_t
 draw_spiral_stroke (cairo_t *cr,
 		    align_t align,
 		    int width, int height, int loops)
@@ -242,6 +278,39 @@ draw_spiral_nz_na_di (cairo_t *cr, int width, int height, int loops)
 }
 
 static cairo_perf_ticks_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_perf_ticks_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_perf_ticks_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_perf_ticks_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_perf_ticks_t
 draw_spiral_stroke_pa (cairo_t *cr, int width, int height, int loops)
 {
     return draw_spiral_stroke (cr,
@@ -263,6 +332,10 @@ spiral (cairo_perf_t *perf, cairo_t *cr, int width, int height)
     if (! cairo_perf_can_run (perf, "spiral"))
 	return;
 
+    cairo_perf_run (perf, "spiral-box-nonalign-evenodd-fill", draw_spiral_eo_na_box);
+    cairo_perf_run (perf, "spiral-box-nonalign-nonzero-fill", draw_spiral_nz_na_box);
+    cairo_perf_run (perf, "spiral-box-pixalign-evenodd-fill", draw_spiral_eo_pa_box);
+    cairo_perf_run (perf, "spiral-box-pixalign-nonzero-fill", draw_spiral_nz_pa_box);
     cairo_perf_run (perf, "spiral-diag-nonalign-evenodd-fill", draw_spiral_eo_na_di);
     cairo_perf_run (perf, "spiral-diag-nonalign-nonzero-fill", draw_spiral_nz_na_di);
     cairo_perf_run (perf, "spiral-diag-pixalign-evenodd-fill", draw_spiral_eo_pa_di);
commit e79a9a87c311fd0499c96cecbe560839a17d244e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 15 10:33:48 2009 +0100

    [perf] Support objdir!=srcdir in cairo-perf-diff
    
    Crude support to detect when we have cairo-perf-diff-files in the local
    directory which is not srcdir.

diff --git a/perf/cairo-perf-diff b/perf/cairo-perf-diff
index 469da89..52c6d80 100755
--- a/perf/cairo-perf-diff
+++ b/perf/cairo-perf-diff
@@ -223,14 +223,20 @@ run_cairo_perf_if_not_cached() {
 
 git_setup
 
-# Build cairo-perf-diff-files if not available
-if [ ! -e $CAIRO_DIR/perf/cairo-perf-diff-files ]; then
-    echo "Building cairo-perf-diff-files"
-    if [ "x$OS" = "xWindows_NT" ]; then
-        make -f Makefile.win32 -C $CAIRO_DIR/perf/ cairo-perf-diff-files CFG=debug
-    else
-        make -C $CAIRO_DIR/perf/ cairo-perf-diff-files
-    fi
+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
@@ -244,8 +250,8 @@ if [ ! -e $new ]; then
 fi
 
 if [ -z "$html_output" ]; then
-    $CAIRO_DIR/perf/cairo-perf-diff-files $old $new
+    $bindir/cairo-perf-diff-files $old $new
 else
-    $CAIRO_DIR/perf/cairo-perf-diff-files $old $new |
+    $bindir/cairo-perf-diff-files $old $new |
     $CAIRO_DIR/perf/make-html.py > $html_output
 fi
commit 6bfb77a33c3614cafdcd6206de5c71321c5fc1e5
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Aug 14 19:53:17 2009 +0100

    [perf] Add spiral strokes
    
    Use cairo_stroke() to perform the equivalent of
    spiral-rect-(pix|non)align-evenodd-fill. A useful comparison of stroking
    versus filling, as we can assume the composition costs are similar.

diff --git a/perf/spiral.c b/perf/spiral.c
index bd81c7e..0e17e93 100644
--- a/perf/spiral.c
+++ b/perf/spiral.c
@@ -107,6 +107,61 @@ draw_spiral (cairo_t *cr,
 }
 
 static cairo_perf_ticks_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_perf_ticks_t
 draw_spiral_eo_pa_re (cairo_t *cr, int width, int height, int loops)
 {
     return draw_spiral (cr,
@@ -186,6 +241,22 @@ draw_spiral_nz_na_di (cairo_t *cr, int width, int height, int loops)
                         width, height, loops);
 }
 
+static cairo_perf_ticks_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_perf_ticks_t
+draw_spiral_stroke_na (cairo_t *cr, int width, int height, int loops)
+{
+    return draw_spiral_stroke (cr,
+			       NONALIGN,
+			       width, height, loops);
+}
+
 void
 spiral (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
@@ -200,4 +271,6 @@ spiral (cairo_perf_t *perf, cairo_t *cr, int width, int height)
     cairo_perf_run (perf, "spiral-rect-nonalign-nonzero-fill", draw_spiral_nz_na_re);
     cairo_perf_run (perf, "spiral-rect-pixalign-evenodd-fill", draw_spiral_eo_pa_re);
     cairo_perf_run (perf, "spiral-rect-pixalign-nonzero-fill", draw_spiral_nz_pa_re);
+    cairo_perf_run (perf, "spiral-nonalign-stroke", draw_spiral_stroke_na);
+    cairo_perf_run (perf, "spiral-pixalign-stroke", draw_spiral_stroke_pa);
 }
commit 54b2935c3842e77888faef06bbf247f99bd9de8c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 23:23:41 2009 +0100

    [perf] Reset the path after each spiral
    
    Oops we were accumulating paths during each spiral iteration and so the
    tests were getting slower and slower and slower...
    
    [And fix a couple of other instances of path accumulation.]

diff --git a/perf/intersections.c b/perf/intersections.c
index 7a7cc55..1a00b1d 100644
--- a/perf/intersections.c
+++ b/perf/intersections.c
@@ -61,6 +61,7 @@ draw_random (cairo_t *cr, cairo_fill_rule_t fill_rule,
     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]);
@@ -98,6 +99,7 @@ draw_random_curve (cairo_t *cr, cairo_fill_rule_t fill_rule,
     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,
diff --git a/perf/spiral.c b/perf/spiral.c
index 2a4e568..bd81c7e 100644
--- a/perf/spiral.c
+++ b/perf/spiral.c
@@ -89,6 +89,7 @@ draw_spiral (cairo_t *cr,
     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]);
diff --git a/perf/world-map.c b/perf/world-map.c
index 265c8e3..d9a267d 100644
--- a/perf/world-map.c
+++ b/perf/world-map.c
@@ -97,6 +97,8 @@ do_world_map (cairo_t *cr, int width, int height, int loops)
 		break;
 	    e++;
 	}
+
+	cairo_new_path (cr);
     }
 
     cairo_perf_timer_stop ();
commit fddfe4973f5b49f280734ee7141ab72451cf94d2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Aug 13 15:52:06 2009 +0100

    [perf] Add clipped modes to dragon
    
    Investigate the performance hit for unclipped/aligned/unaligned/masked
    modes with a reasonably complex geometry.

diff --git a/perf/dragon.c b/perf/dragon.c
index 366b63c..a145f78 100644
--- a/perf/dragon.c
+++ b/perf/dragon.c
@@ -202,6 +202,38 @@ do_dragon_solid (cairo_t *cr, int width, int height, int loops)
     return cairo_perf_timer_elapsed ();
 }
 
+static cairo_perf_ticks_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_perf_ticks_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_perf_ticks_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);
+}
+
 void
 dragon (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
@@ -209,5 +241,8 @@ dragon (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 	return;
 
     cairo_perf_run (perf, "dragon-solid", do_dragon_solid);
+    cairo_perf_run (perf, "dragon-solid-aligned-clip", do_dragon_solid_aligned_clip);
+    cairo_perf_run (perf, "dragon-solid-unaligned-clip", do_dragon_solid_unaligned_clip);
+    cairo_perf_run (perf, "dragon-solid-circle-clip", do_dragon_solid_circle_clip);
     cairo_perf_run (perf, "dragon", do_dragon);
 }
commit 8b3be26c7be0747378792e3a454e500d290c3982
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Aug 12 00:02:04 2009 +0100

    [test] Remove misleading code/comments from a1-image-sample

diff --git a/test/a1-image-sample.c b/test/a1-image-sample.c
index 552e894..3c349af 100644
--- a/test/a1-image-sample.c
+++ b/test/a1-image-sample.c
@@ -49,11 +49,7 @@ draw (cairo_t *cr, int width, int height)
     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++) {
commit e702df59b05246a52a645792a65dc9c12d5b3feb
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 22 17:53:59 2009 +0100

    [test] Attempt to automatically detect running under gdb
    
    A common mistake is to forget to pass the foreground mode to
    cairo-test-suite when launching it under the debugger, resulting in the
    debugger not attaching to the children and missing the error you were
    trying to capture. Under linux, we can inspect the path to our parent's
    executable and if that looks like gdb, we assume it is and disable forking
    of traces.

diff --git a/test/cairo-test-runner.c b/test/cairo-test-runner.c
index f147b5b..af2991a 100644
--- a/test/cairo-test-runner.c
+++ b/test/cairo-test-runner.c
@@ -49,6 +49,15 @@
 #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>
@@ -146,6 +155,26 @@ _list_free (cairo_test_list_t *list)
     }
 }
 
+static cairo_bool_t
+is_running_under_debugger (void)
+{
+    char buf[1024];
+
+    if (RUNNING_ON_VALGRIND)
+	return TRUE;
+
+#if HAVE_UNISTD_H && HAVE_LIBGEN_H && __linux__
+    sprintf (buf, "/proc/%d/exe", getppid ());
+    if (readlink (buf, buf, sizeof (buf)) != -1 &&
+	strncmp (basename (buf), "gdb", 3) == 0)
+    {
+	return TRUE;
+    }
+#endif
+
+    return FALSE;
+}
+
 #if SHOULD_FORK
 static cairo_test_status_t
 _cairo_test_wait (pid_t pid)
@@ -621,6 +650,9 @@ main (int argc, char **argv)
     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");
 
commit bf4977b645270bb88225501e73848f7814cccead
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 25 07:16:16 2009 +0100

    [test] Exit on first failure '-x'
    
    Add a command line option to the test suite to cause it to exit after the
    first failure. The purpose of this is for integration into 'git bisect run',
    where the failing test is unknown and we are looking for any failure. For
    example, for use in a regression script to find commits in the midst of as
    series that need a refresh of a reference image (or fixing!).

diff --git a/test/cairo-test-runner.c b/test/cairo-test-runner.c
index cf1c006..f147b5b 100644
--- a/test/cairo-test-runner.c
+++ b/test/cairo-test-runner.c
@@ -81,6 +81,7 @@ typedef struct _cairo_test_runner {
     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_test_runner_t;
@@ -285,7 +286,7 @@ static void
 usage (const char *argv0)
 {
     fprintf (stderr,
-	     "Usage: %s [-af] [test-names|keywords ...]\n"
+	     "Usage: %s [-afx] [test-names|keywords ...]\n"
 	     "       %s -l\n"
 	     "\n"
 	     "Run the cairo conformance test suite over the given tests (all by default)\n"
@@ -294,6 +295,7 @@ usage (const char *argv0)
 	     "  -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"
+	     "  -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 exact matches either to a specific\n"
@@ -308,7 +310,7 @@ _parse_cmdline (cairo_test_runner_t *runner, int *argc, char **argv[])
     int c;
 
     while (1) {
-	c = _cairo_getopt (*argc, *argv, ":afl");
+	c = _cairo_getopt (*argc, *argv, ":aflx");
 	if (c == -1)
 	    break;
 
@@ -322,6 +324,9 @@ _parse_cmdline (cairo_test_runner_t *runner, int *argc, char **argv[])
 	case 'f':
 	    runner->foreground = TRUE;
 	    break;
+	case 'x':
+	    runner->exit_on_failure = TRUE;
+	    break;
 	default:
 	    fprintf (stderr, "Internal error: unhandled option: %c\n", c);
 	    /* fall-through */
@@ -625,6 +630,9 @@ main (int argc, char **argv)
 	if (strstr (env, "foreground")) {
 	    runner.foreground = TRUE;
 	}
+	if (strstr (env, "exit-on-failure")) {
+	    runner.exit_on_failure = TRUE;
+	}
     }
 
     _parse_cmdline (&runner, &argc, &argv);
@@ -894,6 +902,9 @@ main (int argc, char **argv)
 
   TEST_NEXT:
 	free (name);
+	if (runner.exit_on_failure && ! runner.passed)
+	    break;
+
     }
 
     _list_free (tests);


More information about the cairo-commit mailing list