[cairo-commit] 4 commits - boilerplate/buffer-diff.c boilerplate/buffer-diff.h boilerplate/cairo-boilerplate.c boilerplate/cairo-boilerplate.h boilerplate/cairo-test.c boilerplate/cairo-test.h boilerplate/Makefile.am boilerplate/README boilerplate/read-png.c boilerplate/read-png.h boilerplate/write-png.c boilerplate/write-png.h boilerplate/xmalloc.c test/buffer-diff.c test/buffer-diff.h test/cairo-test.c test/cairo-test.h test/imagediff.c test/Makefile.am

Carl Worth cworth at kemper.freedesktop.org
Thu Aug 31 01:40:38 PDT 2006


 boilerplate/Makefile.am         |   21 
 boilerplate/README              |   14 
 boilerplate/buffer-diff.c       |  348 ------
 boilerplate/cairo-boilerplate.c | 1511 ++++++++++++++++++++++++++++
 boilerplate/cairo-boilerplate.h |   88 +
 boilerplate/cairo-test.c        | 2147 ----------------------------------------
 boilerplate/read-png.c          |  196 ---
 boilerplate/read-png.h          |   45 
 boilerplate/write-png.c         |   99 -
 boilerplate/write-png.h         |   35 
 boilerplate/xmalloc.c           |    6 
 test/Makefile.am                |   13 
 test/buffer-diff.c              |  306 +++++
 test/cairo-test.c               |  683 ++++++++++++
 test/cairo-test.h               |   13 
 test/imagediff.c                |    2 
 16 files changed, 2627 insertions(+), 2900 deletions(-)

New commits:
diff-tree d52a1f762d33f3ada919b581e0d62f8ba1c2314c (from 95475218858792ccb20ac6ad28db22e233c783d7)
Author: Carl Worth <cworth at cworth.org>
Date:   Thu Aug 31 01:39:06 2006 -0700

    Move test-specific stuff out of boilerplate/ and back into test/
    
    This now gives us two separate libtool convenience libraries,
    so they have to have separate names now:
    libcairoboilerplate.la and libcairotest.la.

diff --git a/boilerplate/Makefile.am b/boilerplate/Makefile.am
index 2cc48ce..4bf07fa 100644
--- a/boilerplate/Makefile.am
+++ b/boilerplate/Makefile.am
@@ -1,23 +1,19 @@
-noinst_LTLIBRARIES = libcairotest.la
+noinst_LTLIBRARIES = libcairoboilerplate.la
 
-libcairotest_la_SOURCES =\
+libcairoboilerplate_la_SOURCES =\
 cairo-boilerplate.c	\
 cairo-bolierplate.h	\
-buffer-diff.c		\
-buffer-diff.h		\
-cairo-test.c		\
-cairo-test.h		\
 xmalloc.c		\
 xmalloc.h
 
 if CAIRO_HAS_BEOS_SURFACE
-libcairotest_la_SOURCES += cairo-test-beos.cpp cairo-test-beos.h
+libcairoboilerplate_la_SOURCES += cairo-test-beos.cpp cairo-test-beos.h
 # BeOS system headers trigger this warning
-libcairotest_la_CXXFLAGS = -Wno-multichar
+libcairoboilerplate_la_CXXFLAGS = -Wno-multichar
 endif
 
 if CAIRO_HAS_DIRECTFB_SURFACE
-libcairotest_la_SOURCES += cairo-test-directfb.c cairo-test-directfb.h
+libcairoboilerplate_la_SOURCES += cairo-test-directfb.c cairo-test-directfb.h
 endif
 
 # We're using _GNU_SOURCE to get the prototype for asprintf. This may
diff --git a/boilerplate/buffer-diff.c b/boilerplate/buffer-diff.c
deleted file mode 100644
index b59b5e8..0000000
--- a/boilerplate/buffer-diff.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/* imagediff - Compare two images
- *
- * Copyright © 2004 Richard D. Worth
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without
- * fee, provided that the above copyright notice appear in all copies
- * and that both that copyright notice and this permission notice
- * appear in supporting documentation, and that the name of Richard Worth
- * not be used in advertising or publicity pertaining to distribution
- * of the software without specific, written prior permission.
- * Richard Worth makes no representations about the suitability of this
- * software for any purpose.  It is provided "as is" without express
- * or implied warranty.
- *
- * RICHARD WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
- * NO EVENT SHALL RICHARD WORTH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
- * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Author: Richard D. Worth <richard at theworths.org> */
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <errno.h>
-#include <string.h>
-#include <pixman.h>
-
-#include "cairo-test.h"
-
-#include "buffer-diff.h"
-#include "xmalloc.h"
-
-static void
-xunlink (const char *pathname)
-{
-    if (unlink (pathname) < 0 && errno != ENOENT) {
-	cairo_test_log ("  Error: Cannot remove %s: %s\n",
-			pathname, strerror (errno));
-	exit (1);
-    }
-}
-
-/* This function should be rewritten to compare all formats supported by
- * cairo_format_t instead of taking a mask as a parameter.
- */
-static int
-buffer_diff_core (unsigned char *_buf_a,
-		  unsigned char *_buf_b,
-		  unsigned char *_buf_diff,
-		  int		width,
-		  int		height,
-		  int		stride_a,
-		  int		stride_b,
-		  int		stride_diff,
-		  pixman_bits_t mask)
-{
-    int x, y;
-    pixman_bits_t *row_a, *row_b, *row;
-    int pixels_changed = 0;
-    pixman_bits_t *buf_a = (pixman_bits_t*)_buf_a;
-    pixman_bits_t *buf_b = (pixman_bits_t*)_buf_b;
-    pixman_bits_t *buf_diff = (pixman_bits_t*)_buf_diff;
-
-    stride_a /= sizeof(pixman_bits_t);
-    stride_b /= sizeof(pixman_bits_t);
-    stride_diff /= sizeof(pixman_bits_t);
-    for (y = 0; y < height; y++)
-    {
-	row_a = buf_a + y * stride_a;
-	row_b = buf_b + y * stride_b;
-	row = buf_diff + y * stride_diff;
-	for (x = 0; x < width; x++)
-	{
-	    /* check if the pixels are the same */
-	    if ((row_a[x] & mask) != (row_b[x] & mask)) {
-		int channel;
-		pixman_bits_t diff_pixel = 0;
-
-		/* calculate a difference value for all 4 channels */
-		for (channel = 0; channel < 4; channel++) {
-		    int value_a = (row_a[x] >> (channel*8)) & 0xff;
-		    int value_b = (row_b[x] >> (channel*8)) & 0xff;
-		    unsigned int diff;
-		    diff = abs (value_a - value_b);
-		    diff *= 4;  /* emphasize */
-		    if (diff)
-		        diff += 128; /* make sure it's visible */
-		    if (diff > 255)
-		        diff = 255;
-		    diff_pixel |= diff << (channel*8);
-		}
-
-		pixels_changed++;
-		row[x] = diff_pixel;
-	    } else {
-		row[x] = 0;
-	    }
-	    row[x] |= 0xff000000; /* Set ALPHA to 100% (opaque) */
-	}
-    }
-
-    return pixels_changed;
-}
-
-int
-buffer_diff (unsigned char *buf_a,
-	     unsigned char *buf_b,
-	     unsigned char *buf_diff,
-	     int	   width,
-	     int	   height,
-	     int	   stride_a,
-	     int	   stride_b,
-	     int	   stride_diff)
-{
-    return buffer_diff_core(buf_a, buf_b, buf_diff,
-			    width, height, stride_a, stride_b, stride_diff, 0xffffffff);
-}
-
-int
-buffer_diff_noalpha (unsigned char *buf_a,
-		     unsigned char *buf_b,
-		     unsigned char *buf_diff,
-		     int	   width,
-		     int	   height,
-		     int	   stride_a,
-		     int	   stride_b,
-		     int	   stride_diff)
-{
-    return buffer_diff_core(buf_a, buf_b, buf_diff,
-			    width, height, stride_a, stride_b, stride_diff, 0x00ffffff);
-}
-
-static cairo_status_t
-stdio_write_func (void *closure, const unsigned char *data, unsigned int length)
-{
-    FILE *file = closure;
-
-    if (fwrite (data, 1, length, file) != length)
-	return CAIRO_STATUS_WRITE_ERROR;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-/* Flatten an ARGB surface by blending it over white. The resulting
- * surface, (still in ARGB32 format, but with only alpha==1.0
- * everywhere) is returned in the same surface pointer.
- *
- * The original surface will be destroyed.
- *
- * The (x,y) value specify an origin of interest for the original
- * image. The flattened image will be generated only from the box
- * extending from (x,y) to (width,height).
- */
-static void
-flatten_surface (cairo_surface_t **surface, int x, int y)
-{
-    cairo_surface_t *flat;
-    cairo_t *cr;
-
-    flat = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-				       cairo_image_surface_get_width (*surface) - x,
-				       cairo_image_surface_get_height (*surface) - y);
-    cairo_surface_set_device_offset (flat, -x, -y);
-
-    cr = cairo_create (flat);
-    cairo_set_source_rgb (cr, 1, 1, 1);
-    cairo_paint (cr);
-    cairo_set_source_surface (cr, *surface, 0, 0);
-    cairo_paint (cr);
-    cairo_destroy (cr);
-
-    cairo_surface_destroy (*surface);
-    *surface = flat;
-}
-
-/* Image comparison code courtesy of Richard Worth <richard at theworths.org>
- * Returns number of pixels changed, (or -1 on error).
- * Also saves a "diff" image intended to visually show where the
- * images differ.
- */
-static int
-image_diff_core (const char *filename_a,
-		 const char *filename_b,
-		 const char *filename_diff,
-		 int		ax,
-		 int		ay,
-		 int		bx,
-		 int		by,
-		 cairo_bool_t	flatten)
-{
-    int pixels_changed;
-    unsigned int width_a, height_a, stride_a;
-    unsigned int width_b, height_b, stride_b;
-    cairo_surface_t *surface_a, *surface_b, *surface_diff;
-
-    surface_a = cairo_image_surface_create_from_png (filename_a);
-    if (cairo_surface_status (surface_a))
-	return -1;
-
-    surface_b = cairo_image_surface_create_from_png (filename_b);
-    if (cairo_surface_status (surface_b)) {
-	cairo_surface_destroy (surface_a);
-	return -1;
-    }
-
-    width_a = cairo_image_surface_get_width (surface_a) - ax;
-    height_a = cairo_image_surface_get_height (surface_a) - ay;
-    width_b = cairo_image_surface_get_width (surface_b) - bx;
-    height_b = cairo_image_surface_get_height (surface_b) - by;
-
-    if (width_a  != width_b  ||
-	height_a != height_b)
-    {
-	cairo_test_log ("Error: Image size mismatch: (%dx%d) vs. (%dx%d)\n"
-			"       for %s vs. %s\n",
-			width_a, height_a,
-			width_b, height_b,
-			filename_a, filename_b);
-	cairo_surface_destroy (surface_a);
-	cairo_surface_destroy (surface_b);
-	return -1;
-    }
-
-    if (flatten) {
-	flatten_surface (&surface_a, ax, ay);
-	flatten_surface (&surface_b, bx, by);
-	ax = ay = bx = by = 0;
-    }
-
-    surface_diff = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-					       width_a, height_a);
-
-    stride_a = cairo_image_surface_get_stride (surface_a);
-    stride_b = cairo_image_surface_get_stride (surface_b);
-
-    pixels_changed = buffer_diff (cairo_image_surface_get_data (surface_a)
-				  + (ay * stride_a) + ax * 4,
-                                  cairo_image_surface_get_data (surface_b)
-				  + (by * stride_b) + by * 4,
-                                  cairo_image_surface_get_data (surface_diff),
-                                  width_a, height_a,
-				  stride_a, stride_b,
-				  cairo_image_surface_get_stride (surface_diff));
-
-    if (pixels_changed) {
-	FILE *png_file;
-
-	if (filename_diff)
-	    png_file = fopen (filename_diff, "wb");
-	else
-	    png_file = stdout;
-
-	cairo_surface_write_to_png_stream (surface_diff, stdio_write_func, png_file);
-
-	if (png_file != stdout)
-	    fclose (png_file);
-    } else {
-	if (filename_diff)
-	    xunlink (filename_diff);
-    }
-
-    cairo_surface_destroy (surface_a);
-    cairo_surface_destroy (surface_b);
-    cairo_surface_destroy (surface_diff);
-
-    return pixels_changed;
-}
-
-int
-image_diff (const char *filename_a,
-	    const char *filename_b,
-	    const char *filename_diff,
-	    int		ax,
-	    int		ay,
-	    int		bx,
-	    int		by)
-{
-    return image_diff_core (filename_a, filename_b, filename_diff,
-			    ax, ay, bx, by,
-			    FALSE);
-}
-
-int
-image_diff_flattened (const char *filename_a,
-		      const char *filename_b,
-		      const char *filename_diff,
-		      int	  ax,
-		      int	  ay,
-		      int	  bx,
-		      int	  by)
-{
-    return image_diff_core (filename_a, filename_b, filename_diff,
-			    ax, ay, bx, by,
-			    TRUE);
-}
diff --git a/boilerplate/buffer-diff.h b/boilerplate/buffer-diff.h
deleted file mode 100644
index 2be2b39..0000000
--- a/boilerplate/buffer-diff.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* imagediff - Compare two images
- *
- * Copyright © 2004 Richard D. Worth
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without
- * fee, provided that the above copyright notice appear in all copies
- * and that both that copyright notice and this permission notice
- * appear in supporting documentation, and that the name of Richard Worth
- * not be used in advertising or publicity pertaining to distribution
- * of the software without specific, written prior permission.
- * Richard Worth makes no representations about the suitability of this
- * software for any purpose.  It is provided "as is" without express
- * or implied warranty.
- *
- * RICHARD WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
- * NO EVENT SHALL RICHARD WORTH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
- * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Author: Richard D. Worth <richard at theworths.org> */
-
-#ifndef BUFFER_DIFF_H
-#define BUFFER_DIFF_H
-
-/* Returns number of pixels changed.
- * Also fills in a "diff" buffer intended to visually show where the
- * images differ.
- */
-int
-buffer_diff (unsigned char *buf_a,
-	     unsigned char *buf_b,
-	     unsigned char *buf_diff,
-	     int	    width,
-	     int	    height,
-	     int	    stride_a,
-	     int	    stride_b,
-	     int	    stride_diff);
-
-/* Returns number of pixels changed ignoring the alpha channel.
- * Also fills in a "diff" buffer intended to visually show where the
- * images differ.
- */
-int
-buffer_diff_noalpha (unsigned char *buf_a,
-		     unsigned char *buf_b,
-		     unsigned char *buf_diff,
-		     int	    width,
-		     int	    height,
-		     int	    stride_a,
-		     int	    stride_b,
-		     int	    stride_diff);
-
-/* Returns number of pixels changed, (or -1 on error).
- * Also saves a "diff" image intended to visually show where the
- * images differ.
- */
-int
-image_diff (const char *filename_a,
-	    const char *filename_b,
-	    const char *filename_diff,
-	    int		ax,
-	    int		ay,
-	    int		bx,
-	    int		by);
-
-/* Like image_diff, but blending the contents of b over white first. */
-int
-image_diff_flattened (const char *filename_a,
-		      const char *filename_b,
-		      const char *filename_diff,
-                      int         ax,
-                      int         ay,
-                      int         bx,
-                      int         by);
-
-#endif
diff --git a/boilerplate/cairo-test.c b/boilerplate/cairo-test.c
deleted file mode 100644
index 0d915a5..0000000
--- a/boilerplate/cairo-test.c
+++ /dev/null
@@ -1,683 +0,0 @@
-/*
- * Copyright © 2004 Red Hat, Inc.
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without
- * fee, provided that the above copyright notice appear in all copies
- * and that both that copyright notice and this permission notice
- * appear in supporting documentation, and that the name of
- * Red Hat, Inc. not be used in advertising or publicity pertaining to
- * distribution of the software without specific, written prior
- * permission. Red Hat, Inc. makes no representations about the
- * suitability of this software for any purpose.  It is provided "as
- * is" without express or implied warranty.
- *
- * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
- * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
- * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
- * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
- * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Author: Carl D. Worth <cworth at cworth.org>
- */
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <setjmp.h>
-#ifdef HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-#include <assert.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <errno.h>
-#include <string.h>
-#if HAVE_FCFINI
-#include <fontconfig/fontconfig.h>
-#endif
-
-#include "cairo-test.h"
-
-#include "buffer-diff.h"
-#include "xmalloc.h"
-
-/* This is copied from cairoint.h. That makes it painful to keep in
- * sync, but the slim stuff makes cairoint.h "hard" to include when
- * not actually building the cairo library itself. Fortunately, since
- * we're checking all these values, we do have a safeguard for keeping
- * them in sync.
- */
-typedef enum cairo_internal_surface_type {
-    CAIRO_INTERNAL_SURFACE_TYPE_META = 0x1000,
-    CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
-    CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
-    CAIRO_INTERNAL_SURFACE_TYPE_TEST_META,
-    CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
-    CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED
-} cairo_internal_surface_type_t;
-
-#ifdef _MSC_VER
-#define vsnprintf _vsnprintf
-#define access _access
-#define F_OK 0
-#endif
-#ifndef FALSE
-#define FALSE 0
-#endif
-#ifndef TRUE
-#define TRUE !FALSE
-#endif
-
-static void
-xunlink (const char *pathname);
-
-static const char *fail_face = "", *normal_face = "";
-
-#define CAIRO_TEST_LOG_SUFFIX ".log"
-#define CAIRO_TEST_PNG_SUFFIX "-out.png"
-#define CAIRO_TEST_REF_SUFFIX "-ref.png"
-#define CAIRO_TEST_DIFF_SUFFIX "-diff.png"
-
-#define NUM_DEVICE_OFFSETS 2
-
-/* Static data is messy, but we're coding for tests here, not a
- * general-purpose library, and it keeps the tests cleaner to avoid a
- * context object there, (though not a whole lot). */
-FILE *cairo_test_log_file = NULL;
-const char *srcdir;
-
-/* Used to catch crashes in a test, such that we report it as such and
- * continue testing, although one crasher may already have corrupted memory in
- * an nonrecoverable fashion. */
-jmp_buf jmpbuf;
-
-void
-cairo_test_init (const char *test_name)
-{
-    char *log_name;
-
-    xasprintf (&log_name, "%s%s", test_name, CAIRO_TEST_LOG_SUFFIX);
-    xunlink (log_name);
-
-    cairo_test_log_file = fopen (log_name, "a");
-    if (cairo_test_log_file == NULL) {
-	fprintf (stderr, "Error opening log file: %s\n", log_name);
-	cairo_test_log_file = stderr;
-    }
-    free (log_name);
-
-    printf ("\nTESTING %s\n", test_name);
-}
-
-void
-cairo_test_log (const char *fmt, ...)
-{
-    va_list va;
-    FILE *file = cairo_test_log_file ? cairo_test_log_file : stderr;
-
-    va_start (va, fmt);
-    vfprintf (file, fmt, va);
-    va_end (va);
-}
-
-static void
-xunlink (const char *pathname)
-{
-    if (unlink (pathname) < 0 && errno != ENOENT) {
-	cairo_test_log ("Error: Cannot remove %s: %s\n",
-			pathname, strerror (errno));
-	exit (1);
-    }
-}
-
-static char *
-cairo_ref_name_for_test_target_format (const char *test_name,
-				       const char *target_name,
-				       const char *format)
-{
-    char *ref_name = NULL;
-
-    /* First look for a target/format-specific reference image. */
-    xasprintf (&ref_name, "%s/%s-%s-%s%s", srcdir,
-	       test_name,
-	       target_name,
-	       format,
-	       CAIRO_TEST_REF_SUFFIX);
-    if (access (ref_name, F_OK) != 0)
-	free (ref_name);
-    else
-	goto done;
-
-    /* Next, look for taget-specifc reference image. */
-    xasprintf (&ref_name, "%s/%s-%s%s", srcdir,
-	       test_name,
-	       target_name,
-	       CAIRO_TEST_REF_SUFFIX);
-    if (access (ref_name, F_OK) != 0)
-	free (ref_name);
-    else
-	goto done;
-
-    /* Next, look for format-specifc reference image. */
-    xasprintf (&ref_name, "%s/%s-%s%s", srcdir,
-	       test_name,
-	       format,
-	       CAIRO_TEST_REF_SUFFIX);
-    if (access (ref_name, F_OK) != 0)
-	free (ref_name);
-    else
-	goto done;
-
-    /* Finally, look for the standard reference image. */
-    xasprintf (&ref_name, "%s/%s%s", srcdir,
-	       test_name,
-	       CAIRO_TEST_REF_SUFFIX);
-    if (access (ref_name, F_OK) != 0)
-	free (ref_name);
-    else
-	goto done;
-
-    ref_name = NULL;
-
-done:
-    return ref_name;
-}
-
-static cairo_test_status_t
-cairo_test_for_target (cairo_test_t		 *test,
-		       cairo_test_target_t	 *target,
-		       int			  dev_offset)
-{
-    cairo_test_status_t status;
-    cairo_surface_t *surface;
-    cairo_t *cr;
-    char *png_name, *ref_name, *diff_name, *offset_str;
-    cairo_test_status_t ret;
-    cairo_content_t expected_content;
-    cairo_font_options_t *font_options;
-    const char *format;
-
-    /* Get the strings ready that we'll need. */
-    format = _cairo_test_content_name (target->content);
-    if (dev_offset)
-	xasprintf (&offset_str, "-%d", dev_offset);
-    else
-	offset_str = strdup("");
-
-    xasprintf (&png_name, "%s-%s-%s%s%s",
-	       test->name,
-	       target->name,
-	       format,
-	       offset_str, CAIRO_TEST_PNG_SUFFIX);
-    ref_name = cairo_ref_name_for_test_target_format (test->name, target->name, format);
-    xasprintf (&diff_name, "%s-%s-%s%s%s",
-	       test->name,
-	       target->name,
-	       format,
-	       offset_str, CAIRO_TEST_DIFF_SUFFIX);
-
-    /* Run the actual drawing code. */
-    if (test->width && test->height) {
-	test->width += dev_offset;
-	test->height += dev_offset;
-    }
-
-    surface = (target->create_target_surface) (test->name,
-					       target->content,
-					       test->width,
-					       test->height,
-					       &target->closure);
-
-    if (test->width && test->height) {
-	test->width -= dev_offset;
-	test->height -= dev_offset;;
-    }
-
-    if (surface == NULL) {
-	cairo_test_log ("Error: Failed to set %s target\n", target->name);
-	ret = CAIRO_TEST_UNTESTED;
-	goto UNWIND_STRINGS;
-    }
-
-    /* Check that we created a surface of the expected type. */
-    if (cairo_surface_get_type (surface) != target->expected_type) {
-	cairo_test_log ("Error: Created surface is of type %d (expected %d)\n",
-			cairo_surface_get_type (surface), target->expected_type);
-	ret = CAIRO_TEST_FAILURE;
-	goto UNWIND_SURFACE;
-    }
-
-    /* Check that we created a surface of the expected content,
-     * (ignore the articifical
-     * CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED value).
-     */
-    expected_content = target->content;
-    if (expected_content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
-	expected_content = CAIRO_CONTENT_COLOR_ALPHA;
-
-    if (cairo_surface_get_content (surface) != expected_content) {
-	cairo_test_log ("Error: Created surface has content %d (expected %d)\n",
-			cairo_surface_get_content (surface), expected_content);
-	ret = CAIRO_TEST_FAILURE;
-	goto UNWIND_SURFACE;
-    }
-
-    cairo_surface_set_device_offset (surface, dev_offset, dev_offset);
-
-    cr = cairo_create (surface);
-
-    /* Clear to transparent (or black) depending on whether the target
-     * surface supports alpha. */
-    cairo_save (cr);
-    cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
-    cairo_paint (cr);
-    cairo_restore (cr);
-
-    /* Set all components of font_options to avoid backend differences
-     * and reduce number of needed reference images. */
-    font_options = cairo_font_options_create ();
-    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
-    cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_ON);
-    cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_GRAY);
-    cairo_set_font_options (cr, font_options);
-    cairo_font_options_destroy (font_options);
-
-    status = (test->draw) (cr, test->width, test->height);
-
-    /* Then, check all the different ways it could fail. */
-    if (status) {
-	cairo_test_log ("Error: Function under test failed\n");
-	ret = status;
-	goto UNWIND_CAIRO;
-    }
-
-    cairo_show_page (cr);
-
-    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
-	cairo_test_log ("Error: Function under test left cairo status in an error state: %s\n",
-			cairo_status_to_string (cairo_status (cr)));
-	ret = CAIRO_TEST_FAILURE;
-	goto UNWIND_CAIRO;
-    }
-
-    /* Skip image check for tests with no image (width,height == 0,0) */
-    if (test->width != 0 && test->height != 0) {
-	int pixels_changed;
-	xunlink (png_name);
-	(target->write_to_png) (surface, png_name);
-
-	if (!ref_name) {
-	    cairo_test_log ("Error: Cannot find reference image for %s/%s-%s-%s%s\n",srcdir,
-			    test->name,
-			    target->name,
-			    format,
-			    CAIRO_TEST_REF_SUFFIX);
-	    ret = CAIRO_TEST_FAILURE;
-	    goto UNWIND_CAIRO;
-	}
-
-	if (target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
-	    pixels_changed = image_diff_flattened (png_name, ref_name, diff_name, dev_offset, dev_offset, 0, 0);
-	else
-	    pixels_changed = image_diff (png_name, ref_name, diff_name, dev_offset, dev_offset, 0, 0);
-	if (pixels_changed) {
-	    if (pixels_changed > 0)
-		cairo_test_log ("Error: %d pixels differ from reference image %s\n",
-				pixels_changed, ref_name);
-	    ret = CAIRO_TEST_FAILURE;
-	    goto UNWIND_CAIRO;
-	}
-    }
-
-    ret = CAIRO_TEST_SUCCESS;
-
-UNWIND_CAIRO:
-    cairo_destroy (cr);
-UNWIND_SURFACE:
-    cairo_surface_destroy (surface);
-
-    cairo_debug_reset_static_data ();
-
-    if (target->cleanup_target)
-	target->cleanup_target (target->closure);
-
-UNWIND_STRINGS:
-    if (png_name)
-      free (png_name);
-    if (ref_name)
-      free (ref_name);
-    if (diff_name)
-      free (diff_name);
-    if (offset_str)
-      free (offset_str);
-
-    return ret;
-}
-
-#ifdef HAVE_SIGNAL_H
-static void
-segfault_handler (int signal)
-{
-    longjmp (jmpbuf, signal);
-}
-#endif
-
-static cairo_test_status_t
-cairo_test_expecting (cairo_test_t *test,
-		      cairo_test_status_t expectation)
-{
-    /* we use volatile here to make sure values are not clobbered
-     * by longjmp */
-    volatile size_t i, j, num_targets;
-    volatile cairo_bool_t limited_targets = FALSE, print_fail_on_stdout = TRUE;
-    const char *tname;
-#ifdef HAVE_SIGNAL_H
-    void (*old_segfault_handler)(int);
-#endif
-    volatile cairo_test_status_t status, ret;
-    cairo_test_target_t ** volatile targets_to_test;
-
-#ifdef HAVE_UNISTD_H
-    if (isatty (2)) {
-	fail_face = "\033[41m\033[37m\033[1m";
-	normal_face = "\033[m";
-	if (isatty (1))
-	    print_fail_on_stdout = FALSE;
-    }
-#endif
-
-    srcdir = getenv ("srcdir");
-    if (!srcdir)
-	srcdir = ".";
-
-    cairo_test_init (test->name);
-    printf ("%s\n", test->description);
-
-    if (expectation == CAIRO_TEST_FAILURE)
-    printf ("Expecting failure\n");
-
-    if ((tname = getenv ("CAIRO_TEST_TARGET")) != NULL && *tname) {
-
-	limited_targets = TRUE;
-
-	num_targets = 0;
-	targets_to_test = NULL;
-
-	while (*tname) {
-	    int found = 0;
-	    const char *end = strpbrk (tname, " \t\r\n;:,");
-	    if (!end)
-	        end = tname + strlen (tname);
-
-	    for (i = 0; targets[i].name != NULL; i++) {
-		if (0 == strncmp (targets[i].name, tname, end - tname) &&
-		    !isalnum (targets[i].name[end - tname])) {
-		    /* realloc isn't exactly the best thing here, but meh. */
-		    targets_to_test = realloc (targets_to_test, sizeof(cairo_test_target_t *) * (num_targets+1));
-		    targets_to_test[num_targets++] = &targets[i];
-		    found = 1;
-		}
-	    }
-
-	    if (!found) {
-		fprintf (stderr, "Cannot test target '%.*s'\n", (int)(end - tname), tname);
-		exit(-1);
-	    }
-
-	    if (*end)
-	      end++;
-	    tname = end;
-	}
-    } else {
-	num_targets = 0;
-	for (i = 0; targets[i].name != NULL; i++)
-	    num_targets++;
-	targets_to_test = malloc (sizeof(cairo_test_target_t*) * num_targets);
-	for (i = 0; i < num_targets; i++) {
-	    targets_to_test[i] = &targets[i];
-	}
-    }
-
-    /* The intended logic here is that we return overall SUCCESS
-     * iff. there is at least one tested backend and that all tested
-     * backends return SUCCESS, OR, there's no backend to test at all.
-     * In other words:
-     *
-     *  if      no backend to test
-     *          -> SUCCESS
-     *	else if any backend not SUCCESS
-     *		-> FAILURE
-     *	else if all backends UNTESTED
-     *		-> FAILURE
-     *	else    (== some backend SUCCESS)
-     *		-> SUCCESS
-     */
-    ret = CAIRO_TEST_UNTESTED;
-    for (i = 0; i < num_targets; i++) {
-	for (j = 0; j < NUM_DEVICE_OFFSETS; j++) {
-	    cairo_test_target_t * volatile target = targets_to_test[i];
-	    volatile int dev_offset = j * 25;
-
-	    cairo_test_log ("Testing %s with %s target (dev offset %d)\n", test->name, target->name, dev_offset);
-	    printf ("%s-%s-%s [%d]:\t", test->name, target->name,
-		    _cairo_test_content_name (target->content),
-		    dev_offset);
-
-#ifdef HAVE_SIGNAL_H
-	    /* Set up a checkpoint to get back to in case of segfaults. */
-	    old_segfault_handler = signal (SIGSEGV, segfault_handler);
-	    if (0 == setjmp (jmpbuf))
-#endif
-		status = cairo_test_for_target (test, target, dev_offset);
-#ifdef HAVE_SIGNAL_H
-	    else
-	        status = CAIRO_TEST_CRASHED;
-	    signal (SIGSEGV, old_segfault_handler);
-#endif
-
-	    cairo_test_log ("TEST: %s TARGET: %s FORMAT: %s OFFSET: %d RESULT: ",
-			    test->name, target->name,
-			    _cairo_test_content_name (target->content),
-			    dev_offset);
-
-	    switch (status) {
-	    case CAIRO_TEST_SUCCESS:
-		printf ("PASS\n");
-		cairo_test_log ("PASS\n");
-		if (ret == CAIRO_TEST_UNTESTED)
-		    ret = CAIRO_TEST_SUCCESS;
-		break;
-	    case CAIRO_TEST_UNTESTED:
-		printf ("UNTESTED\n");
-		cairo_test_log ("UNTESTED\n");
-		break;
-	    case CAIRO_TEST_CRASHED:
-		if (print_fail_on_stdout) {
-		    printf ("!!!CRASHED!!!\n");
-		} else {
-		    /* eat the test name */
-		    printf ("\r");
-		    fflush (stdout);
-		}
-		cairo_test_log ("CRASHED\n");
-		fprintf (stderr, "%s-%s-%s [%d]:\t%s!!!CRASHED!!!%s\n",
-			 test->name, target->name,
-			 _cairo_test_content_name (target->content), dev_offset,
-			 fail_face, normal_face);
-		ret = CAIRO_TEST_FAILURE;
-		break;
-	    default:
-	    case CAIRO_TEST_FAILURE:
-		if (expectation == CAIRO_TEST_FAILURE) {
-		    printf ("XFAIL\n");
-		    cairo_test_log ("XFAIL\n");
-		} else {
-		    if (print_fail_on_stdout) {
-			printf ("FAIL\n");
-		    } else {
-			/* eat the test name */
-			printf ("\r");
-			fflush (stdout);
-		    }
-		    fprintf (stderr, "%s-%s-%s [%d]:\t%sFAIL%s\n",
-			     test->name, target->name,
-			     _cairo_test_content_name (target->content), dev_offset,
-			     fail_face, normal_face);
-		    cairo_test_log ("FAIL\n");
-		}
-		ret = status;
-		break;
-	    }
-	}
-    }
-
-    if (ret != CAIRO_TEST_SUCCESS)
-        printf ("Check %s%s out for more information.\n", test->name, CAIRO_TEST_LOG_SUFFIX);
-
-    /* if no target was requested for test, succeed, otherwise if all
-     * were untested, fail. */
-    if (ret == CAIRO_TEST_UNTESTED)
-	ret = num_targets ? CAIRO_TEST_FAILURE : CAIRO_TEST_SUCCESS;
-
-    /* if targets are limited using CAIRO_TEST_TARGET, and expecting failure,
-     * make it fail, such that we can pass test suite by limiting backends
-     * to test without triggering XPASS failures. */
-    if (limited_targets && expectation == CAIRO_TEST_FAILURE && ret == CAIRO_TEST_SUCCESS) {
-	printf ("All tested backends passed, but tested targets are manually limited\n"
-		"and the test suite expects this test to fail for at least one target.\n"
-		"Intentionally failing the test, to not fail the suite.\n");
-	ret = CAIRO_TEST_FAILURE;
-    }
-
-    fclose (cairo_test_log_file);
-
-    free (targets_to_test);
-
-#if HAVE_FCFINI
-    FcFini ();
-#endif
-
-    return ret;
-}
-
-cairo_test_status_t
-cairo_test (cairo_test_t *test)
-{
-    cairo_test_status_t expectation = CAIRO_TEST_SUCCESS;
-    const char *xfails;
-
-    if ((xfails = getenv ("CAIRO_XFAIL_TESTS")) != NULL) {
-	while (*xfails) {
-	    const char *end = strpbrk (xfails, " \t\r\n;:,");
-	    if (!end)
-	        end = xfails + strlen (xfails);
-
-	    if (0 == strncmp (test->name, xfails, end - xfails) &&
-		'\0' == test->name[end - xfails]) {
-		expectation = CAIRO_TEST_FAILURE;
-		break;
-	    }
-
-	    if (*end)
-	      end++;
-	    xfails = end;
-	}
-    }
-
-    return cairo_test_expecting (test, expectation);
-}
-
-cairo_surface_t *
-cairo_test_create_surface_from_png (const char *filename)
-{
-    cairo_surface_t *image;
-    char *srcdir = getenv ("srcdir");
-
-    image = cairo_image_surface_create_from_png (filename);
-    if (cairo_surface_status(image)) {
-        /* expect not found when running with srcdir != builddir
-         * such as when 'make distcheck' is run
-         */
-	if (srcdir) {
-	    char *srcdir_filename;
-	    xasprintf (&srcdir_filename, "%s/%s", srcdir, filename);
-	    image = cairo_image_surface_create_from_png (srcdir_filename);
-	    free (srcdir_filename);
-	}
-	if (cairo_surface_status(image))
-	    return NULL;
-    }
-
-    return image;
-}
-
-cairo_pattern_t *
-cairo_test_create_pattern_from_png (const char *filename)
-{
-    cairo_surface_t *image;
-    cairo_pattern_t *pattern;
-
-    image = cairo_test_create_surface_from_png (filename);
-
-    pattern = cairo_pattern_create_for_surface (image);
-
-    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
-
-    cairo_surface_destroy (image);
-
-    return pattern;
-}
-
-static cairo_status_t
-_draw_check (cairo_surface_t *surface, int width, int height)
-{
-    cairo_t *cr;
-    cairo_status_t status;
-
-    cr = cairo_create (surface);
-    cairo_set_source_rgb (cr, 0.75, 0.75, 0.75); /* light gray */
-    cairo_paint (cr);
-
-    cairo_set_source_rgb (cr, 0.25, 0.25, 0.25); /* dark gray */
-    cairo_rectangle (cr, width / 2,  0, width / 2, height / 2);
-    cairo_rectangle (cr, 0, height / 2, width / 2, height / 2);
-    cairo_fill (cr);
-
-    status = cairo_status (cr);
-
-    cairo_destroy (cr);
-
-    return status;
-}
-
-cairo_status_t
-cairo_test_paint_checkered (cairo_t *cr)
-{
-    cairo_status_t status;
-    cairo_surface_t *check;
-
-    check = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 12, 12);
-    status = _draw_check (check, 12, 12);
-    if (status)
-	return status;
-
-    cairo_save (cr);
-    cairo_set_source_surface (cr, check, 0, 0);
-    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
-    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
-    cairo_paint (cr);
-    cairo_restore (cr);
-
-    cairo_surface_destroy (check);
-
-    return CAIRO_STATUS_SUCCESS;
-}
diff --git a/boilerplate/cairo-test.h b/boilerplate/cairo-test.h
deleted file mode 100644
index 75eadb4..0000000
--- a/boilerplate/cairo-test.h
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright © 2004 Red Hat, Inc.
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without
- * fee, provided that the above copyright notice appear in all copies
- * and that both that copyright notice and this permission notice
- * appear in supporting documentation, and that the name of
- * Red Hat, Inc. not be used in advertising or publicity pertaining to
- * distribution of the software without specific, written prior
- * permission. Red Hat, Inc. makes no representations about the
- * suitability of this software for any purpose.  It is provided "as
- * is" without express or implied warranty.
- *
- * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
- * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
- * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
- * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
- * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Author: Carl D. Worth <cworth at cworth.org>
- */
-
-#ifndef _CAIRO_TEST_H_
-#define _CAIRO_TEST_H_
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h>
-#include <math.h>
-#include <cairo.h>
-
-#define CAIRO_BOILERPLATE_LOG(...) cairo_test_log (__VA_ARGS__)
-#include "cairo-boilerplate.h"
-
-CAIRO_BEGIN_DECLS
-
-#if   HAVE_STDINT_H
-# include <stdint.h>
-#elif HAVE_INTTYPES_H
-# include <inttypes.h>
-#elif HAVE_SYS_INT_TYPES_H
-# include <sys/int_types.h>
-#elif defined(_MSC_VER)
-typedef __int8 int8_t;
-typedef unsigned __int8 uint8_t;
-typedef __int16 int16_t;
-typedef unsigned __int16 uint16_t;
-typedef __int32 int32_t;
-typedef unsigned __int32 uint32_t;
-typedef __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-# ifndef HAVE_UINT64_T
-#  define HAVE_UINT64_T 1
-# endif
-#else
-#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, \etc.)
-#endif
-
-typedef enum cairo_test_status {
-    CAIRO_TEST_SUCCESS = 0,
-    CAIRO_TEST_FAILURE,
-    CAIRO_TEST_UNTESTED,
-    CAIRO_TEST_CRASHED
-} cairo_test_status_t;
-
-typedef cairo_test_status_t  (cairo_test_draw_function_t) (cairo_t *cr, int width, int height);
-
-typedef struct _cairo_test {
-    const char *name;
-    const char *description;
-    int width;
-    int height;
-    cairo_test_draw_function_t *draw;
-} cairo_test_t;
-
-/* The standard test interface which works by examining result image.
- *
- * cairo_test() accepts a test struct which will be called once for
- * each testable backend. The following checks will be performed for
- * each backend:
- *
- * 1) If draw() does not return CAIRO_TEST_SUCCESS then this backend
- *    fails.
- *
- * 2) Otherwise, if cairo_status(cr) indicates an error then this
- *    backend fails.
- *
- * 3) Otherwise, if the image size is 0, then this backend passes.
- *
- * 4) Otherwise, if every channel of every pixel exactly matches the
- *    reference image then this backend passes. If not, this backend
- *    fails.
- *
- * The overall test result is PASS if and only if there is at least
- * one backend that is tested and if all tested backend pass according
- * to the four criteria above.
- */
-cairo_test_status_t
-cairo_test (cairo_test_t *test);
-
-/* cairo_test_init() and cairo_test_log() exist to help in writing
- * tests for which cairo_test() is not appropriate for one reason or
- * another. For example, some tests might not be doing any drawing at
- * all, or may need to create their own cairo_t rather than be handed
- * one by cairo_test.
- */
-
-/* Initialize test-specific resources, (log files, etc.) */
-void
-cairo_test_init (const char *test_name);
-
-/* Print a message to the log file, ala printf. */
-void
-cairo_test_log (const char *fmt, ...) CAIRO_PRINTF_FORMAT(1, 2);
-
-/* Helper functions that take care of finding source images even when
- * building in a non-srcdir manner, (ie. the tests will be run in a
- * directory that is different from the one where the source image
- * exists). */
-cairo_surface_t *
-cairo_test_create_surface_from_png (const char *filename);
-
-cairo_pattern_t *
-cairo_test_create_pattern_from_png (const char *filename);
-
-cairo_status_t
-cairo_test_paint_checkered (cairo_t *cr);
-
-CAIRO_END_DECLS
-
-#endif
diff --git a/boilerplate/xmalloc.c b/boilerplate/xmalloc.c
index e98541c..10d1cef 100644
--- a/boilerplate/xmalloc.c
+++ b/boilerplate/xmalloc.c
@@ -26,7 +26,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "cairo-test.h"
+#include "cairo-boilerplate.h"
 #include "xmalloc.h"
 
 void *
@@ -36,7 +36,7 @@ xmalloc (size_t size)
 
     buf = malloc (size);
     if (!buf) {
-	cairo_test_log ("Error: Out of memory. Exiting.\n");
+	CAIRO_BOILERPLATE_LOG ("Error: Out of memory. Exiting.\n");
 	exit (1);
     }
 
@@ -50,7 +50,7 @@ xcalloc (size_t nmemb, size_t size)
 
     buf = calloc (nmemb, size);
     if (!buf) {
-	cairo_test_log ("Error: Out of memory. Exiting\n");
+	CAIRO_BOILERPLATE_LOG ("Error: Out of memory. Exiting\n");
 	exit (1);
     }
 
diff --git a/test/Makefile.am b/test/Makefile.am
index 54834c9..f595dcd 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -375,12 +375,23 @@ SUPPORT_PROGS =
 # problem.
 INCLUDES =					\
 	-D_GNU_SOURCE				\
+	-I$(srcdir)				\
 	-I$(top_srcdir)/boilerplate		\
 	-I$(top_srcdir)/pixman/src		\
 	-I$(top_srcdir)/src			\
 	$(CAIRO_CFLAGS)
 
-LDADD = $(top_builddir)/boilerplate/libcairotest.la $(top_builddir)/src/libcairo.la
+EXTRA_LTLIBRARIES = libcairotest.la
+
+libcairotest_la_SOURCES =\
+	buffer-diff.c	\
+	buffer-diff.h	\
+	cairo-test.c	\
+	cairo-test.h
+
+LDADD = libcairotest.la \
+        $(top_builddir)/boilerplate/libcairoboilerplate.la \
+	$(top_builddir)/src/libcairo.la
 
 if CAIRO_CAN_TEST_GLITZ_AGL_SURFACE
 LDADD += $(GLITZ_AGL_LIBS)
diff --git a/test/buffer-diff.c b/test/buffer-diff.c
new file mode 100644
index 0000000..b59b5e8
--- /dev/null
+++ b/test/buffer-diff.c
@@ -0,0 +1,306 @@
+/* imagediff - Compare two images
+ *
+ * Copyright © 2004 Richard D. Worth
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Richard Worth
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission.
+ * Richard Worth makes no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * RICHARD WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL RICHARD WORTH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Richard D. Worth <richard at theworths.org> */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#include <pixman.h>
+
+#include "cairo-test.h"
+
+#include "buffer-diff.h"
+#include "xmalloc.h"
+
+static void
+xunlink (const char *pathname)
+{
+    if (unlink (pathname) < 0 && errno != ENOENT) {
+	cairo_test_log ("  Error: Cannot remove %s: %s\n",
+			pathname, strerror (errno));
+	exit (1);
+    }
+}
+
+/* This function should be rewritten to compare all formats supported by
+ * cairo_format_t instead of taking a mask as a parameter.
+ */
+static int
+buffer_diff_core (unsigned char *_buf_a,
+		  unsigned char *_buf_b,
+		  unsigned char *_buf_diff,
+		  int		width,
+		  int		height,
+		  int		stride_a,
+		  int		stride_b,
+		  int		stride_diff,
+		  pixman_bits_t mask)
+{
+    int x, y;
+    pixman_bits_t *row_a, *row_b, *row;
+    int pixels_changed = 0;
+    pixman_bits_t *buf_a = (pixman_bits_t*)_buf_a;
+    pixman_bits_t *buf_b = (pixman_bits_t*)_buf_b;
+    pixman_bits_t *buf_diff = (pixman_bits_t*)_buf_diff;
+
+    stride_a /= sizeof(pixman_bits_t);
+    stride_b /= sizeof(pixman_bits_t);
+    stride_diff /= sizeof(pixman_bits_t);
+    for (y = 0; y < height; y++)
+    {
+	row_a = buf_a + y * stride_a;
+	row_b = buf_b + y * stride_b;
+	row = buf_diff + y * stride_diff;
+	for (x = 0; x < width; x++)
+	{
+	    /* check if the pixels are the same */
+	    if ((row_a[x] & mask) != (row_b[x] & mask)) {
+		int channel;
+		pixman_bits_t diff_pixel = 0;
+
+		/* calculate a difference value for all 4 channels */
+		for (channel = 0; channel < 4; channel++) {
+		    int value_a = (row_a[x] >> (channel*8)) & 0xff;
+		    int value_b = (row_b[x] >> (channel*8)) & 0xff;
+		    unsigned int diff;
+		    diff = abs (value_a - value_b);
+		    diff *= 4;  /* emphasize */
+		    if (diff)
+		        diff += 128; /* make sure it's visible */
+		    if (diff > 255)
+		        diff = 255;
+		    diff_pixel |= diff << (channel*8);
+		}
+
+		pixels_changed++;
+		row[x] = diff_pixel;
+	    } else {
+		row[x] = 0;
+	    }
+	    row[x] |= 0xff000000; /* Set ALPHA to 100% (opaque) */
+	}
+    }
+
+    return pixels_changed;
+}
+
+int
+buffer_diff (unsigned char *buf_a,
+	     unsigned char *buf_b,
+	     unsigned char *buf_diff,
+	     int	   width,
+	     int	   height,
+	     int	   stride_a,
+	     int	   stride_b,
+	     int	   stride_diff)
+{
+    return buffer_diff_core(buf_a, buf_b, buf_diff,
+			    width, height, stride_a, stride_b, stride_diff, 0xffffffff);
+}
+
+int
+buffer_diff_noalpha (unsigned char *buf_a,
+		     unsigned char *buf_b,
+		     unsigned char *buf_diff,
+		     int	   width,
+		     int	   height,
+		     int	   stride_a,
+		     int	   stride_b,
+		     int	   stride_diff)
+{
+    return buffer_diff_core(buf_a, buf_b, buf_diff,
+			    width, height, stride_a, stride_b, stride_diff, 0x00ffffff);
+}
+
+static cairo_status_t
+stdio_write_func (void *closure, const unsigned char *data, unsigned int length)
+{
+    FILE *file = closure;
+
+    if (fwrite (data, 1, length, file) != length)
+	return CAIRO_STATUS_WRITE_ERROR;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Flatten an ARGB surface by blending it over white. The resulting
+ * surface, (still in ARGB32 format, but with only alpha==1.0
+ * everywhere) is returned in the same surface pointer.
+ *
+ * The original surface will be destroyed.
+ *
+ * The (x,y) value specify an origin of interest for the original
+ * image. The flattened image will be generated only from the box
+ * extending from (x,y) to (width,height).
+ */
+static void
+flatten_surface (cairo_surface_t **surface, int x, int y)
+{
+    cairo_surface_t *flat;
+    cairo_t *cr;
+
+    flat = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+				       cairo_image_surface_get_width (*surface) - x,
+				       cairo_image_surface_get_height (*surface) - y);
+    cairo_surface_set_device_offset (flat, -x, -y);
+
+    cr = cairo_create (flat);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_surface (cr, *surface, 0, 0);
+    cairo_paint (cr);
+    cairo_destroy (cr);
+
+    cairo_surface_destroy (*surface);
+    *surface = flat;
+}
+
+/* Image comparison code courtesy of Richard Worth <richard at theworths.org>
+ * Returns number of pixels changed, (or -1 on error).
+ * Also saves a "diff" image intended to visually show where the
+ * images differ.
+ */
+static int
+image_diff_core (const char *filename_a,
+		 const char *filename_b,
+		 const char *filename_diff,
+		 int		ax,
+		 int		ay,
+		 int		bx,
+		 int		by,
+		 cairo_bool_t	flatten)
+{
+    int pixels_changed;
+    unsigned int width_a, height_a, stride_a;
+    unsigned int width_b, height_b, stride_b;
+    cairo_surface_t *surface_a, *surface_b, *surface_diff;
+
+    surface_a = cairo_image_surface_create_from_png (filename_a);
+    if (cairo_surface_status (surface_a))
+	return -1;
+
+    surface_b = cairo_image_surface_create_from_png (filename_b);
+    if (cairo_surface_status (surface_b)) {
+	cairo_surface_destroy (surface_a);
+	return -1;
+    }
+
+    width_a = cairo_image_surface_get_width (surface_a) - ax;
+    height_a = cairo_image_surface_get_height (surface_a) - ay;
+    width_b = cairo_image_surface_get_width (surface_b) - bx;
+    height_b = cairo_image_surface_get_height (surface_b) - by;
+
+    if (width_a  != width_b  ||
+	height_a != height_b)
+    {
+	cairo_test_log ("Error: Image size mismatch: (%dx%d) vs. (%dx%d)\n"
+			"       for %s vs. %s\n",
+			width_a, height_a,
+			width_b, height_b,
+			filename_a, filename_b);
+	cairo_surface_destroy (surface_a);
+	cairo_surface_destroy (surface_b);
+	return -1;
+    }
+
+    if (flatten) {
+	flatten_surface (&surface_a, ax, ay);
+	flatten_surface (&surface_b, bx, by);
+	ax = ay = bx = by = 0;
+    }
+
+    surface_diff = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+					       width_a, height_a);
+
+    stride_a = cairo_image_surface_get_stride (surface_a);
+    stride_b = cairo_image_surface_get_stride (surface_b);
+
+    pixels_changed = buffer_diff (cairo_image_surface_get_data (surface_a)
+				  + (ay * stride_a) + ax * 4,
+                                  cairo_image_surface_get_data (surface_b)
+				  + (by * stride_b) + by * 4,
+                                  cairo_image_surface_get_data (surface_diff),
+                                  width_a, height_a,
+				  stride_a, stride_b,
+				  cairo_image_surface_get_stride (surface_diff));
+
+    if (pixels_changed) {
+	FILE *png_file;
+
+	if (filename_diff)
+	    png_file = fopen (filename_diff, "wb");
+	else
+	    png_file = stdout;
+
+	cairo_surface_write_to_png_stream (surface_diff, stdio_write_func, png_file);
+
+	if (png_file != stdout)
+	    fclose (png_file);
+    } else {
+	if (filename_diff)
+	    xunlink (filename_diff);
+    }
+
+    cairo_surface_destroy (surface_a);
+    cairo_surface_destroy (surface_b);
+    cairo_surface_destroy (surface_diff);
+
+    return pixels_changed;
+}
+
+int
+image_diff (const char *filename_a,
+	    const char *filename_b,
+	    const char *filename_diff,
+	    int		ax,
+	    int		ay,
+	    int		bx,
+	    int		by)
+{
+    return image_diff_core (filename_a, filename_b, filename_diff,
+			    ax, ay, bx, by,
+			    FALSE);
+}
+
+int
+image_diff_flattened (const char *filename_a,
+		      const char *filename_b,
+		      const char *filename_diff,
+		      int	  ax,
+		      int	  ay,
+		      int	  bx,
+		      int	  by)
+{
+    return image_diff_core (filename_a, filename_b, filename_diff,
+			    ax, ay, bx, by,
+			    TRUE);
+}
diff --git a/test/buffer-diff.h b/test/buffer-diff.h
new file mode 100644
index 0000000..2be2b39
--- /dev/null
+++ b/test/buffer-diff.h
@@ -0,0 +1,80 @@
+/* imagediff - Compare two images
+ *
+ * Copyright © 2004 Richard D. Worth
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Richard Worth
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission.
+ * Richard Worth makes no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * RICHARD WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL RICHARD WORTH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Richard D. Worth <richard at theworths.org> */
+
+#ifndef BUFFER_DIFF_H
+#define BUFFER_DIFF_H
+
+/* Returns number of pixels changed.
+ * Also fills in a "diff" buffer intended to visually show where the
+ * images differ.
+ */
+int
+buffer_diff (unsigned char *buf_a,
+	     unsigned char *buf_b,
+	     unsigned char *buf_diff,
+	     int	    width,
+	     int	    height,
+	     int	    stride_a,
+	     int	    stride_b,
+	     int	    stride_diff);
+
+/* Returns number of pixels changed ignoring the alpha channel.
+ * Also fills in a "diff" buffer intended to visually show where the
+ * images differ.
+ */
+int
+buffer_diff_noalpha (unsigned char *buf_a,
+		     unsigned char *buf_b,
+		     unsigned char *buf_diff,
+		     int	    width,
+		     int	    height,
+		     int	    stride_a,
+		     int	    stride_b,
+		     int	    stride_diff);
+
+/* Returns number of pixels changed, (or -1 on error).
+ * Also saves a "diff" image intended to visually show where the
+ * images differ.
+ */
+int
+image_diff (const char *filename_a,
+	    const char *filename_b,
+	    const char *filename_diff,
+	    int		ax,
+	    int		ay,
+	    int		bx,
+	    int		by);
+
+/* Like image_diff, but blending the contents of b over white first. */
+int
+image_diff_flattened (const char *filename_a,
+		      const char *filename_b,
+		      const char *filename_diff,
+                      int         ax,
+                      int         ay,
+                      int         bx,
+                      int         by);
+
+#endif
diff --git a/test/cairo-test.c b/test/cairo-test.c
new file mode 100644
index 0000000..0d915a5
--- /dev/null
+++ b/test/cairo-test.c
@@ -0,0 +1,683 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth at cworth.org>
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <setjmp.h>
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#if HAVE_FCFINI
+#include <fontconfig/fontconfig.h>
+#endif
+
+#include "cairo-test.h"
+
+#include "buffer-diff.h"
+#include "xmalloc.h"
+
+/* This is copied from cairoint.h. That makes it painful to keep in
+ * sync, but the slim stuff makes cairoint.h "hard" to include when
+ * not actually building the cairo library itself. Fortunately, since
+ * we're checking all these values, we do have a safeguard for keeping
+ * them in sync.
+ */
+typedef enum cairo_internal_surface_type {
+    CAIRO_INTERNAL_SURFACE_TYPE_META = 0x1000,
+    CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
+    CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
+    CAIRO_INTERNAL_SURFACE_TYPE_TEST_META,
+    CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
+    CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED
+} cairo_internal_surface_type_t;
+
+#ifdef _MSC_VER
+#define vsnprintf _vsnprintf
+#define access _access
+#define F_OK 0
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE !FALSE
+#endif
+
+static void
+xunlink (const char *pathname);
+
+static const char *fail_face = "", *normal_face = "";
+
+#define CAIRO_TEST_LOG_SUFFIX ".log"
+#define CAIRO_TEST_PNG_SUFFIX "-out.png"
+#define CAIRO_TEST_REF_SUFFIX "-ref.png"
+#define CAIRO_TEST_DIFF_SUFFIX "-diff.png"
+
+#define NUM_DEVICE_OFFSETS 2
+
+/* Static data is messy, but we're coding for tests here, not a
+ * general-purpose library, and it keeps the tests cleaner to avoid a
+ * context object there, (though not a whole lot). */
+FILE *cairo_test_log_file = NULL;
+const char *srcdir;
+
+/* Used to catch crashes in a test, such that we report it as such and
+ * continue testing, although one crasher may already have corrupted memory in
+ * an nonrecoverable fashion. */
+jmp_buf jmpbuf;
+
+void
+cairo_test_init (const char *test_name)
+{
+    char *log_name;
+
+    xasprintf (&log_name, "%s%s", test_name, CAIRO_TEST_LOG_SUFFIX);
+    xunlink (log_name);
+
+    cairo_test_log_file = fopen (log_name, "a");
+    if (cairo_test_log_file == NULL) {
+	fprintf (stderr, "Error opening log file: %s\n", log_name);
+	cairo_test_log_file = stderr;
+    }
+    free (log_name);
+
+    printf ("\nTESTING %s\n", test_name);
+}
+
+void
+cairo_test_log (const char *fmt, ...)
+{
+    va_list va;
+    FILE *file = cairo_test_log_file ? cairo_test_log_file : stderr;
+
+    va_start (va, fmt);
+    vfprintf (file, fmt, va);
+    va_end (va);
+}
+
+static void
+xunlink (const char *pathname)
+{
+    if (unlink (pathname) < 0 && errno != ENOENT) {
+	cairo_test_log ("Error: Cannot remove %s: %s\n",
+			pathname, strerror (errno));
+	exit (1);
+    }
+}
+
+static char *
+cairo_ref_name_for_test_target_format (const char *test_name,
+				       const char *target_name,
+				       const char *format)
+{
+    char *ref_name = NULL;
+
+    /* First look for a target/format-specific reference image. */
+    xasprintf (&ref_name, "%s/%s-%s-%s%s", srcdir,
+	       test_name,
+	       target_name,
+	       format,
+	       CAIRO_TEST_REF_SUFFIX);
+    if (access (ref_name, F_OK) != 0)
+	free (ref_name);
+    else
+	goto done;
+
+    /* Next, look for taget-specifc reference image. */
+    xasprintf (&ref_name, "%s/%s-%s%s", srcdir,
+	       test_name,
+	       target_name,
+	       CAIRO_TEST_REF_SUFFIX);
+    if (access (ref_name, F_OK) != 0)
+	free (ref_name);
+    else
+	goto done;
+
+    /* Next, look for format-specifc reference image. */
+    xasprintf (&ref_name, "%s/%s-%s%s", srcdir,
+	       test_name,
+	       format,
+	       CAIRO_TEST_REF_SUFFIX);
+    if (access (ref_name, F_OK) != 0)
+	free (ref_name);
+    else
+	goto done;
+
+    /* Finally, look for the standard reference image. */
+    xasprintf (&ref_name, "%s/%s%s", srcdir,
+	       test_name,
+	       CAIRO_TEST_REF_SUFFIX);
+    if (access (ref_name, F_OK) != 0)
+	free (ref_name);
+    else
+	goto done;
+
+    ref_name = NULL;
+
+done:
+    return ref_name;
+}
+
+static cairo_test_status_t
+cairo_test_for_target (cairo_test_t		 *test,
+		       cairo_test_target_t	 *target,
+		       int			  dev_offset)
+{
+    cairo_test_status_t status;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    char *png_name, *ref_name, *diff_name, *offset_str;
+    cairo_test_status_t ret;
+    cairo_content_t expected_content;
+    cairo_font_options_t *font_options;
+    const char *format;
+
+    /* Get the strings ready that we'll need. */
+    format = _cairo_test_content_name (target->content);
+    if (dev_offset)
+	xasprintf (&offset_str, "-%d", dev_offset);
+    else
+	offset_str = strdup("");
+
+    xasprintf (&png_name, "%s-%s-%s%s%s",
+	       test->name,
+	       target->name,
+	       format,
+	       offset_str, CAIRO_TEST_PNG_SUFFIX);
+    ref_name = cairo_ref_name_for_test_target_format (test->name, target->name, format);
+    xasprintf (&diff_name, "%s-%s-%s%s%s",
+	       test->name,
+	       target->name,
+	       format,
+	       offset_str, CAIRO_TEST_DIFF_SUFFIX);
+
+    /* Run the actual drawing code. */
+    if (test->width && test->height) {
+	test->width += dev_offset;
+	test->height += dev_offset;
+    }
+
+    surface = (target->create_target_surface) (test->name,
+					       target->content,
+					       test->width,
+					       test->height,
+					       &target->closure);
+
+    if (test->width && test->height) {
+	test->width -= dev_offset;
+	test->height -= dev_offset;;
+    }
+
+    if (surface == NULL) {
+	cairo_test_log ("Error: Failed to set %s target\n", target->name);
+	ret = CAIRO_TEST_UNTESTED;
+	goto UNWIND_STRINGS;
+    }
+
+    /* Check that we created a surface of the expected type. */
+    if (cairo_surface_get_type (surface) != target->expected_type) {
+	cairo_test_log ("Error: Created surface is of type %d (expected %d)\n",
+			cairo_surface_get_type (surface), target->expected_type);
+	ret = CAIRO_TEST_FAILURE;
+	goto UNWIND_SURFACE;
+    }
+
+    /* Check that we created a surface of the expected content,
+     * (ignore the articifical
+     * CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED value).
+     */
+    expected_content = target->content;
+    if (expected_content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
+	expected_content = CAIRO_CONTENT_COLOR_ALPHA;
+
+    if (cairo_surface_get_content (surface) != expected_content) {
+	cairo_test_log ("Error: Created surface has content %d (expected %d)\n",
+			cairo_surface_get_content (surface), expected_content);
+	ret = CAIRO_TEST_FAILURE;
+	goto UNWIND_SURFACE;
+    }
+
+    cairo_surface_set_device_offset (surface, dev_offset, dev_offset);
+
+    cr = cairo_create (surface);
+
+    /* Clear to transparent (or black) depending on whether the target
+     * surface supports alpha. */
+    cairo_save (cr);
+    cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    /* Set all components of font_options to avoid backend differences
+     * and reduce number of needed reference images. */
+    font_options = cairo_font_options_create ();
+    cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
+    cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_ON);
+    cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_GRAY);
+    cairo_set_font_options (cr, font_options);
+    cairo_font_options_destroy (font_options);
+
+    status = (test->draw) (cr, test->width, test->height);
+
+    /* Then, check all the different ways it could fail. */
+    if (status) {
+	cairo_test_log ("Error: Function under test failed\n");
+	ret = status;
+	goto UNWIND_CAIRO;
+    }
+
+    cairo_show_page (cr);
+
+    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
+	cairo_test_log ("Error: Function under test left cairo status in an error state: %s\n",
+			cairo_status_to_string (cairo_status (cr)));
+	ret = CAIRO_TEST_FAILURE;
+	goto UNWIND_CAIRO;
+    }
+
+    /* Skip image check for tests with no image (width,height == 0,0) */
+    if (test->width != 0 && test->height != 0) {
+	int pixels_changed;
+	xunlink (png_name);
+	(target->write_to_png) (surface, png_name);
+
+	if (!ref_name) {
+	    cairo_test_log ("Error: Cannot find reference image for %s/%s-%s-%s%s\n",srcdir,
+			    test->name,
+			    target->name,
+			    format,
+			    CAIRO_TEST_REF_SUFFIX);
+	    ret = CAIRO_TEST_FAILURE;
+	    goto UNWIND_CAIRO;
+	}
+
+	if (target->content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
+	    pixels_changed = image_diff_flattened (png_name, ref_name, diff_name, dev_offset, dev_offset, 0, 0);
+	else
+	    pixels_changed = image_diff (png_name, ref_name, diff_name, dev_offset, dev_offset, 0, 0);
+	if (pixels_changed) {
+	    if (pixels_changed > 0)
+		cairo_test_log ("Error: %d pixels differ from reference image %s\n",
+				pixels_changed, ref_name);
+	    ret = CAIRO_TEST_FAILURE;
+	    goto UNWIND_CAIRO;
+	}
+    }
+
+    ret = CAIRO_TEST_SUCCESS;
+
+UNWIND_CAIRO:
+    cairo_destroy (cr);
+UNWIND_SURFACE:
+    cairo_surface_destroy (surface);
+
+    cairo_debug_reset_static_data ();
+
+    if (target->cleanup_target)
+	target->cleanup_target (target->closure);
+
+UNWIND_STRINGS:
+    if (png_name)
+      free (png_name);
+    if (ref_name)
+      free (ref_name);
+    if (diff_name)
+      free (diff_name);
+    if (offset_str)
+      free (offset_str);
+
+    return ret;
+}
+
+#ifdef HAVE_SIGNAL_H
+static void
+segfault_handler (int signal)
+{
+    longjmp (jmpbuf, signal);
+}
+#endif
+
+static cairo_test_status_t
+cairo_test_expecting (cairo_test_t *test,
+		      cairo_test_status_t expectation)
+{
+    /* we use volatile here to make sure values are not clobbered
+     * by longjmp */
+    volatile size_t i, j, num_targets;
+    volatile cairo_bool_t limited_targets = FALSE, print_fail_on_stdout = TRUE;
+    const char *tname;
+#ifdef HAVE_SIGNAL_H
+    void (*old_segfault_handler)(int);
+#endif
+    volatile cairo_test_status_t status, ret;
+    cairo_test_target_t ** volatile targets_to_test;
+
+#ifdef HAVE_UNISTD_H
+    if (isatty (2)) {
+	fail_face = "\033[41m\033[37m\033[1m";
+	normal_face = "\033[m";
+	if (isatty (1))
+	    print_fail_on_stdout = FALSE;
+    }
+#endif
+
+    srcdir = getenv ("srcdir");
+    if (!srcdir)
+	srcdir = ".";
+
+    cairo_test_init (test->name);
+    printf ("%s\n", test->description);
+
+    if (expectation == CAIRO_TEST_FAILURE)
+    printf ("Expecting failure\n");
+
+    if ((tname = getenv ("CAIRO_TEST_TARGET")) != NULL && *tname) {
+
+	limited_targets = TRUE;
+
+	num_targets = 0;
+	targets_to_test = NULL;
+
+	while (*tname) {
+	    int found = 0;
+	    const char *end = strpbrk (tname, " \t\r\n;:,");
+	    if (!end)
+	        end = tname + strlen (tname);
+
+	    for (i = 0; targets[i].name != NULL; i++) {
+		if (0 == strncmp (targets[i].name, tname, end - tname) &&
+		    !isalnum (targets[i].name[end - tname])) {
+		    /* realloc isn't exactly the best thing here, but meh. */
+		    targets_to_test = realloc (targets_to_test, sizeof(cairo_test_target_t *) * (num_targets+1));
+		    targets_to_test[num_targets++] = &targets[i];
+		    found = 1;
+		}
+	    }
+
+	    if (!found) {
+		fprintf (stderr, "Cannot test target '%.*s'\n", (int)(end - tname), tname);
+		exit(-1);
+	    }
+
+	    if (*end)
+	      end++;
+	    tname = end;
+	}
+    } else {
+	num_targets = 0;
+	for (i = 0; targets[i].name != NULL; i++)
+	    num_targets++;
+	targets_to_test = malloc (sizeof(cairo_test_target_t*) * num_targets);
+	for (i = 0; i < num_targets; i++) {
+	    targets_to_test[i] = &targets[i];
+	}
+    }
+
+    /* The intended logic here is that we return overall SUCCESS
+     * iff. there is at least one tested backend and that all tested
+     * backends return SUCCESS, OR, there's no backend to test at all.
+     * In other words:
+     *
+     *  if      no backend to test
+     *          -> SUCCESS
+     *	else if any backend not SUCCESS
+     *		-> FAILURE
+     *	else if all backends UNTESTED
+     *		-> FAILURE
+     *	else    (== some backend SUCCESS)
+     *		-> SUCCESS
+     */
+    ret = CAIRO_TEST_UNTESTED;
+    for (i = 0; i < num_targets; i++) {
+	for (j = 0; j < NUM_DEVICE_OFFSETS; j++) {
+	    cairo_test_target_t * volatile target = targets_to_test[i];
+	    volatile int dev_offset = j * 25;
+
+	    cairo_test_log ("Testing %s with %s target (dev offset %d)\n", test->name, target->name, dev_offset);
+	    printf ("%s-%s-%s [%d]:\t", test->name, target->name,
+		    _cairo_test_content_name (target->content),
+		    dev_offset);
+
+#ifdef HAVE_SIGNAL_H
+	    /* Set up a checkpoint to get back to in case of segfaults. */
+	    old_segfault_handler = signal (SIGSEGV, segfault_handler);
+	    if (0 == setjmp (jmpbuf))
+#endif
+		status = cairo_test_for_target (test, target, dev_offset);
+#ifdef HAVE_SIGNAL_H
+	    else
+	        status = CAIRO_TEST_CRASHED;
+	    signal (SIGSEGV, old_segfault_handler);
+#endif
+
+	    cairo_test_log ("TEST: %s TARGET: %s FORMAT: %s OFFSET: %d RESULT: ",
+			    test->name, target->name,
+			    _cairo_test_content_name (target->content),
+			    dev_offset);
+
+	    switch (status) {
+	    case CAIRO_TEST_SUCCESS:
+		printf ("PASS\n");
+		cairo_test_log ("PASS\n");
+		if (ret == CAIRO_TEST_UNTESTED)
+		    ret = CAIRO_TEST_SUCCESS;
+		break;
+	    case CAIRO_TEST_UNTESTED:
+		printf ("UNTESTED\n");
+		cairo_test_log ("UNTESTED\n");
+		break;
+	    case CAIRO_TEST_CRASHED:
+		if (print_fail_on_stdout) {
+		    printf ("!!!CRASHED!!!\n");
+		} else {
+		    /* eat the test name */
+		    printf ("\r");
+		    fflush (stdout);
+		}
+		cairo_test_log ("CRASHED\n");
+		fprintf (stderr, "%s-%s-%s [%d]:\t%s!!!CRASHED!!!%s\n",
+			 test->name, target->name,
+			 _cairo_test_content_name (target->content), dev_offset,
+			 fail_face, normal_face);
+		ret = CAIRO_TEST_FAILURE;
+		break;
+	    default:
+	    case CAIRO_TEST_FAILURE:
+		if (expectation == CAIRO_TEST_FAILURE) {
+		    printf ("XFAIL\n");
+		    cairo_test_log ("XFAIL\n");
+		} else {
+		    if (print_fail_on_stdout) {
+			printf ("FAIL\n");
+		    } else {
+			/* eat the test name */
+			printf ("\r");
+			fflush (stdout);
+		    }
+		    fprintf (stderr, "%s-%s-%s [%d]:\t%sFAIL%s\n",
+			     test->name, target->name,
+			     _cairo_test_content_name (target->content), dev_offset,
+			     fail_face, normal_face);
+		    cairo_test_log ("FAIL\n");
+		}
+		ret = status;
+		break;
+	    }
+	}
+    }
+
+    if (ret != CAIRO_TEST_SUCCESS)
+        printf ("Check %s%s out for more information.\n", test->name, CAIRO_TEST_LOG_SUFFIX);
+
+    /* if no target was requested for test, succeed, otherwise if all
+     * were untested, fail. */
+    if (ret == CAIRO_TEST_UNTESTED)
+	ret = num_targets ? CAIRO_TEST_FAILURE : CAIRO_TEST_SUCCESS;
+
+    /* if targets are limited using CAIRO_TEST_TARGET, and expecting failure,
+     * make it fail, such that we can pass test suite by limiting backends
+     * to test without triggering XPASS failures. */
+    if (limited_targets && expectation == CAIRO_TEST_FAILURE && ret == CAIRO_TEST_SUCCESS) {
+	printf ("All tested backends passed, but tested targets are manually limited\n"
+		"and the test suite expects this test to fail for at least one target.\n"
+		"Intentionally failing the test, to not fail the suite.\n");
+	ret = CAIRO_TEST_FAILURE;
+    }
+
+    fclose (cairo_test_log_file);
+
+    free (targets_to_test);
+
+#if HAVE_FCFINI
+    FcFini ();
+#endif
+
+    return ret;
+}
+
+cairo_test_status_t
+cairo_test (cairo_test_t *test)
+{
+    cairo_test_status_t expectation = CAIRO_TEST_SUCCESS;
+    const char *xfails;
+
+    if ((xfails = getenv ("CAIRO_XFAIL_TESTS")) != NULL) {
+	while (*xfails) {
+	    const char *end = strpbrk (xfails, " \t\r\n;:,");
+	    if (!end)
+	        end = xfails + strlen (xfails);
+
+	    if (0 == strncmp (test->name, xfails, end - xfails) &&
+		'\0' == test->name[end - xfails]) {
+		expectation = CAIRO_TEST_FAILURE;
+		break;
+	    }
+
+	    if (*end)
+	      end++;
+	    xfails = end;
+	}
+    }
+
+    return cairo_test_expecting (test, expectation);
+}
+
+cairo_surface_t *
+cairo_test_create_surface_from_png (const char *filename)
+{
+    cairo_surface_t *image;
+    char *srcdir = getenv ("srcdir");
+
+    image = cairo_image_surface_create_from_png (filename);
+    if (cairo_surface_status(image)) {
+        /* expect not found when running with srcdir != builddir
+         * such as when 'make distcheck' is run
+         */
+	if (srcdir) {
+	    char *srcdir_filename;
+	    xasprintf (&srcdir_filename, "%s/%s", srcdir, filename);
+	    image = cairo_image_surface_create_from_png (srcdir_filename);
+	    free (srcdir_filename);
+	}
+	if (cairo_surface_status(image))
+	    return NULL;
+    }
+
+    return image;
+}
+
+cairo_pattern_t *
+cairo_test_create_pattern_from_png (const char *filename)
+{
+    cairo_surface_t *image;
+    cairo_pattern_t *pattern;
+
+    image = cairo_test_create_surface_from_png (filename);
+
+    pattern = cairo_pattern_create_for_surface (image);
+
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+
+    cairo_surface_destroy (image);
+
+    return pattern;
+}
+
+static cairo_status_t
+_draw_check (cairo_surface_t *surface, int width, int height)
+{
+    cairo_t *cr;
+    cairo_status_t status;
+
+    cr = cairo_create (surface);
+    cairo_set_source_rgb (cr, 0.75, 0.75, 0.75); /* light gray */
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0.25, 0.25, 0.25); /* dark gray */
+    cairo_rectangle (cr, width / 2,  0, width / 2, height / 2);
+    cairo_rectangle (cr, 0, height / 2, width / 2, height / 2);
+    cairo_fill (cr);
+
+    status = cairo_status (cr);
+
+    cairo_destroy (cr);
+
+    return status;
+}
+
+cairo_status_t
+cairo_test_paint_checkered (cairo_t *cr)
+{
+    cairo_status_t status;
+    cairo_surface_t *check;
+
+    check = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 12, 12);
+    status = _draw_check (check, 12, 12);
+    if (status)
+	return status;
+
+    cairo_save (cr);
+    cairo_set_source_surface (cr, check, 0, 0);
+    cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+    cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+    cairo_paint (cr);
+    cairo_restore (cr);
+
+    cairo_surface_destroy (check);
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/test/cairo-test.h b/test/cairo-test.h
new file mode 100644
index 0000000..75eadb4
--- /dev/null
+++ b/test/cairo-test.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth at cworth.org>
+ */
+
+#ifndef _CAIRO_TEST_H_
+#define _CAIRO_TEST_H_
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <cairo.h>
+
+#define CAIRO_BOILERPLATE_LOG(...) cairo_test_log (__VA_ARGS__)
+#include "cairo-boilerplate.h"
+
+CAIRO_BEGIN_DECLS
+
+#if   HAVE_STDINT_H
+# include <stdint.h>
+#elif HAVE_INTTYPES_H
+# include <inttypes.h>
+#elif HAVE_SYS_INT_TYPES_H
+# include <sys/int_types.h>
+#elif defined(_MSC_VER)
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+# ifndef HAVE_UINT64_T
+#  define HAVE_UINT64_T 1
+# endif
+#else
+#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, \etc.)
+#endif
+
+typedef enum cairo_test_status {
+    CAIRO_TEST_SUCCESS = 0,
+    CAIRO_TEST_FAILURE,
+    CAIRO_TEST_UNTESTED,
+    CAIRO_TEST_CRASHED
+} cairo_test_status_t;
+
+typedef cairo_test_status_t  (cairo_test_draw_function_t) (cairo_t *cr, int width, int height);
+
+typedef struct _cairo_test {
+    const char *name;
+    const char *description;
+    int width;
+    int height;
+    cairo_test_draw_function_t *draw;
+} cairo_test_t;
+
+/* The standard test interface which works by examining result image.
+ *
+ * cairo_test() accepts a test struct which will be called once for
+ * each testable backend. The following checks will be performed for
+ * each backend:
+ *
+ * 1) If draw() does not return CAIRO_TEST_SUCCESS then this backend
+ *    fails.
+ *
+ * 2) Otherwise, if cairo_status(cr) indicates an error then this
+ *    backend fails.
+ *
+ * 3) Otherwise, if the image size is 0, then this backend passes.
+ *
+ * 4) Otherwise, if every channel of every pixel exactly matches the
+ *    reference image then this backend passes. If not, this backend
+ *    fails.
+ *
+ * The overall test result is PASS if and only if there is at least
+ * one backend that is tested and if all tested backend pass according
+ * to the four criteria above.
+ */
+cairo_test_status_t
+cairo_test (cairo_test_t *test);
+
+/* cairo_test_init() and cairo_test_log() exist to help in writing
+ * tests for which cairo_test() is not appropriate for one reason or
+ * another. For example, some tests might not be doing any drawing at
+ * all, or may need to create their own cairo_t rather than be handed
+ * one by cairo_test.
+ */
+
+/* Initialize test-specific resources, (log files, etc.) */
+void
+cairo_test_init (const char *test_name);
+
+/* Print a message to the log file, ala printf. */
+void
+cairo_test_log (const char *fmt, ...) CAIRO_PRINTF_FORMAT(1, 2);
+
+/* Helper functions that take care of finding source images even when
+ * building in a non-srcdir manner, (ie. the tests will be run in a
+ * directory that is different from the one where the source image
+ * exists). */
+cairo_surface_t *
+cairo_test_create_surface_from_png (const char *filename);
+
+cairo_pattern_t *
+cairo_test_create_pattern_from_png (const char *filename);
+
+cairo_status_t
+cairo_test_paint_checkered (cairo_t *cr);
+
+CAIRO_END_DECLS
+
+#endif
diff-tree 95475218858792ccb20ac6ad28db22e233c783d7 (from 37ce7058906a9a8c7e80bf4ed59c17ec912087cf)
Author: Carl Worth <cworth at cworth.org>
Date:   Wed Aug 30 23:41:48 2006 -0700

    boilerplate: Remove custom read/write-png code in favor of using cairo surfaces
    
    Also combine image_diff and image_diff_flattened into a single function

diff --git a/boilerplate/Makefile.am b/boilerplate/Makefile.am
index b8ac3d3..2cc48ce 100644
--- a/boilerplate/Makefile.am
+++ b/boilerplate/Makefile.am
@@ -7,10 +7,6 @@ buffer-diff.c		\
 buffer-diff.h		\
 cairo-test.c		\
 cairo-test.h		\
-read-png.c		\
-read-png.h		\
-write-png.c		\
-write-png.h		\
 xmalloc.c		\
 xmalloc.h
 
diff --git a/boilerplate/buffer-diff.c b/boilerplate/buffer-diff.c
index ade0cc8..b59b5e8 100644
--- a/boilerplate/buffer-diff.c
+++ b/boilerplate/buffer-diff.c
@@ -39,8 +39,6 @@
 #include "cairo-test.h"
 
 #include "buffer-diff.h"
-#include "read-png.h"
-#include "write-png.h"
 #include "xmalloc.h"
 
 static void
@@ -143,71 +141,128 @@ buffer_diff_noalpha (unsigned char *buf_
 			    width, height, stride_a, stride_b, stride_diff, 0x00ffffff);
 }
 
+static cairo_status_t
+stdio_write_func (void *closure, const unsigned char *data, unsigned int length)
+{
+    FILE *file = closure;
+
+    if (fwrite (data, 1, length, file) != length)
+	return CAIRO_STATUS_WRITE_ERROR;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* Flatten an ARGB surface by blending it over white. The resulting
+ * surface, (still in ARGB32 format, but with only alpha==1.0
+ * everywhere) is returned in the same surface pointer.
+ *
+ * The original surface will be destroyed.
+ *
+ * The (x,y) value specify an origin of interest for the original
+ * image. The flattened image will be generated only from the box
+ * extending from (x,y) to (width,height).
+ */
+static void
+flatten_surface (cairo_surface_t **surface, int x, int y)
+{
+    cairo_surface_t *flat;
+    cairo_t *cr;
+
+    flat = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+				       cairo_image_surface_get_width (*surface) - x,
+				       cairo_image_surface_get_height (*surface) - y);
+    cairo_surface_set_device_offset (flat, -x, -y);
+
+    cr = cairo_create (flat);
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+    cairo_set_source_surface (cr, *surface, 0, 0);
+    cairo_paint (cr);
+    cairo_destroy (cr);
+
+    cairo_surface_destroy (*surface);
+    *surface = flat;
+}
+
 /* Image comparison code courtesy of Richard Worth <richard at theworths.org>
  * Returns number of pixels changed, (or -1 on error).
  * Also saves a "diff" image intended to visually show where the
  * images differ.
  */
-int
-image_diff (const char *filename_a,
-	    const char *filename_b,
-	    const char *filename_diff,
-	    int		ax,
-	    int		ay,
-	    int		bx,
-	    int		by)
+static int
+image_diff_core (const char *filename_a,
+		 const char *filename_b,
+		 const char *filename_diff,
+		 int		ax,
+		 int		ay,
+		 int		bx,
+		 int		by,
+		 cairo_bool_t	flatten)
 {
     int pixels_changed;
     unsigned int width_a, height_a, stride_a;
     unsigned int width_b, height_b, stride_b;
-    unsigned int stride_diff;
-    unsigned char *buf_a, *buf_b, *buf_diff;
-    read_png_status_t status;
+    cairo_surface_t *surface_a, *surface_b, *surface_diff;
 
-    status = read_png_argb32 (filename_a, &buf_a, &width_a, &height_a, &stride_a);
-    if (status)
+    surface_a = cairo_image_surface_create_from_png (filename_a);
+    if (cairo_surface_status (surface_a))
 	return -1;
 
-    status = read_png_argb32 (filename_b, &buf_b, &width_b, &height_b, &stride_b);
-    if (status) {
-	free (buf_a);
+    surface_b = cairo_image_surface_create_from_png (filename_b);
+    if (cairo_surface_status (surface_b)) {
+	cairo_surface_destroy (surface_a);
 	return -1;
     }
 
-    width_a -= ax;
-    height_a -= ay;
-    width_b -= bx;
-    height_b -= by;
+    width_a = cairo_image_surface_get_width (surface_a) - ax;
+    height_a = cairo_image_surface_get_height (surface_a) - ay;
+    width_b = cairo_image_surface_get_width (surface_b) - bx;
+    height_b = cairo_image_surface_get_height (surface_b) - by;
 
     if (width_a  != width_b  ||
 	height_a != height_b)
     {
-	cairo_test_log ("Error: Image size mismatch: (%dx%d@%d) vs. (%dx%d@%d)\n"
+	cairo_test_log ("Error: Image size mismatch: (%dx%d) vs. (%dx%d)\n"
 			"       for %s vs. %s\n",
-			width_a, height_a, stride_a,
-			width_b, height_b, stride_b,
+			width_a, height_a,
+			width_b, height_b,
 			filename_a, filename_b);
-	free (buf_a);
-	free (buf_b);
+	cairo_surface_destroy (surface_a);
+	cairo_surface_destroy (surface_b);
 	return -1;
     }
 
-    stride_diff = 4 * width_a;
-    buf_diff = xcalloc (stride_diff * height_a, 1);
+    if (flatten) {
+	flatten_surface (&surface_a, ax, ay);
+	flatten_surface (&surface_b, bx, by);
+	ax = ay = bx = by = 0;
+    }
+
+    surface_diff = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+					       width_a, height_a);
 
-    pixels_changed = buffer_diff (buf_a + (ay * stride_a) + ax * 4,
-                                  buf_b + (by * stride_b) + by * 4,
-                                  buf_diff,
+    stride_a = cairo_image_surface_get_stride (surface_a);
+    stride_b = cairo_image_surface_get_stride (surface_b);
+
+    pixels_changed = buffer_diff (cairo_image_surface_get_data (surface_a)
+				  + (ay * stride_a) + ax * 4,
+                                  cairo_image_surface_get_data (surface_b)
+				  + (by * stride_b) + by * 4,
+                                  cairo_image_surface_get_data (surface_diff),
                                   width_a, height_a,
-				  stride_a, stride_b, stride_diff);
+				  stride_a, stride_b,
+				  cairo_image_surface_get_stride (surface_diff));
 
     if (pixels_changed) {
 	FILE *png_file;
+
 	if (filename_diff)
 	    png_file = fopen (filename_diff, "wb");
 	else
 	    png_file = stdout;
-	write_png_argb32 (buf_diff, png_file, width_a, height_a, stride_diff);
+
+	cairo_surface_write_to_png_stream (surface_diff, stdio_write_func, png_file);
+
 	if (png_file != stdout)
 	    fclose (png_file);
     } else {
@@ -215,25 +270,27 @@ image_diff (const char *filename_a,
 	    xunlink (filename_diff);
     }
 
-    free (buf_a);
-    free (buf_b);
-    free (buf_diff);
+    cairo_surface_destroy (surface_a);
+    cairo_surface_destroy (surface_b);
+    cairo_surface_destroy (surface_diff);
 
     return pixels_changed;
 }
 
-/* Like image_diff, but first "flatten" the contents of filename_b by
- * blending over white.
- *
- * Yes, this is an ugly copy-and-paste of another function. I'm doing
- * this for two reasons:
- *
- * 1) I want to rewrite all of the image_diff interfaces anyway
- *    (should use cairo_image_surface_create_from_png, should save
- *    loaded buffers for re-use).
- *
- * 2) There is a second reason no more.
- */
+int
+image_diff (const char *filename_a,
+	    const char *filename_b,
+	    const char *filename_diff,
+	    int		ax,
+	    int		ay,
+	    int		bx,
+	    int		by)
+{
+    return image_diff_core (filename_a, filename_b, filename_diff,
+			    ax, ay, bx, by,
+			    FALSE);
+}
+
 int
 image_diff_flattened (const char *filename_a,
 		      const char *filename_b,
@@ -243,106 +300,7 @@ image_diff_flattened (const char *filena
 		      int	  bx,
 		      int	  by)
 {
-    int pixels_changed;
-    unsigned int width_a, height_a, stride_a;
-    unsigned int width_b, height_b, stride_b;
-    unsigned char *buf_a, *buf_b, *buf_diff;
-    unsigned char *a_flat, *b_flat;
-    cairo_surface_t *buf_a_surface, *a_flat_surface;
-    cairo_surface_t *buf_b_surface, *b_flat_surface;
-    cairo_t *cr;
-    read_png_status_t status;
-
-    status = read_png_argb32 (filename_a, &buf_a, &width_a, &height_a, &stride_a);
-    if (status)
-	return -1;
-
-    status = read_png_argb32 (filename_b, &buf_b, &width_b, &height_b, &stride_b);
-    if (status) {
-	free (buf_a);
-	return -1;
-    }
-
-    width_a -= ax;
-    height_a -= ay;
-    width_b -= bx;
-    height_b -= by;
-
-    if (width_a  != width_b  ||
-	height_a != height_b)
-    {
-	cairo_test_log ("Error: Image size mismatch: (%dx%d@%d) vs. (%dx%d@%d)\n"
-			"       for %s vs. %s\n",
-			width_a, height_a, stride_a,
-			width_b, height_b, stride_b,
-			filename_a, filename_b);
-	free (buf_a);
-	free (buf_b);
-	return -1;
-    }
-
-    buf_a_surface =  cairo_image_surface_create_for_data (buf_a,
-							  CAIRO_FORMAT_ARGB32,
-							  width_a + ax, height_a + ay,
-							  stride_a);
-    buf_b_surface =  cairo_image_surface_create_for_data (buf_b,
-							  CAIRO_FORMAT_ARGB32,
-							  width_b + bx, height_b + by,
-							  stride_b);
-
-    buf_diff = xcalloc (stride_a * height_a, 1);
-
-    a_flat = xcalloc (stride_a * height_a, 1);
-    b_flat = xcalloc (stride_b * height_b, 1);
-
-    a_flat_surface = cairo_image_surface_create_for_data (a_flat,
-							  CAIRO_FORMAT_ARGB32,
-							  width_a, height_a,
-							  stride_a);
-    cairo_surface_set_device_offset (a_flat_surface, -ax, -ay);
-    b_flat_surface = cairo_image_surface_create_for_data (b_flat,
-							  CAIRO_FORMAT_ARGB32,
-							  width_b, height_b,
-							  stride_b);
-    cairo_surface_set_device_offset (b_flat_surface, -bx, -by);
-
-    cr = cairo_create (a_flat_surface);
-    cairo_set_source_rgb (cr, 1, 1, 1);
-    cairo_paint (cr);
-    cairo_set_source_surface (cr, buf_a_surface, 0, 0);
-    cairo_paint (cr);
-    cairo_destroy (cr);
-    cairo_surface_destroy (a_flat_surface);
-    cairo_surface_destroy (buf_a_surface);
-
-    cr = cairo_create (b_flat_surface);
-    cairo_set_source_rgb (cr, 1, 1, 1);
-    cairo_paint (cr);
-    cairo_set_source_surface (cr, buf_b_surface, 0, 0);
-    cairo_paint (cr);
-    cairo_destroy (cr);
-    cairo_surface_destroy (b_flat_surface);
-    cairo_surface_destroy (buf_b_surface);
-
-    pixels_changed = buffer_diff (a_flat,
-                                  b_flat,
-                                  buf_diff,
-				  width_a, height_a,
-                                  stride_a, stride_b, stride_a);
-
-    if (pixels_changed) {
-	FILE *png_file = fopen (filename_diff, "wb");
-	write_png_argb32 (buf_diff, png_file, width_a, height_a, stride_a);
-	fclose (png_file);
-    } else {
-	xunlink (filename_diff);
-    }
-
-    free (buf_a);
-    free (buf_b);
-    free (a_flat);
-    free (b_flat);
-    free (buf_diff);
-
-    return pixels_changed;
+    return image_diff_core (filename_a, filename_b, filename_diff,
+			    ax, ay, bx, by,
+			    TRUE);
 }
diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
index 60b4f5c..eb4ab05 100644
--- a/boilerplate/cairo-boilerplate.c
+++ b/boilerplate/cairo-boilerplate.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2004 Red Hat, Inc.
+ * Copyright © 2004,2006 Red Hat, Inc.
  *
  * Permission to use, copy, modify, distribute, and sell this software
  * and its documentation for any purpose is hereby granted without
diff --git a/boilerplate/cairo-boilerplate.h b/boilerplate/cairo-boilerplate.h
index 183f6a2..f91ebff 100644
--- a/boilerplate/cairo-boilerplate.h
+++ b/boilerplate/cairo-boilerplate.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2004-2006 Red Hat, Inc.
+ * Copyright © 2004,2006 Red Hat, Inc.
  *
  * Permission to use, copy, modify, distribute, and sell this software
  * and its documentation for any purpose is hereby granted without
@@ -41,6 +41,14 @@
 const char *
 _cairo_test_content_name (cairo_content_t content);
 
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
 typedef cairo_surface_t *
 (*cairo_test_create_target_surface_t) (const char	 *name,
 				       cairo_content_t	  content,
diff --git a/boilerplate/cairo-test.c b/boilerplate/cairo-test.c
index 0243edf..0d915a5 100644
--- a/boilerplate/cairo-test.c
+++ b/boilerplate/cairo-test.c
@@ -48,8 +48,6 @@
 #include "cairo-test.h"
 
 #include "buffer-diff.h"
-#include "read-png.h"
-#include "write-png.h"
 #include "xmalloc.h"
 
 /* This is copied from cairoint.h. That makes it painful to keep in
diff --git a/boilerplate/read-png.c b/boilerplate/read-png.c
deleted file mode 100644
index bb02e50..0000000
--- a/boilerplate/read-png.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright © 2003 USC, Information Sciences Institute
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without
- * fee, provided that the above copyright 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
- * University of Southern California not be used in advertising or
- * publicity pertaining to distribution of the software without
- * specific, written prior permission. The University of Southern
- * California makes no representations about the suitability of this
- * software for any purpose.  It is provided "as is" without express
- * or implied warranty.
- *
- * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
- * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
- * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Author: Carl D. Worth <cworth at isi.edu>
- */
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#if   HAVE_STDINT_H
-# include <stdint.h>
-#elif HAVE_INTTYPES_H
-# include <inttypes.h>
-#elif HAVE_SYS_INT_TYPES_H
-# include <sys/int_types.h>
-#elif defined(_MSC_VER)
-  typedef __int8 int8_t;
-  typedef unsigned __int8 uint8_t;
-  typedef __int16 int16_t;
-  typedef unsigned __int16 uint16_t;
-  typedef __int32 int32_t;
-  typedef unsigned __int32 uint32_t;
-  typedef __int64 int64_t;
-  typedef unsigned __int64 uint64_t;
-# ifndef HAVE_UINT64_T
-#  define HAVE_UINT64_T 1
-# endif
-#else
-#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.)
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <png.h>
-
-#include "cairo-test.h"
-#include "read-png.h"
-#include "xmalloc.h"
-
-static void
-premultiply_data (png_structp   png,
-                  png_row_infop row_info,
-                  png_bytep     data)
-{
-    size_t i;
-
-    for (i = 0; i < row_info->rowbytes; i += 4) {
-	uint8_t *base = &data[i];
-	uint8_t  blue = base[0];
-	uint8_t  green = base[1];
-	uint8_t  red = base[2];
-	uint8_t  alpha = base[3];
-	uint32_t p;
-
-	red = ((unsigned) red * (unsigned) alpha + 127) / 255;
-	green = ((unsigned) green * (unsigned) alpha + 127) / 255;
-	blue = ((unsigned) blue * (unsigned) alpha + 127) / 255;
-	p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
-	memcpy (base, &p, sizeof (uint32_t));
-    }
-}
-
-read_png_status_t
-read_png_argb32 (const char         *filename,
-		 unsigned char      **data,
-		 unsigned int       *width,
-		 unsigned int       *height,
-		 unsigned int	    *stride)
-{
-    size_t i;
-    FILE *file;
-#define PNG_SIG_SIZE 8
-    unsigned char png_sig[PNG_SIG_SIZE];
-    int sig_bytes;
-    png_struct *png;
-    png_info *info;
-    png_uint_32 png_width, png_height;
-    int depth, color_type, interlace;
-    unsigned int pixel_size;
-    png_byte **row_pointers;
-
-    file = fopen (filename, "rb");
-    if (file == NULL) {
-	cairo_test_log ("Error: File not found: %s\n", filename);
-	return READ_PNG_FILE_NOT_FOUND;
-    }
-
-    sig_bytes = fread (png_sig, 1, PNG_SIG_SIZE, file);
-    if (png_check_sig (png_sig, sig_bytes) == 0) {
-        fclose (file);
-	cairo_test_log ("Error: File is not a PNG image: %s\n", filename);
-	return READ_PNG_FILE_NOT_PNG;
-    }
-
-    /* XXX: Perhaps we'll want some other error handlers? */
-    png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
-                                  NULL,
-                                  NULL,
-                                  NULL);
-    if (png == NULL) {
-        fclose (file);
-	cairo_test_log ("Error: Out of memory while reading %s\n", filename);
-	return READ_PNG_NO_MEMORY;
-    }
-
-    info = png_create_info_struct (png);
-    if (info == NULL) {
-        fclose (file);
-        png_destroy_read_struct (&png, NULL, NULL);
-	cairo_test_log ("Error: Out of memory while reading %s\n", filename);
-	return READ_PNG_NO_MEMORY;
-    }
-
-    png_init_io (png, file);
-    png_set_sig_bytes (png, sig_bytes);
-
-    png_read_info (png, info);
-
-    png_get_IHDR (png, info,
-                  &png_width, &png_height, &depth,
-                  &color_type, &interlace, NULL, NULL);
-    *width = png_width;
-    *height = png_height;
-    *stride = 4 * png_width;
-
-    /* convert palette/gray image to rgb */
-    if (color_type == PNG_COLOR_TYPE_PALETTE)
-        png_set_palette_to_rgb (png);
-
-    /* expand gray bit depth if needed */
-    if (color_type == PNG_COLOR_TYPE_GRAY && depth < 8)
-        png_set_gray_1_2_4_to_8 (png);
-    /* transform transparency to alpha */
-    if (png_get_valid(png, info, PNG_INFO_tRNS))
-        png_set_tRNS_to_alpha (png);
-
-    if (depth == 16)
-        png_set_strip_16 (png);
-
-    if (depth < 8)
-        png_set_packing (png);
-
-    /* convert grayscale to RGB */
-    if (color_type == PNG_COLOR_TYPE_GRAY
-        || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-        png_set_gray_to_rgb (png);
-
-    if (interlace != PNG_INTERLACE_NONE)
-        png_set_interlace_handling (png);
-
-    png_set_bgr (png);
-    png_set_filler (png, 0xff, PNG_FILLER_AFTER);
-
-    png_set_read_user_transform_fn (png, premultiply_data);
-
-    png_read_update_info (png, info);
-
-    pixel_size = 4;
-    *data = xmalloc (png_width * png_height * pixel_size);
-
-    row_pointers = malloc (png_height * sizeof(char *));
-    for (i=0; i < png_height; i++)
-        row_pointers[i] = (png_byte *) (*data + i * png_width * pixel_size);
-
-    png_read_image (png, row_pointers);
-    png_read_end (png, info);
-
-    free (row_pointers);
-    fclose (file);
-
-    png_destroy_read_struct (&png, &info, NULL);
-
-    return READ_PNG_SUCCESS;
-}
diff --git a/boilerplate/read-png.h b/boilerplate/read-png.h
deleted file mode 100644
index 9c9ba43..0000000
--- a/boilerplate/read-png.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright © 2003 USC, Information Sciences Institute
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without
- * fee, provided that the above copyright 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
- * University of Southern California not be used in advertising or
- * publicity pertaining to distribution of the software without
- * specific, written prior permission. The University of Southern
- * California makes no representations about the suitability of this
- * software for any purpose.  It is provided "as is" without express
- * or implied warranty.
- *
- * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
- * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
- * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Author: Carl D. Worth <cworth at isi.edu>
- */
-
-#ifndef READ_PNG_H
-#define READ_PNG_H
-
-typedef enum {
-    READ_PNG_SUCCESS = 0,
-    READ_PNG_FILE_NOT_FOUND,
-    READ_PNG_FILE_NOT_PNG,
-    READ_PNG_NO_MEMORY
-} read_png_status_t;
-
-read_png_status_t
-read_png_argb32 (const char         *filename,
-		 unsigned char      **data,
-		 unsigned int       *width,
-		 unsigned int       *height,
-		 unsigned int	    *stride);
-
-#endif
diff --git a/boilerplate/write-png.c b/boilerplate/write-png.c
deleted file mode 100644
index c906df5..0000000
--- a/boilerplate/write-png.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright © 2003 USC, Information Sciences Institute
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without
- * fee, provided that the above copyright 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
- * University of Southern California not be used in advertising or
- * publicity pertaining to distribution of the software without
- * specific, written prior permission. The University of Southern
- * California makes no representations about the suitability of this
- * software for any purpose.  It is provided "as is" without express
- * or implied warranty.
- *
- * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
- * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
- * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Author: Carl D. Worth <cworth at cworth.org>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <png.h>
-
-#include "write-png.h"
-
-static void
-unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
-{
-    size_t i;
-
-    for (i = 0; i < row_info->rowbytes; i += 4) {
-        unsigned char *b = &data[i];
-        unsigned int pixel;
-        unsigned char alpha;
-
-	memcpy (&pixel, b, sizeof (unsigned int));
-	alpha = (pixel & 0xff000000) >> 24;
-        if (alpha == 0) {
-	    b[0] = b[1] = b[2] = b[3] = 0;
-	} else {
-            b[0] = (((pixel & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
-            b[1] = (((pixel & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
-            b[2] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
-	    b[3] = alpha;
-	}
-    }
-}
-
-void
-write_png_argb32 (unsigned char *buffer, FILE *file,
-		  int width, int height, int stride)
-{
-    int i;
-    png_struct *png;
-    png_info *info;
-    png_byte **rows;
-    png_color_16 white;
-
-    rows = malloc (height * sizeof(png_byte*));
-
-    for (i = 0; i < height; i++) {
-	rows[i] = buffer + i * stride;
-    }
-
-    png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
-    info = png_create_info_struct (png);
-
-    png_init_io (png, file);
-    png_set_IHDR (png, info,
-		  width, height, 8,
-		  PNG_COLOR_TYPE_RGB_ALPHA,
-		  PNG_INTERLACE_NONE,
-		  PNG_COMPRESSION_TYPE_DEFAULT,
-		  PNG_FILTER_TYPE_DEFAULT);
-
-    white.red = 0xff;
-    white.blue = 0xff;
-    white.green = 0xff;
-    png_set_bKGD (png, info, &white);
-
-    png_set_write_user_transform_fn (png, unpremultiply_data);
-    png_set_bgr (png);
-
-    png_write_info (png, info);
-    png_write_image (png, rows);
-    png_write_end (png, info);
-
-    png_destroy_write_struct (&png, &info);
-
-    free (rows);
-}
diff --git a/boilerplate/write-png.h b/boilerplate/write-png.h
deleted file mode 100644
index 8074666..0000000
--- a/boilerplate/write-png.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright © 2003 USC, Information Sciences Institute
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without
- * fee, provided that the above copyright 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
- * University of Southern California not be used in advertising or
- * publicity pertaining to distribution of the software without
- * specific, written prior permission. The University of Southern
- * California makes no representations about the suitability of this
- * software for any purpose.  It is provided "as is" without express
- * or implied warranty.
- *
- * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
- * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
- * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Author: Carl D. Worth <cworth at isi.edu>
- */
-
-#ifndef WRITE_PNG_H
-#define WRITE_PNG_H
-
-void
-write_png_argb32 (unsigned char *buffer, FILE * file,
-		  int width, int height, int stride);
-
-#endif
diff --git a/test/imagediff.c b/test/imagediff.c
index d030e76..9030877 100644
--- a/test/imagediff.c
+++ b/test/imagediff.c
@@ -27,8 +27,6 @@
 #include <stdlib.h>
 
 #include "buffer-diff.h"
-#include "read-png.h"
-#include "write-png.h"
 #include "xmalloc.h"
 
 int
diff-tree 37ce7058906a9a8c7e80bf4ed59c17ec912087cf (from a0ca4369ff71ca76e593ea8db3e728218814814d)
Author: Carl Worth <cworth at cworth.org>
Date:   Wed Aug 30 22:56:36 2006 -0700

    Separate the sharable stuff out of cairo-test.c into cairo-boilerplate.c

diff --git a/boilerplate/Makefile.am b/boilerplate/Makefile.am
index 7fa27d9..b8ac3d3 100644
--- a/boilerplate/Makefile.am
+++ b/boilerplate/Makefile.am
@@ -1,6 +1,8 @@
 noinst_LTLIBRARIES = libcairotest.la
 
 libcairotest_la_SOURCES =\
+cairo-boilerplate.c	\
+cairo-bolierplate.h	\
 buffer-diff.c		\
 buffer-diff.h		\
 cairo-test.c		\
@@ -30,6 +32,5 @@ INCLUDES =					\
 	-D_GNU_SOURCE				\
 	-I$(srcdir)				\
 	-I$(top_srcdir)/pixman/src		\
-	-I$(top_builddir)/src			\
 	-I$(top_srcdir)/src			\
 	$(CAIRO_CFLAGS)
diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
new file mode 100644
index 0000000..60b4f5c
--- /dev/null
+++ b/boilerplate/cairo-boilerplate.c
@@ -0,0 +1,1511 @@
+/*
+ * Copyright © 2004 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth at cworth.org>
+ */
+
+#include "cairo-boilerplate.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <setjmp.h>
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#if HAVE_FCFINI
+#include <fontconfig/fontconfig.h>
+#endif
+
+static const char *vector_ignored_tests[] = {
+    /* We can't match the results of tests that depend on
+     * CAIRO_ANTIALIAS_NONE/SUBPIXEL for vector backends
+     * (nor do we care). */
+    "ft-text-antialias-none",
+    "rectangle-rounding-error",
+    "text-antialias-gray",
+    "text-antialias-none",
+    "text-antialias-subpixel",
+    "unantialiased-shapes",
+    NULL
+};
+
+const char *
+_cairo_test_content_name (cairo_content_t content)
+{
+    /* For the purpose of the content name, we don't distinguish the
+     * flattened content value.
+     */
+    if (content == CAIRO_TEST_CONTENT_COLOR_ALPHA_FLATTENED)
+	content = CAIRO_CONTENT_COLOR_ALPHA;
+
+    switch (content) {
+    case CAIRO_CONTENT_COLOR:
+	return "rgb24";
+    case CAIRO_CONTENT_COLOR_ALPHA:
+	return "argb32";
+    case CAIRO_CONTENT_ALPHA:
+    default:
+	assert (0); /* not reached */
+	return "---";
+    }
+}
+
+static cairo_surface_t *
+create_image_surface (const char	 *name,
+		      cairo_content_t	  content,
+		      int		  width,
+		      int		  height,
+		      void		**closure)
+{
+    cairo_format_t format;
+    *closure = NULL;
+
+    if (content == CAIRO_CONTENT_COLOR_ALPHA) {
+	format = CAIRO_FORMAT_ARGB32;
+    } else if (content == CAIRO_CONTENT_COLOR) {
+	format = CAIRO_FORMAT_RGB24;
+    } else {
+	assert (0); /* not reached */
+	return NULL;
+    }
+
+    return cairo_image_surface_create (format, width, height);
+}
+
+#ifdef CAIRO_HAS_TEST_SURFACES
+
+#include "test-fallback-surface.h"
+#include "test-meta-surface.h"
+#include "test-paginated-surface.h"
+
+static cairo_surface_t *
+create_test_fallback_surface (const char	 *name,
+			      cairo_content_t	  content,
+			      int		  width,
+			      int		  height,
+			      void		**closure)
+{
+    *closure = NULL;
+    return _test_fallback_surface_create (content, width, height);
+}
+
+static cairo_surface_t *
+create_test_meta_surface (const char		 *name,
+			  cairo_content_t	  content,
+			  int			  width,
+			  int			  height,
+			  void			**closure)
+{
+    *closure = NULL;
+    return _test_meta_surface_create (content, width, height);
+}
+
+static const cairo_user_data_key_t test_paginated_closure_key;
+
+typedef struct {
+    unsigned char *data;
+    cairo_content_t content;
+    int width;
+    int height;
+    int stride;
+} test_paginated_closure_t;
+
+static cairo_surface_t *
+create_test_paginated_surface (const char	 *name,
+			       cairo_content_t	  content,
+			       int		  width,
+			       int		  height,
+			       void		**closure)
+{
+    test_paginated_closure_t *tpc;
+    cairo_surface_t *surface;
+
+    *closure = tpc = xmalloc (sizeof (test_paginated_closure_t));
+
+    tpc->content = content;
+    tpc->width = width;
+    tpc->height = height;
+    tpc->stride = width * 4;
+
+    tpc->data = xcalloc (tpc->stride * height, 1);
+
+    surface = _test_paginated_surface_create_for_data (tpc->data,
+						       tpc->content,
+						       tpc->width,
+						       tpc->height,
+						       tpc->stride);
+
+    cairo_surface_set_user_data (surface, &test_paginated_closure_key,
+				 tpc, NULL);
+
+    return surface;
+}
+
+/* The only reason we go through all these machinations to write a PNG
+ * image is to _really ensure_ that the data actually landed in our
+ * buffer through the paginated surface to the test_paginated_surface.
+ *
+ * If we didn't implement this function then the default
+ * cairo_surface_write_to_png would result in the paginated_surface's
+ * acquire_source_image function replaying the meta-surface to an
+ * intermediate image surface. And in that case the
+ * test_paginated_surface would not be involved and wouldn't be
+ * tested.
+ */
+static cairo_status_t
+test_paginated_write_to_png (cairo_surface_t *surface,
+			     const char	     *filename)
+{
+    cairo_surface_t *image;
+    cairo_format_t format;
+    test_paginated_closure_t *tpc;
+
+    tpc = cairo_surface_get_user_data (surface, &test_paginated_closure_key);
+
+    switch (tpc->content) {
+    case CAIRO_CONTENT_COLOR:
+	format = CAIRO_FORMAT_RGB24;
+	break;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+	format = CAIRO_FORMAT_ARGB32;
+	break;
+    case CAIRO_CONTENT_ALPHA:
+    default:
+	assert (0); /* not reached */
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    image = cairo_image_surface_create_for_data (tpc->data,
+						 format,
+						 tpc->width,
+						 tpc->height,
+						 tpc->stride);
+
+    cairo_surface_write_to_png (image, filename);
+
+    cairo_surface_destroy (image);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cleanup_test_paginated (void *closure)
+{
+    test_paginated_closure_t *tpc = closure;
+
+    free (tpc->data);
+    free (tpc);
+}
+
+#endif
+
+#ifdef CAIRO_HAS_GLITZ_SURFACE
+#include <glitz.h>
+#include <cairo-glitz.h>
+
+static const cairo_user_data_key_t glitz_closure_key;
+
+typedef struct _glitz_target_closure_base {
+    int width;
+    int height;
+    cairo_content_t content;
+} glitz_target_closure_base_t;
+
+#if CAIRO_CAN_TEST_GLITZ_GLX_SURFACE
+#include <glitz-glx.h>
+
+typedef struct _glitz_glx_target_closure {
+    glitz_target_closure_base_t base;
+    Display        *dpy;
+    int             scr;
+    Window          win;
+} glitz_glx_target_closure_t;
+
+static glitz_surface_t *
+create_glitz_glx_surface (glitz_format_name_t	      formatname,
+			  int			      width,
+			  int			      height,
+			  glitz_glx_target_closure_t *closure)
+{
+    Display                 * dpy = closure->dpy;
+    int                       scr = closure->scr;
+    glitz_drawable_format_t   templ;
+    glitz_drawable_format_t * dformat = NULL;
+    unsigned long             mask;
+    glitz_drawable_t        * drawable = NULL;
+    glitz_format_t          * format;
+    glitz_surface_t         * sr;
+
+    XSizeHints                xsh;
+    XSetWindowAttributes      xswa;
+    XVisualInfo             * vinfo;
+
+    memset(&templ, 0, sizeof(templ));
+    templ.color.red_size = 8;
+    templ.color.green_size = 8;
+    templ.color.blue_size = 8;
+    templ.color.alpha_size = 8;
+    templ.color.fourcc = GLITZ_FOURCC_RGB;
+    templ.samples = 1;
+
+    glitz_glx_init (NULL);
+
+    mask = GLITZ_FORMAT_SAMPLES_MASK | GLITZ_FORMAT_FOURCC_MASK |
+	GLITZ_FORMAT_RED_SIZE_MASK | GLITZ_FORMAT_GREEN_SIZE_MASK |
+	GLITZ_FORMAT_BLUE_SIZE_MASK;
+    if (formatname == GLITZ_STANDARD_ARGB32)
+	mask |= GLITZ_FORMAT_ALPHA_SIZE_MASK;
+
+    /* Try for a pbuffer first */
+    if (!getenv("CAIRO_TEST_FORCE_GLITZ_WINDOW"))
+	dformat = glitz_glx_find_pbuffer_format (dpy, scr, mask, &templ, 0);
+
+    if (dformat) {
+	closure->win = None;
+
+	drawable = glitz_glx_create_pbuffer_drawable (dpy, scr, dformat,
+						      width, height);
+	if (!drawable)
+	    goto FAIL;
+    } else {
+	/* No pbuffer, try window */
+	dformat = glitz_glx_find_window_format (dpy, scr, mask, &templ, 0);
+
+	if (!dformat)
+	    goto FAIL;
+
+	vinfo = glitz_glx_get_visual_info_from_format(dpy,
+						      DefaultScreen(dpy),
+						      dformat);
+
+	if (!vinfo)
+	    goto FAIL;
+
+	xsh.flags = PSize;
+	xsh.x = 0;
+	xsh.y = 0;
+	xsh.width = width;
+	xsh.height = height;
+
+	xswa.colormap = XCreateColormap (dpy, RootWindow(dpy, scr),
+					 vinfo->visual, AllocNone);
+	closure->win = XCreateWindow (dpy, RootWindow(dpy, scr),
+				      xsh.x, xsh.y, xsh.width, xsh.height,
+				      0, vinfo->depth, CopyFromParent,
+				      vinfo->visual, CWColormap, &xswa);
+	XFree (vinfo);
+
+	drawable =
+	    glitz_glx_create_drawable_for_window (dpy, scr,
+						  dformat, closure->win,
+						  width, height);
+
+	if (!drawable)
+	    goto DESTROY_WINDOW;
+    }
+
+    format = glitz_find_standard_format (drawable, formatname);
+    if (!format)
+	goto DESTROY_DRAWABLE;
+
+    sr = glitz_surface_create (drawable, format, width, height, 0, NULL);
+    if (!sr)
+	goto DESTROY_DRAWABLE;
+
+    if (closure->win == None || dformat->doublebuffer) {
+	glitz_surface_attach (sr, drawable, GLITZ_DRAWABLE_BUFFER_BACK_COLOR);
+    } else {
+	XMapWindow (closure->dpy, closure->win);
+	glitz_surface_attach (sr, drawable, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR);
+    }
+
+    glitz_drawable_destroy (drawable);
+
+    return sr;
+ DESTROY_DRAWABLE:
+    glitz_drawable_destroy (drawable);
+ DESTROY_WINDOW:
+    if (closure->win)
+	XDestroyWindow (dpy, closure->win);
+ FAIL:
+    return NULL;
+}
+
+static cairo_surface_t *
+create_cairo_glitz_glx_surface (const char	 *name,
+				cairo_content_t   content,
+				int		  width,
+				int		  height,
+				void		**closure)
+{
+    int width = width;
+    int height = height;
+    glitz_glx_target_closure_t *gxtc;
+    glitz_surface_t  * glitz_surface;
+    cairo_surface_t  * surface;
+
+    *closure = gxtc = xmalloc (sizeof (glitz_glx_target_closure_t));
+
+    if (width == 0)
+	width = 1;
+    if (height == 0)
+	height = 1;
+
+    gxtc->dpy = XOpenDisplay (getenv("CAIRO_TEST_GLITZ_DISPLAY"));
+    if (!gxtc->dpy) {
+	CAIRO_BOILERPLATE_LOG ("Failed to open display: %s\n", XDisplayName(0));
+	goto FAIL;
+    }
+
+    XSynchronize (gxtc->dpy, 1);
+
+    gxtc->scr = DefaultScreen(gxtc->dpy);
+
+    switch (content) {
+    case CAIRO_CONTENT_COLOR:
+	glitz_surface = create_glitz_glx_surface (GLITZ_STANDARD_RGB24, width, height, gxtc);
+	break;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+	glitz_surface = create_glitz_glx_surface (GLITZ_STANDARD_ARGB32, width, height, gxtc);
+	break;
+    default:
+	CAIRO_BOILERPLATE_LOG ("Invalid content for glitz-glx test: %d\n", content);
+	goto FAIL_CLOSE_DISPLAY;
+    }
+    if (!glitz_surface) {
+	CAIRO_BOILERPLATE_LOG ("Failed to create glitz-glx surface\n");
+	goto FAIL_CLOSE_DISPLAY;
+    }
+
+    surface = cairo_glitz_surface_create (glitz_surface);
+
+    gxtc->base.width = width;
+    gxtc->base.height = height;
+    gxtc->base.content = content;
+    cairo_surface_set_user_data (surface, &glitz_closure_key,
+				 gxtc, NULL);
+
+    return surface;
+
+ FAIL_CLOSE_DISPLAY:
+    XCloseDisplay (gxtc->dpy);
+ FAIL:
+    return NULL;
+}
+
+static void
+cleanup_cairo_glitz_glx (void *closure)
+{
+    glitz_glx_target_closure_t *gxtc = closure;
+
+    glitz_glx_fini ();
+
+    if (gxtc->win)
+	XDestroyWindow (gxtc->dpy, gxtc->win);
+
+    XCloseDisplay (gxtc->dpy);
+
+    free (gxtc);
+}
+
+#endif /* CAIRO_CAN_TEST_GLITZ_GLX_SURFACE */
+
+#if CAIRO_CAN_TEST_GLITZ_AGL_SURFACE
+#include <glitz-agl.h>
+
+typedef struct _glitz_agl_target_closure {
+    glitz_target_closure_base_t base;
+} glitz_agl_target_closure_t;
+
+static glitz_surface_t *
+create_glitz_agl_surface (glitz_format_name_t formatname,
+			  int width, int height,
+			  glitz_agl_target_closure_t *closure)
+{
+    glitz_drawable_format_t *dformat;
+    glitz_drawable_format_t templ;
+    glitz_drawable_t *gdraw;
+    glitz_format_t *format;
+    glitz_surface_t *sr = NULL;
+    unsigned long mask;
+
+    memset(&templ, 0, sizeof(templ));
+    templ.color.red_size = 8;
+    templ.color.green_size = 8;
+    templ.color.blue_size = 8;
+    templ.color.alpha_size = 8;
+    templ.color.fourcc = GLITZ_FOURCC_RGB;
+    templ.samples = 1;
+
+    mask = GLITZ_FORMAT_SAMPLES_MASK | GLITZ_FORMAT_FOURCC_MASK |
+	GLITZ_FORMAT_RED_SIZE_MASK | GLITZ_FORMAT_GREEN_SIZE_MASK |
+	GLITZ_FORMAT_BLUE_SIZE_MASK;
+    if (formatname == GLITZ_STANDARD_ARGB32)
+	mask |= GLITZ_FORMAT_ALPHA_SIZE_MASK;
+
+    dformat = glitz_agl_find_pbuffer_format (mask, &templ, 0);
+    if (!dformat) {
+	CAIRO_BOILERPLATE_LOG ("Glitz failed to find pbuffer format for template.");
+	goto FAIL;
+    }
+
+    gdraw = glitz_agl_create_pbuffer_drawable (dformat, width, height);
+    if (!gdraw) {
+	CAIRO_BOILERPLATE_LOG ("Glitz failed to create pbuffer drawable.");
+	goto FAIL;
+    }
+
+    format = glitz_find_standard_format (gdraw, formatname);
+    if (!format) {
+	CAIRO_BOILERPLATE_LOG ("Glitz failed to find standard format for drawable.");
+	goto DESTROY_DRAWABLE;
+    }
+
+    sr = glitz_surface_create (gdraw, format, width, height, 0, NULL);
+    if (!sr) {
+	CAIRO_BOILERPLATE_LOG ("Glitz failed to create a surface.");
+	goto DESTROY_DRAWABLE;
+    }
+
+    glitz_surface_attach (sr, gdraw, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR);
+
+ DESTROY_DRAWABLE:
+    glitz_drawable_destroy (gdraw);
+
+ FAIL:
+    return sr; /* will be NULL unless we create it and attach */
+}
+
+static cairo_surface_t *
+create_cairo_glitz_agl_surface (const char	 *name,
+				cairo_content_t   content,
+				int		  width,
+				int		  height,
+				void		**closure)
+{
+    glitz_surface_t *glitz_surface;
+    cairo_surface_t *surface;
+    glitz_agl_target_closure_t *aglc;
+
+    glitz_agl_init ();
+
+    *closure = aglc = xmalloc (sizeof (glitz_agl_target_closure_t));
+
+    switch (content) {
+    case CAIRO_CONTENT_COLOR:
+	glitz_surface = create_glitz_agl_surface (GLITZ_STANDARD_RGB24, width, height, NULL);
+	break;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+	glitz_surface = create_glitz_agl_surface (GLITZ_STANDARD_ARGB32, width, height, NULL);
+	break;
+    default:
+	CAIRO_BOILERPLATE_LOG ("Invalid content for glitz-agl test: %d\n", content);
+	goto FAIL;
+    }
+
+    if (!glitz_surface)
+	goto FAIL;
+
+    surface = cairo_glitz_surface_create (glitz_surface);
+
+    aglc->base.width = width;
+    aglc->base.height = height;
+    aglc->base.content = content;
+    cairo_surface_set_user_data (surface, &glitz_closure_key, aglc, NULL);
+
+    return surface;
+
+ FAIL:
+    return NULL;
+}
+
+static void
+cleanup_cairo_glitz_agl (void *closure)
+{
+    free (closure);
+    glitz_agl_fini ();
+}
+
+#endif /* CAIRO_CAN_TEST_GLITZ_AGL_SURFACE */
+
+#if CAIRO_CAN_TEST_GLITZ_WGL_SURFACE
+#include <glitz-wgl.h>
+
+typedef struct _glitz_wgl_target_closure {
+    glitz_target_closure_base_t base;
+} glitz_wgl_target_closure_t;
+
+static glitz_surface_t *
+create_glitz_wgl_surface (glitz_format_name_t formatname,
+			  int width, int height,
+			  glitz_wgl_target_closure_t *closure)
+{
+    glitz_drawable_format_t *dformat;
+    glitz_drawable_format_t templ;
+    glitz_drawable_t *gdraw;
+    glitz_format_t *format;
+    glitz_surface_t *sr = NULL;
+    unsigned long mask;
+
+    memset(&templ, 0, sizeof(templ));
+    templ.color.red_size = 8;
+    templ.color.green_size = 8;
+    templ.color.blue_size = 8;
+    templ.color.alpha_size = 8;
+    templ.color.fourcc = GLITZ_FOURCC_RGB;
+    templ.samples = 1;
+
+    mask = GLITZ_FORMAT_SAMPLES_MASK | GLITZ_FORMAT_FOURCC_MASK |
+	GLITZ_FORMAT_RED_SIZE_MASK | GLITZ_FORMAT_GREEN_SIZE_MASK |
+	GLITZ_FORMAT_BLUE_SIZE_MASK;
+    if (formatname == GLITZ_STANDARD_ARGB32)
+	mask |= GLITZ_FORMAT_ALPHA_SIZE_MASK;
+
+    dformat = glitz_wgl_find_pbuffer_format (mask, &templ, 0);
+    if (!dformat) {
+	CAIRO_BOILERPLATE_LOG ("Glitz failed to find pbuffer format for template.");
+	goto FAIL;
+    }
+
+    gdraw = glitz_wgl_create_pbuffer_drawable (dformat, width, height);
+    if (!gdraw) {
+	CAIRO_BOILERPLATE_LOG ("Glitz failed to create pbuffer drawable.");
+	goto FAIL;
+    }
+
+    format = glitz_find_standard_format (gdraw, formatname);
+    if (!format) {
+	CAIRO_BOILERPLATE_LOG ("Glitz failed to find standard format for drawable.");
+	goto DESTROY_DRAWABLE;
+    }
+
+    sr = glitz_surface_create (gdraw, format, width, height, 0, NULL);
+    if (!sr) {
+	CAIRO_BOILERPLATE_LOG ("Glitz failed to create a surface.");
+	goto DESTROY_DRAWABLE;
+    }
+
+    glitz_surface_attach (sr, gdraw, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR);
+
+ DESTROY_DRAWABLE:
+    glitz