[Spice-commits] 31 commits - client/application.cpp client/cache.hpp client/canvas.cpp client/canvas.h client/cursor_channel.cpp client/display_channel.h client/glz_decoder_window.cpp client/gui client/lines.cpp client/Makefile.am client/pixman_utils.cpp client/red_cairo_canvas.cpp client/red_gdi_canvas.cpp client/red_gl_canvas.cpp client/screen.cpp client/screen_layer.cpp client/shared_cache.hpp client/utils.h client/windows client/x11 common/cairo_canvas.c common/cairo_canvas.h common/canvas_base.c common/canvas_base.h common/canvas_utils.c common/canvas_utils.h common/gdi_canvas.c common/gdi_canvas.h common/gl_canvas.c common/gl_canvas.h common/glc.c common/glc.h common/gl_utils.h common/lines.c common/lines.h common/lookup3.c common/lookup3.h common/lz.c common/lz_common.h common/lz_compress_tmpl.c common/lz_config.h common/lz_decompress_tmpl.c common/lz.h common/Makefile.am common/mutex.h common/ogl_ctx.c common/ogl_ctx.h common/pixman_utils.c common/pixman_utils.h common/quic. c common/quic_config.h common/quic_family_tmpl.c common/quic.h common/quic_rgb_tmpl.c common/quic_tmpl.c common/rect.h common/region.c common/region.h common/ring.h common/rop3.c common/rop3.h configure.ac server/Makefile.am server/red_worker.c

Alexander Larsson alexl at kemper.freedesktop.org
Tue Feb 23 14:04:39 PST 2010


 client/Makefile.am              |    2 
 client/application.cpp          |   28 
 client/cache.hpp                |    4 
 client/canvas.cpp               |   30 
 client/canvas.h                 |  102 -
 client/cursor_channel.cpp       |   14 
 client/display_channel.h        |    1 
 client/glz_decoder_window.cpp   |    4 
 client/gui/gui.cpp              |   26 
 client/lines.cpp                |   24 
 client/pixman_utils.cpp         |   25 
 client/red_cairo_canvas.cpp     |   43 
 client/red_gdi_canvas.cpp       |    6 
 client/red_gl_canvas.cpp        |   25 
 client/screen.cpp               |   50 
 client/screen_layer.cpp         |   21 
 client/shared_cache.hpp         |    4 
 client/utils.h                  |    3 
 client/windows/redc.vcproj      |    4 
 client/x11/Makefile.am          |    7 
 client/x11/pixels_source.cpp    |   23 
 client/x11/pixels_source_p.h    |    9 
 client/x11/red_drawable.cpp     |  199 +-
 client/x11/red_pixmap_cairo.cpp |   38 
 client/x11/red_window.cpp       |    5 
 common/Makefile.am              |    4 
 common/cairo_canvas.c           | 2962 +++++++++++++++++++-------------
 common/cairo_canvas.h           |   21 
 common/canvas_base.c            |  389 ++--
 common/canvas_base.h            |   39 
 common/canvas_utils.c           |  152 -
 common/canvas_utils.h           |   30 
 common/gdi_canvas.c             |  181 -
 common/gdi_canvas.h             |    9 
 common/gl_canvas.c              |  233 +-
 common/gl_canvas.h              |   16 
 common/gl_utils.h               |    1 
 common/glc.c                    |    1 
 common/glc.h                    |    1 
 common/lines.c                  | 3632 ++++++++++++++++++++++++++++++++++++++++
 common/lines.h                  |  130 +
 common/lookup3.c                |    1 
 common/lookup3.h                |    1 
 common/lz.c                     |    1 
 common/lz.h                     |    1 
 common/lz_common.h              |    1 
 common/lz_compress_tmpl.c       |    1 
 common/lz_config.h              |    1 
 common/lz_decompress_tmpl.c     |    1 
 common/mutex.h                  |    1 
 common/ogl_ctx.c                |    1 
 common/ogl_ctx.h                |    1 
 common/pixman_utils.c           |  938 ++++++++++
 common/pixman_utils.h           |  103 +
 common/quic.c                   |    1 
 common/quic.h                   |    1 
 common/quic_config.h            |    1 
 common/quic_family_tmpl.c       |    1 
 common/quic_rgb_tmpl.c          |    1 
 common/quic_tmpl.c              |    1 
 common/rect.h                   |    1 
 common/region.c                 | 1172 ++++--------
 common/region.h                 |   21 
 common/ring.h                   |    1 
 common/rop3.c                   |   57 
 common/rop3.h                   |    9 
 configure.ac                    |   10 
 server/Makefile.am              |    6 
 server/red_worker.c             |  119 -
 69 files changed, 8077 insertions(+), 2875 deletions(-)

New commits:
commit 239b2b66b2cfd46abe8d84162a74a7e47a54d85b
Author: Alexander Larsson <alexl at redhat.com>
Date:   Thu Feb 18 21:00:24 2010 +0100

    Remove qcairo dependency, only use pixman

diff --git a/client/canvas.h b/client/canvas.h
index 6585656..79400cc 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -21,7 +21,6 @@
 #include "common.h"
 #include "debug.h"
 #include "region.h"
-#include "cairo.h"
 #include <spice/protocol.h>
 #include "cache.hpp"
 #include "shared_cache.hpp"
diff --git a/client/display_channel.h b/client/display_channel.h
index 802898f..b2e3791 100644
--- a/client/display_channel.h
+++ b/client/display_channel.h
@@ -22,7 +22,6 @@
 #include "canvas.h"
 #include "region.h"
 #include "red_channel.h"
-#include "cairo.h"
 #include "cache.hpp"
 #include "screen_layer.h"
 #include "process_loop.h"
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index d645c38..d703101 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -17,7 +17,7 @@ INCLUDES = \
 	$(GL_CFLAGS)					\
 	$(ALSA_CFLAGS)					\
 	$(FFMPEG_CFLAGS)				\
-	$(QCAIRO_CFLAGS)				\
+	$(PIXMAN_CFLAGS)				\
 	$(LOG4CPP_CFLAGS)				\
 	$(CELT051_CFLAGS)				\
 	$(SSL_CFLAGS)					\
@@ -155,7 +155,7 @@ spicec_LDFLAGS = \
 	$(CEGUI_LIBS)
 
 spicec_LDADD =						\
-	$(QCAIRO_LIBS)					\
+	$(PIXMAN_LIBS)					\
 	$(FFMPEG_LIBS)					\
 	$(ALSA_LIBS)					\
 	$(GL_LIBS)					\
diff --git a/configure.ac b/configure.ac
index dc67286..8b28900 100644
--- a/configure.ac
+++ b/configure.ac
@@ -120,12 +120,10 @@ AC_SUBST(SLIRP_CFLAGS)
 AC_SUBST(SLIRP_LIBS)
 SPICE_REQUIRES+=" slirp"
 
-PKG_CHECK_MODULES(QCAIRO, qcairo >= 1.4.6)
-AC_SUBST(QCAIRO_CFLAGS)
-AC_SUBST(QCAIRO_LIBS)
-QCAIRO_LIBDIR=`pkg-config --variable=libdir qcairo`
-AC_SUBST(QCAIRO_LIBDIR)
-SPICE_REQUIRES+=" qcairo >= 1.4.6"
+PKG_CHECK_MODULES(PIXMAN, pixman-1 >= 0.17.7)
+AC_SUBST(PIXMAN_CFLAGS)
+AC_SUBST(PIXMAN_LIBS)
+SPICE_REQUIRES+=" pixman-1 >= 0.17.7"
 
 PKG_CHECK_MODULES(CELT051, celt051 >= 0.5.1.1)
 AC_SUBST(CELT051_CFLAGS)
diff --git a/server/Makefile.am b/server/Makefile.am
index aaef6a7..f91debd 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -6,7 +6,7 @@ INCLUDES = \
 	-I$(top_srcdir)/common/linux	\
 	$(PROTOCOL_CFLAGS)			\
 	$(FFMPEG_CFLAGS)			\
-	$(QCAIRO_CFLAGS)			\
+	$(PIXMAN_CFLAGS)			\
 	$(GL_CFLAGS)				\
 	$(LOG4CPP_CFLAGS)			\
 	$(SSL_CFLAGS)				\
@@ -42,7 +42,7 @@ libspice_la_LDFLAGS = 				\
 libspice_la_LIBADD = 				\
 	$(GL_LIBS)				\
 	$(FFMPEG_LIBS)				\
-	$(QCAIRO_LIBS)				\
+	$(PIXMAN_LIBS)				\
 	$(SSL_LIBS)				\
 	$(CELT051_LIBS)				\
 	$(SLIRP_LIBS)				\
commit 295738b453f44f43a941d072290c5e975c3a6cb4
Author: Alexander Larsson <alexl at redhat.com>
Date:   Fri Feb 19 16:48:55 2010 +0100

    Remove cairo use in client

diff --git a/client/x11/pixels_source.cpp b/client/x11/pixels_source.cpp
index 7b9ed05..144c296 100644
--- a/client/x11/pixels_source.cpp
+++ b/client/x11/pixels_source.cpp
@@ -25,9 +25,9 @@
 
 
 static void create_image(const PixmapHeader* pixmap, PixelsSource_p& pixels_source,
-                         cairo_format_t cairo_format)
+                         pixman_format_code_t format)
 {
-    cairo_surface_t* cairo_surf;
+    pixman_image_t *pixman_image;
     XImage *image = new XImage;
 
     memset(image, 0, sizeof(*image));
@@ -53,10 +53,11 @@ static void create_image(const PixmapHeader* pixmap, PixelsSource_p& pixels_sour
             THROW("init image failed");
         }
 
-        cairo_surf = cairo_image_surface_create_for_data(pixmap->data, cairo_format,
-                                                         pixmap->width, pixmap->height,
-                                                         pixmap->stride);
-        if (cairo_surface_status(cairo_surf) != CAIRO_STATUS_SUCCESS) {
+        pixman_image = pixman_image_create_bits(format,
+                                                pixmap->width, pixmap->height,
+                                                (uint32_t *)pixmap->data,
+                                                pixmap->stride);
+        if (pixman_image == NULL) {
             THROW("surf create failed");
         }
     } catch (...) {
@@ -66,7 +67,7 @@ static void create_image(const PixmapHeader* pixmap, PixelsSource_p& pixels_sour
 
     pixels_source.type = PIXELS_SOURCE_TYPE_PIXMAP;
     pixels_source.pixmap.x_image = image;
-    pixels_source.pixmap.cairo_surf = cairo_surf;
+    pixels_source.pixmap.pixman_image = pixman_image;
 }
 
 PixelsSource::PixelsSource()
@@ -85,12 +86,12 @@ ImageFromRes::ImageFromRes(int res_id)
     if (!pixmap) {
         THROW("no image %d", res_id);
     }
-    create_image(pixmap, *(PixelsSource_p*)get_opaque(), CAIRO_FORMAT_RGB24);
+    create_image(pixmap, *(PixelsSource_p*)get_opaque(), PIXMAN_x8r8g8b8);
 }
 
 ImageFromRes::~ImageFromRes()
 {
-    cairo_surface_destroy(((PixelsSource_p*)get_opaque())->pixmap.cairo_surf);
+    pixman_image_unref(((PixelsSource_p*)get_opaque())->pixmap.pixman_image);
     delete ((PixelsSource_p*)get_opaque())->pixmap.x_image;
 }
 
@@ -109,12 +110,12 @@ AlphaImageFromRes::AlphaImageFromRes(int res_id)
     if (!pixmap) {
         THROW("no image %d", res_id);
     }
-    create_image(pixmap, *(PixelsSource_p*)get_opaque(), CAIRO_FORMAT_ARGB32);
+    create_image(pixmap, *(PixelsSource_p*)get_opaque(), PIXMAN_a8r8g8b8);
 }
 
 AlphaImageFromRes::~AlphaImageFromRes()
 {
-    cairo_surface_destroy(((PixelsSource_p*)get_opaque())->pixmap.cairo_surf);
+    pixman_image_unref(((PixelsSource_p*)get_opaque())->pixmap.pixman_image);
     delete ((PixelsSource_p*)get_opaque())->pixmap.x_image;
 }
 
diff --git a/client/x11/pixels_source_p.h b/client/x11/pixels_source_p.h
index 69d6998..9605ae4 100644
--- a/client/x11/pixels_source_p.h
+++ b/client/x11/pixels_source_p.h
@@ -24,7 +24,7 @@
 #include <X11/extensions/XShm.h>
 #include "red_window.h"
 #include "red_pixmap_gl.h"
-#include "cairo.h"
+#include "pixman_utils.h"
 
 enum {
     PIXELS_SOURCE_TYPE_INVALID,
@@ -53,12 +53,12 @@ struct PixelsSource_p {
         struct {
             XImage* x_image;
             XShmSegmentInfo *shminfo;
-            cairo_surface_t* cairo_surf;
+            pixman_image_t* pixman_image;
         } x_shm_drawable;
 
         struct {
             XImage* x_image;
-            cairo_surface_t* cairo_surf;
+            pixman_image_t* pixman_image;
         } pixmap;
 
         struct {
@@ -79,9 +79,6 @@ struct PixelsSource_p {
 
 struct RedDrawable_p {
     PixelsSource_p source;
-    union {
-        cairo_t* cairo;
-    };
 };
 
 #endif
diff --git a/client/x11/red_drawable.cpp b/client/x11/red_drawable.cpp
index 7485c94..e020b84 100644
--- a/client/x11/red_drawable.cpp
+++ b/client/x11/red_drawable.cpp
@@ -237,18 +237,18 @@ static inline void copy_to_pixmap_from_shmdrawable(const RedDrawable_p* dest,
                                                    const PixelsSource_p* source,
                                                    int src_x, int src_y)
 {
-    cairo_t* cairo = dest->cairo;
-    cairo_surface_t* surf = source->x_shm_drawable.cairo_surf;
-
-    ASSERT(cairo);
-    ASSERT(surf);
-
-    cairo_set_source_surface(cairo, surf, area.left + offset.x - src_x,
-                             area.top + offset.y - src_y);
-    cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
-    cairo_rectangle(cairo, area.left + offset.x, area.top + offset.y, area.right - area.left,
-                    area.bottom - area.top);
-    cairo_fill(cairo);
+    pixman_image_t *dest_surface =  dest->source.pixmap.pixman_image;
+    pixman_image_t *src_surface = source->x_shm_drawable.pixman_image;
+
+    pixman_image_composite32(PIXMAN_OP_SRC,
+                             src_surface, NULL, dest_surface,
+                             src_x + offset.x,
+                             src_y + offset.y,
+                             0, 0,
+                             area.left + offset.x,
+                             area.top + offset.y,
+                             area.right - area.left,
+                             area.bottom - area.top);
 }
 
 static inline void copy_to_pixmap_from_pixmap(const RedDrawable_p* dest,
@@ -257,18 +257,18 @@ static inline void copy_to_pixmap_from_pixmap(const RedDrawable_p* dest,
                                               const PixelsSource_p* source,
                                               int src_x, int src_y)
 {
-    cairo_t* cairo = dest->cairo;
-    cairo_surface_t* surf = source->pixmap.cairo_surf;
-
-    ASSERT(cairo);
-    ASSERT(surf);
-
-    cairo_set_source_surface(cairo, surf, area.left + offset.x - src_x,
-                             area.top + offset.y - src_y);
-    cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
-    cairo_rectangle(cairo, area.left + offset.x, area.top + offset.y, area.right - area.left,
-                    area.bottom - area.top);
-    cairo_fill(cairo);
+    pixman_image_t *dest_surface =  dest->source.pixmap.pixman_image;
+    pixman_image_t *src_surface = source->pixmap.pixman_image;
+
+    pixman_image_composite32(PIXMAN_OP_SRC,
+                             src_surface, NULL, dest_surface,
+                             src_x + offset.x,
+                             src_y + offset.y,
+                             0, 0,
+                             area.left + offset.x,
+                             area.top + offset.y,
+                             area.right - area.left,
+                             area.bottom - area.top);
 }
 
 static inline void copy_to_pixmap_from_gltexture(const RedDrawable_p* dest,
@@ -384,18 +384,18 @@ static inline void blend_to_pixmap_from_pixmap(const RedDrawable_p* dest,
                                                const PixelsSource_p* source,
                                                int src_x, int src_y)
 {
-    cairo_t* cairo = dest->cairo;
-    cairo_surface_t* surf = source->pixmap.cairo_surf;
-
-    ASSERT(cairo);
-    ASSERT(surf);
-
-    cairo_set_source_surface(cairo, surf, (area.left + offset.x - src_x),
-                             (area.top + offset.y - src_y));
-    cairo_set_operator(cairo, CAIRO_OPERATOR_ATOP);
-    cairo_rectangle(cairo, area.left + offset.x, area.top + offset.y, area.right - area.left,
-                    area.bottom - area.top);
-    cairo_fill(cairo);
+    pixman_image_t *dest_surface =  dest->source.pixmap.pixman_image;
+    pixman_image_t *src_surface = source->pixmap.pixman_image;
+
+    pixman_image_composite32 (PIXMAN_OP_ATOP,
+                              src_surface, NULL, dest_surface,
+                              src_x + offset.x,
+                              src_y + offset.y,
+                              0, 0,
+                              area.left + offset.x,
+                              area.top + offset.y,
+                              area.right - area.left,
+                              area.bottom - area.top);
 }
 
 static inline void blend_to_pixmap(const RedDrawable_p* dest,
@@ -460,41 +460,74 @@ static inline void combine_to_pixmap_from_pixmap(const RedDrawable_p* dest,
                                                  int src_x, int src_y,
                                                  RedDrawable::CombineOP op)
 {
-    cairo_t* cairo = dest->cairo;
-    cairo_surface_t* surf = source->pixmap.cairo_surf;
+    pixman_image_t *dest_surface =  dest->source.pixmap.pixman_image;
+    pixman_image_t *src_surface = source->pixmap.pixman_image;
 
-    ASSERT(cairo);
-    ASSERT(surf);
-    cairo_operator_t cairo_op;
+    SpiceROP rop;
     switch (op) {
     case RedDrawable::OP_COPY:
-        cairo_op = CAIRO_OPERATOR_RASTER_COPY;
+        rop = SPICE_ROP_COPY;
         break;
     case RedDrawable::OP_AND:
-        cairo_op = CAIRO_OPERATOR_RASTER_AND;
+        rop = SPICE_ROP_AND;
         break;
     case RedDrawable::OP_XOR:
-        cairo_op = CAIRO_OPERATOR_RASTER_XOR;
+        rop = SPICE_ROP_XOR;
         break;
     default:
         THROW("invalid op %d", op);
     }
 
 
-    cairo_set_operator(cairo, cairo_op);
-    if (cairo_image_surface_get_format(surf) == CAIRO_FORMAT_A1) {
-        cairo_rectangle(cairo, area.left + offset.x, area.top + offset.y, area.right - area.left,
-                        area.bottom - area.top);
-        cairo_clip(cairo);
-        cairo_set_source_rgb(cairo, 1, 1, 1);
-        cairo_mask_surface(cairo, surf, area.left + offset.x - src_x, area.top + offset.y - src_y);
-        cairo_reset_clip(cairo);
+    if (pixman_image_get_depth (src_surface) == 1) {
+        pixman_image_t *temp;
+
+        temp = pixman_image_create_bits(pixman_image_get_depth(dest_surface) == 24 ?
+                                        PIXMAN_x8r8g8b8 : PIXMAN_a8r8g8b8,
+                                        area.right - area.left,
+                                        area.bottom - area.top, NULL, 0);
+
+        /* Copy from dest to temp */
+        pixman_image_composite32(PIXMAN_OP_SRC,
+                                 dest_surface, NULL, temp,
+                                 area.left + offset.x,
+                                 area.top + offset.y,
+                                 0, 0,
+                                 0, 0,
+                                 area.right - area.left,
+                                 area.bottom - area.top);
+
+        /* rop white over temp */
+        spice_pixman_fill_rect_rop(temp,
+                                   0, 0,
+                                   area.right - area.left,
+                                   area.bottom - area.top,
+                                   0xffffff,
+                                   rop);
+
+        /* copy back using a1 mask */
+        pixman_image_composite32(PIXMAN_OP_SRC,
+                                 temp, src_surface, dest_surface,
+                                 0, 0,
+                                 src_x + offset.x,
+                                 src_y + offset.y,
+                                 area.left + offset.x,
+                                 area.top + offset.y,
+                                 area.right - area.left,
+                                 area.bottom - area.top);
+
+        pixman_image_unref(temp);
+
     } else {
-        cairo_set_source_surface(cairo, surf, area.left + offset.x - src_x,
-                                 area.top + offset.y - src_y);
-        cairo_rectangle(cairo, area.left + offset.x, area.top + offset.y, area.right - area.left,
-                        area.bottom - area.top);
-        cairo_fill(cairo);
+        spice_pixman_blit_rop(dest_surface,
+                              src_surface,
+                              src_x + offset.x,
+                              src_y + offset.y,
+                              area.left + offset.x,
+                              area.top + offset.y,
+                              area.right - area.left,
+                              area.bottom - area.top,
+                              rop);
     }
 }
 
@@ -606,17 +639,13 @@ static inline void fill_gl_drawable(RedDrawable_p* dest, const SpiceRect& area,
 static inline void fill_pixmap(RedDrawable_p* dest, const SpiceRect& area, rgb32_t color,
                                const SpicePoint& offset)
 {
-    cairo_t* cairo = dest->cairo;
-
-    ASSERT(cairo);
-    cairo_set_source_rgb(cairo,
-                         (double)rgb32_get_red(color) / 0xff,
-                         (double)rgb32_get_green(color) / 0xff,
-                         (double)rgb32_get_blue(color) / 0xff);
-    cairo_rectangle(cairo, area.left + offset.x, area.top + offset.y, area.right - area.left,
-                    area.bottom - area.top);
-    cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
-    cairo_fill(cairo);
+    pixman_image_t *dest_surface =  dest->source.pixmap.pixman_image;
+
+    spice_pixman_fill_rect(dest_surface,
+                           area.left + offset.x, area.top + offset.y,
+                           area.right - area.left,
+                           area.bottom - area.top,
+                           color);
 }
 
 void RedDrawable::fill_rect(const SpiceRect& area, rgb32_t color)
@@ -668,18 +697,28 @@ static inline void frame_drawable(RedDrawable_p* dest, const SpiceRect& area, rg
 static inline void frame_pixmap(RedDrawable_p* dest, const SpiceRect& area, rgb32_t color,
                                 const SpicePoint& offset)
 {
-    cairo_t* cairo = dest->cairo;
-
-    ASSERT(cairo);
-    cairo_set_source_rgb(cairo,
-                         (double)rgb32_get_red(color) / 0xff,
-                         (double)rgb32_get_green(color) / 0xff,
-                         (double)rgb32_get_blue(color) / 0xff);
-    cairo_rectangle(cairo, area.left + offset.x, area.top + offset.y, area.right - area.left,
-                    area.bottom - area.top);
-    cairo_set_line_width(cairo, 1);
-    cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
-    cairo_stroke(cairo);
+    pixman_image_t *dest_surface =  dest->source.pixmap.pixman_image;
+
+    spice_pixman_fill_rect(dest_surface,
+                           area.left + offset.x, area.top + offset.y,
+                           area.right - area.left,
+                           1,
+                           color);
+    spice_pixman_fill_rect(dest_surface,
+                           area.left + offset.x, area.bottom + offset.y,
+                           area.right - area.left,
+                           1,
+                           color);
+    spice_pixman_fill_rect(dest_surface,
+                           area.left + offset.x, area.top + offset.y,
+                           1,
+                           area.bottom - area.top,
+                           color);
+    spice_pixman_fill_rect(dest_surface,
+                           area.right + offset.x, area.top + offset.y,
+                           1,
+                           area.bottom - area.top,
+                           color);
 }
 
 void RedDrawable::frame_rect(const SpiceRect& area, rgb32_t color)
diff --git a/client/x11/red_pixmap_cairo.cpp b/client/x11/red_pixmap_cairo.cpp
index 7ca9315..7b8f074 100644
--- a/client/x11/red_pixmap_cairo.cpp
+++ b/client/x11/red_pixmap_cairo.cpp
@@ -28,10 +28,9 @@ RedPixmapCairo::RedPixmapCairo(int width, int height, RedPixmap::Format format,
                                bool top_bottom, rgb32_t* pallet, RedWindow *win)
     : RedPixmap(width, height, format, top_bottom, pallet)
 {
-    cairo_surface_t* cairo_surf = NULL;
-    cairo_t* cairo = NULL;
     ASSERT(format == RedPixmap::ARGB32 || format == RedPixmap::RGB32 || format == RedPixmap::A1);
     ASSERT(sizeof(RedDrawable_p) <= PIXELES_SOURCE_OPAQUE_SIZE);
+    pixman_image_t *pixman_image;
     XImage *image = NULL;
     XShmSegmentInfo *shminfo = NULL;
     _data = NULL;
@@ -40,7 +39,7 @@ RedPixmapCairo::RedPixmapCairo(int width, int height, RedPixmap::Format format,
 
 
     try {
-        cairo_format_t cairo_format;
+        pixman_format_code_t pixman_format;
 
         if (win) {
             vinfo = XPlatform::get_vinfo()[win->get_screen_num()];
@@ -55,12 +54,12 @@ RedPixmapCairo::RedPixmapCairo(int width, int height, RedPixmap::Format format,
             case RedPixmap::ARGB32:
             case RedPixmap::RGB32:
                 depth = XPlatform::get_vinfo()[0]->depth;
-                cairo_format = format == RedPixmap::ARGB32 ? CAIRO_FORMAT_ARGB32 :
-                                                             CAIRO_FORMAT_RGB24;
+                pixman_format = format == RedPixmap::ARGB32 ? PIXMAN_a8r8g8b8 :
+                                                             PIXMAN_x8r8g8b8;
                 break;
             case RedPixmap::A1:
                 depth = 1;
-                cairo_format = CAIRO_FORMAT_A1;
+                pixman_format = PIXMAN_a1;
                 break;
             default:
                 THROW("unsupported format %d", format);
@@ -122,13 +121,13 @@ RedPixmapCairo::RedPixmapCairo(int width, int height, RedPixmap::Format format,
                 image->red_mask = 0x00ff0000;
                 image->green_mask = 0x0000ff00;
                 image->blue_mask = 0x000000ff;
-                cairo_format = format == RedPixmap::ARGB32 ? CAIRO_FORMAT_ARGB32 :
-                                                             CAIRO_FORMAT_RGB24;
+                pixman_format = format == RedPixmap::ARGB32 ? PIXMAN_a8r8g8b8 :
+                                                             PIXMAN_x8r8g8b8;
                 break;
             case RedPixmap::A1:
                 image->depth = 1;
                 image->format = XYBitmap;
-                cairo_format = CAIRO_FORMAT_A1;
+                pixman_format = PIXMAN_a1;
                 break;
             default:
                 THROW("unsupported format %d", format);
@@ -138,23 +137,17 @@ RedPixmapCairo::RedPixmapCairo(int width, int height, RedPixmap::Format format,
                 THROW("init image failed");
             }
         }
-        cairo_surf = cairo_image_surface_create_for_data(_data, cairo_format, _width, _height,
-                                                         _stride);
-        if (cairo_surface_status(cairo_surf) != CAIRO_STATUS_SUCCESS) {
+        pixman_image = pixman_image_create_bits(pixman_format, _width, _height,
+                                                (uint32_t *)_data, _stride);
+        if (pixman_image == NULL) {
             THROW("surf create failed");
         }
 
-        cairo = cairo_create(cairo_surf);
-        cairo_surface_destroy(cairo_surf);
-        if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS) {
-            THROW("cairo create failed failed");
-        }
         if (!using_shm) {
-            ((PixelsSource_p*)get_opaque())->pixmap.cairo_surf = cairo_surf;
+            ((PixelsSource_p*)get_opaque())->pixmap.pixman_image = pixman_image;
         } else {
-            ((PixelsSource_p*)get_opaque())->x_shm_drawable.cairo_surf = cairo_surf;
+            ((PixelsSource_p*)get_opaque())->x_shm_drawable.pixman_image = pixman_image;
         }
-        ((RedDrawable_p*)get_opaque())->cairo = cairo;
     } catch (...) {
         if (using_shm) {
             if (image) {
@@ -178,11 +171,12 @@ RedPixmapCairo::RedPixmapCairo(int width, int height, RedPixmap::Format format,
 
 RedPixmapCairo::~RedPixmapCairo()
 {
-    cairo_destroy(((RedDrawable_p*)get_opaque())->cairo);
     if (((PixelsSource_p*)get_opaque())->type == PIXELS_SOURCE_TYPE_PIXMAP) {
+        pixman_image_unref(((PixelsSource_p*)get_opaque())->pixmap.pixman_image);
         delete ((PixelsSource_p*)get_opaque())->pixmap.x_image;
         delete[] _data;
     } else {
+        pixman_image_unref(((PixelsSource_p*)get_opaque())->x_shm_drawable.pixman_image);
         XShmSegmentInfo *shminfo = ((PixelsSource_p*)get_opaque())->x_shm_drawable.shminfo;
         XShmDetach(XPlatform::get_display(), shminfo);
         XDestroyImage(((PixelsSource_p*)get_opaque())->x_shm_drawable.x_image);
commit 1caa4b65c54e4371fe7f6eeda0a356e70663bb28
Author: Alexander Larsson <alexl at redhat.com>
Date:   Thu Feb 18 20:51:02 2010 +0100

    Remove no longer needed CAIRO_CANVAS_CACH_IS_SHARED define and code

diff --git a/client/application.cpp b/client/application.cpp
index 0fa25b4..73a3461 100644
--- a/client/application.cpp
+++ b/client/application.cpp
@@ -53,10 +53,6 @@
 
 #define CA_FILE_NAME "spice_truststore.pem"
 
-#ifdef CAIRO_CANVAS_CACH_IS_SHARED
-mutex_t cairo_surface_user_data_mutex;
-#endif
-
 static const char* app_name = "spicec";
 
 void ConnectedEvent::response(AbstractProcessLoop& events_loop)
@@ -2017,10 +2013,6 @@ void Application::init_globals()
     gdi_canvas_init();
 #endif
 
-#ifdef CAIRO_CANVAS_CACH_IS_SHARED
-    MUTEX_INIT(cairo_surface_user_data_mutex);
-#endif
-
     Platform::init();
     RedWindow::init();
 }
diff --git a/client/windows/redc.vcproj b/client/windows/redc.vcproj
index deff391..06d93c0 100644
--- a/client/windows/redc.vcproj
+++ b/client/windows/redc.vcproj
@@ -42,7 +42,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				AdditionalIncludeDirectories=".;..;..\..\common;..\..\common\win;&quot;..\..\common\win\my_getopt-1.5&quot;;&quot;$(SPICE_LIBS)\include&quot;;&quot;$(SPICE_LIBS)\include\qcairo&quot;;&quot;$(SPICE_LIBS)\include\ffmpeg&quot;;&quot;$(SPICE_LIBS)\include\CEGUI-0.6.2&quot;"
-				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;CAIRO_CANVAS_ACCESS_TEST;CAIRO_CANVAS_CACHE;CAIRO_CANVAS_CACH_IS_SHARED;RED_DEBUG;CAIRO_CANVAS_NO_CHUNKS;_WIN32_WINNT=0x0500;LOG4CPLUS_STATIC;USE_GLZ;PTW32_STATIC_LIB;CEGUI_STATIC"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;CAIRO_CANVAS_ACCESS_TEST;CAIRO_CANVAS_CACHE;RED_DEBUG;CAIRO_CANVAS_NO_CHUNKS;_WIN32_WINNT=0x0500;LOG4CPLUS_STATIC;USE_GLZ;PTW32_STATIC_LIB;CEGUI_STATIC"
 				MinimalRebuild="false"
 				BasicRuntimeChecks="3"
 				RuntimeLibrary="1"
@@ -125,7 +125,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				AdditionalIncludeDirectories=".;..;..\..\common;..\..\common\win;&quot;..\..\common\win\my_getopt-1.5&quot;;&quot;$(SPICE_LIBS)\include&quot;;&quot;$(SPICE_LIBS)\include\qcairo&quot;;&quot;$(SPICE_LIBS)\include\ffmpeg&quot;;&quot;$(SPICE_LIBS)\include\CEGUI-0.6.2&quot;"
-				PreprocessorDefinitions="WIN32;_WINDOWS;CAIRO_CANVAS_ACCESS_TEST;CAIRO_CANVAS_CACHE;CAIRO_CANVAS_NO_CHUNKS;CAIRO_CANVAS_CACH_IS_SHARED;_WIN32_WINNT=0x0500;LOG4CPLUS_STATIC;USE_GLZ;PTW32_STATIC_LIB;CEGUI_STATIC"
+				PreprocessorDefinitions="WIN32;_WINDOWS;CAIRO_CANVAS_ACCESS_TEST;CAIRO_CANVAS_CACHE;CAIRO_CANVAS_NO_CHUNKS;_WIN32_WINNT=0x0500;LOG4CPLUS_STATIC;USE_GLZ;PTW32_STATIC_LIB;CEGUI_STATIC"
 				RuntimeLibrary="0"
 				UsePrecompiledHeader="0"
 				WarningLevel="3"
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index 47cb698..d645c38 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -5,7 +5,6 @@ SUBDIRS = images
 INCLUDES = \
 	-DCAIRO_CANVAS_ACCESS_TEST			\
 	-DCAIRO_CANVAS_CACHE				\
-	-DCAIRO_CANVAS_CACH_IS_SHARED			\
 	-DCAIRO_CANVAS_NO_CHUNKS			\
 	-DUSE_GLZ					\
 	-DUSE_OGL					\
commit e995040b1ea32c62eba542cfb0b28c50b4ac662c
Author: Alexander Larsson <alexl at redhat.com>
Date:   Thu Feb 18 20:44:28 2010 +0100

    Remove last cairo use from canvas_base

diff --git a/common/canvas_base.c b/common/canvas_base.c
index 8b2434b..09fdca0 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -17,7 +17,6 @@
 */
 
 #include <stdarg.h>
-#include <cairo.h>
 #include <stdlib.h>
 #include <setjmp.h>
 #include <stdio.h>
@@ -91,8 +90,6 @@ typedef struct __attribute__ ((__packed__)) LZImage {
     };
 } LZImage;
 
-static const cairo_user_data_key_t pixman_data_type = {0};
-
  static inline int fix_to_int(SPICE_FIXED28_4 fixed)
 {
     int val, rem;
commit 7992266ccfb42d4b1242b6b78b80801f511d4b95
Author: Alexander Larsson <alexl at redhat.com>
Date:   Thu Feb 18 20:42:58 2010 +0100

    Remove cairo_t from cairo canvas

diff --git a/client/red_cairo_canvas.cpp b/client/red_cairo_canvas.cpp
index c2496ff..87023e1 100644
--- a/client/red_cairo_canvas.cpp
+++ b/client/red_cairo_canvas.cpp
@@ -40,9 +40,7 @@ CCanvas::~CCanvas()
 void CCanvas::destroy()
 {
     if (_canvas) {
-        cairo_t *cairo = canvas_get_cairo(_canvas);
         canvas_destroy(_canvas);
-        cairo_destroy(cairo);
         _canvas = NULL;
     }
     destroy_pixmap();
@@ -90,30 +88,25 @@ void CCanvas::copy_pixels(const QRegion& region, RedDrawable* dest_dc, const Pix
 
 void CCanvas::set_mode(int width, int height, int depth, RedWindow *win)
 {
-    cairo_surface_t *cairo_surface;
-    cairo_t *cairo;
+    pixman_image_t *surface;
 
     destroy();
     create_pixmap(width, height, win);
-    cairo_surface = cairo_image_surface_create_for_data(_pixmap->get_data(), CAIRO_FORMAT_RGB24,
-                                                        width, height, _pixmap->get_stride());
-    if (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS) {
-        THROW("create surface failed, %s",
-              cairo_status_to_string(cairo_surface_status(cairo_surface)));
+    surface = pixman_image_create_bits(PIXMAN_x8r8g8b8, width, height,
+                                       (uint32_t *)_pixmap->get_data(),
+                                       _pixmap->get_stride());
+    if (surface == NULL) {
+        THROW("create surface failed, out of memory");
     }
 
-    cairo = cairo_create(cairo_surface);
-    cairo_surface_destroy(cairo_surface);
-    if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS) {
-        THROW("create cairo failed, %s", cairo_status_to_string(cairo_status(cairo)));
-    }
-    if (!(_canvas = canvas_create(cairo, depth,
+    if (!(_canvas = canvas_create(surface, depth,
                                   &pixmap_cache().base,
                                   &palette_cache().base,
                                   &glz_decoder(),
                                   glz_decode))) {
         THROW("create canvas failed");
     }
+    pixman_image_unref (surface);
 }
 
 void CCanvas::set_access_params(unsigned long base, unsigned long max)
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index d545d9e..15e4e24 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -30,7 +30,6 @@
 
 struct CairoCanvas {
     CanvasBase base;
-    cairo_t *cairo;
     uint32_t *private_data;
     int private_data_size;
     pixman_image_t *image;
@@ -2126,11 +2125,6 @@ void canvas_clear(CairoCanvas *canvas)
                            0);
 }
 
-cairo_t *canvas_get_cairo(CairoCanvas *canvas)
-{
-    return canvas->cairo;
-}
-
 #ifdef CAIRO_CANVAS_ACCESS_TEST
 void canvas_set_access_params(CairoCanvas *canvas, unsigned long base, unsigned long max)
 {
@@ -2154,14 +2148,14 @@ void canvas_destroy(CairoCanvas *canvas)
 static int need_init = 1;
 
 #ifdef CAIRO_CANVAS_CACHE
-CairoCanvas *canvas_create(cairo_t *cairo, int bits,
+CairoCanvas *canvas_create(pixman_image_t *image, int bits,
                            SpiceImageCache *bits_cache,
                            SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
-CairoCanvas *canvas_create(cairo_t *cairo, int bits,
+CairoCanvas *canvas_create(pixman_image_t *image, int bits,
                            SpiceImageCache *bits_cache
 #else
-CairoCanvas *canvas_create(cairo_t *cairo, int bits
+CairoCanvas *canvas_create(pixman_image_t *image, int bits
 #endif
 #ifdef USE_GLZ
                             , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
@@ -2202,12 +2196,10 @@ CairoCanvas *canvas_create(cairo_t *cairo, int bits
                                validate_virt
 #endif
                                );
-    canvas->cairo = cairo;
     canvas->private_data = NULL;
     canvas->private_data_size = 0;
-    cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
 
-    canvas->image = pixman_image_from_surface (cairo_get_target (cairo));
+    canvas->image = pixman_image_ref(image);
     pixman_region32_init_rect(&canvas->canvas_region,
                               0, 0,
                               pixman_image_get_width (canvas->image),
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
index 2b93509..205c62c 100644
--- a/common/cairo_canvas.h
+++ b/common/cairo_canvas.h
@@ -22,7 +22,7 @@
 #include <stdint.h>
 
 #include <spice/draw.h>
-#include "cairo.h"
+#include "pixman_utils.h"
 #include "canvas_base.h"
 #include "region.h"
 
@@ -59,17 +59,15 @@ void canvas_set_addr_delta(CairoCanvas *canvas, SPICE_ADDRESS delta);
 void canvas_set_access_params(CairoCanvas *canvas, unsigned long base, unsigned long max);
 #endif
 
-cairo_t *canvas_get_cairo(CairoCanvas *canvas);
-
 #ifdef CAIRO_CANVAS_CACHE
-CairoCanvas *canvas_create(cairo_t *cairo, int bits,
+CairoCanvas *canvas_create(pixman_image_t *image, int bits,
                            SpiceImageCache *bits_cache,
                            SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
-CairoCanvas *canvas_create(cairo_t *cairo, int bits,
+CairoCanvas *canvas_create(pixman_image_t *image, int bits,
                            SpiceImageCache *bits_cache
 #else
-CairoCanvas *canvas_create(cairo_t *cairo, int bits
+CairoCanvas *canvas_create(pixman_image_t *image, int bits
 #endif
 #ifdef USE_GLZ
                            , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 55404af..8b2434b 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -225,47 +225,6 @@ typedef struct ATTR_PACKED DataChunk {
 
 #endif
 
-#ifdef CANVAS_USE_PIXMAN
-
-static pixman_format_code_t
-pixman_format_from_cairo_format (cairo_format_t format)
-{
-    switch (format) {
-    case CAIRO_FORMAT_A1:
-        return PIXMAN_a1;
-    case CAIRO_FORMAT_A8:
-        return PIXMAN_a8;
-    case CAIRO_FORMAT_RGB24:
-        return PIXMAN_x8r8g8b8;
-    case CAIRO_FORMAT_ARGB32:
-    default:
-        return PIXMAN_a8r8g8b8;
-    }
-}
-
-static pixman_image_t *
-pixman_image_from_surface (cairo_surface_t *surface)
-{
-  pixman_image_t *image;
-  cairo_format_t format;
-
-  format = cairo_image_surface_get_format (surface);
-
-  image = (pixman_image_t *)cairo_surface_get_user_data(surface, &pixman_data_type);
-
-  if (image)
-      return pixman_image_ref (image);
-
-  image = pixman_image_create_bits (pixman_format_from_cairo_format (format),
-                                    cairo_image_surface_get_width (surface),
-                                    cairo_image_surface_get_height (surface),
-                                    (uint32_t *)cairo_image_surface_get_data (surface),
-                                    cairo_image_surface_get_stride (surface));
-
-  return image;
-}
-#endif
-
 static inline void canvas_localize_palette(CanvasBase *canvas, SpicePalette *palette)
 {
     if (canvas->color_shift == 5) {
diff --git a/server/red_worker.c b/server/red_worker.c
index fd0b20c..100f309 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -35,7 +35,6 @@
 #include "region.h"
 #include <spice/protocol.h>
 #include "red_worker.h"
-#include "cairo.h"
 #include "cairo_canvas.h"
 #include "gl_canvas.h"
 #include "ogl_ctx.h"
@@ -7445,15 +7444,11 @@ static void red_migrate_display(RedWorker *worker)
 
 static void destroy_cairo_canvas(CairoCanvas *canvas)
 {
-    cairo_t *cairo;
-
     if (!canvas) {
         return;
     }
 
-    cairo = canvas_get_cairo(canvas);
     canvas_destroy(canvas);
-    cairo_destroy(cairo);
 }
 
 static void validate_area_nop(void *canvas, int32_t stride, uint8_t *line_0, const SpiceRect *area)
@@ -7486,25 +7481,20 @@ static void init_cairo_draw_funcs(RedWorker *worker)
 static CairoCanvas *create_cairo_context(RedWorker *worker, uint32_t width, uint32_t height,
                                          int32_t stride, uint8_t depth, void *line_0)
 {
-    cairo_surface_t *cairo_surface;
-    cairo_t *cairo;
+    CairoCanvas *canvas;
+    pixman_image_t *surface;
 
-    cairo_surface = cairo_image_surface_create_for_data(line_0,  CAIRO_FORMAT_RGB24, width, height,
-                                                        stride);
-    if (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS) {
-        red_error("create cairo surface failed, %s",
-                  cairo_status_to_string(cairo_surface_status(cairo_surface)));
-    }
-    cairo = cairo_create(cairo_surface);
-    cairo_surface_destroy(cairo_surface);
-    if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS) {
-        red_error("create cairo failed, %s",
-                  cairo_status_to_string(cairo_status(cairo)));
+    surface = pixman_image_create_bits(PIXMAN_x8r8g8b8, width, height,
+                                       (uint32_t *)line_0,
+                                       stride);
+    if (surface == NULL) {
+        red_error("create cairo surface failed");
     }
-
-    return canvas_create(cairo, depth, &worker->image_cache.base,
-                         worker, cb_get_virt_preload_group, worker,
-                         cb_validate_virt_preload_group);
+    canvas = canvas_create(surface, depth, &worker->image_cache.base,
+                           worker, cb_get_virt_preload_group, worker,
+                           cb_validate_virt_preload_group);
+    pixman_image_unref (surface);
+    return canvas;
 }
 
 static void destroy_gl_canvas(GLCanvas *canvas)
commit b5693ca0fc06bfe367770a63395ba01ec18ee3b2
Author: Alexander Larsson <alexl at redhat.com>
Date:   Thu Feb 18 19:04:35 2010 +0100

    Covert cairo canvas put_image() to pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index a1f79d0..d545d9e 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -1110,106 +1110,62 @@ void canvas_draw_copy(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
 }
 
 #ifdef WIN32
-void canvas_put_image(CairoCanvas *canvas, HDC dc, const SpiceRect *dest, const uint8_t *_src_data,
+void canvas_put_image(CairoCanvas *canvas, HDC dc, const SpiceRect *dest, const uint8_t *src_data,
                       uint32_t src_width, uint32_t src_height, int src_stride,
                       const QRegion *clip)
 #else
-void canvas_put_image(CairoCanvas *canvas, const SpiceRect *dest, const uint8_t *_src_data,
+void canvas_put_image(CairoCanvas *canvas, const SpiceRect *dest, const uint8_t *src_data,
                       uint32_t src_width, uint32_t src_height, int src_stride,
                       const QRegion *clip)
 #endif
 {
-    cairo_t *cairo = canvas->cairo;
-    cairo_surface_t* surf = NULL;
+    pixman_image_t *src;
     int dest_width;
     int dest_height;
-    uint8_t *src_data = (uint8_t *)_src_data;
-    uint32_t *data;
-    int nstride;
+    double sx, sy;
+    pixman_transform_t transform;
+
+    src = pixman_image_create_bits(PIXMAN_x8r8g8b8,
+                                   src_width,
+                                   src_height,
+                                   (uint32_t*)src_data,
+                                   src_stride);
 
-    cairo_save(cairo);
 
     if (clip) {
-        int num_rects;
-        pixman_box32_t *rects = pixman_region32_rectangles((pixman_region32_t *)clip,
-                                                           &num_rects);
-        const pixman_box32_t *now = rects;
-        const pixman_box32_t *end = rects + num_rects;
-        for (; now < end; now++) {
-            cairo_rectangle(cairo, now->x1, now->y1, now->x2 - now->x1,
-                            now->y2 - now->y1);
-        }
-        cairo_clip(cairo);
+        pixman_image_set_clip_region32 (canvas->image, (pixman_region32_t *)clip);
     }
 
-
     dest_width = dest->right - dest->left;
     dest_height = dest->bottom - dest->top;
 
     if (dest_width != src_width || dest_height != src_height) {
-        int x, y;
-        int x_mul = (uint32_t)((src_width << 16) / dest_width);
-        int y_mul = (uint32_t)((src_height << 16) / dest_height);
-        int new_y;
-        int set_y;
-        int nsrc_stride;
-
-        if (src_stride < 0) {
-            nsrc_stride = -src_stride;
-            src_data = src_data - (src_height - 1) * nsrc_stride;
-            nsrc_stride = nsrc_stride / 4;
-        } else {
-            nsrc_stride = src_stride / 4;
-        }
-        if ((dest_width * dest_height) > canvas->private_data_size) {
-            if (canvas->private_data) {
-                free(canvas->private_data);
-                canvas->private_data = NULL;
-                canvas->private_data_size = 0;
-            }
-            canvas->private_data = (uint32_t *)malloc(4 * dest_width * dest_height);
-            if (!canvas->private_data) {
-                return;
-            }
-            canvas->private_data_size = dest_width * dest_height;
-        }
-        if (!clip) {
-            surf = cairo_get_target(cairo);
-            data = (uint32_t *)cairo_image_surface_get_data(surf);
-            nstride = cairo_image_surface_get_stride(surf) / 4;
-            data += dest->top * nstride + dest->left + (dest_height - 1) * nstride;
-        } else {
-            data = (uint32_t *)canvas->private_data;
-            nstride = dest_width;
-            data += (dest_height - 1) * nstride;
-        }
+        sx = (double)(src_width) / (dest_width);
+        sy = (double)(src_height) / (dest_height);
 
-        for (y = 0; y < dest_height; ++y) {
-            int y_mul_stride = -y * nstride;
-            new_y = ((y * y_mul) >> 16);
-            set_y = (new_y * nsrc_stride);
-            for (x = 0; x < dest_width; ++x) {
-                data[y_mul_stride + x] = ((uint32_t *)src_data)[set_y + ((x * x_mul) >> 16)];
-            }
-        }
-        if (clip) {
-            surf = cairo_image_surface_create_for_data((uint8_t *)canvas->private_data,
-                                                       CAIRO_FORMAT_RGB24, dest_width,
-                                                       dest_height, 4 * dest_width);
-        }
-    } else {
-        surf = cairo_image_surface_create_for_data((uint8_t *)src_data, CAIRO_FORMAT_RGB24,
-                                                   src_width, src_height, src_stride);
+        pixman_transform_init_scale(&transform,
+                                    pixman_double_to_fixed(sx),
+                                    pixman_double_to_fixed(sy));
+        pixman_image_set_transform(src, &transform);
+        pixman_image_set_filter(src,
+                                PIXMAN_FILTER_NEAREST,
+                                NULL, 0);
     }
 
-    if (clip || !(dest_width != src_width || dest_height != src_height)) {
-        cairo_set_source_surface(cairo, surf, dest->left, dest->top);
-        cairo_surface_destroy(surf);
+    pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
 
-        cairo_rectangle(cairo, dest->left, dest->top, dest_width, dest_height);
-        cairo_fill(cairo);
+    pixman_image_composite32(PIXMAN_OP_SRC,
+                             src, NULL, canvas->image,
+                             0, 0, /* src */
+                             0, 0, /* mask */
+                             dest->left, dest->top, /* dst */
+                             dest_width, dest_height);
+
+
+    if (clip) {
+        pixman_image_set_clip_region32(canvas->image, NULL);
     }
-    cairo_restore(cairo);
+    pixman_image_unref(src);
 }
 
 void canvas_draw_transparent(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
commit 98dde80ed3c01f6ac08bcd14d34d6643da9f8418
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Feb 17 21:33:23 2010 +0100

    Replace custom region implementation with pixman_region32_t
    
    pixman_region32_t is an efficient well tested region implementation (its
    the one used in X) that we already depend on via pixman and use in
    some places. No need to have a custom region implementation.

diff --git a/client/application.cpp b/client/application.cpp
index cca7d43..0fa25b4 100644
--- a/client/application.cpp
+++ b/client/application.cpp
@@ -171,13 +171,23 @@ InfoLayer::InfoLayer()
 
 void InfoLayer::draw_info(const QRegion& dest_region, RedDrawable& dest)
 {
-    for (int i = 0; i < (int)dest_region.num_rects; i++) {
-        SpiceRect* r = &dest_region.rects[i];
+    pixman_box32_t *rects;
+    int num_rects;
+
+    rects = pixman_region32_rectangles((pixman_region32_t *)&dest_region, &num_rects);
+    for (int i = 0; i < num_rects; i++) {
+        SpiceRect r;
+
+        r.left = rects[i].x1;
+        r.top = rects[i].y1;
+        r.right = rects[i].x2;
+        r.bottom = rects[i].y2;
+
         /* is rect inside sticky region or info region? */
-        if (_sticky_on && rect_intersects(*r, _sticky_rect)) {
-            dest.blend_pixels(_sticky_pixmap, r->left - _sticky_pos.x, r->top - _sticky_pos.y, *r);
+        if (_sticky_on && rect_intersects(r, _sticky_rect)) {
+            dest.blend_pixels(_sticky_pixmap, r.left - _sticky_pos.x, r.top - _sticky_pos.y, r);
         } else {
-            dest.blend_pixels(_info_pixmap, r->left - _info_pos.x, r->top - _info_pos.y, *r);
+            dest.blend_pixels(_info_pixmap, r.left - _info_pos.x, r.top - _info_pos.y, r);
         }
     }
 }
diff --git a/client/canvas.h b/client/canvas.h
index 1bd3180..6585656 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -20,6 +20,7 @@
 
 #include "common.h"
 #include "debug.h"
+#include "region.h"
 #include "cairo.h"
 #include <spice/protocol.h>
 #include "cache.hpp"
@@ -36,8 +37,6 @@ enum CanvasType {
     CANVAS_TYPE_GDI,
 };
 
-struct QRegion;
-
 class PixmapCacheTreat {
 public:
     static inline pixman_image_t *get(pixman_image_t *surf)
diff --git a/client/cursor_channel.cpp b/client/cursor_channel.cpp
index 30c52d9..0909f5c 100644
--- a/client/cursor_channel.cpp
+++ b/client/cursor_channel.cpp
@@ -434,16 +434,26 @@ void CursorChannel::remove_cursor()
 
 void CursorChannel::copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc)
 {
+    pixman_box32_t *rects;
+    int num_rects;
+
     Lock lock(_update_lock);
 
     if (!_cursor_visible) {
         return;
     }
 
-    for (int i = 0; i < (int)dest_region.num_rects; i++) {
+    rects = pixman_region32_rectangles((pixman_region32_t *)&dest_region, &num_rects);
+    for (int i = 0; i < num_rects; i++) {
+        SpiceRect r;
+
+        r.left = rects[i].x1;
+        r.top = rects[i].y1;
+        r.right = rects[i].x2;
+        r.bottom = rects[i].y2;
         ASSERT(_cursor && _cursor->get_opaque());
         ((NaitivCursor*)_cursor->get_opaque())->draw(dest_dc, _cursor_rect.left, _cursor_rect.top,
-                                                     dest_region.rects[i]);
+                                                     r);
     }
 }
 
diff --git a/client/gui/gui.cpp b/client/gui/gui.cpp
index e4d69ee..0ff442e 100644
--- a/client/gui/gui.cpp
+++ b/client/gui/gui.cpp
@@ -1025,19 +1025,33 @@ void GUI::update_layer_area()
 
 void GUI::copy_pixels(const QRegion& dest_region, RedDrawable& dest)
 {
+    pixman_box32_t *rects;
+    int num_rects;
+
     if (region_is_empty(&dest_region)) {
         return;
     }
 
-    for (int i = 0; i < (int)dest_region.num_rects; i++) {
-        SpiceRect* r = &dest_region.rects[i];
-        _pixmap->copy_pixels(dest, r->left, r->top, *r);
+    rects = pixman_region32_rectangles((pixman_region32_t *)&dest_region, &num_rects);
+    for (int i = 0; i < num_rects; i++) {
+        SpiceRect r;
+
+        r.left = rects[i].x1;
+        r.top = rects[i].y1;
+        r.right = rects[i].x2;
+        r.bottom = rects[i].y2;
+        _pixmap->copy_pixels(dest, r.left, r.top, r);
     }
 
     _gui_system->renderGUI();
-    for (int i = 0; i < (int)dest_region.num_rects; i++) {
-        SpiceRect* r = &dest_region.rects[i];
-        dest.copy_pixels(*_pixmap, r->left, r->top, *r);
+    for (int i = 0; i < num_rects; i++) {
+        SpiceRect r;
+
+        r.left = rects[i].x1;
+        r.top = rects[i].y1;
+        r.right = rects[i].x2;
+        r.bottom = rects[i].y2;
+        dest.copy_pixels(*_pixmap, r.left, r.top, r);
     }
 }
 
diff --git a/client/red_cairo_canvas.cpp b/client/red_cairo_canvas.cpp
index 80a89b7..c2496ff 100644
--- a/client/red_cairo_canvas.cpp
+++ b/client/red_cairo_canvas.cpp
@@ -68,9 +68,18 @@ void CCanvas::create_pixmap(int width, int height, RedWindow *win)
 
 void CCanvas::copy_pixels(const QRegion& region, RedDrawable& dest_dc)
 {
-    for (int i = 0; i < (int)region.num_rects; i++) {
-        SpiceRect* r = &region.rects[i];
-        dest_dc.copy_pixels(*_pixmap, r->left, r->top, *r);
+    pixman_box32_t *rects;
+    int num_rects;
+
+    rects = pixman_region32_rectangles((pixman_region32_t *)&region, &num_rects);
+    for (int i = 0; i < num_rects; i++) {
+        SpiceRect r;
+
+        r.left = rects[i].x1;
+        r.top = rects[i].y1;
+        r.right = rects[i].x2;
+        r.bottom = rects[i].y2;
+        dest_dc.copy_pixels(*_pixmap, r.left, r.top, r);
     }
 }
 
diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp
index 43bf424..6598425 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -69,9 +69,19 @@ void GCanvas::create_pixmap(int width, int height, RedWindow *win,
 
 void GCanvas::copy_pixels(const QRegion& region, RedDrawable& dest_dc)
 {
-    for (int i = 0; i < (int)region.num_rects; i++) {
-        SpiceRect* r = &region.rects[i];
-        dest_dc.copy_pixels(*_pixmap, r->left, r->top, *r);
+    pixman_box32_t *rects;
+    int num_rects;
+
+    rects = pixman_region32_rectangles((pixman_region32_t *)&region, &num_rects);
+    for (int i = 0; i < num_rects; i++) {
+        SpiceRect r;
+
+        r.left = rects[i].x1;
+        r.top = rects[i].y1;
+        r.right = rects[i].x2;
+        r.bottom = rects[i].y2;
+
+        dest_dc.copy_pixels(*_pixmap, r.left, r.top, r);
     }
 }
 
diff --git a/client/screen.cpp b/client/screen.cpp
index 9e6b04e..40e0676 100644
--- a/client/screen.cpp
+++ b/client/screen.cpp
@@ -288,9 +288,18 @@ void RedScreen::detach_layer(ScreenLayer& layer)
 
 void RedScreen::composit_to_screen(RedDrawable& win_dc, const QRegion& region)
 {
-    for (int i = 0; i < (int)region.num_rects; i++) {
-        SpiceRect* r = &region.rects[i];
-        win_dc.copy_pixels(*_composit_area, r->left, r->top, *r);
+    pixman_box32_t *rects;
+    int num_rects;
+
+    rects = pixman_region32_rectangles((pixman_region32_t *)&region, &num_rects);
+    for (int i = 0; i < num_rects; i++) {
+        SpiceRect r;
+
+        r.left = rects[i].x1;
+        r.top = rects[i].y1;
+        r.right = rects[i].x2;
+        r.bottom = rects[i].y2;
+        win_dc.copy_pixels(*_composit_area, r.left, r.top, r);
     }
 }
 
@@ -474,17 +483,40 @@ uint64_t RedScreen::invalidate(const SpiceRect& rect, bool urgent)
 
 void RedScreen::invalidate(const QRegion &region)
 {
-    SpiceRect *r = region.rects;
-    SpiceRect *end = r + region.num_rects;
-    while (r != end) {
-        invalidate(*r++, false);
+    pixman_box32_t *rects, *end;
+    int num_rects;
+
+    rects = pixman_region32_rectangles((pixman_region32_t *)&region, &num_rects);
+    end = rects + num_rects;
+
+    while (rects != end) {
+        SpiceRect r;
+
+        r.left = rects->x1;
+        r.top = rects->y1;
+        r.right = rects->x2;
+        r.bottom = rects->y2;
+        rects++;
+
+        invalidate(r, false);
     }
 }
 
 inline void RedScreen::erase_background(RedDrawable& dc, const QRegion& composit_rgn)
 {
-    for (int i = 0; i < (int)composit_rgn.num_rects; i++) {
-        dc.fill_rect(composit_rgn.rects[i], 0);
+    pixman_box32_t *rects;
+    int num_rects;
+
+    rects = pixman_region32_rectangles((pixman_region32_t *)&composit_rgn, &num_rects);
+    for (int i = 0; i < num_rects; i++) {
+        SpiceRect r;
+
+        r.left = rects[i].x1;
+        r.top = rects[i].y1;
+        r.right = rects[i].x2;
+        r.bottom = rects[i].y2;
+
+        dc.fill_rect(r, 0);
     }
 }
 
diff --git a/client/screen_layer.cpp b/client/screen_layer.cpp
index af25211..ef5ef95 100644
--- a/client/screen_layer.cpp
+++ b/client/screen_layer.cpp
@@ -93,13 +93,26 @@ uint64_t ScreenLayer::invalidate(const SpiceRect& r, bool urgent)
 
 void ScreenLayer::invalidate(const QRegion& region)
 {
+    pixman_box32_t *rects, *end;
+    int num_rects;
+
     if (!_screen) {
         return;
     }
-    SpiceRect *r = region.rects;
-    SpiceRect *end = r + region.num_rects;
-    while (r != end) {
-        invalidate_rect(*r++, false);
+
+    rects = pixman_region32_rectangles((pixman_region32_t *)&region, &num_rects);
+    end = rects + num_rects;
+
+    while (rects != end) {
+        SpiceRect r;
+
+        r.left = rects->x1;
+        r.top = rects->y1;
+        r.right = rects->x2;
+        r.bottom = rects->y2;
+        rects++;
+
+        invalidate_rect(r, false);
     }
 }
 
diff --git a/client/x11/red_window.cpp b/client/x11/red_window.cpp
index 49c5533..a38f7db 100644
--- a/client/x11/red_window.cpp
+++ b/client/x11/red_window.cpp
@@ -1835,7 +1835,10 @@ public:
         if (region_is_empty(_region)) {
             bbox.left = bbox.right = bbox.top = bbox.bottom = 0;
         } else {
-            bbox = _region->bbox;
+            bbox.left = _region->extents.x1;
+            bbox.top = _region->extents.y1;
+            bbox.right = _region->extents.x2;
+            bbox.bottom = _region->extents.y2;
         }
     }
 
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 1f92c34..a1f79d0 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -1130,11 +1130,14 @@ void canvas_put_image(CairoCanvas *canvas, const SpiceRect *dest, const uint8_t
     cairo_save(cairo);
 
     if (clip) {
-        const SpiceRect *now = clip->rects;
-        const SpiceRect *end = clip->rects + clip->num_rects;
+        int num_rects;
+        pixman_box32_t *rects = pixman_region32_rectangles((pixman_region32_t *)clip,
+                                                           &num_rects);
+        const pixman_box32_t *now = rects;
+        const pixman_box32_t *end = rects + num_rects;
         for (; now < end; now++) {
-            cairo_rectangle(cairo, now->left, now->top, now->right - now->left,
-                            now->bottom - now->top);
+            cairo_rectangle(cairo, now->x1, now->y1, now->x2 - now->x1,
+                            now->y2 - now->y1);
         }
         cairo_clip(cairo);
     }
@@ -2137,23 +2140,16 @@ void canvas_read_bits(CairoCanvas *canvas, uint8_t *dest, int dest_stride, const
     }
 }
 
-void canvas_group_start(CairoCanvas *canvas, int n_clip_rects, SpiceRect *clip_rects)
+void canvas_group_start(CairoCanvas *canvas, QRegion *region)
 {
-    pixman_region32_t dest_region;
-
     pixman_region32_fini(&canvas->canvas_region);
-    spice_pixman_region32_init_rects(&canvas->canvas_region,
-                                     clip_rects, n_clip_rects);
-
-    pixman_region32_init_rect(&dest_region,
-                              0, 0,
-                              pixman_image_get_width(canvas->image),
-                              pixman_image_get_height(canvas->image));
-
     /* Make sure we always clip to canvas size */
-    pixman_region32_intersect(&canvas->canvas_region, &canvas->canvas_region, &dest_region);
+    pixman_region32_init_rect(&canvas->canvas_region,
+                              0, 0,
+                              pixman_image_get_width (canvas->image),
+                              pixman_image_get_height (canvas->image));
 
-    pixman_region32_fini(&dest_region);
+    pixman_region32_intersect(&canvas->canvas_region, &canvas->canvas_region, region);
 }
 
 void canvas_group_end(CairoCanvas *canvas)
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
index b0c0820..2b93509 100644
--- a/common/cairo_canvas.h
+++ b/common/cairo_canvas.h
@@ -52,7 +52,7 @@ void canvas_put_image(CairoCanvas *canvas, const SpiceRect *dest, const uint8_t
 #endif
 void canvas_clear(CairoCanvas *canvas);
 void canvas_read_bits(CairoCanvas *canvas, uint8_t *dest, int dest_stride, const SpiceRect *area);
-void canvas_group_start(CairoCanvas *canvas, int n_clip_rects, SpiceRect *clip_rects);
+void canvas_group_start(CairoCanvas *canvas, QRegion *region);
 void canvas_group_end(CairoCanvas *canvas);
 void canvas_set_addr_delta(CairoCanvas *canvas, SPICE_ADDRESS delta);
 #ifdef CAIRO_CANVAS_ACCESS_TEST
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index 8d152ee..076090e 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -164,6 +164,13 @@ static GLCPath get_path(GLCanvas *canvas, void *addr)
     (dest)->height = (src)->bottom - (src)->top;    \
 }
 
+#define SET_GLC_BOX(dest, src) {                    \
+    (dest)->x = (src)->x1;                          \
+    (dest)->y = (src)->y1;                          \
+    (dest)->width = (src)->x2 - (src)->x1;          \
+    (dest)->height = (src)->y2 - (src)->y1;         \
+}
+
 static void set_clip(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip)
 {
     GLCRect rect;
@@ -706,14 +713,21 @@ void gl_canvas_read_pixels(GLCanvas *canvas, uint8_t *dest, int dest_stride, con
     glc_read_pixels(canvas->glc, area->left, area->top, &image);
 }
 
-void gl_canvas_set_top_mask(GLCanvas *canvas, int num_rect, const SpiceRect *rects)
+void gl_canvas_set_top_mask(GLCanvas *canvas, QRegion *region)
 {
-    GLCRect *glc_rects = (GLCRect *)malloc(num_rect * sizeof(GLCRect));
-    GLCRect *now = glc_rects;
-    GLCRect *end = glc_rects + num_rect;
+    GLCRect *glc_rects;
+    GLCRect *now, *end;
+    int num_rect;
+    pixman_box32_t *rects;
+
+    rects = pixman_region32_rectangles(region, &num_rect);
+
+    glc_rects = (GLCRect *)malloc(num_rect * sizeof(GLCRect));
+    now = glc_rects;
+    end = glc_rects + num_rect;
 
     for (; now < end; now++, rects++) {
-        SET_GLC_RECT(now, rects);
+        SET_GLC_BOX(now, rects);
     }
     glc_mask_rects(canvas->glc, num_rect, glc_rects, GLC_MASK_B);
 
@@ -733,15 +747,18 @@ void gl_canvas_put_image(GLCanvas *canvas, const SpiceRect *dest, const uint8_t
     glc_clip_reset(canvas->glc);
 
     if (clip) {
+        int num_rects;
+        pixman_box32_t *rects = pixman_region32_rectangles((pixman_region32_t *)clip,
+                                                           &num_rects);
         GLCRect rect;
-        if (clip->num_rects == 0) {
+        if (num_rects == 0) {
             rect.x = rect.y = rect.width = rect.height = 0;
             glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
         } else {
-            SET_GLC_RECT(&rect, clip->rects);
+            SET_GLC_BOX(&rect, rects);
             glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
-            for (i = 1; i < clip->num_rects; i++) {
-                SET_GLC_RECT(&rect, clip->rects + i);
+            for (i = 1; i < num_rects; i++) {
+                SET_GLC_BOX(&rect, rects + i);
                 glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_OR);
             }
         }
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index 794b1ea..39b2792 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -45,7 +45,7 @@ void gl_canvas_put_image(GLCanvas *canvas, const SpiceRect *dest, const uint8_t
 
 void gl_canvas_clear(GLCanvas *canvas);
 
-void gl_canvas_set_top_mask(GLCanvas *canvas, int num_rect, const SpiceRect *rects);
+void gl_canvas_set_top_mask(GLCanvas *canvas, QRegion *region);
 void gl_canvas_clear_top_mask(GLCanvas *canvas);
 
 #ifdef CAIRO_CANVAS_ACCESS_TEST
diff --git a/common/region.c b/common/region.c
index f0bb614..06b76d5 100644
--- a/common/region.c
+++ b/common/region.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
@@ -22,9 +23,6 @@
 #include "region.h"
 #include "rect.h"
 
-//#define ALLOC_ON_STEAL
-//#define REGION_DEBUG
-
 #define FALSE 0
 #define TRUE 1
 
@@ -36,927 +34,540 @@
     abort();                                                \
 }
 
-#ifdef REGION_DEBUG
-#define REGION_IS_VALID(region) region_is_valid(region)
-#else
-#define REGION_IS_VALID(region) TRUE
-#endif
+/*  true iff two Boxes overlap */
+#define EXTENTCHECK(r1, r2)        \
+    (!( ((r1)->x2 <= (r2)->x1)  || \
+        ((r1)->x1 >= (r2)->x2)  || \
+        ((r1)->y2 <= (r2)->y1)  || \
+        ((r1)->y1 >= (r2)->y2) ) )
 
-static int rect_is_valid(const SpiceRect *r)
-{
-    if (r->top > r->bottom || r->left > r->right) {
-        printf("%s: invalid rect\n", __FUNCTION__);
-        return FALSE;
-    }
-    return TRUE;
-}
+/* true iff Box r1 contains Box r2 */
+#define SUBSUMES(r1, r2)        \
+    ( ((r1)->x1 <= (r2)->x1) && \
+      ((r1)->x2 >= (r2)->x2) && \
+      ((r1)->y1 <= (r2)->y1) && \
+      ((r1)->y2 >= (r2)->y2) )
 
-#ifdef REGION_TEST
-static void rect_set(SpiceRect *r, int32_t top, int32_t left, int32_t bottom, int32_t right)
-{
-    r->top = top;
-    r->left = left;
-    r->bottom = bottom;
-    r->right = right;
-    ASSERT(rect_is_valid(r));
-}
-
-#endif
-
-static inline void __region_init(QRegion *rgn)
-{
-    rgn->num_rects = 0;
-    rgn->rects = rgn->buf;
-    rgn->rects_size = RECTS_BUF_SIZE;
-}
 
 void region_init(QRegion *rgn)
 {
-    __region_init(rgn);
-    ASSERT(REGION_IS_VALID(rgn));
+    pixman_region32_init(rgn);
 }
 
 void region_clear(QRegion *rgn)
 {
-    rgn->num_rects = 0;
+    pixman_region32_fini(rgn);
+    pixman_region32_init(rgn);
 }
 
 void region_destroy(QRegion *rgn)
 {
-    ASSERT(REGION_IS_VALID(rgn));
-    if (rgn->rects != rgn->buf) {
-        free(rgn->rects);
-    }
+    pixman_region32_fini(rgn);
 }
 
 void region_clone(QRegion *dest, const QRegion *src)
 {
-    ASSERT(REGION_IS_VALID(src));
-    dest->bbox = src->bbox;
-    if ((dest->num_rects = src->num_rects) <= RECTS_BUF_SIZE) {
-        dest->rects = dest->buf;
-        dest->rects_size = RECTS_BUF_SIZE;
-    } else {
-        dest->rects = (SpiceRect *)malloc(sizeof(SpiceRect) * dest->num_rects);
-        dest->rects_size = dest->num_rects;
-    }
-    memcpy(dest->rects, src->rects, dest->num_rects * sizeof(SpiceRect));
-    ASSERT(REGION_IS_VALID(src));
-    ASSERT(REGION_IS_VALID(dest));
+    pixman_region32_init(dest);
+    pixman_region32_copy(dest, (pixman_region32_t *)src);
 }
 
-int region_is_valid(const QRegion *rgn)
+#define FIND_BAND(r, r_band_end, r_end, ry1)                         \
+    do {                                                             \
+        ry1 = r->y1;                                                 \
+        r_band_end = r + 1;                                          \
+        while ((r_band_end != r_end) && (r_band_end->y1 == ry1)) {   \
+            r_band_end++;                                            \
+        }                                                            \
+    } while (0)
+
+static int test_band(int query,
+                     int res,
+                     pixman_box32_t *r1,
+                     pixman_box32_t *r1_end,
+                     pixman_box32_t *r2,
+                     pixman_box32_t *r2_end)
 {
-    if (rgn->num_rects) {
-        uint32_t i;
-        SpiceRect bbox;
+    int x1;
+    int x2;
 
-        if (!rect_is_valid(&rgn->bbox)) {
-            return FALSE;
-        }
-        bbox = rgn->rects[0];
-        if (!rect_is_valid(&bbox) || rect_is_empty(&bbox)) {
-            return FALSE;
-        }
-        for (i = 1; i < rgn->num_rects; i++) {
-            SpiceRect *r;
+    do {
+        x1 = MAX(r1->x1, r2->x1);
+        x2 = MIN(r1->x2, r2->x2);
+
+        /*
+         * Is there any overlap between the two rectangles?
+         */
+        if (x1 < x2) {
+            res |= REGION_TEST_SHARED;
 
-            r = &rgn->rects[i];
-            if (!rect_is_valid(r) || rect_is_empty(r)) {
-                return FALSE;
+            if (r1->x1 < r2->x1 || r1->x2 > r2->x2) {
+                res |= REGION_TEST_LEFT_EXCLUSIVE;
             }
 
-            SpiceRect *priv = r - 1;
-            if (r->top < priv->top) {
-                return FALSE;
-            } else if (r->top == priv->top) {
-                if (r->bottom != priv->bottom) {
-                    return FALSE;
-                }
-                if (r->left < priv->right) {
-                    return FALSE;
-                }
-            } else if (priv->bottom > r->top) {
-                return FALSE;
+            if (r2->x1 < r1->x1 || r2->x2 > r1->x2) {
+                res |= REGION_TEST_RIGHT_EXCLUSIVE;
+            }
+        } else {
+            /* No overlap at all, the leftmost is exclusive */
+            if (r1->x1 < r2->x1) {
+                res |= REGION_TEST_LEFT_EXCLUSIVE;
+            } else {
+                res |= REGION_TEST_RIGHT_EXCLUSIVE;
             }
-            bbox.top = MIN(bbox.top, r->top);
-            bbox.left = MIN(bbox.left, r->left);
-            bbox.bottom = MAX(bbox.bottom, r->bottom);
-            bbox.right = MAX(bbox.right, r->right);
         }
-        return rect_is_equal(&bbox, &rgn->bbox);
-    }
-    return TRUE;
-}
-
-void region_dump(const QRegion *rgn, const char *prefix)
-{
-    char *indent;
-    int len;
-    uint32_t i;
-
-    len = strlen(prefix);
-    if (!(indent = (char *)malloc(len + 1))) {
-        printf("%s: malloc failed\n", __FUNCTION__);
-        return;
-    }
-    memset(indent, ' ', len);
-    indent[len] = 0;
 
+        if ((res & query) == query) {
+            return res;
+        }
 
-    printf("%sREGION: %p, size %u storage is %s, ",
-           prefix,
-           rgn,
-           rgn->rects_size,
-           (rgn->rects == rgn->buf) ? "BUF" : "MALLOC");
+        /*
+         * Advance the pointer(s) with the leftmost right side, since the next
+         * rectangle on that list may still overlap the other region's
+         * current rectangle.
+         */
+        if (r1->x2 == x2) {
+            r1++;
+        }
+        if (r2->x2 == x2) {
+            r2++;
+        }
+    } while ((r1 != r1_end) && (r2 != r2_end));
+
+    /*
+     * Deal with whichever band (if any) still has rectangles left.
+     */
+    if (r1 != r1_end) {
+        res |= REGION_TEST_LEFT_EXCLUSIVE;
+    } else if (r2 != r2_end) {
+        res |= REGION_TEST_RIGHT_EXCLUSIVE;
+    }
+
+    return res;
+}
+
+static int test_generic (pixman_region32_t *reg1,
+                         pixman_region32_t *reg2,
+                         int query)
+{
+    pixman_box32_t *r1;             /* Pointer into first region     */
+    pixman_box32_t *r2;             /* Pointer into 2d region        */
+    pixman_box32_t *r1_end;         /* End of 1st region             */
+    pixman_box32_t *r2_end;         /* End of 2d region              */
+    int ybot;                       /* Bottom of intersection        */
+    int ytop;                       /* Top of intersection           */
+    pixman_box32_t * r1_band_end;   /* End of current band in r1     */
+    pixman_box32_t * r2_band_end;   /* End of current band in r2     */
+    int top;                        /* Top of non-overlapping band   */
+    int bot;                        /* Bottom of non-overlapping band*/
+    int r1y1;                       /* Temps for r1->y1 and r2->y1   */
+    int r2y1;
+    int r1_num_rects;
+    int r2_num_rects;
+    int res;
+
+    r1 = pixman_region32_rectangles(reg1, &r1_num_rects);
+    r1_end = r1 + r1_num_rects;
+
+    r2 = pixman_region32_rectangles(reg2, &r2_num_rects);
+    r2_end = r2 + r2_num_rects;
+
+    res = 0;
+
+    /*
+     * Initialize ybot.
+     * In the upcoming loop, ybot and ytop serve different functions depending
+     * on whether the band being handled is an overlapping or non-overlapping
+     * band.
+     *  In the case of a non-overlapping band (only one of the regions
+     * has points in the band), ybot is the bottom of the most recent
+     * intersection and thus clips the top of the rectangles in that band.
+     * ytop is the top of the next intersection between the two regions and
+     * serves to clip the bottom of the rectangles in the current band.
+     *  For an overlapping band (where the two regions intersect), ytop clips
+     * the top of the rectangles of both regions and ybot clips the bottoms.
+     */
+
+    ybot = MIN(r1->y1, r2->y1);
 
-    if (rgn->num_rects == 0) {
-        printf("EMPTY\n");
-        return;
-    }
+    do {
+        /*
+         * This algorithm proceeds one source-band (as opposed to a
+         * destination band, which is determined by where the two regions
+         * intersect) at a time. r1_band_end and r2_band_end serve to mark the
+         * rectangle after the last one in the current band for their
+         * respective regions.
+         */
+        FIND_BAND(r1, r1_band_end, r1_end, r1y1);
+        FIND_BAND(r2, r2_band_end, r2_end, r2y1);
+
+        /*
+         * First handle the band that doesn't intersect, if any.
+         *
+         * Note that attention is restricted to one band in the
+         * non-intersecting region at once, so if a region has n
+         * bands between the current position and the next place it overlaps
+         * the other, this entire loop will be passed through n times.
+         */
+        if (r1y1 < r2y1) {
+            top = MAX (r1y1, ybot);
+            bot = MIN (r1->y2, r2y1);
+            if (top != bot) {
+                res |= REGION_TEST_LEFT_EXCLUSIVE;
+
+                if ((res & query) == query) {
+                    return res & query;
+                }
+            }
 
-    printf("num %u bounds (%d, %d, %d, %d)\n",
-           rgn->num_rects,
-           rgn->bbox.top,
-           rgn->bbox.left,
-           rgn->bbox.bottom,
-           rgn->bbox.right);
-
-    for (i = 0; i < rgn->num_rects; i++) {
-        printf("%s  %12d %12d %12d %12d\n",
-               indent,
-               rgn->rects[i].top,
-               rgn->rects[i].left,
-               rgn->rects[i].bottom,
-               rgn->rects[i].right);
-    }
-    free(indent);
-    ASSERT(region_is_valid(rgn));
-}
+            ytop = r2y1;
+        } else if (r2y1 < r1y1) {
+            top = MAX (r2y1, ybot);
+            bot = MIN (r2->y2, r1y1);
 
-int region_is_empty(const QRegion *rgn)
-{
-    ASSERT(REGION_IS_VALID(rgn));
-    return !rgn->num_rects;
-}
+            if (top != bot) {
+                res |= REGION_TEST_RIGHT_EXCLUSIVE;
 
-#ifdef REGION_USE_IMPROVED
+                if ((res & query) == query) {
+                    return res & query;
+                }
+            }
+            ytop = r1y1;
+        } else {
+            ytop = r1y1;
+        }
 
-int region_is_equal(const QRegion *rgn1, const QRegion *rgn2)
-{
-    int test_res;
+        /*
+         * Now see if we've hit an intersecting band. The two bands only
+         * intersect if ybot > ytop
+         */
+        ybot = MIN (r1->y2, r2->y2);
+        if (ybot > ytop) {
+            res = test_band(query, res,
+                            r1, r1_band_end,
+                            r2, r2_band_end);
+            if ((res & query) == query) {
+                return res & query;
+            }
+        }
 
-    ASSERT(REGION_IS_VALID(rgn1));
-    ASSERT(REGION_IS_VALID(rgn2));
+        /*
+         * If we've finished with a band (y2 == ybot) we skip forward
+         * in the region to the next band.
+         */
+        if (r1->y2 == ybot) {
+            r1 = r1_band_end;
+        }
 
-    if (rgn1->num_rects == 0 || rgn2->num_rects == 0) {
-        return rgn1->num_rects == rgn2->num_rects;
-    }
+        if (r2->y2 == ybot) {
+            r2 = r2_band_end;
+        }
 
-    if (!rect_is_equal(&rgn1->bbox, &rgn2->bbox)) {
-        return FALSE;
     }
+    while (r1 != r1_end && r2 != r2_end);
 
-    test_res = region_test(rgn1, rgn2, REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE);
-    return !test_res;
-}
-
-#else
-
-int region_is_equal(const QRegion *rgn1, const QRegion *rgn2)
-{
-    QRegion tmp_rgn;
-    int ret;
-
-    ASSERT(REGION_IS_VALID(rgn1));
-    ASSERT(REGION_IS_VALID(rgn2));
+    /*
+     * Deal with whichever region (if any) still has rectangles left.
+     */
 
-    if (rgn1->num_rects == 0 || rgn2->num_rects == 0) {
-        return rgn1->num_rects == rgn2->num_rects;
+    if (r1 != r1_end) {
+        res |= REGION_TEST_LEFT_EXCLUSIVE;
+    } else if (r2 != r2_end) {
+        res |= REGION_TEST_RIGHT_EXCLUSIVE;
     }
 
-    if (!rect_is_equal(&rgn1->bbox, &rgn2->bbox)) {
-        return FALSE;
-    }
-
-    region_clone(&tmp_rgn, rgn1);
-    region_xor(&tmp_rgn, rgn2);
-    ret = region_is_empty(&tmp_rgn);
-    region_destroy(&tmp_rgn);
-    return ret;
+    return res & query;
 }
 
-#endif
-
-typedef struct RgnOpCtx {
-    SpiceRect *now;
-    SpiceRect *end;
-    SpiceRect *scan_line;
-    SpiceRect r;
-    SpiceRect split;
-#ifdef REGION_USE_IMPROVED
-    int abort;
-#endif
-} RgnOpCtx;
-
-static inline int op_ctx_is_valid(RgnOpCtx *ctx)
+int region_test(const QRegion *_reg1, const QRegion *_reg2, int query)
 {
-    return ctx->now != ctx->end;
-}
+    int res;
+    pixman_region32_t *reg1 = (pixman_region32_t *)_reg1;
+    pixman_region32_t *reg2 = (pixman_region32_t *)_reg2;
 
-static void op_context_next(RgnOpCtx *ctx)
-{
-    SpiceRect *now;
-    SpiceRect *next;
-
-    ASSERT(op_ctx_is_valid(ctx));
-    now = ctx->now;
-    next = now + 1;
-
-    if (next == ctx->end || now->top != next->top) {
-        if (now->bottom != ctx->r.bottom) { //h_split
-            ctx->r.top = ctx->r.bottom;
-            ctx->r.bottom = now->bottom;
-            next = ctx->scan_line;
-        } else {
-            if (next == ctx->end) {
-#ifdef REGION_USE_IMPROVED
-                ctx->scan_line = ++ctx->now;
-#else
-                ++ctx->now;
-#endif
-                ctx->r.top = ctx->r.left = ctx->r.bottom = ctx->r.right = (1U << 31) - 1;
-                return;
-            }
-            ctx->scan_line = next;
-            ctx->r.top = next->top;
-            ctx->r.bottom = next->bottom;
-        }
-    }
-    ctx->r.left = next->left;
-    ctx->r.right = next->right;
-    ctx->now = next;
-}
+    query = (query) ? query & REGION_TEST_ALL : REGION_TEST_ALL;
 
-static void op_context_init(RgnOpCtx *ctx, uint32_t num_rects, SpiceRect *rects)
-{
-    ctx->scan_line = ctx->now = rects;
-    ctx->end = ctx->now + num_rects;
-#ifdef REGION_USE_IMPROVED
-    ctx->abort = FALSE;
-#endif
-    if (!op_ctx_is_valid(ctx)) {
-        ctx->r.top = ctx->r.left = ctx->r.bottom = ctx->r.right = (1U << 31) - 1;
-    } else {
-        ctx->r = *ctx->now;
-    }
-}
+    res = 0;
 
-static inline void op_ctx_h_split(RgnOpCtx *ctx, int32_t h_line)
-{
-    ctx->r.bottom = h_line;
-    ctx->split = ctx->r;
-    op_context_next(ctx);
-}
-
-static inline void op_ctx_v_split(RgnOpCtx *ctx, int32_t v_line)
-{
-    ctx->split = ctx->r;
-    ctx->r.left = ctx->split.right = v_line;
-    if (rect_is_empty(&ctx->r)) {
-        op_context_next(ctx);
-    }
-}
+    if (!pixman_region32_not_empty(reg1) || !pixman_region32_not_empty(reg2) ||
+        !EXTENTCHECK (&reg1->extents, &reg2->extents)) {
+        /* One or more regions are empty or they are disjoint */
 
-static inline void op_ctx_split(RgnOpCtx *ctx, int32_t h_line)
-{
-    ASSERT(ctx->now == ctx->scan_line);
-    ctx->r.bottom = h_line;
-}
-
-static void region_steal_rects(QRegion *rgn, uint32_t *num_rects, SpiceRect **rects)
-{
-    ASSERT(REGION_IS_VALID(rgn));
-    if ((*num_rects = rgn->num_rects)) {
-        if (rgn->rects == rgn->buf) {
-            *rects = (SpiceRect *)malloc(sizeof(SpiceRect) * rgn->num_rects);
-            memcpy(*rects, rgn->rects, sizeof(SpiceRect) * rgn->num_rects);
-        } else {
-            *rects = rgn->rects;
-#ifdef ALLOC_ON_STEAL
-            rgn->rects = (SpiceRect *)malloc(sizeof(SpiceRect) * rgn->num_rects);
-            rgn->rects_size = rgn->num_rects;
-            rgn->num_rects = 0;
-            return;
-#endif
+        if (pixman_region32_not_empty(reg1)) {
+            res |= REGION_TEST_LEFT_EXCLUSIVE;
         }
-    } else {
-        *rects = NULL;
-    }
-    __region_init(rgn);
-    ASSERT(REGION_IS_VALID(rgn));
-}
 
-typedef struct JoinContext {
-    QRegion *rgn;
-    SpiceRect *line0;
-    SpiceRect *line1;
-    SpiceRect *end;
-} JoinContext;
+        if (pixman_region32_not_empty(reg2)) {
+            res |= REGION_TEST_RIGHT_EXCLUSIVE;
+        }
 
-static inline SpiceRect *__get_line(QRegion *rgn, SpiceRect *pos)
-{
-    SpiceRect *end = rgn->rects + rgn->num_rects;
+        return res & query;
+    } else if (!reg1->data && !reg2->data) {
+        /* Just two rectangles that intersect */
+        res |= REGION_TEST_SHARED;
 
-    if (pos < end) {
-        int32_t line_top = pos->top;
-        while (++pos < end && pos->top == line_top) {
-            ASSERT((pos - 1)->right < pos->left); //join in region_push_rect
+        if (!SUBSUMES(&reg1->extents, &reg2->extents)) {
+            res |= REGION_TEST_RIGHT_EXCLUSIVE;
         }
-    }
-    return pos;
-}
 
-static inline int region_join_init(QRegion *rgn, JoinContext *context)
-{
-    context->rgn = rgn;
-    context->end = __get_line(rgn, (context->line1 = rgn->rects));
-    return context->end != context->line1;
-}
+        if (!SUBSUMES(&reg2->extents, &reg1->extents)) {
+            res |= REGION_TEST_LEFT_EXCLUSIVE;
+        }
 
-static inline int region_join_next(JoinContext *context)
-{
-    context->line0 = context->line1;
-    context->line1 = context->end;
-    context->end = __get_line(context->rgn, context->line1);
-    return context->end != context->line1;
-}
+        return res & query;
+    } else if (!reg2->data && SUBSUMES (&reg2->extents, &reg1->extents)) {
+        /* reg2 is just a rect that contains all of reg1 */
 
-static inline void region_join_join(JoinContext *context)
-{
-    SpiceRect *pos_0 = context->line0;
-    SpiceRect *pos_1 = context->line1;
-    int32_t bottom;
-    QRegion *rgn;
+        res |= REGION_TEST_SHARED; /* some piece must be shared, because reg is not empty */
+        res |= REGION_TEST_RIGHT_EXCLUSIVE; /* reg2 contains all of reg1 and then some */
 
-    if (pos_0->bottom != pos_1->top) {
-        return;
-    }
+        return res & query;
+    } else if (!reg1->data && SUBSUMES (&reg1->extents, &reg2->extents)) {
+        /* reg1 is just a rect that contains all of reg2 */
 
-    if (pos_1 - pos_0 != context->end - pos_1) {
-        return;
-    }
+        res |= REGION_TEST_SHARED; /* some piece must be shared, because reg is not empty */
+        res |= REGION_TEST_LEFT_EXCLUSIVE; /* reg1 contains all of reg2 and then some */
 
-    for (; pos_1 < context->end; pos_0++, pos_1++) {
-        if (pos_0->left != pos_1->left || pos_0->right != pos_1->right) {
-            return;
-        }
-    }
-    bottom = context->line1->bottom;
-    pos_0 = context->line0;
-    for (; pos_0 < context->line1; pos_0++) {
-        pos_0->bottom = bottom;
+        return res & query;
+    } else if (reg1 == reg2) {
+        res |= REGION_TEST_SHARED;
+        return res & query;
+    } else {
+        /* General purpose intersection */
+        return test_generic (reg1, reg2, query);
     }
-    rgn = context->rgn;
-    memmove(context->line1, context->end,
-            (unsigned long)(rgn->rects + rgn->num_rects) - (unsigned long)context->end);
-    rgn->num_rects -= (context->line1 - context->line0);
-    context->end = context->line1;
-    context->line1 = context->line0;
 }
 
-static inline void region_join(QRegion *rgn)
+int region_is_valid(const QRegion *rgn)
 {
-    JoinContext context;
-
-    ASSERT(REGION_IS_VALID(rgn));
-
-    if (!region_join_init(rgn, &context)) {
-        return;
-    }
-    while (region_join_next(&context)) {
-        region_join_join(&context);
-    }
-
-    ASSERT(REGION_IS_VALID(rgn));
+    return pixman_region32_selfcheck((pixman_region32_t *)rgn);
 }
 
-static void region_push_rect(QRegion *rgn, SpiceRect *r)
+int region_is_empty(const QRegion *rgn)
 {
-    ASSERT(REGION_IS_VALID(rgn));
-    ASSERT(rect_is_valid(r));
-    if (rgn->num_rects == 0) {
-        rgn->num_rects++;
-        rgn->rects[0] = rgn->bbox = *r;
-        return;
-    } else {
-        SpiceRect *priv = &rgn->rects[rgn->num_rects - 1];
-
-        if (priv->top == r->top && priv->right == r->left) {
-            ASSERT(priv->bottom == r->bottom);
-            priv->right = r->right;
-            rgn->bbox.right = MAX(rgn->bbox.right, priv->right);
-            return;
-        }
-        if (rgn->rects_size == rgn->num_rects) {
-            SpiceRect *old = rgn->rects;
-            rgn->rects_size = rgn->rects_size * 2;
-            rgn->rects = (SpiceRect *)malloc(sizeof(SpiceRect) * rgn->rects_size);
-            memcpy(rgn->rects, old, sizeof(SpiceRect) * rgn->num_rects);
-            if (old != rgn->buf) {
-                free(old);
-            }
-        }
-        rgn->rects[rgn->num_rects++] = *r;
-        rect_union(&rgn->bbox, r);
-    }
+    return !pixman_region32_not_empty((pixman_region32_t *)rgn);
 }
 
-#ifdef REGION_USE_IMPROVED
-
-static SpiceRect *op_context_find_area_below(RgnOpCtx *ctx, int32_t val)
+SpiceRect *region_dup_rects(const QRegion *rgn, uint32_t *num_rects)
 {
-    SpiceRect *start = ctx->now;
-    SpiceRect *end = ctx->end;
+    pixman_box32_t *boxes;
+    SpiceRect *rects;
+    int n, i;
 
-    while (start != end) {
-        int pos = (end - start) / 2;
-        if (start[pos].bottom <= val) {
-            start = &start[pos + 1];
-        } else {
-            end = &start[pos];
-        }
+    boxes = pixman_region32_rectangles((pixman_region32_t *)rgn, &n);
+    if (num_rects) {
+        *num_rects = n;
     }
-    return start;
-}
-
-static int op_context_skip_v(RgnOpCtx *ctx, int32_t top)
-{
-    SpiceRect *end = op_context_find_area_below(ctx, top);
-    if (end != ctx->now) {
-        ctx->now = ctx->scan_line = end;
-        if (ctx->now == ctx->end) {
-            ctx->r.top = ctx->r.left = ctx->r.bottom = ctx->r.right = (1U << 31) - 1;
-        } else {
-            ctx->r = *ctx->now;
-        }
-        return TRUE;
+    rects = (SpiceRect *)malloc(sizeof(SpiceRect)*n);
+    for (i = 0; i < n; i++) {
+        rects[i].left = boxes[i].x1;
+        rects[i].top = boxes[i].y1;
+        rects[i].right = boxes[i].x2;
+        rects[i].bottom = boxes[i].y2;
     }
-    return FALSE;
+    return rects;
 }
 
-typedef void (*op_callback_t)(RgnOpCtx *context, SpiceRect *, SpiceRect *);
 
-static void op_context_skip(RgnOpCtx *self, RgnOpCtx *other, op_callback_t on_self,
-                            op_callback_t on_other)
+int region_is_equal(const QRegion *rgn1, const QRegion *rgn2)
 {
-    SpiceRect *save1 = self->now;
-    SpiceRect *save2 = other->now;
-    int more;
-    do {
-        op_context_skip_v(self, other->r.top);
-        if (save1 != self->now) {
-            if (on_self) {
-                on_self(self, save1, self->now);
-            }
-            save1 = self->now;
-        }
-        more = op_context_skip_v(other, self->r.top);
-        if (save2 != other->now) {
-            if (on_other) {
-                on_other(self, save2, other->now);
-            }
-            save2 = other->now;
-        }
-    } while (more && !self->abort);
+    return pixman_region32_equal((pixman_region32_t *)rgn1, (pixman_region32_t *)rgn2);
 }
 
-static inline int op_context_more_overlap(RgnOpCtx *ctx, int32_t *bottom)
+int region_intersects(const QRegion *rgn1, const QRegion *rgn2)
 {
-    if (!op_ctx_is_valid(ctx)) {
+    int test_res;
+
+    if (!region_bounds_intersects(rgn1, rgn2)) {
         return FALSE;
     }
 
-    if (ctx->scan_line->bottom > *bottom && ctx->scan_line->top < *bottom) {
-        *bottom = ctx->scan_line->bottom;
-    }
-    return ctx->scan_line->top < *bottom;
+    test_res = region_test(rgn1, rgn2, REGION_TEST_SHARED);
+    return !!test_res;
 }
 
-static inline void op_context_overlap(RgnOpCtx *self, RgnOpCtx *other, op_callback_t on_self,
-                                      op_callback_t on_other, op_callback_t on_both)
+int region_bounds_intersects(const QRegion *rgn1, const QRegion *rgn2)
 {
-    int32_t bottom = MAX(self->scan_line->bottom, other->scan_line->bottom);
+    pixman_box32_t *extents1, *extents2;
 
-    do {
-        if (self->r.top < other->r.top) {
-            op_ctx_h_split(self, MIN(other->r.top, self->r.bottom));
-            if (on_self) {
-                on_self(self, &self->split, &self->split + 1);
-            }
-        } else if (self->r.top > other->r.top) {
-            op_ctx_h_split(other, MIN(self->r.top, other->r.bottom));
-            if (on_other) {
-                on_other(self, &other->split, &other->split + 1);
-            }
-        } else {
-            if (self->r.bottom > other->r.bottom) {
-                op_ctx_split(self, other->r.bottom);
-            } else if (other->r.bottom > self->r.bottom) {
-                op_ctx_split(other, self->r.bottom);
-            }
-            if (self->r.left < other->r.left) {
-                op_ctx_v_split(self, MIN(other->r.left, self->r.right));
-                if (on_self) {
-                    on_self(self, &self->split, &self->split + 1);
-                }
-            } else if (self->r.left > other->r.left) {
-                op_ctx_v_split(other, MIN(self->r.left, other->r.right));
-                if (on_other) {
-                    on_other(self, &other->split, &other->split + 1);
-                }
-            } else {
-                int32_t right = MIN(self->r.right, other->r.right);
-                op_ctx_v_split(self, right);
-                op_ctx_v_split(other, right);
-                if (on_both) {
-                    on_both(self, &self->split, &self->split + 1);
-                }
-            }
-        }
-    } while (!self->abort && (op_context_more_overlap(self, &bottom) ||
-                              op_context_more_overlap(other, &bottom)));
-}
+    extents1 = pixman_region32_extents((pixman_region32_t *)rgn1);
+    extents2 = pixman_region32_extents((pixman_region32_t *)rgn1);
 
-static inline void op_context_op(RgnOpCtx *self, RgnOpCtx *other, op_callback_t on_self,
-                                 op_callback_t on_other, op_callback_t on_both)
-{
-    for (;;) {
-        op_context_skip(self, other, on_self, on_other);
-        if (self->abort || !op_ctx_is_valid(self)) {
-            ASSERT(self->abort || !op_ctx_is_valid(other));
-            return;
-        }
-        op_context_overlap(self, other, on_self, on_other, on_both);
-    }
+    return EXTENTCHECK(extents1, extents2);
 }
 
-typedef struct SelfOpCtx {
-    RgnOpCtx ctx;
-    QRegion *rgn;
-} SelfOpCtx;
-
-static void add_rects(RgnOpCtx *ctx, SpiceRect *now, SpiceRect *end)
+int region_contains(const QRegion *rgn, const QRegion *other)
 {
-    SelfOpCtx *self_ctx = (SelfOpCtx *)ctx;
-    for (; now < end; now++) {
-        region_push_rect(self_ctx->rgn, now);
-    }
+    int test_res;
+
+    test_res = region_test(rgn, other, REGION_TEST_RIGHT_EXCLUSIVE);
+    return !test_res;
 }
 
-static void region_op(QRegion *rgn, const QRegion *other_rgn, op_callback_t on_self,
-                      op_callback_t on_other, op_callback_t on_both)
+int region_contains_point(const QRegion *rgn, int32_t x, int32_t y)
 {
-    SelfOpCtx self;
-    RgnOpCtx other;
-    uint32_t num_rects;
-    SpiceRect *rects;
-
-    region_steal_rects(rgn, &num_rects, &rects);
-    op_context_init(&self.ctx, num_rects, rects);
-    self.rgn = rgn;
-    op_context_init(&other, other_rgn->num_rects, other_rgn->rects);
-    op_context_op(&self.ctx, &other, on_self, on_other, on_both);
-    free(rects);
-    region_join(rgn);
+    return pixman_region32_contains_point((pixman_region32_t *)rgn, x, y, NULL);
 }
 
 void region_or(QRegion *rgn, const QRegion *other_rgn)
 {
-    region_op(rgn, other_rgn, add_rects, add_rects, add_rects);
+    pixman_region32_union(rgn, rgn, (pixman_region32_t *)other_rgn);
 }
 
 void region_and(QRegion *rgn, const QRegion *other_rgn)
 {
-    if (!region_bounds_intersects(rgn, other_rgn)) {
-        region_clear(rgn);
-        return;
-    }
-    region_op(rgn, other_rgn, NULL, NULL, add_rects);
+    pixman_region32_intersect(rgn, rgn, (pixman_region32_t *)other_rgn);
 }
 
 void region_xor(QRegion *rgn, const QRegion *other_rgn)
 {
-    region_op(rgn, other_rgn, add_rects, add_rects, NULL);
-}
+    pixman_region32_t intersection;
 
-void region_exclude(QRegion *rgn, const QRegion *other_rgn)
-{
-    if (!region_bounds_intersects(rgn, other_rgn)) {
-        return;
-    }
-    region_op(rgn, other_rgn, add_rects, NULL, NULL);
+    pixman_region32_copy(&intersection, rgn);
+    pixman_region32_intersect(&intersection,
+                              &intersection,
+                              (pixman_region32_t *)other_rgn);
+    pixman_region32_union(rgn, rgn, (pixman_region32_t *)other_rgn);
+    pixman_region32_subtract(rgn, rgn, &intersection);
+    pixman_region32_fini(&intersection);
 }
 
-typedef struct TestOpCtx {
-    RgnOpCtx ctx;
-    int result;
-    int abort_on;
-} TestOpCtx;
-
-
-static void region_test_on_self(RgnOpCtx *ctx, SpiceRect *now, SpiceRect *end)
+void region_exclude(QRegion *rgn, const QRegion *other_rgn)
 {
-    TestOpCtx *test_ctx = (TestOpCtx *)ctx;
-    test_ctx->result |= REGION_TEST_LEFT_EXCLUSIVE;
-    test_ctx->result &= test_ctx->abort_on;
-    if (test_ctx->result == test_ctx->abort_on) {
-        test_ctx->ctx.abort = TRUE;
-    }
+    pixman_region32_subtract(rgn, rgn, (pixman_region32_t *)other_rgn);
 }
 
-static void region_test_on_other(RgnOpCtx *ctx, SpiceRect *now, SpiceRect *end)
+void region_add(QRegion *rgn, const SpiceRect *r)
 {
-    TestOpCtx *test_ctx = (TestOpCtx *)ctx;
-    test_ctx->result |= REGION_TEST_RIGHT_EXCLUSIVE;
-    test_ctx->result &= test_ctx->abort_on;
-    if (test_ctx->result == test_ctx->abort_on) {
-        test_ctx->ctx.abort = TRUE;
-    }
+    pixman_region32_union_rect(rgn, rgn, r->left, r->top,
+                               r->right - r->left,
+                               r->bottom - r->top);
 }
 
-static void region_test_on_both(RgnOpCtx *ctx, SpiceRect *now, SpiceRect *end)
+void region_remove(QRegion *rgn, const SpiceRect *r)
 {
-    TestOpCtx *test_ctx = (TestOpCtx *)ctx;
-    test_ctx->result |= REGION_TEST_SHARED;
-    test_ctx->result &= test_ctx->abort_on;
-    if (test_ctx->result == test_ctx->abort_on) {
-        test_ctx->ctx.abort = TRUE;
-    }
-}
+    pixman_region32_t rg;
 
-int region_test(const QRegion *rgn, const QRegion *other_rgn, int query)
-{
-    TestOpCtx self;
-    RgnOpCtx other;
-
-    op_context_init(&self.ctx, rgn->num_rects, rgn->rects);
-    self.result = 0;
-    self.abort_on = (query) ? query & REGION_TEST_ALL : REGION_TEST_ALL;
-    op_context_init(&other, other_rgn->num_rects, other_rgn->rects);
-    op_context_op(&self.ctx, &other, region_test_on_self, region_test_on_other,
-                  region_test_on_both);
-    return self.result;
+    pixman_region32_init_rect(&rg, r->left, r->top,
+                              r->right - r->left,
+                              r->bottom - r->top);
+    pixman_region32_subtract(rgn, rgn, &rg);
+    pixman_region32_fini(&rg);
 }
 
-#else
-
-#define RIGION_OP_ADD_SELF (1 << 0)
-#define RIGION_OP_ADD_OTHER (1 << 1)
-#define RIGION_OP_ADD_COMMON (1 << 2)
 
-static inline void region_on_self(QRegion *rgn, SpiceRect *r, uint32_t op)
+void region_offset(QRegion *rgn, int32_t dx, int32_t dy)
 {
-    ASSERT(REGION_IS_VALID(rgn));
-    if (op & RIGION_OP_ADD_SELF) {
-        region_push_rect(rgn, r);
-    }
+    pixman_region32_translate(rgn, dx, dy);
 }
 
-static inline void region_on_other(QRegion *rgn, SpiceRect *r, uint32_t op)
+void region_dump(const QRegion *rgn, const char *prefix)
 {
-    ASSERT(REGION_IS_VALID(rgn));
-    if (op & RIGION_OP_ADD_OTHER) {
-        region_push_rect(rgn, r);
-    }
-}
+    pixman_box32_t *rects, *extents;
+    int n_rects, i;
 
-static inline void region_on_both(QRegion *rgn, SpiceRect *r, uint32_t op)
-{
-    ASSERT(REGION_IS_VALID(rgn));
-    if (op & RIGION_OP_ADD_COMMON) {
-        region_push_rect(rgn, r);
-    }
-}
+    printf("%sREGION: %p, ", prefix, rgn);
 
-static void region_op(QRegion *rgn, const QRegion *other_rgn, uint32_t op)
-{
-    RgnOpCtx self;
-    RgnOpCtx other;
-    uint32_t num_rects;
-    SpiceRect *rects;
+    if (!pixman_region32_not_empty((pixman_region32_t *)rgn)) {
+        printf("EMPTY\n");
+        return;
+    }
 
-    ASSERT(REGION_IS_VALID(rgn));
-    ASSERT(REGION_IS_VALID(other_rgn));
-    region_steal_rects(rgn, &num_rects, &rects);
+    extents = pixman_region32_extents((pixman_region32_t *)rgn);
+    rects = pixman_region32_rectangles((pixman_region32_t *)rgn, &n_rects);
+    printf("num %u bounds (%d, %d, %d, %d)\n",
+           n_rects,
+           extents->x1,
+           extents->y1,
+           extents->x2,
+           extents->y2);
 
-    op_context_init(&self, num_rects, rects);
-    op_context_init(&other, other_rgn->num_rects, other_rgn->rects);
 
-    while (op_ctx_is_valid(&self) || op_ctx_is_valid(&other)) {
-        if (self.r.top < other.r.top) {
-            op_ctx_h_split(&self, MIN(other.r.top, self.r.bottom));
-            region_on_self(rgn, &self.split, op);
-        } else if (self.r.top > other.r.top) {
-            op_ctx_h_split(&other, MIN(self.r.top, other.r.bottom));
-            region_on_other(rgn, &other.split, op);
-        } else {
-            if (self.r.bottom > other.r.bottom) {
-                op_ctx_split(&self, other.r.bottom);
-            } else if (other.r.bottom > self.r.bottom) {
-                op_ctx_split(&other, self.r.bottom);
-            }
-            if (self.r.left < other.r.left) {
-                op_ctx_v_split(&self, MIN(other.r.left, self.r.right));
-                region_on_self(rgn, &self.split, op);
-            } else if (self.r.left > other.r.left) {
-                op_ctx_v_split(&other, MIN(self.r.left, other.r.right));
-                region_on_other(rgn, &other.split, op);
-            } else {
-                int32_t right = MIN(self.r.right, other.r.right);
-                op_ctx_v_split(&self, right);
-                op_ctx_v_split(&other, right);
-                region_on_both(rgn, &self.split, op);
-            }
-        }
+    for (i = 0; i < n_rects; i++) {
+        printf("%*s  %12d %12d %12d %12d\n",
+               (int)strlen(prefix), "",
+               rects[i].x1,
+               rects[i].y1,
+               rects[i].x2,
+               rects[i].y2);
     }
-    free(rects);
-    region_join(rgn);
-}
-
-void region_or(QRegion *rgn, const QRegion *other_rgn)
-{
-    region_op(rgn, other_rgn, RIGION_OP_ADD_SELF | RIGION_OP_ADD_OTHER | RIGION_OP_ADD_COMMON);
-}
-
-void region_and(QRegion *rgn, const QRegion *other_rgn)
-{
-    region_op(rgn, other_rgn, RIGION_OP_ADD_COMMON);
 }
 
-void region_xor(QRegion *rgn, const QRegion *other_rgn)
-{
-    region_op(rgn, other_rgn, RIGION_OP_ADD_SELF | RIGION_OP_ADD_OTHER);
-}
+#ifdef REGION_TEST
 
-void region_exclude(QRegion *rgn, const QRegion *other_rgn)
+static int slow_region_test(const QRegion *rgn, const QRegion *other_rgn, int query)
 {
-    region_op(rgn, other_rgn, RIGION_OP_ADD_SELF);
-}
+    pixman_region32_t intersection;
+    int res;
 
-#endif
+    pixman_region32_init(&intersection);
+    pixman_region32_intersect(&intersection,
+                              (pixman_region32_t *)rgn,
+                              (pixman_region32_t *)other_rgn);
 
+    res = 0;
 
-void region_offset(QRegion *rgn, int32_t dx, int32_t dy)
-{
-    SpiceRect *now;
-    SpiceRect *end;
-    ASSERT(REGION_IS_VALID(rgn));
-    if (region_is_empty(rgn)) {
-        return;
-    }
-    rect_offset(&rgn->bbox, dx, dy);
-    now = rgn->rects;
-    end = now + rgn->num_rects;
-    for (; now < end; now++) {
-        rect_offset(now, dx, dy);
+    if (query & REGION_TEST_SHARED &&
+        pixman_region32_not_empty(&intersection)) {
+        res |= REGION_TEST_SHARED;
     }
-}
 
-void region_add(QRegion *rgn, const SpiceRect *r)
-{
-    ASSERT(REGION_IS_VALID(rgn));
-    ASSERT(rect_is_valid(r));
+    if (query & REGION_TEST_LEFT_EXCLUSIVE &&
+        !pixman_region32_equal(&intersection, (pixman_region32_t *)rgn)) {
+        res |= REGION_TEST_LEFT_EXCLUSIVE;
+    }
 
-    if (!rgn->num_rects) {
-        if (rect_is_empty(r)) {
-            return;
-        }
-        rgn->num_rects++;
-        rgn->rects[0] = rgn->bbox = *r;
-    } else {
-        QRegion rect_rgn;
-        region_init(&rect_rgn);
-        region_add(&rect_rgn, r);
-        region_or(rgn, &rect_rgn);
+    if (query & REGION_TEST_RIGHT_EXCLUSIVE &&
+        !pixman_region32_equal(&intersection, (pixman_region32_t *)other_rgn)) {
+        res |= REGION_TEST_RIGHT_EXCLUSIVE;
     }
-}
 
-void region_remove(QRegion *rgn, const SpiceRect *r)
-{
-    ASSERT(REGION_IS_VALID(rgn));
-    ASSERT(rect_is_valid(r));
-    if (rgn->num_rects) {
-        QRegion rect_rgn;
+    pixman_region32_fini(&intersection);
 
-        region_init(&rect_rgn);
-        region_add(&rect_rgn, r);
-        region_exclude(rgn, &rect_rgn);
-    }
+    return res;
 }
 
-#ifdef REGION_USE_IMPROVED
 
-int region_intersects(const QRegion *rgn1, const QRegion *rgn2)
+static int rect_is_valid(const SpiceRect *r)
 {
-    int test_res;
-
-    ASSERT(REGION_IS_VALID(rgn1));
-    ASSERT(REGION_IS_VALID(rgn2));
-
-    if (!region_bounds_intersects(rgn1, rgn2)) {
+    if (r->top > r->bottom || r->left > r->right) {
+        printf("%s: invalid rect\n", __FUNCTION__);
         return FALSE;
     }
-
-    test_res = region_test(rgn1, rgn2, REGION_TEST_SHARED);
-    return !!test_res;
-}
-
-#else
-
-int region_intersects(const QRegion *rgn1, const QRegion *rgn2)
-{
-    QRegion tmp;
-    int ret;
-
-    ASSERT(REGION_IS_VALID(rgn1));
-    ASSERT(REGION_IS_VALID(rgn2));
-
-    region_clone(&tmp, rgn1);
-    region_and(&tmp, rgn2);
-    ret = !region_is_empty(&tmp);
-    region_destroy(&tmp);
-    return ret;
-}
-
-#endif
-
-int region_bounds_intersects(const QRegion *rgn1, const QRegion *rgn2)
-{
-    return !region_is_empty(rgn1) && !region_is_empty(rgn2) &&
-           rect_intersects(&rgn1->bbox, &rgn2->bbox);
-}
-
-#ifdef REGION_USE_IMPROVED
-
-int region_contains(const QRegion *rgn, const QRegion *other)
-{
-    int test_res;
-
-    ASSERT(REGION_IS_VALID(rgn));
-    ASSERT(REGION_IS_VALID(other));
-
-    test_res = region_test(rgn, other, REGION_TEST_RIGHT_EXCLUSIVE);
-    return !test_res;
+    return TRUE;
 }
 
-#else
-
-int region_contains(const QRegion *rgn, const QRegion *other)
+static void rect_set(SpiceRect *r, int32_t top, int32_t left, int32_t bottom, int32_t right)
 {
-    QRegion tmp;
-    int ret;
-
-    ASSERT(REGION_IS_VALID(rgn));
-    ASSERT(REGION_IS_VALID(other));
-
-    region_clone(&tmp, rgn);
-    region_and(&tmp, other);
-    ret = region_is_equal(&tmp, other);
-    region_destroy(&tmp);
-    return ret;
+    r->top = top;
+    r->left = left;
+    r->bottom = bottom;
+    r->right = right;
+    ASSERT(rect_is_valid(r));
 }
 
-#endif
-
-int region_contains_point(const QRegion *rgn, int32_t x, int32_t y)
+static void random_region(QRegion *reg)
 {
-    if (region_is_empty(rgn)) {
-        return FALSE;
-    }
-    SpiceRect point;
-    point.left = x;
-    point.right = point.left + 1;
-    point.top = y;
-    point.bottom = point.top + 1;
-
-    if (!rect_intersects(&rgn->bbox, &point)) {
-        return FALSE;
-    }
+    int i;
+    int num_rects;
+    int x, y, w, h;
+    SpiceRect _r;
+    SpiceRect *r = &_r;
 
-    SpiceRect* now = rgn->rects;
-    SpiceRect* end = now + rgn->num_rects;
+    region_clear(reg);
 
-    for (; now < end; now++) {
-        if (rect_intersects(now, &point)) {
-            return TRUE;
-        }
+    num_rects = rand() % 20;
+    for (i = 0; i < num_rects; i++) {
+        x = rand()%100;
+        y = rand()%100;
+        w = rand()%100;
+        h = rand()%100;
+        rect_set(r,
+                 x, y,
+                 x+w, y+h);
+        region_add(reg, r);
     }
-    return FALSE;
 }
 
-#ifdef REGION_TEST
-
 static void test(const QRegion *r1, const QRegion *r2, int *expected)
 {
     printf("r1 is_empty %s [%s]\n",
@@ -993,6 +604,7 @@ int main(void)
     SpiceRect _r;
     SpiceRect *r = &_r;
     int expected[5];
+    int i, j;
 
     region_init(r1);
     region_init(r2);
@@ -1218,6 +830,36 @@ int main(void)
     test(r1, r3, expected);
     printf("\n");
 
+    j = 0;
+    for (i = 0; i < 1000000; i++) {
+        int res1, res2, test;
+        int tests[] = {
+            REGION_TEST_LEFT_EXCLUSIVE,
+            REGION_TEST_RIGHT_EXCLUSIVE,
+            REGION_TEST_SHARED,
+            REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE,
+            REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_SHARED,
+            REGION_TEST_RIGHT_EXCLUSIVE | REGION_TEST_SHARED,
+            REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE | REGION_TEST_SHARED
+        };
+
+        random_region(r1);
+        random_region(r2);
+
+        for (test = 0; test < 7; test++) {
+            res1 = region_test(r1, r2, tests[test]);
+            res2 = slow_region_test(r1, r2, tests[test]);
+            if (res1 != res2) {
+                printf ("Error in region_test %d, got %d, expected %d, query=%d\n",
+                        j, res1, res2, tests[test]);
+                printf ("r1:\n");
+                region_dump(r1, "");
+                printf ("r2:\n");
+                region_dump(r2, "");
+            }
+            j++;
+        }
+    }
 
     region_destroy(r3);
     region_destroy(r1);
diff --git a/common/region.h b/common/region.h
index f981ef3..c0bf408 100644
--- a/common/region.h
+++ b/common/region.h
@@ -21,20 +21,9 @@
 
 #include <stdint.h>
 #include <spice/draw.h>
+#include <pixman_utils.h>
 
-#define REGION_USE_IMPROVED
-
-#define RECTS_BUF_SIZE 4
-
-typedef struct QRegion {
-    uint32_t num_rects;
-    SpiceRect bbox;
-    SpiceRect *rects;
-    uint32_t rects_size;
-    SpiceRect buf[RECTS_BUF_SIZE];
-} QRegion;
-
-#ifdef REGION_USE_IMPROVED
+typedef pixman_region32_t QRegion;
 
 #define REGION_TEST_LEFT_EXCLUSIVE (1 << 0)
 #define REGION_TEST_RIGHT_EXCLUSIVE (1 << 1)
@@ -42,16 +31,13 @@ typedef struct QRegion {
 #define REGION_TEST_ALL \
     (REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE | REGION_TEST_SHARED)
 
-#endif
-
 void region_init(QRegion *rgn);
 void region_clear(QRegion *rgn);
 void region_destroy(QRegion *rgn);
 void region_clone(QRegion *dest, const QRegion *src);
+SpiceRect *region_dup_rects(const QRegion *rgn, uint32_t *num_rects);
 
-#ifdef REGION_USE_IMPROVED
 int region_test(const QRegion *rgn, const QRegion *other_rgn, int query);
-#endif
 int region_is_valid(const QRegion *rgn);
 int region_is_empty(const QRegion *rgn);
 int region_is_equal(const QRegion *rgn1, const QRegion *rgn2);
diff --git a/server/red_worker.c b/server/red_worker.c
index b2cb1f6..fd0b20c 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -427,7 +427,8 @@ typedef struct StreamClipItem {
     int refs;
     StreamAgent *stream_agent;
     int clip_type;
-    QRegion region;
+    SpiceRect *rects;
+    uint32_t n_rects;
 } StreamClipItem;
 
 typedef struct RedCompressBuf RedCompressBuf;
@@ -826,7 +827,8 @@ typedef struct UpgradeItem {
     PipeItem base;
     int refs;
     Drawable *drawable;
-    QRegion region;
+    SpiceRect *rects;
+    uint32_t n_rects;
 } UpgradeItem;
 
 typedef void (*draw_fill_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
@@ -843,7 +845,7 @@ typedef void (*draw_invers_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, Sp
 typedef void (*draw_transparent_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent);
 typedef void (*draw_alpha_blend_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend);
 typedef void (*read_pixels_t)(void *canvas, uint8_t *dest, int dest_stride, const SpiceRect *area);
-typedef void (*set_top_mask_t)(void *canvas, int num_rect, const SpiceRect *rects);
+typedef void (*set_top_mask_t)(void *canvas, QRegion *region);
 typedef void (*clear_top_mask_t)(void *canvas);
 typedef void (*validate_area_t)(void *canvas, int32_t stride, uint8_t *line_0, const SpiceRect *area);
 typedef void (*destroy_t)(void *canvas);
@@ -1306,10 +1308,10 @@ static void show_draw_item(RedWorker *worker, DrawItem *draw_item, const char *p
     }
     printf("effect %d bbox(%d %d %d %d)\n",
            draw_item->effect,
-           draw_item->base.rgn.bbox.top,
-           draw_item->base.rgn.bbox.left,
-           draw_item->base.rgn.bbox.bottom,
-           draw_item->base.rgn.bbox.right);
+           draw_item->base.rgn.extents.x1,
+           draw_item->base.rgn.extents.y1,
+           draw_item->base.rgn.extents.x2,
+           draw_item->base.rgn.extents.y2);
 }
 
 static inline void red_pipe_item_init(PipeItem *item, int type)
@@ -1428,7 +1430,7 @@ static void release_upgrade_item(RedWorker* worker, UpgradeItem *item)
 {
     if (!--item->refs) {
         release_drawable(worker, item->drawable);
-        region_destroy(&item->region);
+        free(item->rects);
         free(item);
     }
 }
@@ -1904,13 +1906,13 @@ static inline void __exclude_region(RedWorker *worker, TreeItem *item, QRegion *
 
             if (draw->shadow) {
                 Shadow *shadow;
-                int32_t x = item->rgn.bbox.left;
-                int32_t y = item->rgn.bbox.top;
+                int32_t x = item->rgn.extents.x1;
+                int32_t y = item->rgn.extents.y1;
 
                 region_exclude(&draw->base.rgn, &and_rgn);
                 shadow = draw->shadow;
-                region_offset(&and_rgn, shadow->base.rgn.bbox.left - x,
-                              shadow->base.rgn.bbox.top - y);
+                region_offset(&and_rgn, shadow->base.rgn.extents.x1 - x,
+                              shadow->base.rgn.extents.y1 - y);
                 region_exclude(&shadow->base.rgn, &and_rgn);
                 region_and(&and_rgn, &shadow->on_hold);
                 if (!region_is_empty(&and_rgn)) {
@@ -2295,11 +2297,13 @@ static void push_stream_clip_by_drawable(DisplayChannel* channel, StreamAgent *a
     }
 
     if (drawable->qxl_drawable->clip.type == SPICE_CLIP_TYPE_NONE) {
-        region_init(&item->region);
+        item->n_rects = 0;
+        item->rects = NULL;
         item->clip_type = SPICE_CLIP_TYPE_NONE;
     } else {
         item->clip_type = SPICE_CLIP_TYPE_RECTS;
-        region_clone(&item->region, &drawable->tree_item.base.rgn);
+        item->rects = region_dup_rects(&drawable->tree_item.base.rgn,
+                                       &item->n_rects);
     }
     red_pipe_add((RedChannel*)channel, (PipeItem *)item);
 }
@@ -2311,7 +2315,8 @@ static void push_stream_clip(DisplayChannel* channel, StreamAgent *agent)
         PANIC("alloc failed");
     }
     item->clip_type = SPICE_CLIP_TYPE_RECTS;
-    region_clone(&item->region, &agent->vis_region);
+    item->rects = region_dup_rects(&agent->vis_region,
+                                   &item->n_rects);
     red_pipe_add((RedChannel*)channel, (PipeItem *)item);
 }
 
@@ -2319,7 +2324,9 @@ static void red_display_release_stream_clip(DisplayChannel* channel, StreamClipI
 {
     if (!--item->refs) {
         red_display_release_stream(channel, item->stream_agent);
-        region_destroy(&item->region);
+        if (item->rects) {
+            free(item->rects);
+        }
         free(item);
     }
 }
@@ -2387,7 +2394,8 @@ static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *strea
         red_pipe_item_init(&upgrade_item->base, PIPE_ITEM_TYPE_UPGRADE);
         upgrade_item->drawable = stream->current;
         upgrade_item->drawable->refs++;
-        region_clone(&upgrade_item->region, &upgrade_item->drawable->tree_item.base.rgn);
+        upgrade_item->rects = region_dup_rects(&upgrade_item->drawable->tree_item.base.rgn,
+                                               &upgrade_item->n_rects);
         red_pipe_add((RedChannel *)channel, &upgrade_item->base);
     }
     red_detach_stream(worker, stream);
@@ -2404,7 +2412,8 @@ static inline void red_stop_stream_gracefully(RedWorker *worker, Stream *stream)
             red_pipe_item_init(&item->base, PIPE_ITEM_TYPE_UPGRADE);
             item->drawable = stream->current;
             item->drawable->refs++;
-            region_clone(&item->region, &item->drawable->tree_item.base.rgn);
+            item->rects = region_dup_rects(&item->drawable->tree_item.base.rgn,
+                                           &item->n_rects);
             red_pipe_add((RedChannel *)worker->display_channel, &item->base);
         }
     }
@@ -4304,8 +4313,7 @@ static void red_draw_drawable(RedWorker *worker, Drawable *drawable)
 #ifdef UPDATE_AREA_BY_TREE
     //todo: add need top mask flag
     worker->draw_funcs.set_top_mask(worker->surface.context.canvas,
-                                    drawable->tree_item.base.rgn.num_rects,
-                                    drawable->tree_item.base.rgn.rects);
+                                    &drawable->tree_item.base.rgn);
 #endif
     red_draw_qxl_drawable(worker, drawable);
 #ifdef UPDATE_AREA_BY_TREE
@@ -6980,8 +6988,8 @@ static void red_display_send_upgrade(DisplayChannel *display_channel, UpgradeIte
     copy->base.box = qxl_drawable->bbox;
     copy->base.clip.type = SPICE_CLIP_TYPE_RECTS;
     copy->base.clip.data = channel->send_data.header.size;
-    add_buf(channel, BUF_TYPE_RAW, &item->region.num_rects, sizeof(uint32_t), 0, 0);
-    add_buf(channel, BUF_TYPE_RAW, item->region.rects, sizeof(SpiceRect) * item->region.num_rects, 0, 0);
+    add_buf(channel, BUF_TYPE_RAW, &item->n_rects, sizeof(uint32_t), 0, 0);
+    add_buf(channel, BUF_TYPE_RAW, item->rects, sizeof(SpiceRect) * item->n_rects, 0, 0);
     copy->data = qxl_drawable->u.copy;
     fill_bits(display_channel, &copy->data.src_bitmap, item->drawable);
 
@@ -7046,9 +7054,8 @@ static void red_display_send_stream_clip(DisplayChannel *display_channel,
     } else {
         ASSERT(stream_clip->clip.type == SPICE_CLIP_TYPE_RECTS);
         stream_clip->clip.data = channel->send_data.header.size;
-        add_buf(channel, BUF_TYPE_RAW, &item->region.num_rects, sizeof(uint32_t), 0, 0);
-        add_buf(channel, BUF_TYPE_RAW, item->region.rects,
-                item->region.num_rects * sizeof(SpiceRect), 0, 0);
+        add_buf(channel, BUF_TYPE_RAW, &item->n_rects, sizeof(uint32_t), 0, 0);
+        add_buf(channel, BUF_TYPE_RAW, item->rects, item->n_rects * sizeof(SpiceRect), 0, 0);
     }
     display_begin_send_massage(display_channel, item);
 }
commit 8f912e49179803fa640b3bddf75b62e81b2f7178
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Feb 17 16:11:18 2010 +0100

    Convert cairo canvas clear() to pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index c73bfbd..1f92c34 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -2167,14 +2167,11 @@ void canvas_group_end(CairoCanvas *canvas)
 
 void canvas_clear(CairoCanvas *canvas)
 {
-    cairo_t *cairo = canvas->cairo;
-
-    ASSERT(cairo);
-    cairo_save(cairo);
-    cairo_reset_clip(cairo);
-    cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
-    cairo_paint(cairo);
-    cairo_restore(cairo);
+    spice_pixman_fill_rect(canvas->image,
+                           0, 0,
+                           pixman_image_get_width(canvas->image),
+                           pixman_image_get_height(canvas->image),
+                           0);
 }
 
 cairo_t *canvas_get_cairo(CairoCanvas *canvas)
commit d430a6c79b8ca4d0823172bb668ca460e9513306
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Feb 17 16:09:27 2010 +0100

    Convert cairo canvas group_start/end to pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 25fb079..c73bfbd 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -2139,26 +2139,30 @@ void canvas_read_bits(CairoCanvas *canvas, uint8_t *dest, int dest_stride, const
 
 void canvas_group_start(CairoCanvas *canvas, int n_clip_rects, SpiceRect *clip_rects)
 {
-    cairo_t *cairo = canvas->cairo;
+    pixman_region32_t dest_region;
 
-    cairo_save(cairo);
+    pixman_region32_fini(&canvas->canvas_region);
+    spice_pixman_region32_init_rects(&canvas->canvas_region,
+                                     clip_rects, n_clip_rects);
 
-    if (n_clip_rects) {
-        SpiceRect *end = clip_rects + n_clip_rects;
-        for (; clip_rects < end; clip_rects++) {
-            cairo_rectangle(cairo,
-                            clip_rects->left,
-                            clip_rects->top,
-                            clip_rects->right - clip_rects->left,
-                            clip_rects->bottom - clip_rects->top);
-        }
-        cairo_clip(cairo);
-    }
+    pixman_region32_init_rect(&dest_region,
+                              0, 0,
+                              pixman_image_get_width(canvas->image),
+                              pixman_image_get_height(canvas->image));
+
+    /* Make sure we always clip to canvas size */
+    pixman_region32_intersect(&canvas->canvas_region, &canvas->canvas_region, &dest_region);
+
+    pixman_region32_fini(&dest_region);
 }
 
 void canvas_group_end(CairoCanvas *canvas)
 {
-    cairo_restore(canvas->cairo);
+    pixman_region32_fini(&canvas->canvas_region);
+    pixman_region32_init_rect(&canvas->canvas_region,
+                              0, 0,
+                              pixman_image_get_width(canvas->image),
+                              pixman_image_get_height(canvas->image));
 }
 
 void canvas_clear(CairoCanvas *canvas)
commit ceff9ca6428d5cb3768d87c9cd9bcba19831d344
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Feb 17 16:09:06 2010 +0100

    Convert cairo canvas read_bits() to pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index a183777..25fb079 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -2120,18 +2120,17 @@ void canvas_draw_stroke(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
 
 void canvas_read_bits(CairoCanvas *canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
 {
-    cairo_t *cairo = canvas->cairo;
-    cairo_surface_t* surface;
+    pixman_image_t* surface;
     uint8_t *src;
     int src_stride;
     uint8_t *dest_end;
 
     ASSERT(canvas && area);
 
-    surface = cairo_get_target(cairo);
-    src_stride = cairo_image_surface_get_stride(surface);
-    src = cairo_image_surface_get_data(surface) + area->top * src_stride +
-                                                  area->left * sizeof(uint32_t);
+    surface = canvas->image;
+    src_stride = pixman_image_get_stride(surface);
+    src = (uint8_t *)pixman_image_get_data(surface) +
+        area->top * src_stride + area->left * sizeof(uint32_t);
     dest_end = dest + (area->bottom - area->top) * dest_stride;
     for (; dest != dest_end; dest += dest_stride, src += src_stride) {
         memcpy(dest, src, dest_stride);
commit f7c071514bfe745dba9748f9bcab2c9f47b62c63
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Feb 23 12:02:29 2010 +0100

    Remove unused cairo helper functions

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 2b73f8f..a183777 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -334,345 +334,6 @@ static void canvas_mask_pixman(CairoCanvas *canvas,
     pixman_image_unref(image);
 }
 
-static inline void canvas_invers_32bpp(uint8_t *dest, int dest_stride, uint8_t *src, int src_stride,
-                                       int width, uint8_t *end)
-{
-    for (; src != end; src += src_stride, dest += dest_stride) {
-        uint32_t *src_line = (uint32_t *)src;
-        uint32_t *src_line_end = src_line + width;
-        uint32_t *dest_line = (uint32_t *)dest;
-
-        while (src_line < src_line_end) {
-            *(dest_line++) = ~*(src_line++) & 0x00ffffff;
-        }
-    }
-}
-
-static inline void canvas_invers_24bpp(uint8_t *dest, int dest_stride, uint8_t *src, int src_stride,
-                                       int width, uint8_t *end)
-{
-    for (; src != end; src += src_stride, dest += dest_stride) {
-        uint8_t *src_line = src;
-        uint8_t *src_line_end = src_line + width * 3;
-        uint8_t *dest_line = dest;
-
-        for (; src_line < src_line_end; ++dest_line) {
-            *(dest_line++) = ~*(src_line++);
-            *(dest_line++) = ~*(src_line++);
-            *(dest_line++) = ~*(src_line++);
-        }
-    }
-}
-
-static inline void canvas_invers_16bpp(uint8_t *dest, int dest_stride, uint8_t *src, int src_stride,
-                                       int width, uint8_t *end)
-{
-    for (; src != end; src += src_stride, dest += dest_stride) {
-        uint16_t *src_line = (uint16_t*)src;
-        uint16_t *src_line_end = src_line + width;
-        uint32_t *dest_line = (uint32_t*)dest;
-
-        for (; src_line < src_line_end; ++dest_line, src_line++) {
-            *dest_line = ~canvas_16bpp_to_32bpp(*src_line) & 0x00ffffff;
-        }
-    }
-}
-
-static inline void canvas_invers_8bpp(uint8_t *dest, int dest_stride, uint8_t *src, int src_stride,
-                                      int width, uint8_t *end, SpicePalette *palette)
-{
-    if (!palette) {
-        CANVAS_ERROR("no palette");
-    }
-
-    for (; src != end; src += src_stride, dest += dest_stride) {
-        uint32_t *dest_line = (uint32_t*)dest;
-        uint8_t *src_line = src;
-        uint8_t *src_line_end = src_line + width;
-
-        while (src_line < src_line_end) {
-            ASSERT(*src_line < palette->num_ents);
-            *(dest_line++) = ~palette->ents[*(src_line++)] & 0x00ffffff;
-        }
-    }
-}
-
-static inline void canvas_invers_4bpp_be(uint8_t* dest, int dest_stride, uint8_t* src,
-                                         int src_stride, int width, uint8_t* end,
-                                         SpicePalette *palette)
-{
-    if (!palette) {
-        CANVAS_ERROR("no palette");
-    }
-
-    for (; src != end; src += src_stride, dest += dest_stride) {
-        uint32_t *dest_line = (uint32_t *)dest;
-        uint8_t *now = src;
-        int i;
-
-        for (i = 0; i < (width >> 1); i++) {
-            ASSERT((*now & 0x0f) < palette->num_ents);
-            ASSERT(((*now >> 4) & 0x0f) < palette->num_ents);
-            *(dest_line++) = ~palette->ents[(*now >> 4) & 0x0f] & 0x00ffffff;
-            *(dest_line++) = ~palette->ents[*(now++) & 0x0f] & 0x00ffffff;
-        }
-        if (width & 1) {
-            *(dest_line) = ~palette->ents[(*src >> 4) & 0x0f] & 0x00ffffff;
-        }
-    }
-}
-
-static inline void canvas_invers_1bpp_be(uint8_t* dest, int dest_stride, uint8_t* src,
-                                         int src_stride, int width, uint8_t* end,
-                                         SpicePalette *palette)
-{
-    uint32_t fore_color;
-    uint32_t back_color;
-
-    if (!palette) {
-        CANVAS_ERROR("no palette");
-    }
-
-    fore_color = palette->ents[1];
-    back_color = palette->ents[0];
-
-    for (; src != end; src += src_stride, dest += dest_stride) {
-        uint32_t* dest_line = (uint32_t*)dest;
-        int i;
-
-        for (i = 0; i < width; i++) {
-            if (test_bit_be(src, i)) {
-                *(dest_line++) = ~fore_color & 0x00ffffff;
-            } else {
-                *(dest_line++) = ~back_color & 0x00ffffff;
-            }
-        }
-    }
-}
-
-static pixman_image_t *canvas_bitmap_to_invers_surface(CairoCanvas *canvas, SpiceBitmap* bitmap,
-                                                        SpicePalette *palette)
-{
-    uint8_t* src = (uint8_t *)SPICE_GET_ADDRESS(bitmap->data);
-    int src_stride;
-    uint8_t* end;
-    uint8_t* dest;
-    int dest_stride;
-    pixman_image_t* surface;
-
-    src_stride = bitmap->stride;
-    end = src + (bitmap->y * src_stride);
-    access_test(&canvas->base, src, bitmap->y * src_stride);
-
-    surface = pixman_image_create_bits((bitmap->format == SPICE_BITMAP_FMT_RGBA) ?
-                                       PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
-                                       bitmap->x, bitmap->y, NULL, 0);
-    if (surface == NULL) {
-        CANVAS_ERROR("create surface failed");
-    }
-    dest = (uint8_t *)pixman_image_get_data(surface);
-    dest_stride = pixman_image_get_stride(surface);
-
-    if (!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
-        ASSERT(bitmap->y > 0);
-        dest += dest_stride * (bitmap->y - 1);
-        dest_stride = -dest_stride;
-    }
-    switch (bitmap->format) {
-    case SPICE_BITMAP_FMT_32BIT:
-    case SPICE_BITMAP_FMT_RGBA:
-        canvas_invers_32bpp(dest, dest_stride, src, src_stride, bitmap->x, end);
-        break;
-    case SPICE_BITMAP_FMT_24BIT:
-        canvas_invers_24bpp(dest, dest_stride, src, src_stride, bitmap->x, end);
-        break;
-    case SPICE_BITMAP_FMT_16BIT:
-        canvas_invers_16bpp(dest, dest_stride, src, src_stride, bitmap->x, end);
-        break;
-    case SPICE_BITMAP_FMT_8BIT:
-        canvas_invers_8bpp(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
-        break;
-    case SPICE_BITMAP_FMT_4BIT_BE:
-        canvas_invers_4bpp_be(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
-        break;
-    case SPICE_BITMAP_FMT_1BIT_BE:
-        canvas_invers_1bpp_be(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
-        break;
-    }
-    return surface;
-}
-
-#if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
-
-#ifndef CAIRO_CANVAS_CACHE
-static SpicePalette *canvas_get_palette(CairoCanvas *canvas, SpiceBitmap *bitmap)
-{
-    SpicePalette *local_palette;
-    SpicePalette *palette;
-    int size;
-
-    if (!bitmap->palette) {
-        return NULL;
-    }
-
-    palette = (SpicePalette *)SPICE_GET_ADDRESS(bitmap->palette);
-    if (canvas->base.color_shift != 5) {
-        return palette;
-    }
-
-    size = sizeof(SpicePalette) + (palette->num_ents << 2);
-    local_palette = malloc(size);
-    memcpy(local_palette, palette, size);
-    canvas_localize_palette(&canvas->base, palette);
-
-    return local_palette;
-}
-
-static void free_palette(SpiceBitmap *bitmap, SpicePalette *palette)
-{
-    if (!palette || palette == SPICE_GET_ADDRESS(bitmap->palette)) {
-        return;
-    }
-    free(palette);
-}
-
-#endif
-
-static pixman_image_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRESS addr)
-{
-    SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
-    pixman_image_t *surface;
-    pixman_image_t *invers = NULL;
-
-    access_test(&canvas->base, descriptor, sizeof(SpiceImageDescriptor));
-
-    int cache_me = descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_ME;
-
-    switch (descriptor->type) {
-    case SPICE_IMAGE_TYPE_QUIC: {
-        SpiceQUICImage *image = (SpiceQUICImage *)descriptor;
-        access_test(&canvas->base, descriptor, sizeof(SpiceQUICImage));
-        if (cache_me) {
-            surface = canvas_get_quic(&canvas->base, image, 0);
-        } else {
-            return canvas_get_quic(&canvas->base, image, 1);
-        }
-        break;
-    }
-#ifdef CAIRO_CANVAS_NO_CHUNKS
-    case SPICE_IMAGE_TYPE_LZ_PLT: {
-        access_test(&canvas->base, descriptor, sizeof(SpiceLZPLTImage));
-        LZImage *image = (LZImage *)descriptor;
-        if (cache_me) {
-            surface = canvas_get_lz(&canvas->base, image, 0);
-        } else {
-            return canvas_get_lz(&canvas->base, image, 1);
-        }
-        break;
-    }
-    case SPICE_IMAGE_TYPE_LZ_RGB: {
-        access_test(&canvas->base, descriptor, sizeof(SpiceLZRGBImage));
-        LZImage *image = (LZImage *)descriptor;
-        if (cache_me) {
-            surface = canvas_get_lz(&canvas->base, image, 0);
-        } else {
-            return canvas_get_lz(&canvas->base, image, 1);
-        }
-        break;
-    }
-#endif
-#ifdef USE_GLZ
-    case SPICE_IMAGE_TYPE_GLZ_RGB: {
-        access_test(&canvas->base, descriptor, sizeof(SpiceLZRGBImage));
-        LZImage *image = (LZImage *)descriptor;
-        surface = canvas_get_glz(&canvas->base, image);
-        break;
-    }
-#endif
-    case SPICE_IMAGE_TYPE_FROM_CACHE:
-        surface = canvas->base.bits_cache->ops->get(canvas->base.bits_cache, descriptor->id);
-        break;
-    case SPICE_IMAGE_TYPE_BITMAP: {
-        SpiceBitmapImage *bitmap = (SpiceBitmapImage *)descriptor;
-        access_test(&canvas->base, descriptor, sizeof(SpiceBitmapImage));
-        if (cache_me) {
-            surface = canvas_get_bits(&canvas->base, &bitmap->bitmap);
-        } else {
-#ifdef CAIRO_CANVAS_CACHE
-            return canvas_bitmap_to_invers_surface(canvas, &bitmap->bitmap,
-                                                   canvas_get_palett(&canvas->base,
-                                                                     bitmap->bitmap.palette,
-                                                                     bitmap->bitmap.flags));
-#else
-            SpicePalette *palette = canvas_get_palette(canvas, &bitmap->bitmap);
-            surface = canvas_bitmap_to_invers_surface(canvas, &bitmap->bitmap, palette);
-            free_palette(&bitmap->bitmap, palette);
-            return surface;
-#endif
-        }
-        break;
-    }
-    default:
-        CANVAS_ERROR("invalid image type");
-    }
-
-    if (cache_me) {
-        canvas->base.bits_cache->ops->put(canvas->base.bits_cache, descriptor->id, surface);
-    }
-
-    invers = canvas_handle_inverse_user_data(surface);
-    pixman_image_unref(surface);
-    return invers;
-}
-
-#else
-
-static pixman_image_t *canvas_get_invers(CairoCanvas *canvas, SpiceBitmap *bitmap)
-{
-    SpicePalette *palette;
-
-    if (!bitmap->palette) {
-        return canvas_bitmap_to_invers_surface(canvas, bitmap, NULL);
-    }
-    palette = (SpicePalette *)SPICE_GET_ADDRESS(bitmap->palette);
-    if (canvas->color_shift == 5) {
-        int size = sizeof(SpicePalette) + (palette->num_ents << 2);
-        SpicePalette *local_palette = malloc(size);
-        pixman_image_t* surface;
-
-        memcpy(local_palette, palette, size);
-        canvas_localize_palette(canvas, palette);
-        surface = canvas_bitmap_to_invers_surface(canvas, bitmap, local_palette);
-        free(local_palette);
-        return surface;
-    } else {
-        return canvas_bitmap_to_invers_surface(canvas, bitmap, palette);
-    }
-}
-
-static pixman_image_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRESS addr)
-{
-    SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
-
-    access_test(canvas, descriptor, sizeof(SpiceImageDescriptor));
-
-    switch (descriptor->type) {
-    case SPICE_IMAGE_TYPE_QUIC: {
-        SpiceQUICImage *image = (SpiceQUICImage *)descriptor;
-        access_test(canvas, descriptor, sizeof(SpiceQUICImage));
-        return canvas_get_quic(canvas, image, 1);
-    }
-    case SPICE_IMAGE_TYPE_BITMAP: {
-        SpiceBitmapImage *bitmap = (SpiceBitmapImage *)descriptor;
-        access_test(canvas, descriptor, sizeof(SpiceBitmapImage));
-        return canvas_get_invers(canvas, &bitmap->bitmap);
-    }
-    default:
-        CANVAS_ERROR("invalid image type");
-    }
-}
-
-#endif
 
 static pixman_image_t* canvas_surface_from_self(CairoCanvas *canvas,
                                                 int x, int y,
@@ -741,122 +402,6 @@ static pixman_image_t *canvas_get_pixman_brush(CairoCanvas *canvas,
 }
 
 
-static cairo_pattern_t *canvas_get_brush(CairoCanvas *canvas, SpiceBrush *brush, uint32_t invers)
-{
-    switch (brush->type) {
-    case SPICE_BRUSH_TYPE_SOLID: {
-        uint32_t color = (invers) ? ~brush->u.color : brush->u.color;
-        double r, g, b;
-
-        b = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
-        color >>= canvas->base.color_shift;
-        g = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
-        color >>= canvas->base.color_shift;
-        r = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
-        return cairo_pattern_create_rgb(r, g, b);
-    }
-    case SPICE_BRUSH_TYPE_PATTERN: {
-        pixman_image_t* surface;
-        cairo_surface_t* cairo_surface;
-        cairo_pattern_t *pattern;
-        cairo_matrix_t matrix;
-
-        if (invers) {
-            surface = canvas_get_invers_image(canvas, brush->u.pattern.pat);
-        } else {
-            surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
-        }
-        cairo_surface = surface_from_pixman_image (surface);
-        pixman_image_unref (surface);
-        pattern = cairo_pattern_create_for_surface(cairo_surface);
-        if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
-            cairo_surface_destroy(cairo_surface);
-            CANVAS_ERROR("create pattern failed, %s",
-                         cairo_status_to_string(cairo_pattern_status(pattern)));
-        }
-        cairo_surface_destroy(cairo_surface);
-        cairo_matrix_init_translate(&matrix, -brush->u.pattern.pos.x, -brush->u.pattern.pos.y);
-        cairo_pattern_set_matrix(pattern, &matrix);
-        cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
-        return pattern;
-    }
-    case SPICE_BRUSH_TYPE_NONE:
-        return NULL;
-    default:
-        CANVAS_ERROR("invalid brush type");
-    }
-}
-
-typedef void (*DrawMethod)(void *);
-
-static inline void __canvas_draw_invers(CairoCanvas *canvas, DrawMethod draw_method, void *data)
-{
-    cairo_t *cairo = canvas->cairo;
-    cairo_set_source_rgb(cairo, 1, 1, 1);
-    cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_XOR);
-    draw_method(data);
-}
-
-static inline int canvas_set_ropd_operator(cairo_t *cairo, uint16_t rop_decriptor)
-{
-    cairo_operator_t cairo_op;
-
-    if (rop_decriptor & SPICE_ROPD_OP_PUT) {
-        cairo_op = CAIRO_OPERATOR_RASTER_COPY;
-    } else if (rop_decriptor & SPICE_ROPD_OP_XOR) {
-        cairo_op = CAIRO_OPERATOR_RASTER_XOR;
-    } else if (rop_decriptor & SPICE_ROPD_OP_OR) {
-        cairo_op = CAIRO_OPERATOR_RASTER_OR;
-    } else if (rop_decriptor & SPICE_ROPD_OP_AND) {
-        cairo_op = CAIRO_OPERATOR_RASTER_AND;
-    } else {
-        return 0;
-    }
-    cairo_set_operator(cairo, cairo_op);
-    return 1;
-}
-
-static void canvas_draw_with_pattern(CairoCanvas *canvas, cairo_pattern_t *pattern,
-                                     uint16_t rop_decriptor,
-                                     DrawMethod draw_method, void *data)
-{
-    cairo_t *cairo = canvas->cairo;
-
-    if (rop_decriptor & SPICE_ROPD_OP_BLACKNESS) {
-        cairo_set_source_rgb(cairo, 0, 0, 0);
-        draw_method(data);
-    } else if (rop_decriptor & SPICE_ROPD_OP_WHITENESS) {
-        cairo_set_source_rgb(cairo, 1, 1, 1);
-        draw_method(data);
-    } else if (rop_decriptor & SPICE_ROPD_OP_INVERS) {
-        __canvas_draw_invers(canvas, draw_method, data);
-    } else {
-        if (rop_decriptor & SPICE_ROPD_INVERS_DEST) {
-            __canvas_draw_invers(canvas, draw_method, data);
-        }
-
-        if (canvas_set_ropd_operator(cairo, rop_decriptor)) {
-            ASSERT(pattern);
-            cairo_set_source(cairo, pattern);
-            draw_method(data);
-        }
-
-        if (rop_decriptor & SPICE_ROPD_INVERS_RES) {
-            __canvas_draw_invers(canvas, draw_method, data);
-        }
-    }
-}
-
-static inline void canvas_draw(CairoCanvas *canvas, SpiceBrush *brush, uint16_t rop_decriptor,
-                               DrawMethod draw_method, void *data)
-{
-    cairo_pattern_t *pattern = canvas_get_brush(canvas, brush, rop_decriptor & SPICE_ROPD_INVERS_BRUSH);
-    canvas_draw_with_pattern(canvas, pattern, rop_decriptor, draw_method, data);
-    if (pattern) {
-        cairo_pattern_destroy(pattern);
-    }
-}
-
 static void copy_region(CairoCanvas *canvas,
                         pixman_region32_t *dest_region,
                         int dx, int dy)
diff --git a/common/canvas_base.c b/common/canvas_base.c
index a2917d2..55404af 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -264,45 +264,6 @@ pixman_image_from_surface (cairo_surface_t *surface)
 
   return image;
 }
-
-static cairo_format_t
-cairo_format_from_depth (int depth)
-{
-    switch (depth) {
-    case 1:
-        return CAIRO_FORMAT_A1;
-    case 8:
-        return CAIRO_FORMAT_A8;
-    case 24:
-        return CAIRO_FORMAT_RGB24;
-    case 32:
-    default:
-        return CAIRO_FORMAT_ARGB32;
-    }
-}
-
-static cairo_surface_t *
-surface_from_pixman_image (pixman_image_t *image)
-{
-  cairo_surface_t *surface;
-  int depth;
-
-  depth = pixman_image_get_depth (image);
-
-  surface = cairo_image_surface_create_for_data ((uint8_t *)pixman_image_get_data (image),
-                                                 cairo_format_from_depth (depth),
-                                                 pixman_image_get_width (image),
-                                                 pixman_image_get_height (image),
-                                                 pixman_image_get_stride (image));
-
-
-  if (cairo_surface_set_user_data (surface, &pixman_data_type,
-                                   image, (cairo_destroy_func_t) pixman_image_unref) == CAIRO_STATUS_SUCCESS)
-      pixman_image_ref (image);
-
-  return surface;
-}
-
 #endif
 
 static inline void canvas_localize_palette(CanvasBase *canvas, SpicePalette *palette)
commit 23d4fdbe8a7d67c1e0786d5f7efc40ef6d261f40
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Feb 17 15:51:29 2010 +0100

    Convert cairo canvas to use pixman for draw_stroke

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 25663d4..2b73f8f 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -23,6 +23,7 @@
 #include "rop3.h"
 #include "rect.h"
 #include "region.h"
+#include "lines.h"
 #include "pixman_utils.h"
 
 #define ROUND(_x) floor((_x) + 0.5)
@@ -333,155 +334,6 @@ static void canvas_mask_pixman(CairoCanvas *canvas,
     pixman_image_unref(image);
 }
 
-static void canvas_set_path(CairoCanvas *canvas, void *addr)
-{
-    cairo_t *cairo = canvas->cairo;
-    uint32_t* data_size = (uint32_t*)addr;
-    access_test(&canvas->base, data_size, sizeof(uint32_t));
-    uint32_t more = *data_size;
-
-    SpicePathSeg* seg = (SpicePathSeg*)(data_size + 1);
-
-    do {
-        access_test(&canvas->base, seg, sizeof(SpicePathSeg));
-
-        uint32_t flags = seg->flags;
-        SpicePointFix* point = (SpicePointFix*)seg->data;
-        SpicePointFix* end_point = point + seg->count;
-        access_test(&canvas->base, point, (unsigned long)end_point - (unsigned long)point);
-        ASSERT(point < end_point);
-        more -= ((unsigned long)end_point - (unsigned long)seg);
-        seg = (SpicePathSeg*)end_point;
-
-        if (flags & SPICE_PATH_BEGIN) {
-            cairo_new_sub_path(cairo);
-            cairo_move_to(cairo, fix_to_double(point->x), fix_to_double(point->y));
-            point++;
-        }
-
-        if (flags & SPICE_PATH_BEZIER) {
-            ASSERT((point - end_point) % 3 == 0);
-            for (; point + 2 < end_point; point += 3) {
-                cairo_curve_to(cairo,
-                               fix_to_double(point[0].x), fix_to_double(point[0].y),
-                               fix_to_double(point[1].x), fix_to_double(point[1].y),
-                               fix_to_double(point[2].x), fix_to_double(point[2].y));
-            }
-        } else {
-            for (; point < end_point; point++) {
-                cairo_line_to(cairo, fix_to_double(point->x), fix_to_double(point->y));
-            }
-        }
-        if (flags & SPICE_PATH_END) {
-            if (flags & SPICE_PATH_CLOSE) {
-                cairo_close_path(cairo);
-            }
-            cairo_new_sub_path(cairo);
-        }
-    } while (more);
-}
-
-static void canvas_clip(CairoCanvas *canvas, SpiceClip *clip)
-{
-    cairo_t *cairo = canvas->cairo;
-
-    switch (clip->type) {
-    case SPICE_CLIP_TYPE_NONE:
-        break;
-    case SPICE_CLIP_TYPE_RECTS: {
-        uint32_t *n = (uint32_t *)SPICE_GET_ADDRESS(clip->data);
-        access_test(&canvas->base, n, sizeof(uint32_t));
-
-        SpiceRect *now = (SpiceRect *)(n + 1);
-        SpiceRect *end = now + *n;
-        access_test(&canvas->base, now, (unsigned long)end - (unsigned long)now);
-
-        for (; now < end; now++) {
-            cairo_rectangle(cairo, now->left, now->top, now->right - now->left,
-                            now->bottom - now->top);
-        }
-        cairo_clip(cairo);
-        break;
-    }
-    case SPICE_CLIP_TYPE_PATH:
-        canvas_set_path(canvas, SPICE_GET_ADDRESS(clip->data));
-        cairo_clip(cairo);
-        break;
-    default:
-        CANVAS_ERROR("invalid clip type");
-    }
-}
-
-static inline cairo_line_cap_t canvas_line_cap_to_cairo(int end_style)
-{
-    switch (end_style) {
-    case SPICE_LINE_CAP_ROUND:
-        return CAIRO_LINE_CAP_ROUND;
-    case SPICE_LINE_CAP_SQUARE:
-        return CAIRO_LINE_CAP_SQUARE;
-    case SPICE_LINE_CAP_BUTT:
-        return CAIRO_LINE_CAP_BUTT;
-    default:
-        CANVAS_ERROR("bad end style %d", end_style);
-    }
-}
-
-static inline cairo_line_join_t canvas_line_join_to_cairo(int join_style)
-{
-    switch (join_style) {
-    case SPICE_LINE_JOIN_ROUND:
-        return CAIRO_LINE_JOIN_ROUND;
-    case SPICE_LINE_JOIN_BEVEL:
-        return CAIRO_LINE_JOIN_BEVEL;
-    case SPICE_LINE_JOIN_MITER:
-        return CAIRO_LINE_JOIN_MITER;
-    default:
-        CANVAS_ERROR("bad join style %d", join_style);
-    }
-}
-
-static void canvas_set_line_attr_no_dash(CairoCanvas *canvas, SpiceLineAttr *attr)
-{
-    cairo_t *cairo = canvas->cairo;
-
-    cairo_set_line_width(cairo, fix_to_double(attr->width));
-    cairo_set_line_cap(cairo, canvas_line_cap_to_cairo(attr->end_style));
-    cairo_set_line_join(cairo, canvas_line_join_to_cairo(attr->join_style));
-    cairo_set_miter_limit(cairo, fix_to_double(attr->miter_limit));
-    cairo_set_dash(cairo, NULL, 0, 0);
-}
-
-static void canvas_set_dash(CairoCanvas *canvas, uint8_t nseg, SPICE_ADDRESS addr, int start_is_gap)
-{
-    SPICE_FIXED28_4* style = (SPICE_FIXED28_4*)SPICE_GET_ADDRESS(addr);
-    double offset = 0;
-    double *local_style;
-    int i;
-
-    access_test(&canvas->base, style, nseg * sizeof(*style));
-
-    if (nseg == 0) {
-        CANVAS_ERROR("bad nseg");
-    }
-    local_style = (double *)malloc(nseg * sizeof(*local_style));
-
-    if (start_is_gap) {
-        offset = fix_to_double(*style);
-        local_style[nseg - 1] = fix_to_double(*style);
-        style++;
-
-        for (i = 0; i < nseg - 1; i++, style++) {
-            local_style[i] = fix_to_double(*style);
-        }
-    } else {
-        for (i = 0; i < nseg; i++, style++) {
-            local_style[i] = fix_to_double(*style);
-        }
-    }
-    cairo_set_dash(canvas->cairo, local_style, nseg, offset);
-    free(local_style);
-}
-
 static inline void canvas_invers_32bpp(uint8_t *dest, int dest_stride, uint8_t *src, int src_stride,
                                        int width, uint8_t *end)
 {
@@ -1005,31 +857,6 @@ static inline void canvas_draw(CairoCanvas *canvas, SpiceBrush *brush, uint16_t
     }
 }
 
-static cairo_pattern_t *canvas_get_mask_pattern(CairoCanvas *canvas, SpiceQMask *mask, int x, int y)
-{
-    pixman_image_t *surface;
-    cairo_surface_t *cairo_surface;
-    cairo_pattern_t *pattern;
-    cairo_matrix_t matrix;
-
-    if (!(surface = canvas_get_mask(&canvas->base, mask, NULL))) {
-        return NULL;
-    }
-    cairo_surface = surface_from_pixman_image (surface);
-    pixman_image_unref (surface);
-    pattern = cairo_pattern_create_for_surface(cairo_surface);
-    if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
-        cairo_surface_destroy(cairo_surface);
-        CANVAS_ERROR("create pattern failed, %s",
-                     cairo_status_to_string(cairo_pattern_status(pattern)));
-    }
-    cairo_surface_destroy(cairo_surface);
-
-    cairo_matrix_init_translate(&matrix, mask->pos.x - x, mask->pos.y - y);
-    cairo_pattern_set_matrix(pattern, &matrix);
-    return pattern;
-}
-
 static void copy_region(CairoCanvas *canvas,
                         pixman_region32_t *dest_region,
                         int dx, int dy)
@@ -2044,29 +1871,6 @@ void canvas_draw_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Sp
     pixman_region32_fini(&dest_region);
 }
 
-static inline void canvas_fill_common(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
-                                      SpiceQMask *qxl_mask)
-{
-    cairo_t *cairo = canvas->cairo;
-    cairo_pattern_t *mask;
-
-    canvas_clip(canvas, clip);
-    if ((mask = canvas_get_mask_pattern(canvas, qxl_mask, bbox->left, bbox->top))) {
-        cairo_rectangle(cairo,
-                        bbox->left,
-                        bbox->top,
-                        bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        cairo_clip(cairo);
-        cairo_mask(cairo, mask);
-        cairo_pattern_destroy(mask);
-    } else {
-        cairo_rectangle(cairo, bbox->left, bbox->top, bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        cairo_fill(cairo);
-    }
-}
-
 void canvas_draw_blackness(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
 {
     pixman_region32_t dest_region;
@@ -2317,25 +2121,456 @@ void canvas_draw_text(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     pixman_region32_fini(&dest_region);
 }
 
+typedef struct {
+    lineGC base;
+    pixman_image_t *dest;
+    pixman_region32_t dest_region;
+    SpiceROP fore_rop;
+    SpiceROP back_rop;
+    int solid;
+    uint32_t color;
+    pixman_image_t *tile;
+    int tile_offset_x;
+    int tile_offset_y;
+} StrokeGC;
+
+static void stroke_fill_spans(lineGC * pGC,
+                              int num_spans,
+                              SpicePoint *points,
+                              int *widths,
+                              int sorted,
+                              int foreground)
+{
+    StrokeGC *strokeGC;
+    int i;
+    pixman_image_t *dest;
+    SpiceROP rop;
+
+    strokeGC = (StrokeGC *)pGC;
+    dest = strokeGC->dest;
+
+    num_spans = spice_canvas_clip_spans(&strokeGC->dest_region,
+                                        points, widths, num_spans,
+                                        points, widths, sorted);
+
+    if (foreground) {
+        rop = strokeGC->fore_rop;
+    } else {
+        rop = strokeGC->back_rop;
+    }
+
+    for (i = 0; i < num_spans; i++) {
+        if (strokeGC->solid) {
+            if (rop == SPICE_ROP_COPY) {
+                spice_pixman_fill_rect(dest, points[i].x, points[i].y, widths[i], 1,
+                                       strokeGC->color);
+            } else {
+                spice_pixman_fill_rect_rop(dest, points[i].x, points[i].y, widths[i], 1,
+                                           strokeGC->color, rop);
+            }
+        } else {
+            if (rop == SPICE_ROP_COPY) {
+                spice_pixman_tile_rect(dest,
+                                       points[i].x, points[i].y,
+                                       widths[i], 1,
+                                       strokeGC->tile,
+                                       strokeGC->tile_offset_x,
+                                       strokeGC->tile_offset_y);
+            } else {
+                spice_pixman_tile_rect_rop(dest,
+                                           points[i].x, points[i].y,
+                                           widths[i], 1,
+                                           strokeGC->tile,
+                                           strokeGC->tile_offset_x,
+                                           strokeGC->tile_offset_y,
+                                           rop);
+            }
+        }
+    }
+}
+
+static void stroke_fill_rects(lineGC * pGC,
+                              int num_rects,
+                              pixman_rectangle32_t *rects,
+                              int foreground)
+{
+    pixman_region32_t area;
+    pixman_box32_t *boxes;
+    StrokeGC *strokeGC;
+    pixman_image_t *dest;
+    SpiceROP rop;
+    int i;
+    pixman_box32_t *area_rects;
+    int n_area_rects;
+
+    strokeGC = (StrokeGC *)pGC;
+    dest = strokeGC->dest;
+
+    if (foreground) {
+        rop = strokeGC->fore_rop;
+    } else {
+        rop = strokeGC->back_rop;
+    }
+
+    /* TODO: We can optimize this for more common cases where
+       dest is one rect */
+
+    boxes = (pixman_box32_t *)malloc(num_rects * sizeof(pixman_box32_t));
+    for (i = 0; i < num_rects; i++) {
+        boxes[i].x1 = rects[i].x;
+        boxes[i].y1 = rects[i].y;
+        boxes[i].x2 = rects[i].x + rects[i].width;
+        boxes[i].y2 = rects[i].y + rects[i].height;
+    }
+    pixman_region32_init_rects(&area, boxes, num_rects);
+    pixman_region32_intersect(&area, &area, &strokeGC->dest_region);
+    free(boxes);
+
+    area_rects = pixman_region32_rectangles(&area, &n_area_rects);
+
+    for (i = 0; i < n_area_rects; i++) {
+        if (strokeGC->solid) {
+            if (rop == SPICE_ROP_COPY) {
+                spice_pixman_fill_rect(dest,
+                                       area_rects[i].x1,
+                                       area_rects[i].y1,
+                                       area_rects[i].x2 - area_rects[i].x1,
+                                       area_rects[i].y2 - area_rects[i].y1,
+                                       strokeGC->color);
+            } else {
+                spice_pixman_fill_rect_rop(dest,
+                                           area_rects[i].x1,
+                                           area_rects[i].y1,
+                                           area_rects[i].x2 - area_rects[i].x1,
+                                           area_rects[i].y2 - area_rects[i].y1,
+                                           strokeGC->color, rop);
+            }
+        } else {
+            if (rop == SPICE_ROP_COPY) {
+                spice_pixman_tile_rect(dest,
+                                       area_rects[i].x1,
+                                       area_rects[i].y1,
+                                       area_rects[i].x2 - area_rects[i].x1,
+                                       area_rects[i].y2 - area_rects[i].y1,
+                                       strokeGC->tile,
+                                       strokeGC->tile_offset_x,
+                                       strokeGC->tile_offset_y);
+            } else {
+                spice_pixman_tile_rect_rop(dest,
+                                           area_rects[i].x1,
+                                           area_rects[i].y1,
+                                           area_rects[i].x2 - area_rects[i].x1,
+                                           area_rects[i].y2 - area_rects[i].y1,
+                                           strokeGC->tile,
+                                           strokeGC->tile_offset_x,
+                                           strokeGC->tile_offset_y,
+                                           rop);
+            }
+        }
+    }
+    pixman_region32_fini(&area);
+}
+
+typedef struct {
+    SpicePoint *points;
+    int num_points;
+    int size;
+} StrokeLines;
+
+static void stroke_lines_init(StrokeLines *lines)
+{
+    lines->points = (SpicePoint *)malloc(10*sizeof(SpicePoint));
+    lines->size = 10;
+    lines->num_points = 0;
+}
+
+static void stroke_lines_free(StrokeLines *lines)
+{
+    free(lines->points);
+}
+
+static void stroke_lines_append(StrokeLines *lines,
+                                int x, int y)
+{
+    if (lines->num_points == lines->size) {
+        lines->size *= 2;
+        lines->points = (SpicePoint *)realloc(lines->points,
+                                              lines->size * sizeof(SpicePoint));
+    }
+    lines->points[lines->num_points].x = x;
+    lines->points[lines->num_points].y = y;
+    lines->num_points++;
+}
+
+static void stroke_lines_append_fix(StrokeLines *lines,
+                                    SpicePointFix *point)
+{
+    stroke_lines_append(lines,
+                        fix_to_int(point->x),
+                        fix_to_int(point->y));
+}
+
+static inline int64_t dot(SPICE_FIXED28_4 x1,
+                          SPICE_FIXED28_4 y1,
+                          SPICE_FIXED28_4 x2,
+                          SPICE_FIXED28_4 y2)
+{
+    return (((int64_t)x1) *((int64_t)x2) +
+            ((int64_t)y1) *((int64_t)y2)) >> 4;
+}
+
+static inline int64_t dot2(SPICE_FIXED28_4 x,
+                           SPICE_FIXED28_4 y)
+{
+    return (((int64_t)x) *((int64_t)x) +
+            ((int64_t)y) *((int64_t)y)) >> 4;
+}
+
+static void subdivide_bezier(StrokeLines *lines,
+                             SpicePointFix point0,
+                             SpicePointFix point1,
+                             SpicePointFix point2,
+                             SpicePointFix point3)
+{
+    int64_t A2, B2, C2, AB, CB, h1, h2;
+
+    A2 = dot2(point1.x - point0.x,
+              point1.y - point0.y);
+    B2 = dot2(point3.x - point0.x,
+              point3.y - point0.y);
+    C2 = dot2(point2.x - point3.x,
+              point2.y - point3.y);
+
+    AB = dot(point1.x - point0.x,
+             point1.y - point0.y,
+             point3.x - point0.x,
+             point3.y - point0.y);
+
+    CB = dot(point2.x - point3.x,
+             point2.y - point3.y,
+             point0.x - point3.x,
+             point0.y - point3.y);
+
+    h1 = (A2*B2 - AB*AB) >> 3;
+    h2 = (C2*B2 - CB*CB) >> 3;
+
+    if (h1 < B2 && h2 < B2) {
+        /* deviation squared less than half a pixel, use straight line */
+        stroke_lines_append_fix(lines, &point3);
+    } else {
+        SpicePointFix point01, point23, point12, point012, point123, point0123;
+
+        point01.x = (point0.x + point1.x) / 2;
+        point01.y = (point0.y + point1.y) / 2;
+        point12.x = (point1.x + point2.x) / 2;
+        point12.y = (point1.y + point2.y) / 2;
+        point23.x = (point2.x + point3.x) / 2;
+        point23.y = (point2.y + point3.y) / 2;
+        point012.x = (point01.x + point12.x) / 2;
+        point012.y = (point01.y + point12.y) / 2;
+        point123.x = (point12.x + point23.x) / 2;
+        point123.y = (point12.y + point23.y) / 2;
+        point0123.x = (point012.x + point123.x) / 2;
+        point0123.y = (point012.y + point123.y) / 2;
+
+        subdivide_bezier(lines, point0, point01, point012, point0123);
+        subdivide_bezier(lines, point0123, point123, point23, point3);
+    }
+}
+
+static void stroke_lines_append_bezier(StrokeLines *lines,
+                                       SpicePointFix *point1,
+                                       SpicePointFix *point2,
+                                       SpicePointFix *point3)
+{
+    SpicePointFix point0;
+
+    point0.x = int_to_fix(lines->points[lines->num_points-1].x);
+    point0.y = int_to_fix(lines->points[lines->num_points-1].y);
+
+    subdivide_bezier(lines, point0, *point1, *point2, *point3);
+}
+
+static void stroke_lines_draw(StrokeLines *lines,
+                              lineGC *gc,
+                              int dashed)
+{
+    if (lines->num_points != 0) {
+        if (dashed) {
+            spice_canvas_zero_dash_line(gc, CoordModeOrigin,
+                                        lines->num_points, lines->points);
+        } else {
+            spice_canvas_zero_line(gc, CoordModeOrigin,
+                                   lines->num_points, lines->points);
+        }
+        lines->num_points = 0;
+    }
+}
+
+
 void canvas_draw_stroke(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
 {
-    cairo_t *cairo = canvas->cairo;
+    StrokeGC gc = { {0} };
+    lineGCOps ops = {
+        stroke_fill_spans,
+        stroke_fill_rects
+    };
+    uint32_t *data_size;
+    uint32_t more;
+    SpicePathSeg *seg;
+    StrokeLines lines;
+    int i;
+    int dashed;
 
-    cairo_save(cairo);
-    canvas_clip(canvas, clip);
-
-    canvas_set_line_attr_no_dash(canvas, &stroke->attr);
-    canvas_set_path(canvas, SPICE_GET_ADDRESS(stroke->path));
-    if (stroke->attr.flags & SPICE_LINE_ATTR_STYLED) {
-        canvas_draw(canvas, &stroke->brush, stroke->back_mode,
-                    (DrawMethod)cairo_stroke_preserve, cairo);
-        canvas_set_dash(canvas, stroke->attr.style_nseg, stroke->attr.style,
-                        !!(stroke->attr.flags & SPICE_LINE_ATTR_STARTGAP));
-    }
-    canvas_draw(canvas, &stroke->brush, stroke->fore_mode, (DrawMethod)cairo_stroke_preserve,
-                cairo);
-    cairo_new_path(cairo);
-    cairo_restore(cairo);
+    pixman_region32_init_rect(&gc.dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
+
+    canvas_clip_pixman(canvas, &gc.dest_region, clip);
+
+    if (pixman_region32_n_rects(&gc.dest_region) == 0) {
+        touch_brush(canvas, &stroke->brush);
+        pixman_region32_fini(&gc.dest_region);
+        return;
+    }
+
+    gc.fore_rop = ropd_descriptor_to_rop(stroke->fore_mode,
+                                         ROP_INPUT_BRUSH,
+                                         ROP_INPUT_DEST);
+    gc.back_rop = ropd_descriptor_to_rop(stroke->back_mode,
+                                         ROP_INPUT_BRUSH,
+                                         ROP_INPUT_DEST);
+
+    gc.dest = canvas->image;
+    gc.base.width = pixman_image_get_width(gc.dest);
+    gc.base.height = pixman_image_get_height(gc.dest);
+    gc.base.alu = gc.fore_rop;
+    gc.base.lineWidth = 0;
+
+    /* dash */
+    gc.base.dashOffset = 0;
+    gc.base.dash = NULL;
+    gc.base.numInDashList = 0;
+    gc.base.lineStyle = LineSolid;
+    /* win32 cosmetic lines are endpoint-exclusive, so use CapNotLast */
+    gc.base.capStyle = CapNotLast;
+    gc.base.joinStyle = JoinMiter;
+    gc.base.ops = &ops;
+
+    dashed = 0;
+    if (stroke->attr.flags & SPICE_LINE_FLAGS_STYLED) {
+        SPICE_FIXED28_4 *style = (SPICE_FIXED28_4*)SPICE_GET_ADDRESS(stroke->attr.style);
+        int nseg;
+
+        dashed = 1;
+
+        nseg = stroke->attr.style_nseg;
+
+        /* To truly handle back_mode we should use LineDoubleDash here
+           and treat !foreground as back_rop using the same brush.
+           However, using the same brush for that seems wrong.
+           The old cairo backend was stroking the non-dashed line with
+           rop_mode before enabling dashes for the foreground which is
+           not right either. The gl an gdi backend don't use back_mode
+           at all */
+        gc.base.lineStyle = LineOnOffDash;
+        gc.base.dash = (unsigned char *)malloc(nseg);
+        gc.base.numInDashList = nseg;
+        access_test(&canvas->base, style, nseg * sizeof(*style));
+
+        if (stroke->attr.flags & SPICE_LINE_FLAGS_START_WITH_GAP) {
+            gc.base.dash[stroke->attr.style_nseg - 1] = fix_to_int(style[0]);
+            for (i = 0; i < stroke->attr.style_nseg - 1; i++) {
+                gc.base.dash[i] = fix_to_int(style[i+1]);
+            }
+            gc.base.dashOffset = gc.base.dash[0];
+        } else {
+            for (i = 0; i < stroke->attr.style_nseg; i++) {
+                gc.base.dash[i] = fix_to_int(style[i]);
+            }
+        }
+    }
+
+    switch (stroke->brush.type) {
+    case SPICE_BRUSH_TYPE_NONE:
+        gc.solid = TRUE;
+        gc.color = 0;
+        break;
+    case SPICE_BRUSH_TYPE_SOLID:
+        gc.solid = TRUE;
+        gc.color = stroke->brush.u.color;
+        break;
+    case SPICE_BRUSH_TYPE_PATTERN:
+        gc.solid = FALSE;
+        gc.tile = canvas_get_image(&canvas->base,
+                                   stroke->brush.u.pattern.pat);
+        gc.tile_offset_x = stroke->brush.u.pattern.pos.x;
+        gc.tile_offset_y = stroke->brush.u.pattern.pos.y;
+        break;
+    default:
+        CANVAS_ERROR("invalid brush type");
+    }
+
+    data_size = (uint32_t*)SPICE_GET_ADDRESS(stroke->path);
+    access_test(&canvas->base, data_size, sizeof(uint32_t));
+    more = *data_size;
+    seg = (SpicePathSeg*)(data_size + 1);
+
+    stroke_lines_init(&lines);
+
+    do {
+        access_test(&canvas->base, seg, sizeof(SpicePathSeg));
+
+        uint32_t flags = seg->flags;
+        SpicePointFix* point = (SpicePointFix*)seg->data;
+        SpicePointFix* end_point = point + seg->count;
+        access_test(&canvas->base, point, (unsigned long)end_point - (unsigned long)point);
+        ASSERT(point < end_point);
+        more -= ((unsigned long)end_point - (unsigned long)seg);
+        seg = (SpicePathSeg*)end_point;
+
+        if (flags & SPICE_PATH_BEGIN) {
+            stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
+            stroke_lines_append_fix(&lines, point);
+            point++;
+        }
+
+        if (flags & SPICE_PATH_BEZIER) {
+            ASSERT((point - end_point) % 3 == 0);
+            for (; point + 2 < end_point; point += 3) {
+                stroke_lines_append_bezier(&lines,
+                                           &point[0],
+                                           &point[1],
+                                           &point[2]);
+            }
+        } else
+            {
+            for (; point < end_point; point++) {
+                stroke_lines_append_fix(&lines, point);
+            }
+        }
+        if (flags & SPICE_PATH_END) {
+            if (flags & SPICE_PATH_CLOSE) {
+                stroke_lines_append(&lines,
+                                    lines.points[0].x, lines.points[0].y);
+            }
+            stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
+        }
+    } while (more);
+
+    stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
+
+    if (gc.base.dash) {
+        free(gc.base.dash);
+    }
+    stroke_lines_free(&lines);
+
+    if (!gc.solid && gc.tile) {
+        pixman_image_unref(gc.tile);
+    }
+
+    pixman_region32_fini(&gc.dest_region);
 }
 
 void canvas_read_bits(CairoCanvas *canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
diff --git a/common/canvas_base.c b/common/canvas_base.c
index a96bd42..a2917d2 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -93,6 +93,23 @@ typedef struct __attribute__ ((__packed__)) LZImage {
 
 static const cairo_user_data_key_t pixman_data_type = {0};
 
+ static inline int fix_to_int(SPICE_FIXED28_4 fixed)
+{
+    int val, rem;
+
+    rem = fixed & 0x0f;
+    val = fixed >> 4;
+    if (rem > 8) {
+        val++;
+    }
+    return val;
+}
+
+ static inline SPICE_FIXED28_4  int_to_fix(int v)
+{
+    return v << 4;
+}
+
 static inline double fix_to_double(SPICE_FIXED28_4 fixed)
 {
     return (double)(fixed & 0x0f) / 0x0f + (fixed >> 4);
commit e57cfae896c70b37ecee1acd0297183f4b4ae758
Author: Alexander Larsson <alexl at redhat.com>
Date:   Mon Feb 15 11:42:26 2010 +0100

    Convert cairo canvas draw_transparent to use pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index d9386ae..25663d4 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -1488,6 +1488,110 @@ static void blend_scale_image(CairoCanvas *canvas,
     pixman_image_set_clip_region32(canvas->image, NULL);
 }
 
+static void colorkey_image(CairoCanvas *canvas,
+                           pixman_region32_t *region,
+                           SPICE_ADDRESS src_bitmap,
+                           int offset_x, int offset_y,
+                           uint32_t transparent_color)
+{
+    pixman_image_t *src_image;
+    pixman_box32_t *rects;
+    int n_rects, i;
+
+    rects = pixman_region32_rectangles(region, &n_rects);
+
+    src_image = canvas_get_image(&canvas->base, src_bitmap);
+    for (i = 0; i < n_rects; i++) {
+        int src_x, src_y, dest_x, dest_y, width, height;
+
+        dest_x = rects[i].x1;
+        dest_y = rects[i].y1;
+        width = rects[i].x2 - rects[i].x1;
+        height = rects[i].y2 - rects[i].y1;
+
+        src_x = rects[i].x1 - offset_x;
+        src_y = rects[i].y1 - offset_y;
+
+        spice_pixman_blit_colorkey(canvas->image,
+                                   src_image,
+                                   src_x, src_y,
+                                   dest_x, dest_y,
+                                   width, height,
+                                   transparent_color);
+    }
+    pixman_image_unref(src_image);
+}
+
+static void colorkey_scale_image(CairoCanvas *canvas,
+                                 pixman_region32_t *region,
+                                 SPICE_ADDRESS src_bitmap,
+                                 int src_x, int src_y,
+                                 int src_width, int src_height,
+                                 int dest_x, int dest_y,
+                                 int dest_width, int dest_height,
+                                 uint32_t transparent_color)
+{
+    pixman_transform_t transform;
+    pixman_image_t *src;
+    pixman_image_t *scaled;
+    pixman_box32_t *rects;
+    int n_rects, i;
+    double sx, sy;
+
+    sx = (double)(src_width) / (dest_width);
+    sy = (double)(src_height) / (dest_height);
+
+    src = canvas_get_image(&canvas->base, src_bitmap);
+
+    scaled = pixman_image_create_bits(PIXMAN_x8r8g8b8,
+                                      dest_width,
+                                      dest_height,
+                                      NULL, 0);
+
+    pixman_region32_translate(region, -dest_x, -dest_y);
+    pixman_image_set_clip_region32(scaled, region);
+
+    pixman_transform_init_scale(&transform,
+                                pixman_double_to_fixed(sx),
+                                pixman_double_to_fixed(sy));
+
+    pixman_image_set_transform(src, &transform);
+    pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+    pixman_image_set_filter(src,
+                            PIXMAN_FILTER_NEAREST,
+                            NULL, 0);
+
+    pixman_image_composite32(PIXMAN_OP_SRC,
+                             src, NULL, scaled,
+                             ROUND(src_x / sx), ROUND(src_y / sy), /* src */
+                             0, 0, /* mask */
+                             0, 0, /* dst */
+                             dest_width,
+                             dest_height);
+
+    pixman_transform_init_identity(&transform);
+    pixman_image_set_transform(src, &transform);
+
+    /* Translate back */
+    pixman_region32_translate(region, dest_x, dest_y);
+
+    rects = pixman_region32_rectangles(region, &n_rects);
+
+    for (i = 0; i < n_rects; i++) {
+        spice_pixman_blit_colorkey(canvas->image,
+                                   scaled,
+                                   rects[i].x1 - dest_x,
+                                   rects[i].y1 - dest_y,
+                                   rects[i].x1, rects[i].y1,
+                                   rects[i].x2 - rects[i].x1,
+                                   rects[i].y2 - rects[i].y1,
+                                   transparent_color);
+    }
+
+    pixman_image_unref(scaled);
+    pixman_image_unref(src);
+}
+
 static void draw_brush(CairoCanvas *canvas,
                        pixman_region32_t *region,
                        SpiceBrush *brush,
@@ -1564,51 +1668,6 @@ void canvas_draw_fill(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     pixman_region32_fini(&dest_region);
 }
 
-static cairo_pattern_t *canvas_src_image_to_pat(CairoCanvas *canvas, SPICE_ADDRESS src_bitmap,
-                                                const SpiceRect *src, const SpiceRect *dest, int invers,
-                                                int scale_mode)
-{
-    cairo_pattern_t *pattern;
-    pixman_image_t *surface;
-    cairo_surface_t *cairo_surface;
-    cairo_matrix_t matrix;
-
-    ASSERT(src_bitmap);
-    ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE || scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
-    if (invers) {
-        surface = canvas_get_invers_image(canvas, src_bitmap);
-    } else {
-        surface = canvas_get_image(&canvas->base, src_bitmap);
-    }
-
-    cairo_surface = surface_from_pixman_image (surface);
-    pixman_image_unref (surface);
-    pattern = cairo_pattern_create_for_surface(cairo_surface);
-    cairo_surface_destroy(cairo_surface);
-    if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create pattern failed, %s",
-                     cairo_status_to_string(cairo_pattern_status(pattern)));
-    }
-
-    if (!rect_is_same_size(src, dest)) {
-        double sx, sy;
-
-        sx = (double)(src->right - src->left) / (dest->right - dest->left);
-        sy = (double)(src->bottom - src->top) / (dest->bottom - dest->top);
-
-        cairo_matrix_init_scale(&matrix, sx, sy);
-        cairo_pattern_set_filter(pattern, (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
-                                 CAIRO_FILTER_NEAREST : CAIRO_FILTER_GOOD);
-
-        cairo_matrix_translate(&matrix, src->left / sx - dest->left, src->top / sy - dest->top);
-    } else {
-        cairo_matrix_init_translate(&matrix, src->left - dest->left, src->top - dest->top);
-    }
-
-    cairo_pattern_set_matrix(pattern, &matrix);
-    return pattern;
-}
-
 void canvas_draw_copy(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
 {
     pixman_region32_t dest_region;
@@ -1778,88 +1837,44 @@ void canvas_put_image(CairoCanvas *canvas, const SpiceRect *dest, const uint8_t
     cairo_restore(cairo);
 }
 
-static cairo_surface_t *canvas_surf_to_color_maks_invers(cairo_surface_t *surface,
-                                                         uint32_t trans_color)
-{
-    int width = cairo_image_surface_get_width(surface);
-    int height = cairo_image_surface_get_height(surface);
-    uint8_t *src_line;
-    uint8_t *end_src_line;
-    int src_stride;
-    uint8_t *dest_line;
-    int dest_stride;
-    cairo_surface_t *mask;
-    int i;
-
-    ASSERT(cairo_image_surface_get_format(surface) == CAIRO_FORMAT_ARGB32 ||
-           cairo_image_surface_get_format(surface) == CAIRO_FORMAT_RGB24);
-
-    mask = cairo_image_surface_create(CAIRO_FORMAT_A1, width, height);
-    if (cairo_surface_status(mask) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(mask)));
-    }
-
-    src_line = cairo_image_surface_get_data(surface);
-    src_stride = cairo_image_surface_get_stride(surface);
-    end_src_line = src_line + src_stride * height;
-
-    dest_line = cairo_image_surface_get_data(mask);
-    dest_stride = cairo_image_surface_get_stride(mask);
-
-    for (; src_line != end_src_line; src_line += src_stride, dest_line += dest_stride) {
-        memset(dest_line, 0xff, dest_stride);
-        for (i = 0; i < width; i++) {
-            if ((((uint32_t*)src_line)[i] & 0x00ffffff) == trans_color) {
-                dest_line[i >> 3] &= ~(1 << (i & 0x07));
-            }
-        }
-    }
-
-    return mask;
-}
-
 void canvas_draw_transparent(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
 {
-    cairo_t *cairo = canvas->cairo;
-    cairo_pattern_t *pattern;
-    cairo_pattern_t *mask;
-    cairo_surface_t *surface;
-    cairo_surface_t *mask_surf;
-    cairo_matrix_t matrix;
+    pixman_region32_t dest_region;
 
-    cairo_save(cairo);
-    cairo_rectangle(cairo,
-                    bbox->left,
-                    bbox->top,
-                    bbox->right - bbox->left,
-                    bbox->bottom - bbox->top);
-    cairo_clip(cairo);
-    canvas_clip(canvas, clip);
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
 
-    pattern = canvas_src_image_to_pat(canvas, transparent->src_bitmap, &transparent->src_area, bbox,
-                                      0, SPICE_IMAGE_SCALE_MODE_NEAREST);
-    if (cairo_pattern_get_surface(pattern, &surface) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("surfase from pattern pattern failed");
-    }
+    canvas_clip_pixman(canvas, &dest_region, clip);
 
-    mask_surf = canvas_surf_to_color_maks_invers(surface, transparent->true_color);
-    mask = cairo_pattern_create_for_surface(mask_surf);
-    cairo_surface_destroy(mask_surf);
-    if (cairo_pattern_status(mask) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create pattern failed, %s",
-                     cairo_status_to_string(cairo_pattern_status(pattern)));
+    if (pixman_region32_n_rects (&dest_region) == 0) {
+        canvas_touch_image(&canvas->base, transparent->src_bitmap);
+        pixman_region32_fini(&dest_region);
+        return;
     }
-    cairo_pattern_get_matrix(pattern, &matrix);
-    cairo_pattern_set_matrix(mask, &matrix);
 
-    cairo_set_source(cairo, pattern);
-    cairo_pattern_destroy(pattern);
-    cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
-    cairo_mask(cairo, mask);
-    cairo_pattern_destroy(mask);
+    if (rect_is_same_size(bbox, &transparent->src_area)) {
+        colorkey_image(canvas, &dest_region,
+                       transparent->src_bitmap,
+                       bbox->left - transparent->src_area.left,
+                       bbox->top - transparent->src_area.top,
+                       transparent->true_color);
+    } else {
+        colorkey_scale_image(canvas, &dest_region,
+                             transparent->src_bitmap,
+                             transparent->src_area.left,
+                             transparent->src_area.top,
+                             transparent->src_area.right - transparent->src_area.left,
+                             transparent->src_area.bottom - transparent->src_area.top,
+                             bbox->left,
+                             bbox->top,
+                             bbox->right - bbox->left,
+                             bbox->bottom - bbox->top,
+                             transparent->true_color);
+    }
 
-    cairo_restore(cairo);
+    pixman_region32_fini(&dest_region);
 }
 
 void canvas_draw_alpha_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
commit f32185a01db1be1189f5ed95ca5e2daee32a8de1
Author: Alexander Larsson <alexl at redhat.com>
Date:   Fri Feb 12 17:02:23 2010 +0100

    Convert cairo canvas draw_rop3 to using pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 320014e..d9386ae 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -822,7 +822,8 @@ static pixman_image_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRES
 
 #endif
 
-static pixman_image_t* canvas_surface_from_self(CairoCanvas *canvas, SpicePoint *pos,
+static pixman_image_t* canvas_surface_from_self(CairoCanvas *canvas,
+                                                int x, int y,
                                                 int32_t width, int32_t heigth)
 {
     pixman_image_t *surface;
@@ -844,7 +845,7 @@ static pixman_image_t* canvas_surface_from_self(CairoCanvas *canvas, SpicePoint
     src_surface = canvas->image;
     src = (uint8_t *)pixman_image_get_data(src_surface);
     src_stride = pixman_image_get_stride(src_surface);
-    src += pos->y * src_stride + (pos->x << 2);
+    src += y * src_stride + (x << 2);
     for (i = 0; i < heigth; i++, dest += dest_stride, src += src_stride) {
         memcpy(dest, src, width << 2);
     }
@@ -2126,33 +2127,32 @@ void canvas_draw_invers(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
 
 void canvas_draw_rop3(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
 {
-    cairo_t *cairo = canvas->cairo;
-    cairo_pattern_t *mask;
-    pixman_image_t *dd;
-    cairo_surface_t *d;
+    pixman_region32_t dest_region;
+    pixman_image_t *d;
     pixman_image_t *s;
-    SpicePoint pos;
+    SpicePoint src_pos;
     int width;
     int heigth;
-    double x_pos;
-    double y_pos;
-    SpicePoint src_pos;
 
-    cairo_save(cairo);
-    canvas_clip(canvas, clip);
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
+
+
+    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_mask_pixman(canvas, &dest_region, &rop3->mask,
+                       bbox->left, bbox->top);
+
     width = bbox->right - bbox->left;
     heigth = bbox->bottom - bbox->top;
-    x_pos = bbox->left;
-    y_pos = bbox->top;
-    cairo_user_to_device(cairo, &x_pos, &y_pos);
-    pos.x = (int32_t)x_pos;
-    pos.y = (int32_t)y_pos;
-    dd = canvas_surface_from_self(canvas, &pos, width, heigth);
+
+    d = canvas_surface_from_self(canvas, bbox->left, bbox->top, width, heigth);
     s = canvas_get_image(&canvas->base, rop3->src_bitmap);
 
     if (!rect_is_same_size(bbox, &rop3->src_area)) {
         pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, width, heigth,
-                                                         rop3->scale_mode);
+                                                        rop3->scale_mode);
         pixman_image_unref(s);
         s = scaled_s;
         src_pos.x = 0;
@@ -2171,33 +2171,22 @@ void canvas_draw_rop3(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
 
         pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % pixman_image_get_width(p);
         pat_pos.y = (bbox->top - rop3->brush.u.pattern.pos.y) % pixman_image_get_height(p);
-        do_rop3_with_pattern(rop3->rop3, dd, s, &src_pos, p, &pat_pos);
+        do_rop3_with_pattern(rop3->rop3, d, s, &src_pos, p, &pat_pos);
         pixman_image_unref(p);
     } else {
         uint32_t color = (canvas->base.color_shift) == 8 ? rop3->brush.u.color :
                                                          canvas_16bpp_to_32bpp(rop3->brush.u.color);
-        do_rop3_with_color(rop3->rop3, dd, s, &src_pos, color);
+        do_rop3_with_color(rop3->rop3, d, s, &src_pos, color);
     }
     pixman_image_unref(s);
-    d = surface_from_pixman_image (dd);
-    cairo_set_source_surface(cairo, d, bbox->left, bbox->top);
-    cairo_surface_destroy(d);
-    pixman_image_unref (dd);
-    if ((mask = canvas_get_mask_pattern(canvas, &rop3->mask, bbox->left, bbox->top))) {
-        cairo_rectangle(cairo,
-                        bbox->left,
-                        bbox->top,
-                        bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        cairo_clip(cairo);
-        cairo_mask(cairo, mask);
-        cairo_pattern_destroy(mask);
-    } else {
-        cairo_rectangle(cairo, bbox->left, bbox->top, bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        cairo_fill(cairo);
-    }
-    cairo_restore(cairo);
+
+    blit_with_region(canvas, &dest_region, d,
+                     bbox->left,
+                     bbox->top);
+
+    pixman_image_unref(d);
+
+    pixman_region32_fini(&dest_region);
 }
 
 void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
commit 3b2f1fcb22e8cf8bdec7a79a308cb8a0cb120eb1
Author: Alexander Larsson <alexl at redhat.com>
Date:   Fri Feb 12 16:22:01 2010 +0100

    Convert draw_blackness/whiteness/invers to using pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 065bfa2..320014e 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -2053,30 +2053,75 @@ static inline void canvas_fill_common(CairoCanvas *canvas, SpiceRect *bbox, Spic
 
 void canvas_draw_blackness(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
 {
-    cairo_t *cairo = canvas->cairo;
-    cairo_save(cairo);
-    cairo_set_source_rgb(cairo, 0, 0, 0);
-    canvas_fill_common(canvas, bbox, clip, &blackness->mask);
-    cairo_restore(cairo);
+    pixman_region32_t dest_region;
+
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
+
+
+    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_mask_pixman(canvas, &dest_region, &blackness->mask,
+                       bbox->left, bbox->top);
+
+    if (pixman_region32_n_rects(&dest_region) == 0) {
+        pixman_region32_fini (&dest_region);
+        return;
+    }
+
+    fill_solid_rects(canvas, &dest_region, 0x000000);
+
+    pixman_region32_fini(&dest_region);
 }
 
 void canvas_draw_whiteness(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
 {
-    cairo_t *cairo = canvas->cairo;
-    cairo_save(cairo);
-    cairo_set_source_rgb(cairo, 1, 1, 1);
-    canvas_fill_common(canvas, bbox, clip, &whiteness->mask);
-    cairo_restore(cairo);
+    pixman_region32_t dest_region;
+
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
+
+
+    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_mask_pixman(canvas, &dest_region, &whiteness->mask,
+                       bbox->left, bbox->top);
+
+    if (pixman_region32_n_rects(&dest_region) == 0) {
+        pixman_region32_fini(&dest_region);
+        return;
+    }
+
+    fill_solid_rects(canvas, &dest_region, 0xffffffff);
+
+    pixman_region32_fini(&dest_region);
 }
 
 void canvas_draw_invers(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
 {
-    cairo_t *cairo = canvas->cairo;
-    cairo_save(cairo);
-    cairo_set_source_rgb(cairo, 1, 1, 1);
-    cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_XOR);
-    canvas_fill_common(canvas, bbox, clip, &invers->mask);
-    cairo_restore(cairo);
+    pixman_region32_t dest_region;
+
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
+
+
+    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_mask_pixman(canvas, &dest_region, &invers->mask,
+                       bbox->left, bbox->top);
+
+    if (pixman_region32_n_rects(&dest_region) == 0) {
+        pixman_region32_fini(&dest_region);
+        return;
+    }
+
+    fill_solid_rects_rop(canvas, &dest_region, 0x00000000,
+                         SPICE_ROP_INVERT);
+
+    pixman_region32_fini(&dest_region);
 }
 
 void canvas_draw_rop3(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
commit 974c662ae4f3375ea4162556ac644f1431af2f9f
Author: Alexander Larsson <alexl at redhat.com>
Date:   Thu Feb 11 17:25:12 2010 +0100

    Convert cairo canvas alpha_blend to using pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 8e9c2d6..065bfa2 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -1389,6 +1389,104 @@ static void scale_image_rop(CairoCanvas *canvas,
     pixman_image_unref(src);
 }
 
+static void blend_image(CairoCanvas *canvas,
+                        pixman_region32_t *region,
+                        SPICE_ADDRESS src_bitmap,
+                        int src_x, int src_y,
+                        int dest_x, int dest_y,
+                        int width, int height,
+                        int overall_alpha)
+{
+    pixman_image_t *src, *mask;
+
+    src = canvas_get_image(&canvas->base, src_bitmap);
+
+    pixman_image_set_clip_region32(canvas->image, region);
+
+    mask = NULL;
+    if (overall_alpha != 0xff) {
+        pixman_color_t color = { 0 };
+        color.alpha = overall_alpha * 0x101;
+        mask = pixman_image_create_solid_fill(&color);
+    }
+
+    pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+
+    pixman_image_composite32(PIXMAN_OP_OVER,
+                             src, mask, canvas->image,
+                             src_x, src_y, /* src */
+                             0, 0, /* mask */
+                             dest_x, dest_y, /* dst */
+                             width,
+                             height);
+
+    if (mask) {
+        pixman_image_unref(mask);
+    }
+    pixman_image_unref(src);
+
+    pixman_image_set_clip_region32(canvas->image, NULL);
+}
+
+static void blend_scale_image(CairoCanvas *canvas,
+                              pixman_region32_t *region,
+                              SPICE_ADDRESS src_bitmap,
+                              int src_x, int src_y,
+                              int src_width, int src_height,
+                              int dest_x, int dest_y,
+                              int dest_width, int dest_height,
+                              int scale_mode,
+                              int overall_alpha)
+{
+    pixman_transform_t transform;
+    pixman_image_t *src, *mask;
+    double sx, sy;
+
+    sx = (double)(src_width) / (dest_width);
+    sy = (double)(src_height) / (dest_height);
+
+    src = canvas_get_image(&canvas->base, src_bitmap);
+
+    pixman_image_set_clip_region32(canvas->image, region);
+
+    pixman_transform_init_scale(&transform,
+                                pixman_double_to_fixed(sx),
+                                pixman_double_to_fixed(sy));
+
+    mask = NULL;
+    if (overall_alpha != 0xff) {
+        pixman_color_t color = { 0 };
+        color.alpha = overall_alpha * 0x101;
+        mask = pixman_image_create_solid_fill(&color);
+    }
+
+    pixman_image_set_transform(src, &transform);
+    pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+    ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
+           scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
+    pixman_image_set_filter(src,
+                            (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
+                            PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
+                            NULL, 0);
+
+    pixman_image_composite32(PIXMAN_OP_OVER,
+                             src, mask, canvas->image,
+                             ROUND(src_x / sx), ROUND(src_y / sy), /* src */
+                             0, 0, /* mask */
+                             dest_x, dest_y, /* dst */
+                             dest_width, dest_height);
+
+    pixman_transform_init_identity(&transform);
+    pixman_image_set_transform(src, &transform);
+
+    if (mask) {
+        pixman_image_unref(mask);
+    }
+    pixman_image_unref(src);
+
+    pixman_image_set_clip_region32(canvas->image, NULL);
+}
+
 static void draw_brush(CairoCanvas *canvas,
                        pixman_region32_t *region,
                        SpiceBrush *brush,
@@ -1765,26 +1863,48 @@ void canvas_draw_transparent(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *cl
 
 void canvas_draw_alpha_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
 {
-    cairo_t *cairo = canvas->cairo;
-    cairo_pattern_t *pattern;
+    pixman_region32_t dest_region;
 
-    cairo_save(cairo);
-    cairo_rectangle(cairo,
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
+
+    canvas_clip_pixman(canvas, &dest_region, clip);
+
+    if (alpha_blend->alpha == 0 ||
+        pixman_region32_n_rects(&dest_region) == 0) {
+        canvas_touch_image(&canvas->base, alpha_blend->src_bitmap);
+        pixman_region32_fini(&dest_region);
+        return;
+    }
+
+    if (rect_is_same_size(bbox, &alpha_blend->src_area)) {
+        blend_image(canvas, &dest_region,
+                    alpha_blend->src_bitmap,
+                    alpha_blend->src_area.left,
+                    alpha_blend->src_area.top,
                     bbox->left,
                     bbox->top,
                     bbox->right - bbox->left,
-                    bbox->bottom - bbox->top);
-    cairo_clip(cairo);
-    canvas_clip(canvas, clip);
-
-    pattern = canvas_src_image_to_pat(canvas, alpha_blend->src_bitmap, &alpha_blend->src_area, bbox,
-                                      0, SPICE_IMAGE_SCALE_MODE_INTERPOLATE);
-    cairo_set_source(cairo, pattern);
-    cairo_pattern_destroy(pattern);
-    cairo_set_operator(cairo, CAIRO_OPERATOR_ATOP);
-    cairo_paint_with_alpha(cairo, (double)alpha_blend->alpha / 0xff);
+                    bbox->bottom - bbox->top,
+                    alpha_blend->alpha);
+    } else {
+        blend_scale_image(canvas, &dest_region,
+                          alpha_blend->src_bitmap,
+                          alpha_blend->src_area.left,
+                          alpha_blend->src_area.top,
+                          alpha_blend->src_area.right - alpha_blend->src_area.left,
+                          alpha_blend->src_area.bottom - alpha_blend->src_area.top,
+                          bbox->left,
+                          bbox->top,
+                          bbox->right - bbox->left,
+                          bbox->bottom - bbox->top,
+                          SPICE_IMAGE_SCALE_MODE_INTERPOLATE,
+                          alpha_blend->alpha);
+    }
 
-    cairo_restore(cairo);
+    pixman_region32_fini(&dest_region);
 }
 
 void canvas_draw_opaque(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
commit 824496405ab1f7550e9f98a445342777e700aae5
Author: Alexander Larsson <alexl at redhat.com>
Date:   Thu Feb 11 16:43:08 2010 +0100

    Convert cairo canvas draw_text to using pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 97fa6c5..8e9c2d6 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -851,6 +851,43 @@ static pixman_image_t* canvas_surface_from_self(CairoCanvas *canvas, SpicePoint
     return surface;
 }
 
+static pixman_image_t *canvas_get_pixman_brush(CairoCanvas *canvas,
+                                               SpiceBrush *brush)
+{
+    switch (brush->type) {
+    case SPICE_BRUSH_TYPE_SOLID: {
+        uint32_t color = brush->u.color;
+        pixman_color_t c;
+
+        c.blue = ((color & canvas->base.color_mask) * 0xffff) / canvas->base.color_mask;
+        color >>= canvas->base.color_shift;
+        c.green = ((color & canvas->base.color_mask) * 0xffff) / canvas->base.color_mask;
+        color >>= canvas->base.color_shift;
+        c.red = ((color & canvas->base.color_mask) * 0xffff) / canvas->base.color_mask;
+        c.alpha = 0xffff;
+
+        return pixman_image_create_solid_fill(&c);
+    }
+    case SPICE_BRUSH_TYPE_PATTERN: {
+        pixman_image_t* surface;
+        pixman_transform_t t;
+
+        surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
+        pixman_transform_init_translate(&t,
+                                        pixman_int_to_fixed(-brush->u.pattern.pos.x),
+                                        pixman_int_to_fixed(-brush->u.pattern.pos.y));
+        pixman_image_set_transform(surface, &t);
+        pixman_image_set_repeat(surface, PIXMAN_REPEAT_NORMAL);
+        return surface;
+    }
+    case SPICE_BRUSH_TYPE_NONE:
+        return NULL;
+    default:
+        CANVAS_ERROR("invalid brush type");
+    }
+}
+
+
 static cairo_pattern_t *canvas_get_brush(CairoCanvas *canvas, SpiceBrush *brush, uint32_t invers)
 {
     switch (brush->type) {
@@ -992,16 +1029,6 @@ static cairo_pattern_t *canvas_get_mask_pattern(CairoCanvas *canvas, SpiceQMask
     return pattern;
 }
 
-typedef struct DrawMaskData {
-    cairo_t *cairo;
-    cairo_pattern_t *mask;
-} DrawMaskData;
-
-static void __draw_mask(void *data)
-{
-    cairo_mask(((DrawMaskData *)data)->cairo, ((DrawMaskData *)data)->mask);
-}
-
 static void copy_region(CairoCanvas *canvas,
                         pixman_region32_t *dest_region,
                         int dx, int dy)
@@ -2040,83 +2067,85 @@ void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     pixman_region32_fini(&dest_region);
 }
 
-static void canvas_draw_raster_str(CairoCanvas *canvas, SpiceString *str, int bpp,
-                                   SpiceBrush *brush, uint16_t rop_decriptor)
+void canvas_draw_text(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
 {
-    pixman_image_t *str_mask;
-    cairo_surface_t *cairo_str_mask;
-    DrawMaskData draw_data;
-    cairo_matrix_t matrix;
+    pixman_region32_t dest_region;
+    pixman_image_t *str_mask, *brush;
+    SpiceString *str;
     SpicePoint pos;
+    int depth;
 
-    str_mask = canvas_get_str_mask(&canvas->base, str, bpp, &pos);
-    cairo_str_mask = surface_from_pixman_image (str_mask);
-    draw_data.cairo = canvas->cairo;
-    draw_data.mask = cairo_pattern_create_for_surface(cairo_str_mask);
-    if (cairo_pattern_status(draw_data.mask) != CAIRO_STATUS_SUCCESS) {
-        cairo_surface_destroy(cairo_str_mask);
-        CANVAS_ERROR("create pattern failed, %s",
-                     cairo_status_to_string(cairo_pattern_status(draw_data.mask)));
-    }
-    cairo_matrix_init_translate(&matrix, -pos.x, -pos.y);
-    cairo_pattern_set_matrix(draw_data.mask, &matrix);
-    canvas_draw(canvas, brush, rop_decriptor, __draw_mask, &draw_data);
-    cairo_pattern_destroy(draw_data.mask);
-    pixman_image_unref(str_mask);
-    cairo_surface_destroy(cairo_str_mask);
-}
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
 
-static void canvas_draw_vector_str(CairoCanvas *canvas, SpiceString *str, SpiceBrush *brush,
-                                   uint16_t rop_decriptor)
-{
-    SpiceVectorGlyph *glyph = (SpiceVectorGlyph *)str->data;
-    int i;
+    canvas_clip_pixman(canvas, &dest_region, clip);
 
-    for (i = 0; i < str->length; i++) {
-        SpiceVectorGlyph *next_glyph = canvas_next_vector_glyph(glyph);
-        access_test(&canvas->base, glyph, (uint8_t *)next_glyph - (uint8_t *)glyph);
-        canvas_set_path(canvas, glyph->data);
-        glyph = next_glyph;
+    if (pixman_region32_n_rects(&dest_region) == 0) {
+        touch_brush(canvas, &text->fore_brush);
+        touch_brush(canvas, &text->back_brush);
+        pixman_region32_fini(&dest_region);
+        return;
     }
-    canvas_draw(canvas, brush, rop_decriptor, (DrawMethod)cairo_fill_preserve, canvas->cairo);
-    cairo_new_path(canvas->cairo);
-}
 
-void canvas_draw_text(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
-{
-    cairo_t *cairo = canvas->cairo;
-    SpiceString *str;
-
-    cairo_save(cairo);
-    canvas_clip(canvas, clip);
     if (!rect_is_empty(&text->back_area)) {
-        cairo_rectangle(cairo,
-                        text->back_area.left,
-                        text->back_area.top,
-                        text->back_area.right - text->back_area.left,
-                        text->back_area.bottom - text->back_area.top);
-        canvas_draw(canvas, &text->back_brush, text->back_mode,
-                    (DrawMethod)cairo_fill_preserve, cairo);
-        cairo_new_path(cairo);
+        pixman_region32_t back_region;
+
+        /* Nothing else makes sense for text and we should deprecate it
+         * and actually it means OVER really */
+        ASSERT(text->fore_mode == SPICE_ROPD_OP_PUT);
+
+        pixman_region32_init_rect(&back_region,
+                                  text->back_area.left,
+                                  text->back_area.top,
+                                  text->back_area.right - text->back_area.left,
+                                  text->back_area.bottom - text->back_area.top);
+
+        pixman_region32_intersect(&back_region, &back_region, &dest_region);
+
+        if (pixman_region32_not_empty(&back_region)) {
+            draw_brush(canvas, &back_region, &text->back_brush, SPICE_ROP_COPY);
+        }
+
+        pixman_region32_fini(&back_region);
     }
     str = (SpiceString *)SPICE_GET_ADDRESS(text->str);
 
     if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) {
-        canvas_draw_raster_str(canvas, str, 1, &text->fore_brush, text->fore_mode);
+        depth = 1;
     } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) {
-        canvas_draw_raster_str(canvas, str, 4, &text->fore_brush, text->fore_mode);
+        depth = 4;
     } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A8) {
-        WARN("untested path A8 glyphs, doing nothing");
-        if (0) {
-            canvas_draw_raster_str(canvas, str, 8, &text->fore_brush, text->fore_mode);
-        }
+        WARN("untested path A8 glyphs");
+        depth = 8;
     } else {
-        WARN("untested path vector glyphs, doing nothing");
-        if (0) {
-            canvas_draw_vector_str(canvas, str, &text->fore_brush, text->fore_mode);
-        }
+        WARN("unsupported path vector glyphs");
+        pixman_region32_fini (&dest_region);
+        return;
     }
-    cairo_restore(cairo);
+
+    brush = canvas_get_pixman_brush(canvas, &text->fore_brush);
+
+    str_mask = canvas_get_str_mask(&canvas->base, str, depth, &pos);
+    if (brush) {
+        pixman_image_set_clip_region32(canvas->image, &dest_region);
+
+        pixman_image_composite32(PIXMAN_OP_OVER,
+                                 brush,
+                                 str_mask,
+                                 canvas->image,
+                                 0, 0,
+                                 0, 0,
+                                 pos.x, pos.y,
+                                 pixman_image_get_width(str_mask),
+                                 pixman_image_get_height(str_mask));
+        pixman_image_unref(brush);
+
+        pixman_image_set_clip_region32(canvas->image, NULL);
+    }
+    pixman_image_unref(str_mask);
+    pixman_region32_fini(&dest_region);
 }
 
 void canvas_draw_stroke(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
commit bf0d038e76b1d875accb73adb04de7cba148f995
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Feb 10 14:00:42 2010 +0100

    Convert cairo canvas copy bits to pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 34f6fa4..97fa6c5 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -1002,6 +1002,75 @@ static void __draw_mask(void *data)
     cairo_mask(((DrawMaskData *)data)->cairo, ((DrawMaskData *)data)->mask);
 }
 
+static void copy_region(CairoCanvas *canvas,
+                        pixman_region32_t *dest_region,
+                        int dx, int dy)
+{
+    pixman_box32_t *dest_rects;
+    int n_rects;
+    int i, j, end_line;
+
+    dest_rects = pixman_region32_rectangles(dest_region, &n_rects);
+
+    if (dy > 0) {
+        if (dx >= 0) {
+            /* south-east: copy x and y in reverse order */
+            for (i = n_rects - 1; i >= 0; i--) {
+                spice_pixman_copy_rect(canvas->image,
+                                       dest_rects[i].x1 - dx, dest_rects[i].y1 - dy,
+                                       dest_rects[i].x2 - dest_rects[i].x1,
+                                       dest_rects[i].y2 - dest_rects[i].y1,
+                                       dest_rects[i].x1, dest_rects[i].y1);
+            }
+        } else {
+            /* south-west: Copy y in reverse order, but x in forward order */
+            i = n_rects - 1;
+
+            while (i >= 0) {
+                /* Copy all rects with same y in forward order */
+                for (end_line = i - 1; end_line >= 0 && dest_rects[end_line].y1 == dest_rects[i].y1; end_line--) {
+                }
+                for (j = end_line + 1; j <= i; j++) {
+                    spice_pixman_copy_rect(canvas->image,
+                                           dest_rects[j].x1 - dx, dest_rects[j].y1 - dy,
+                                           dest_rects[j].x2 - dest_rects[j].x1,
+                                           dest_rects[j].y2 - dest_rects[j].y1,
+                                           dest_rects[j].x1, dest_rects[j].y1);
+                }
+                i = end_line;
+            }
+        }
+    } else {
+        if (dx > 0) {
+            /* north-east: copy y in forward order, but x in reverse order */
+            i = 0;
+
+            while (i < n_rects) {
+                /* Copy all rects with same y in reverse order */
+                for (end_line = i; end_line < n_rects && dest_rects[end_line].y1 == dest_rects[i].y1; end_line++) {
+                }
+                for (j = end_line - 1; j >= i; j--) {
+                    spice_pixman_copy_rect(canvas->image,
+                                           dest_rects[j].x1 - dx, dest_rects[j].y1 - dy,
+                                           dest_rects[j].x2 - dest_rects[j].x1,
+                                           dest_rects[j].y2 - dest_rects[j].y1,
+                                           dest_rects[j].x1, dest_rects[j].y1);
+                }
+                i = end_line;
+            }
+        } else {
+            /* north-west: Copy x and y in forward order */
+            for (i = 0; i < n_rects; i++) {
+                spice_pixman_copy_rect(canvas->image,
+                                       dest_rects[i].x1 - dx, dest_rects[i].y1 - dy,
+                                       dest_rects[i].x2 - dest_rects[i].x1,
+                                       dest_rects[i].y2 - dest_rects[i].y1,
+                                       dest_rects[i].x1, dest_rects[i].y1);
+            }
+        }
+    }
+}
+
 static void fill_solid_rects(CairoCanvas *canvas,
                              pixman_region32_t *region,
                              uint32_t color)
@@ -1939,226 +2008,36 @@ void canvas_draw_rop3(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     cairo_restore(cairo);
 }
 
-#define FAST_COPY_BITS
-
-#ifdef FAST_COPY_BITS
-
-static inline void __canvas_copy_bits_up(uint8_t *data, const int stride,
-                                         const int src_x, const int src_y,
-                                         const int width, const int height,
-                                         const int dest_x, const int dest_y)
-{
-    uint8_t *src = data + src_y * stride + src_x * sizeof(uint32_t);
-    uint8_t *dest = data + dest_y * stride + dest_x * sizeof(uint32_t);
-    uint8_t *end = dest + height * stride;
-    for (; dest != end; dest += stride, src += stride) {
-        memcpy(dest, src, width * sizeof(uint32_t));
-    }
-}
-
-static inline void __canvas_copy_bits_down(uint8_t *data, const int stride,
-                                           const int src_x, const int src_y,
-                                           const int width, const int height,
-                                           const int dest_x, const int dest_y)
-{
-    uint8_t *src = data + (src_y + height - 1) * stride + src_x * sizeof(uint32_t);
-    uint8_t *end = data + (dest_y - 1) * stride + dest_x * sizeof(uint32_t);
-    uint8_t *dest = end + height * stride;
-
-    for (; dest != end; dest -= stride, src -= stride) {
-        memcpy(dest, src, width * sizeof(uint32_t));
-    }
-}
-
-static inline void __canvas_copy_bits_right(uint8_t *data, const int stride,
-                                            const int src_x, const int src_y,
-                                            const int width, const int height,
-                                            const int dest_x, const int dest_y)
-{
-    uint8_t *src = data + src_y * stride + (src_x + width - 1) * sizeof(uint32_t);
-    uint8_t *dest = data + dest_y * stride + (dest_x + width - 1) * sizeof(uint32_t);
-    uint8_t *end = dest + height * stride;
-    for (; dest != end; dest += stride, src += stride) {
-        uint32_t *src_pix = (uint32_t *)src;
-        uint32_t *end_pix = src_pix - width;
-        uint32_t *dest_pix = (uint32_t *)dest;
-
-        for (; src_pix > end_pix; src_pix--, dest_pix--) {
-            *dest_pix = *src_pix;
-        }
-    }
-}
-
-static inline void __canvas_copy_rect_bits(uint8_t *data, const int stride, SpiceRect *dest_rect,
-                                           SpicePoint *src_pos)
-{
-    if (dest_rect->top > src_pos->y) {
-        __canvas_copy_bits_down(data, stride, src_pos->x, src_pos->y,
-                                dest_rect->right - dest_rect->left,
-                                dest_rect->bottom - dest_rect->top,
-                                dest_rect->left, dest_rect->top);
-    } else if (dest_rect->top < src_pos->y || dest_rect->left < src_pos->x) {
-        __canvas_copy_bits_up(data, stride, src_pos->x, src_pos->y,
-                              dest_rect->right - dest_rect->left,
-                              dest_rect->bottom - dest_rect->top,
-                              dest_rect->left, dest_rect->top);
-    } else {
-        __canvas_copy_bits_right(data, stride, src_pos->x, src_pos->y,
-                                 dest_rect->right - dest_rect->left,
-                                 dest_rect->bottom - dest_rect->top,
-                                 dest_rect->left, dest_rect->top);
-    }
-}
-
-static inline void canvas_copy_fix_clip_area(const SpiceRect *dest,
-                                             const SpicePoint *src_pos,
-                                             const SpiceRect *now,
-                                             SpicePoint *ret_pos,
-                                             SpiceRect *ret_dest)
-{
-    *ret_dest = *now;
-    rect_sect(ret_dest, dest);
-    ret_pos->x = src_pos->x + (ret_dest->left - dest->left);
-    ret_pos->y = src_pos->y + (ret_dest->top - dest->top);
-}
-
-static inline void __canvas_copy_region_bits(uint8_t *data, int stride, SpiceRect *dest_rect,
-                                             SpicePoint *src_pos, QRegion *region)
+void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
 {
-    SpiceRect curr_area;
-    SpicePoint curr_pos;
-    SpiceRect *now;
-    SpiceRect *end;
-
-    if (dest_rect->top > src_pos->y) {
-        end = region->rects - 1;
-        now = end + region->num_rects;
-        if (dest_rect->left < src_pos->x) {
-            for (; now > end; now--) {
-                SpiceRect *line_end = now;
-                SpiceRect *line_pos;
-
-                while (now - 1 > end && now->top == now[-1].top) {
-                    now--;
-                }
-
-                for (line_pos = now; line_pos <= line_end; line_pos++) {
-                    canvas_copy_fix_clip_area(dest_rect, src_pos, line_pos, &curr_pos, &curr_area);
-                    __canvas_copy_bits_down(data, stride, curr_pos.x, curr_pos.y,
-                                            curr_area.right - curr_area.left,
-                                            curr_area.bottom - curr_area.top,
-                                            curr_area.left, curr_area.top);
-                }
-            }
-        } else {
-            for (; now > end; now--) {
-                canvas_copy_fix_clip_area(dest_rect, src_pos, now, &curr_pos, &curr_area);
-                __canvas_copy_bits_down(data, stride, curr_pos.x, curr_pos.y,
-                                        curr_area.right - curr_area.left,
-                                        curr_area.bottom - curr_area.top,
-                                        curr_area.left, curr_area.top);
-            }
-        }
-    } else if (dest_rect->top < src_pos->y || dest_rect->left < src_pos->x) {
-        now = region->rects;
-        end = now + region->num_rects;
-        if (dest_rect->left > src_pos->x) {
-            for (; now < end; now++) {
-                SpiceRect *line_end = now;
-                SpiceRect *line_pos;
-
-                while (now + 1 < end && now->top == now[1].top) {
-                    now++;
-                }
-
-                for (line_pos = now; line_pos >= line_end; line_pos--) {
-                    canvas_copy_fix_clip_area(dest_rect, src_pos, line_pos, &curr_pos, &curr_area);
-                    __canvas_copy_bits_up(data, stride, curr_pos.x, curr_pos.y,
-                                          curr_area.right - curr_area.left,
-                                          curr_area.bottom - curr_area.top,
-                                          curr_area.left, curr_area.top);
-                }
-            }
-        } else {
-            for (; now < end; now++) {
-                canvas_copy_fix_clip_area(dest_rect, src_pos, now, &curr_pos, &curr_area);
-                __canvas_copy_bits_up(data, stride, curr_pos.x, curr_pos.y,
-                                      curr_area.right - curr_area.left,
-                                      curr_area.bottom - curr_area.top,
-                                      curr_area.left, curr_area.top);
-            }
-        }
-    } else {
-        end = region->rects - 1;
-        now = end + region->num_rects;
-        for (; now > end; now--) {
-            canvas_copy_fix_clip_area(dest_rect, src_pos, now, &curr_pos, &curr_area);
-            __canvas_copy_bits_right(data, stride, curr_pos.x, curr_pos.y,
-                                     curr_area.right - curr_area.left,
-                                     curr_area.bottom - curr_area.top,
-                                     curr_area.left, curr_area.top);
-        }
-    }
-}
+    pixman_region32_t dest_region;
+    int dx, dy;
 
-#endif
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
 
-void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
-{
-    cairo_t *cairo = canvas->cairo;
-    cairo_surface_t *cairo_surface;
-    pixman_image_t *surface;
-    int32_t width;
-    int32_t heigth;
+    canvas_clip_pixman(canvas, &dest_region, clip);
 
-    cairo_save(cairo);
-#ifdef FAST_COPY_BITS
-    switch (clip->type) {
-    case SPICE_CLIP_TYPE_NONE: {
-        __canvas_copy_rect_bits((uint8_t *)pixman_image_get_data(canvas->image),
-                                pixman_image_get_stride(canvas->image),
-                                bbox, src_pos);
-        break;
-    }
-    case SPICE_CLIP_TYPE_RECTS: {
-        uint32_t *n = (uint32_t *)SPICE_GET_ADDRESS(clip->data);
-        access_test(&canvas->base, n, sizeof(uint32_t));
+    dx = bbox->left - src_pos->x;
+    dy = bbox->top - src_pos->y;
 
-        SpiceRect *now = (SpiceRect *)(n + 1);
-        SpiceRect *end = now + *n;
-        access_test(&canvas->base, now, (unsigned long)end - (unsigned long)now);
-        uint8_t *data = (uint8_t *)pixman_image_get_data(canvas->image);
-        int stride = pixman_image_get_stride(canvas->image);
+    if (dx != 0 || dy != 0) {
+        pixman_region32_t src_region;
 
-        //using QRegion in order to sort and remove intersections
-        QRegion region;
-        region_init(&region);
-        for (; now < end; now++) {
-            region_add(&region, now);
-        }
-        __canvas_copy_region_bits(data, stride, bbox, src_pos, &region);
-        region_destroy(&region);
-        break;
-    }
-    default:
-#endif
-        canvas_clip(canvas, clip);
+        /* Clip so we don't read outside canvas */
+        pixman_region32_init_rect(&src_region,
+                                  dx, dy,
+                                  pixman_image_get_width (canvas->image),
+                                  pixman_image_get_height (canvas->image));
+        pixman_region32_intersect(&dest_region, &dest_region, &src_region);
+        pixman_region32_fini(&src_region);
 
-        width = bbox->right - bbox->left;
-        heigth = bbox->bottom - bbox->top;
-        surface = canvas_surface_from_self(canvas, src_pos, width, heigth);
-        cairo_surface = surface_from_pixman_image (surface);
-        cairo_set_source_surface(cairo, cairo_surface, bbox->left, bbox->top);
-        cairo_rectangle(cairo, bbox->left, bbox->top, width, heigth);
-        cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
-        cairo_fill(cairo);
-        cairo_surface_destroy(cairo_surface);
-        pixman_image_unref (surface);
-#ifdef FAST_COPY_BITS
+        copy_region(canvas, &dest_region, dx, dy);
     }
 
-#endif
-    cairo_restore(cairo);
+    pixman_region32_fini(&dest_region);
 }
 
 static void canvas_draw_raster_str(CairoCanvas *canvas, SpiceString *str, int bpp,
commit 7fe1df4e140eec348137fa5934e33d3b968eede7
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Feb 10 12:09:52 2010 +0100

    Convert cairo canvas draw_copy() to using pixman
    
    This is just identical to draw_blend().

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index f871c53..34f6fa4 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -1416,31 +1416,71 @@ static cairo_pattern_t *canvas_src_image_to_pat(CairoCanvas *canvas, SPICE_ADDRE
 
 void canvas_draw_copy(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
 {
-    cairo_t *cairo = canvas->cairo;
-    cairo_pattern_t *pattern;
-    cairo_pattern_t *mask;
+    pixman_region32_t dest_region;
+    SpiceROP rop;
 
-    cairo_save(cairo);
-    canvas_clip(canvas, clip);
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
 
-    pattern = canvas_src_image_to_pat(canvas, copy->src_bitmap, &copy->src_area, bbox,
-                                      copy->rop_decriptor & SPICE_ROPD_INVERS_SRC, copy->scale_mode);
-    cairo_set_source(cairo, pattern);
-    cairo_pattern_destroy(pattern);
-    cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
-    if ((mask = canvas_get_mask_pattern(canvas, &copy->mask, bbox->left, bbox->top))) {
-        cairo_rectangle(cairo, bbox->left, bbox->top, bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        cairo_clip(cairo);
-        cairo_mask(cairo, mask);
-        cairo_pattern_destroy(mask);
+    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_mask_pixman(canvas, &dest_region, &copy->mask,
+                       bbox->left, bbox->top);
+
+    rop = ropd_descriptor_to_rop(copy->rop_decriptor,
+                                 ROP_INPUT_SRC,
+                                 ROP_INPUT_DEST);
+
+    if (rop == SPICE_ROP_NOOP || pixman_region32_n_rects(&dest_region) == 0) {
+        canvas_touch_image(&canvas->base, copy->src_bitmap);
+        pixman_region32_fini(&dest_region);
+        return;
+    }
+
+    if (rect_is_same_size(bbox, &copy->src_area)) {
+        if (rop == SPICE_ROP_COPY) {
+            blit_image(canvas, &dest_region,
+                       copy->src_bitmap,
+                       bbox->left - copy->src_area.left,
+                       bbox->top - copy->src_area.top);
+        } else {
+            blit_image_rop(canvas, &dest_region,
+                           copy->src_bitmap,
+                           bbox->left - copy->src_area.left,
+                           bbox->top - copy->src_area.top,
+                           rop);
+        }
     } else {
-        cairo_rectangle(cairo, bbox->left, bbox->top, bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        cairo_fill(cairo);
+        if (rop == SPICE_ROP_COPY) {
+            scale_image(canvas, &dest_region,
+                        copy->src_bitmap,
+                        copy->src_area.left,
+                        copy->src_area.top,
+                        copy->src_area.right - copy->src_area.left,
+                        copy->src_area.bottom - copy->src_area.top,
+                        bbox->left,
+                        bbox->top,
+                        bbox->right - bbox->left,
+                        bbox->bottom - bbox->top,
+                        copy->scale_mode);
+        } else {
+            scale_image_rop(canvas, &dest_region,
+                            copy->src_bitmap,
+                            copy->src_area.left,
+                            copy->src_area.top,
+                            copy->src_area.right - copy->src_area.left,
+                            copy->src_area.bottom - copy->src_area.top,
+                            bbox->left,
+                            bbox->top,
+                            bbox->right - bbox->left,
+                            bbox->bottom - bbox->top,
+                            copy->scale_mode,
+                            rop);
+        }
     }
 
-    cairo_restore(cairo);
+    pixman_region32_fini(&dest_region);
 }
 
 #ifdef WIN32
commit eff0aa029833a914cce7b047a48d9aa19b825c0a
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Feb 10 11:22:19 2010 +0100

    Convert cairo canvas draw_blend() to using pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index f364201..f871c53 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -1140,6 +1140,39 @@ static void blit_image(CairoCanvas *canvas,
     pixman_image_unref(src_image);
 }
 
+static void blit_image_rop(CairoCanvas *canvas,
+                           pixman_region32_t *region,
+                           SPICE_ADDRESS src_bitmap,
+                           int offset_x, int offset_y,
+                           SpiceROP rop)
+{
+    pixman_image_t *src_image;
+    pixman_box32_t *rects;
+    int n_rects, i;
+
+    rects = pixman_region32_rectangles(region, &n_rects);
+
+    src_image = canvas_get_image(&canvas->base, src_bitmap);
+    for (i = 0; i < n_rects; i++) {
+        int src_x, src_y, dest_x, dest_y, width, height;
+
+        dest_x = rects[i].x1;
+        dest_y = rects[i].y1;
+        width = rects[i].x2 - rects[i].x1;
+        height = rects[i].y2 - rects[i].y1;
+
+        src_x = rects[i].x1 - offset_x;
+        src_y = rects[i].y1 - offset_y;
+
+        spice_pixman_blit_rop(canvas->image,
+                              src_image,
+                              src_x, src_y,
+                              dest_x, dest_y,
+                              width, height, rop);
+    }
+    pixman_image_unref(src_image);
+}
+
 static void scale_image(CairoCanvas *canvas,
                         pixman_region32_t *region,
                         SPICE_ADDRESS src_bitmap,
@@ -1187,6 +1220,79 @@ static void scale_image(CairoCanvas *canvas,
     pixman_image_unref(src);
 }
 
+static void scale_image_rop(CairoCanvas *canvas,
+                            pixman_region32_t *region,
+                            SPICE_ADDRESS src_bitmap,
+                            int src_x, int src_y,
+                            int src_width, int src_height,
+                            int dest_x, int dest_y,
+                            int dest_width, int dest_height,
+                            int scale_mode, SpiceROP rop)
+{
+    pixman_transform_t transform;
+    pixman_image_t *src;
+    pixman_image_t *scaled;
+    pixman_box32_t *rects;
+    int n_rects, i;
+    double sx, sy;
+
+    sx = (double)(src_width) / (dest_width);
+    sy = (double)(src_height) / (dest_height);
+
+    src = canvas_get_image(&canvas->base, src_bitmap);
+
+    scaled = pixman_image_create_bits(PIXMAN_x8r8g8b8,
+                                      dest_width,
+                                      dest_height,
+                                      NULL, 0);
+
+    pixman_region32_translate(region, -dest_x, -dest_y);
+    pixman_image_set_clip_region32(scaled, region);
+
+    pixman_transform_init_scale(&transform,
+                                pixman_double_to_fixed(sx),
+                                pixman_double_to_fixed(sy));
+
+    pixman_image_set_transform(src, &transform);
+    pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+    ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
+           scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
+    pixman_image_set_filter(src,
+                            (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
+                            PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
+                            NULL, 0);
+
+    pixman_image_composite32(PIXMAN_OP_SRC,
+                             src, NULL, scaled,
+                             ROUND(src_x / sx), ROUND(src_y / sy), /* src */
+                             0, 0, /* mask */
+                             0, 0, /* dst */
+                             dest_width,
+                             dest_height);
+
+    pixman_transform_init_identity(&transform);
+    pixman_image_set_transform(src, &transform);
+
+    /* Translate back */
+    pixman_region32_translate(region, dest_x, dest_y);
+
+    rects = pixman_region32_rectangles(region, &n_rects);
+
+    for (i = 0; i < n_rects; i++) {
+        spice_pixman_blit_rop(canvas->image,
+                              scaled,
+                              rects[i].x1 - dest_x,
+                              rects[i].y1 - dest_y,
+                              rects[i].x1, rects[i].y1,
+                              rects[i].x2 - rects[i].x1,
+                              rects[i].y2 - rects[i].y1,
+                              rop);
+    }
+
+    pixman_image_unref(scaled);
+    pixman_image_unref(src);
+}
+
 static void draw_brush(CairoCanvas *canvas,
                        pixman_region32_t *region,
                        SpiceBrush *brush,
@@ -1594,39 +1700,76 @@ void canvas_draw_opaque(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     pixman_region32_fini(&dest_region);
 }
 
-
 void canvas_draw_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
 {
-    cairo_pattern_t *pattern;
-    DrawMaskData mask_data;
+    pixman_region32_t dest_region;
+    SpiceROP rop;
+
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
 
-    mask_data.cairo = canvas->cairo;
+    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_mask_pixman(canvas, &dest_region, &blend->mask,
+                       bbox->left, bbox->top);
 
-    cairo_save(mask_data.cairo);
-    canvas_clip(canvas, clip);
+    rop = ropd_descriptor_to_rop(blend->rop_decriptor,
+                                 ROP_INPUT_SRC,
+                                 ROP_INPUT_DEST);
 
-    pattern = canvas_src_image_to_pat(canvas, blend->src_bitmap, &blend->src_area, bbox,
-                                      blend->rop_decriptor & SPICE_ROPD_INVERS_SRC, blend->scale_mode);
+    if (rop == SPICE_ROP_NOOP || pixman_region32_n_rects(&dest_region) == 0) {
+        canvas_touch_image(&canvas->base, blend->src_bitmap);
+        pixman_region32_fini(&dest_region);
+        return;
+    }
 
-    if ((mask_data.mask = canvas_get_mask_pattern(canvas, &blend->mask, bbox->left, bbox->top))) {
-        cairo_rectangle(mask_data.cairo,
-                        bbox->left,
-                        bbox->top,
-                        bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        cairo_clip(mask_data.cairo);
-        canvas_draw_with_pattern(canvas, pattern, blend->rop_decriptor, __draw_mask, &mask_data);
-        cairo_pattern_destroy(mask_data.mask);
+    if (rect_is_same_size(bbox, &blend->src_area)) {
+        if (rop == SPICE_ROP_COPY)
+            blit_image(canvas, &dest_region,
+                       blend->src_bitmap,
+                       bbox->left - blend->src_area.left,
+                       bbox->top - blend->src_area.top);
+        else
+            blit_image_rop(canvas, &dest_region,
+                           blend->src_bitmap,
+                           bbox->left - blend->src_area.left,
+                           bbox->top - blend->src_area.top,
+                           rop);
     } else {
-        cairo_rectangle(mask_data.cairo, bbox->left, bbox->top, bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        canvas_draw_with_pattern(canvas, pattern, blend->rop_decriptor,
-                                 (DrawMethod)cairo_fill_preserve,
-                                 mask_data.cairo);
-        cairo_new_path(mask_data.cairo);
+        double sx, sy;
+
+        sx = (double)(blend->src_area.right - blend->src_area.left) / (bbox->right - bbox->left);
+        sy = (double)(blend->src_area.bottom - blend->src_area.top) / (bbox->bottom - bbox->top);
+
+        if (rop == SPICE_ROP_COPY) {
+            scale_image(canvas, &dest_region,
+                         blend->src_bitmap,
+                         blend->src_area.left,
+                         blend->src_area.top,
+                         blend->src_area.right - blend->src_area.left,
+                         blend->src_area.bottom - blend->src_area.top,
+                         bbox->left,
+                         bbox->top,
+                         bbox->right - bbox->left,
+                         bbox->bottom - bbox->top,
+                         blend->scale_mode);
+        } else {
+            scale_image_rop(canvas, &dest_region,
+                            blend->src_bitmap,
+                            blend->src_area.left,
+                            blend->src_area.top,
+                            blend->src_area.right - blend->src_area.left,
+                            blend->src_area.bottom - blend->src_area.top,
+                            bbox->left,
+                            bbox->top,
+                            bbox->right - bbox->left,
+                            bbox->bottom - bbox->top,
+                            blend->scale_mode, rop);
+        }
     }
-    cairo_pattern_destroy(pattern);
-    cairo_restore(mask_data.cairo);
+
+    pixman_region32_fini(&dest_region);
 }
 
 static inline void canvas_fill_common(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
commit 0cb87a507c912550b81af14f992900152e507cce
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Feb 9 20:33:04 2010 +0100

    Convert cairo canvas draw_opaque() to using pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 6b25505..f364201 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -16,6 +16,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include <math.h>
 #include "cairo_canvas.h"
 #define CANVAS_USE_PIXMAN
 #include "canvas_base.c"
@@ -24,6 +25,8 @@
 #include "region.h"
 #include "pixman_utils.h"
 
+#define ROUND(_x) floor((_x) + 0.5)
+
 struct CairoCanvas {
     CanvasBase base;
     cairo_t *cairo;
@@ -1094,6 +1097,96 @@ static void fill_tiled_rects_rop(CairoCanvas *canvas,
     pixman_image_unref(tile);
 }
 
+
+static void blit_with_region(CairoCanvas *canvas,
+                             pixman_region32_t *region,
+                             pixman_image_t *src_image,
+                             int offset_x, int offset_y)
+{
+    pixman_box32_t *rects;
+    int n_rects, i;
+
+    rects = pixman_region32_rectangles(region, &n_rects);
+
+    for (i = 0; i < n_rects; i++) {
+        int src_x, src_y, dest_x, dest_y, width, height;
+
+        dest_x = rects[i].x1;
+        dest_y = rects[i].y1;
+        width = rects[i].x2 - rects[i].x1;
+        height = rects[i].y2 - rects[i].y1;
+
+        src_x = rects[i].x1 - offset_x;
+        src_y = rects[i].y1 - offset_y;
+
+        spice_pixman_blit(canvas->image,
+                          src_image,
+                          src_x, src_y,
+                          dest_x, dest_y,
+                          width, height);
+    }
+}
+
+static void blit_image(CairoCanvas *canvas,
+                       pixman_region32_t *region,
+                       SPICE_ADDRESS src_bitmap,
+                       int offset_x, int offset_y)
+{
+    pixman_image_t *src_image;
+
+    src_image = canvas_get_image(&canvas->base, src_bitmap);
+    blit_with_region(canvas, region, src_image,
+                      offset_x, offset_y);
+    pixman_image_unref(src_image);
+}
+
+static void scale_image(CairoCanvas *canvas,
+                        pixman_region32_t *region,
+                        SPICE_ADDRESS src_bitmap,
+                        int src_x, int src_y,
+                        int src_width, int src_height,
+                        int dest_x, int dest_y,
+                        int dest_width, int dest_height,
+                        int scale_mode)
+{
+    pixman_transform_t transform;
+    pixman_image_t *src;
+    double sx, sy;
+
+    sx = (double)(src_width) / (dest_width);
+    sy = (double)(src_height) / (dest_height);
+
+    src = canvas_get_image(&canvas->base, src_bitmap);
+
+    pixman_image_set_clip_region32(canvas->image, region);
+
+    pixman_transform_init_scale(&transform,
+                                pixman_double_to_fixed(sx),
+                                pixman_double_to_fixed(sy));
+
+    pixman_image_set_transform(src, &transform);
+    pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+    ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
+           scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
+    pixman_image_set_filter(src,
+                            (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
+                            PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
+                            NULL, 0);
+
+    pixman_image_composite32(PIXMAN_OP_SRC,
+                             src, NULL, canvas->image,
+                             ROUND(src_x / sx), ROUND(src_y / sy), /* src */
+                             0, 0, /* mask */
+                             dest_x, dest_y, /* dst */
+                             dest_width, dest_height);
+
+    pixman_transform_init_identity(&transform);
+    pixman_image_set_transform(src, &transform);
+
+    pixman_image_set_clip_region32(canvas->image, NULL);
+    pixman_image_unref(src);
+}
+
 static void draw_brush(CairoCanvas *canvas,
                        pixman_region32_t *region,
                        SpiceBrush *brush,
@@ -1454,41 +1547,54 @@ void canvas_draw_alpha_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *cl
 
 void canvas_draw_opaque(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
 {
-    cairo_pattern_t *pattern;
-    DrawMaskData draw_data;
+    pixman_region32_t dest_region;
+    SpiceROP rop;
 
-    draw_data.cairo = canvas->cairo;
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
 
-    cairo_save(draw_data.cairo);
-    canvas_clip(canvas, clip);
+    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_mask_pixman(canvas, &dest_region, &opaque->mask,
+                       bbox->left, bbox->top);
 
-    pattern = canvas_src_image_to_pat(canvas, opaque->src_bitmap, &opaque->src_area, bbox,
-                                      opaque->rop_decriptor & SPICE_ROPD_INVERS_SRC, opaque->scale_mode);
-    cairo_set_source(draw_data.cairo, pattern);
-    cairo_pattern_destroy(pattern);
-    cairo_set_operator(draw_data.cairo, CAIRO_OPERATOR_RASTER_COPY);
-    if ((draw_data.mask = canvas_get_mask_pattern(canvas, &opaque->mask, bbox->left, bbox->top))) {
-        cairo_rectangle(draw_data.cairo,
-                        bbox->left,
-                        bbox->top,
-                        bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        cairo_clip(draw_data.cairo);
-        cairo_mask(draw_data.cairo, draw_data.mask);
-        canvas_draw(canvas, &opaque->brush, opaque->rop_decriptor, __draw_mask, &draw_data);
-        cairo_pattern_destroy(draw_data.mask);
+    rop = ropd_descriptor_to_rop(opaque->rop_decriptor,
+                                 ROP_INPUT_BRUSH,
+                                 ROP_INPUT_SRC);
+
+    if (rop == SPICE_ROP_NOOP || pixman_region32_n_rects(&dest_region) == 0) {
+        canvas_touch_image(&canvas->base, opaque->src_bitmap);
+        touch_brush(canvas, &opaque->brush);
+        pixman_region32_fini(&dest_region);
+        return;
+    }
+
+    if (rect_is_same_size(bbox, &opaque->src_area)) {
+        blit_image(canvas, &dest_region,
+                   opaque->src_bitmap,
+                   bbox->left - opaque->src_area.left,
+                   bbox->top - opaque->src_area.top);
     } else {
-        cairo_rectangle(draw_data.cairo, bbox->left, bbox->top, bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        cairo_fill_preserve(draw_data.cairo);
-        canvas_draw(canvas, &opaque->brush, opaque->rop_decriptor, (DrawMethod)cairo_fill_preserve,
-                    draw_data.cairo);
-        cairo_new_path(draw_data.cairo);
+        scale_image(canvas, &dest_region,
+                    opaque->src_bitmap,
+                    opaque->src_area.left,
+                    opaque->src_area.top,
+                    opaque->src_area.right - opaque->src_area.left,
+                    opaque->src_area.bottom - opaque->src_area.top,
+                    bbox->left,
+                    bbox->top,
+                    bbox->right - bbox->left,
+                    bbox->bottom - bbox->top,
+                    opaque->scale_mode);
     }
 
-    cairo_restore(draw_data.cairo);
+    draw_brush(canvas, &dest_region, &opaque->brush, rop);
+
+    pixman_region32_fini(&dest_region);
 }
 
+
 void canvas_draw_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
 {
     cairo_pattern_t *pattern;
commit 864881c318638677546ce22c0874f9da715008a2
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Feb 9 17:08:18 2010 +0100

    Convert cairo canvas draw_fill() to using pixman

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 433f3c9..6b25505 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -30,8 +30,306 @@ struct CairoCanvas {
     uint32_t *private_data;
     int private_data_size;
     pixman_image_t *image;
+    pixman_region32_t canvas_region;
 };
 
+typedef enum {
+    ROP_INPUT_SRC,
+    ROP_INPUT_BRUSH,
+    ROP_INPUT_DEST
+} ROPInput;
+
+SpiceROP ropd_descriptor_to_rop(int desc,
+                                ROPInput src_input,
+                                ROPInput dest_input)
+{
+    int old;
+    int invert_masks[] = {
+        SPICE_ROPD_INVERS_SRC,
+        SPICE_ROPD_INVERS_BRUSH,
+        SPICE_ROPD_INVERS_DEST
+    };
+
+    old = desc;
+
+    desc &= ~(SPICE_ROPD_INVERS_SRC | SPICE_ROPD_INVERS_DEST);
+    if (old & invert_masks[src_input]) {
+        desc |= SPICE_ROPD_INVERS_SRC;
+    }
+
+    if (old & invert_masks[dest_input]) {
+        desc |= SPICE_ROPD_INVERS_DEST;
+    }
+
+    if (desc & SPICE_ROPD_OP_PUT) {
+        if (desc & SPICE_ROPD_INVERS_SRC) {
+            if (desc & SPICE_ROPD_INVERS_RES) {
+                return SPICE_ROP_COPY;
+            }
+            return SPICE_ROP_COPY_INVERTED;
+        } else {
+            if (desc & SPICE_ROPD_INVERS_RES) {
+                return SPICE_ROP_COPY_INVERTED;
+            }
+            return SPICE_ROP_COPY;
+        }
+    } else if (desc & SPICE_ROPD_OP_OR) {
+
+        if (desc & SPICE_ROPD_INVERS_RES) {
+            if (desc & SPICE_ROPD_INVERS_SRC) {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* !(!src or !dest) == src and dest*/
+                    return SPICE_ROP_AND;
+                } else {
+                    /* ! (!src or dest) = src and !dest*/
+                    return SPICE_ROP_AND_REVERSE;
+                }
+            } else {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* !(src or !dest) == !src and dest */
+                    return SPICE_ROP_AND_INVERTED;
+                } else {
+                    /* !(src or dest) */
+                    return SPICE_ROP_NOR;
+                }
+            }
+        } else {
+            if (desc & SPICE_ROPD_INVERS_SRC) {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* !src or !dest == !(src and dest)*/
+                    return SPICE_ROP_NAND;
+                } else {
+                    /* !src or dest */
+                    return SPICE_ROP_OR_INVERTED;
+                }
+            } else {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* src or !dest */
+                    return SPICE_ROP_OR_REVERSE;
+                } else {
+                    /* src or dest */
+                    return SPICE_ROP_OR;
+                }
+            }
+        }
+
+    } else if (desc & SPICE_ROPD_OP_AND) {
+
+        if (desc & SPICE_ROPD_INVERS_RES) {
+            if (desc & SPICE_ROPD_INVERS_SRC) {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* !(!src and !dest) == src or dest*/
+                    return SPICE_ROP_OR;
+                } else {
+                    /* ! (!src and dest) = src or !dest*/
+                    return SPICE_ROP_OR_REVERSE;
+                }
+            } else {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* !(src and !dest) == !src or dest */
+                    return SPICE_ROP_OR_INVERTED;
+                } else {
+                    /* !(src and dest) */
+                    return SPICE_ROP_NAND;
+                }
+            }
+        } else {
+            if (desc & SPICE_ROPD_INVERS_SRC) {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* !src and !dest == !(src or dest)*/
+                    return SPICE_ROP_NOR;
+                } else {
+                    /* !src and dest */
+                    return SPICE_ROP_AND_INVERTED;
+                }
+            } else {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* src and !dest */
+                    return SPICE_ROP_AND_REVERSE;
+                } else {
+                    /* src and dest */
+                    return SPICE_ROP_AND;
+                }
+            }
+        }
+
+    } else if (desc & SPICE_ROPD_OP_XOR) {
+
+        if (desc & SPICE_ROPD_INVERS_RES) {
+            if (desc & SPICE_ROPD_INVERS_SRC) {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* !(!src xor !dest) == !src xor dest */
+                    return SPICE_ROP_EQUIV;
+                } else {
+                    /* ! (!src xor dest) = src xor dest*/
+                    return SPICE_ROP_XOR;
+                }
+            } else {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* !(src xor !dest) == src xor dest */
+                    return SPICE_ROP_XOR;
+                } else {
+                    /* !(src xor dest) */
+                    return SPICE_ROP_EQUIV;
+                }
+            }
+        } else {
+            if (desc & SPICE_ROPD_INVERS_SRC) {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* !src xor !dest == src xor dest */
+                    return SPICE_ROP_XOR;
+                } else {
+                    /* !src xor dest */
+                    return SPICE_ROP_EQUIV;
+                }
+            } else {
+                if (desc & SPICE_ROPD_INVERS_DEST) {
+                    /* src xor !dest */
+                    return SPICE_ROP_EQUIV;
+                } else {
+                    /* src xor dest */
+                    return SPICE_ROP_XOR;
+                }
+            }
+        }
+
+    } else if (desc & SPICE_ROPD_OP_BLACKNESS) {
+        return SPICE_ROP_CLEAR;
+    } else if (desc & SPICE_ROPD_OP_WHITENESS) {
+        return SPICE_ROP_SET;
+    } else if (desc & SPICE_ROPD_OP_INVERS) {
+        return SPICE_ROP_INVERT;
+    }
+    return SPICE_ROP_COPY;
+}
+
+static void canvas_clip_pixman(CairoCanvas *canvas,
+                               pixman_region32_t *dest_region,
+                               SpiceClip *clip)
+{
+    pixman_region32_intersect(dest_region, dest_region, &canvas->canvas_region);
+
+    switch (clip->type) {
+    case SPICE_CLIP_TYPE_NONE:
+        break;
+    case SPICE_CLIP_TYPE_RECTS: {
+        uint32_t *n = (uint32_t *)SPICE_GET_ADDRESS(clip->data);
+        access_test(&canvas->base, n, sizeof(uint32_t));
+
+        SpiceRect *now = (SpiceRect *)(n + 1);
+        access_test(&canvas->base, now, (unsigned long)(now + *n) - (unsigned long)now);
+
+        pixman_region32_t clip;
+
+        if (spice_pixman_region32_init_rects(&clip, now, *n)) {
+            pixman_region32_intersect(dest_region, dest_region, &clip);
+            pixman_region32_fini(&clip);
+        }
+
+        break;
+    }
+    case SPICE_CLIP_TYPE_PATH:
+        CANVAS_ERROR("clip paths not supported anymore");
+        break;
+    default:
+        CANVAS_ERROR("invalid clip type");
+    }
+}
+
+static void canvas_mask_pixman(CairoCanvas *canvas,
+                               pixman_region32_t *dest_region,
+                               SpiceQMask *mask, int x, int y)
+{
+    pixman_image_t *image, *subimage;
+    int needs_invert;
+    pixman_region32_t mask_region;
+    uint32_t *mask_data;
+    int mask_x, mask_y;
+    int mask_width, mask_height, mask_stride;
+    pixman_box32_t extents;
+
+    needs_invert = FALSE;
+    image = canvas_get_mask(&canvas->base,
+                            mask,
+                            &needs_invert);
+
+    if (image == NULL) {
+        return; /* no mask */
+    }
+
+    mask_data = pixman_image_get_data(image);
+    mask_width = pixman_image_get_width(image);
+    mask_height = pixman_image_get_height(image);
+    mask_stride = pixman_image_get_stride(image);
+
+    mask_x = mask->pos.x;
+    mask_y = mask->pos.y;
+
+    /* We need to subset the area of the mask that we turn into a region,
+       because a cached mask may be much larger than what is used for
+       the clip operation. */
+    extents = *pixman_region32_extents(dest_region);
+
+    /* convert from destination pixels to mask pixels */
+    extents.x1 -= x - mask_x;
+    extents.y1 -= y - mask_y;
+    extents.x2 -= x - mask_x;
+    extents.y2 -= y - mask_y;
+
+    /* clip to mask size */
+    if (extents.x1 < 0) {
+        extents.x1 = 0;
+    }
+    if (extents.x2 >= mask_width) {
+        extents.x2 = mask_width;
+    }
+    if (extents.x2 < extents.x1) {
+        extents.x2 = extents.x1;
+    }
+    if (extents.y1 < 0) {
+        extents.y1 = 0;
+    }
+    if (extents.y2 >= mask_height) {
+        extents.y2 = mask_height;
+    }
+    if (extents.y2 < extents.y1) {
+        extents.y2 = extents.y1;
+    }
+
+    /* round down X to even 32 pixels (i.e. uint32_t) */
+    extents.x1 = extents.x1 & ~(0x1f);
+
+    mask_data = (uint32_t *)((uint8_t *)mask_data + mask_stride * extents.y1 + extents.x1 / 32);
+    mask_x -= extents.x1;
+    mask_y -= extents.y1;
+    mask_width = extents.x2 - extents.x1;
+    mask_height = extents.y2 - extents.y1;
+
+    subimage = pixman_image_create_bits(PIXMAN_a1, mask_width, mask_height,
+                                        mask_data, mask_stride);
+    pixman_region32_init_from_image(&mask_region,
+                                    subimage);
+    pixman_image_unref(subimage);
+
+    if (needs_invert) {
+        pixman_box32_t rect;
+
+        rect.x1 = rect.y1 = 0;
+        rect.x2 = mask_width;
+        rect.y2 = mask_height;
+
+        pixman_region32_inverse(&mask_region, &mask_region, &rect);
+    }
+
+    pixman_region32_translate(&mask_region,
+                              -mask_x + x, -mask_y + y);
+
+    pixman_region32_intersect(dest_region, dest_region, &mask_region);
+    pixman_region32_fini(&mask_region);
+
+    pixman_image_unref(image);
+}
+
 static void canvas_set_path(CairoCanvas *canvas, void *addr)
 {
     cairo_t *cairo = canvas->cairo;
@@ -701,27 +999,175 @@ static void __draw_mask(void *data)
     cairo_mask(((DrawMaskData *)data)->cairo, ((DrawMaskData *)data)->mask);
 }
 
+static void fill_solid_rects(CairoCanvas *canvas,
+                             pixman_region32_t *region,
+                             uint32_t color)
+{
+    pixman_box32_t *rects;
+    int n_rects;
+    int i;
+
+    rects = pixman_region32_rectangles(region, &n_rects);
+
+    for (i = 0; i < n_rects; i++) {
+        spice_pixman_fill_rect(canvas->image,
+                               rects[i].x1, rects[i].y1,
+                               rects[i].x2 - rects[i].x1,
+                               rects[i].y2 - rects[i].y1,
+                               color);
+    }
+}
+
+static void fill_solid_rects_rop(CairoCanvas *canvas,
+                                 pixman_region32_t *region,
+                                 uint32_t color,
+                                 SpiceROP rop)
+{
+    pixman_box32_t *rects;
+    int n_rects;
+    int i;
+
+    rects = pixman_region32_rectangles(region, &n_rects);
+
+    for (i = 0; i < n_rects; i++) {
+        spice_pixman_fill_rect_rop(canvas->image,
+                                   rects[i].x1, rects[i].y1,
+                                   rects[i].x2 - rects[i].x1,
+                                   rects[i].y2 - rects[i].y1,
+                                   color, rop);
+    }
+}
+
+static void fill_tiled_rects(CairoCanvas *canvas,
+                             pixman_region32_t *region,
+                             SpicePattern *pattern)
+{
+    pixman_image_t *tile;
+    int offset_x, offset_y;
+    pixman_box32_t *rects;
+    int n_rects;
+    int i;
+
+    rects = pixman_region32_rectangles(region, &n_rects);
+
+    tile = canvas_get_image(&canvas->base, pattern->pat);
+    offset_x = pattern->pos.x;
+    offset_y = pattern->pos.y;
+
+    for (i = 0; i < n_rects; i++) {
+        spice_pixman_tile_rect(canvas->image,
+                               rects[i].x1, rects[i].y1,
+                               rects[i].x2 - rects[i].x1,
+                               rects[i].y2 - rects[i].y1,
+                               tile, offset_x, offset_y);
+    }
+
+    pixman_image_unref(tile);
+}
+
+static void fill_tiled_rects_rop(CairoCanvas *canvas,
+                                 pixman_region32_t *region,
+                                 SpicePattern *pattern,
+                                 SpiceROP rop)
+{
+    pixman_image_t *tile;
+    int offset_x, offset_y;
+    pixman_box32_t *rects;
+    int n_rects;
+    int i;
+
+    rects = pixman_region32_rectangles(region, &n_rects);
+
+    tile = canvas_get_image(&canvas->base, pattern->pat);
+    offset_x = pattern->pos.x;
+    offset_y = pattern->pos.y;
+
+    for (i = 0; i < n_rects; i++) {
+        spice_pixman_tile_rect_rop(canvas->image,
+                                   rects[i].x1, rects[i].y1,
+                                   rects[i].x2 - rects[i].x1,
+                                   rects[i].y2 - rects[i].y1,
+                                   tile, offset_x, offset_y,
+                                   rop);
+    }
+
+    pixman_image_unref(tile);
+}
+
+static void draw_brush(CairoCanvas *canvas,
+                       pixman_region32_t *region,
+                       SpiceBrush *brush,
+                       SpiceROP rop)
+{
+    uint32_t color;
+    SpicePattern *pattern;
+
+    switch (brush->type) {
+    case SPICE_BRUSH_TYPE_SOLID:
+        color = brush->u.color;
+        if (rop == SPICE_ROP_COPY) {
+            fill_solid_rects(canvas, region, color);
+        } else {
+            fill_solid_rects_rop(canvas, region, color, rop);
+        }
+        break;
+    case SPICE_BRUSH_TYPE_PATTERN:
+        pattern = &brush->u.pattern;
+
+        if (rop == SPICE_ROP_COPY) {
+            fill_tiled_rects(canvas, region, pattern);
+        } else {
+            fill_tiled_rects_rop(canvas, region, pattern, rop);
+        }
+        break;
+    case SPICE_BRUSH_TYPE_NONE:
+        /* Still need to do *something* here, because rop could be e.g invert dest */
+        fill_solid_rects_rop(canvas, region, 0, rop);
+        break;
+    default:
+        CANVAS_ERROR("invalid brush type");
+    }
+}
+
+/* If we're exiting early we may still have to load an image in case
+   it has to be cached or something */
+static void touch_brush(CairoCanvas *canvas, SpiceBrush *brush)
+{
+    SpicePattern *pattern;
+    if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
+        pattern = &brush->u.pattern;
+        canvas_touch_image(&canvas->base, pattern->pat);
+    }
+}
+
 void canvas_draw_fill(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
 {
-    DrawMaskData draw_data;
-    draw_data.cairo = canvas->cairo;
+    pixman_region32_t dest_region;
+    SpiceROP rop;
 
-    cairo_save(draw_data.cairo);
-    canvas_clip(canvas, clip);
-    if ((draw_data.mask = canvas_get_mask_pattern(canvas, &fill->mask, bbox->left, bbox->top))) {
-        cairo_rectangle(draw_data.cairo, bbox->left, bbox->top, bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        cairo_clip(draw_data.cairo);
-        canvas_draw(canvas, &fill->brush, fill->rop_decriptor, __draw_mask, &draw_data);
-        cairo_pattern_destroy(draw_data.mask);
-    } else {
-        cairo_rectangle(draw_data.cairo, bbox->left, bbox->top, bbox->right - bbox->left,
-                        bbox->bottom - bbox->top);
-        canvas_draw(canvas, &fill->brush, fill->rop_decriptor, (DrawMethod)cairo_fill_preserve,
-                    draw_data.cairo);
-        cairo_new_path(draw_data.cairo);
+    pixman_region32_init_rect(&dest_region,
+                              bbox->left, bbox->top,
+                              bbox->right - bbox->left,
+                              bbox->bottom - bbox->top);
+
+
+    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_mask_pixman(canvas, &dest_region, &fill->mask,
+                       bbox->left, bbox->top);
+
+    rop = ropd_descriptor_to_rop(fill->rop_decriptor,
+                                 ROP_INPUT_BRUSH,
+                                 ROP_INPUT_DEST);
+
+    if (rop == SPICE_ROP_NOOP || pixman_region32_n_rects(&dest_region) == 0) {
+        touch_brush(canvas, &fill->brush);
+        pixman_region32_fini(&dest_region);
+        return;
     }
-    cairo_restore(draw_data.cairo);
+
+    draw_brush(canvas, &dest_region, &fill->brush, rop);
+
+    pixman_region32_fini(&dest_region);
 }
 
 static cairo_pattern_t *canvas_src_image_to_pat(CairoCanvas *canvas, SPICE_ADDRESS src_bitmap,
@@ -1664,6 +2110,10 @@ CairoCanvas *canvas_create(cairo_t *cairo, int bits
     cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
 
     canvas->image = pixman_image_from_surface (cairo_get_target (cairo));
+    pixman_region32_init_rect(&canvas->canvas_region,
+                              0, 0,
+                              pixman_image_get_width (canvas->image),
+                              pixman_image_get_height (canvas->image));
 
     return canvas;
 }
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 6659385..a96bd42 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -841,7 +841,9 @@ static void dump_surface(pixman_image_t *surface, int cache)
 
 //#define DEBUG_LZ
 
-static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
+/* If real get is FALSE, then only do whatever is needed but don't return an image. For instance,
+   if we need to read it to cache it we do. */
+static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRESS addr, int real_get)
 {
     SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
     pixman_image_t *surface;
@@ -850,6 +852,15 @@ static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
     LOG_DEBUG("canvas_get_image image type: " << (int)descriptor->type);
 #endif
 
+    /* When touching, only really allocate if we need to cache, or
+     * if we're loading a GLZ stream (since those need inter-thread communication
+     * to happen which breaks if we don't. */
+    if (!real_get &&
+        !(descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_ME) &&
+        (descriptor->type != SPICE_IMAGE_TYPE_GLZ_RGB)) {
+        return NULL;
+    }
+
     switch (descriptor->type) {
     case SPICE_IMAGE_TYPE_QUIC: {
         SpiceQUICImage *image = (SpiceQUICImage *)descriptor;
@@ -901,17 +912,28 @@ static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
         dump_surface(surface, 0);
 #endif
     }
+
+    if (!real_get) {
+        pixman_image_unref(surface);
+        return NULL;
+    }
+
     return surface;
 }
 
 #else
 
-static pixman_image_t *canvas_get_image(CairoCanvas *canvas, SPICE_ADDRESS addr)
+static pixman_image_t *canvas_get_image_internal(CairoCanvas *canvas, SPICE_ADDRESS addr, int real_get)
 {
     SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
 
     access_test(canvas, descriptor, sizeof(SpiceImageDescriptor));
 
+    /* When touching, never load image. */
+    if (!real_get) {
+        return NULL;
+    }
+
     switch (descriptor->type) {
     case SPICE_IMAGE_TYPE_QUIC: {
         SpiceQUICImage *image = (SpiceQUICImage *)descriptor;
@@ -930,6 +952,18 @@ static pixman_image_t *canvas_get_image(CairoCanvas *canvas, SPICE_ADDRESS addr)
 
 #endif
 
+static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
+{
+    return canvas_get_image_internal(canvas, addr, TRUE);
+}
+
+#ifdef CANVAS_USE_PIXMAN
+static void canvas_touch_image(CanvasBase *canvas, SPICE_ADDRESS addr)
+{
+    canvas_get_image_internal(canvas, addr, FALSE);
+}
+#endif
+
 static inline uint8_t revers_bits(uint8_t byte)
 {
     uint8_t ret = 0;
commit a7ceb98ea1688f0de1613ee12bb78d246869242c
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Feb 9 17:02:36 2010 +0100

    Add possibility to not invert bitmask in canvas_get_mask()
    
    This allows the pixman implementation to instead invert the (generally
    smaller) region instead of duplicating the bitmap to invert it.

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 5ee7ccb..433f3c9 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -673,7 +673,7 @@ static cairo_pattern_t *canvas_get_mask_pattern(CairoCanvas *canvas, SpiceQMask
     cairo_pattern_t *pattern;
     cairo_matrix_t matrix;
 
-    if (!(surface = canvas_get_mask(&canvas->base, mask))) {
+    if (!(surface = canvas_get_mask(&canvas->base, mask, NULL))) {
         return NULL;
     }
     cairo_surface = surface_from_pixman_image (surface);
diff --git a/common/canvas_base.c b/common/canvas_base.c
index f5a6d33..6659385 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -1134,7 +1134,7 @@ static inline pixman_image_t* canvas_handle_inverse_user_data(pixman_image_t* su
     return inv_surf;
 }
 
-static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask)
+static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask, int *needs_invert_out)
 {
     SpiceImageDescriptor *descriptor;
     pixman_image_t *surface;
@@ -1142,6 +1142,10 @@ static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask)
     int is_invers;
     int cache_me;
 
+    if (needs_invert_out) {
+        *needs_invert_out = 0;
+    }
+
     if (!mask->bitmap) {
         return NULL;
     }
@@ -1180,12 +1184,14 @@ static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask)
     }
 
     if (need_invers && !is_invers) { // surface is in cache
-        pixman_image_t *inv_surf;
-
-        inv_surf = canvas_handle_inverse_user_data(surface);
-
-        pixman_image_unref(surface);
-        surface = inv_surf;
+        if (needs_invert_out != NULL) {
+            *needs_invert_out = TRUE;
+        } else {
+            pixman_image_t *inv_surf;
+            inv_surf = canvas_handle_inverse_user_data(surface);
+            pixman_image_unref(surface);
+            surface = inv_surf;
+        }
     }
 #endif
     return surface;
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index 66f8a02..83c5f05 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -784,7 +784,7 @@ static struct BitmapData get_mask_bitmap(struct GdiCanvas *canvas, struct SpiceQ
     PixmanData *pixman_data;
 
     bitmap.hbitmap = NULL;
-    if (!(surface = canvas_get_mask(&canvas->base, mask))) {
+    if (!(surface = canvas_get_mask(&canvas->base, mask, NULL))) {
         return bitmap;
     }
 
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index b6d584f..8d152ee 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -210,7 +210,7 @@ static void set_mask(GLCanvas *canvas, SpiceQMask *mask, int x, int y)
 {
     pixman_image_t *image;
 
-    if (!(image = canvas_get_mask(&canvas->base, mask))) {
+    if (!(image = canvas_get_mask(&canvas->base, mask, NULL))) {
         glc_clear_mask(canvas->glc, GLC_MASK_A);
         return;
     }
commit 16780a7b81376b7019b55cb25068177a0b664d90
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Feb 9 16:39:35 2010 +0100

    Use pixman_image_t instead of cairo_surface_t as the generic pixman container
    
    This allows us to use the simpler dependency of pixman outside of the
    cairo backend, and it later lets us move the cairo backend to using
    pixman only.

diff --git a/client/canvas.h b/client/canvas.h
index 3074d0d..1bd3180 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -40,14 +40,14 @@ struct QRegion;
 
 class PixmapCacheTreat {
 public:
-    static inline cairo_surface_t *get(cairo_surface_t *surf)
+    static inline pixman_image_t *get(pixman_image_t *surf)
     {
-        return cairo_surface_reference(surf);
+        return pixman_image_ref(surf);
     }
 
-    static inline void release(cairo_surface_t *surf)
+    static inline void release(pixman_image_t *surf)
     {
-        cairo_surface_destroy(surf);
+        pixman_image_unref(surf);
     }
 
     static const char* name() { return "pixmap";}
@@ -55,19 +55,19 @@ public:
 
 class SpiceImageCacheBase;
 
-typedef SharedCache<cairo_surface_t, PixmapCacheTreat, 1024, SpiceImageCacheBase> PixmapCache;
+typedef SharedCache<pixman_image_t, PixmapCacheTreat, 1024, SpiceImageCacheBase> PixmapCache;
 
 class SpiceImageCacheBase {
 public:
     SpiceImageCache base;
 
-    static void op_put(SpiceImageCache *c, uint64_t id, cairo_surface_t *surface)
+    static void op_put(SpiceImageCache *c, uint64_t id, pixman_image_t *surface)
     {
         PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
         cache->add(id, surface);
     }
 
-    static cairo_surface_t* op_get(SpiceImageCache *c, uint64_t id)
+    static pixman_image_t* op_get(SpiceImageCache *c, uint64_t id)
     {
         PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
         return cache->get(id);
@@ -185,20 +185,20 @@ public:
 class GlzDecodedSurface: public GlzDecodedImage {
 public:
     GlzDecodedSurface(uint64_t id, uint64_t win_head_id, uint8_t *data, int size,
-                      int bytes_per_pixel, cairo_surface_t *surface)
+                      int bytes_per_pixel, pixman_image_t *surface)
         : GlzDecodedImage(id, win_head_id, data, size, bytes_per_pixel)
         , _surface (surface)
     {
-        cairo_surface_reference(_surface);
+        pixman_image_ref(_surface);
     }
 
     virtual ~GlzDecodedSurface()
     {
-        cairo_surface_destroy(_surface);
+        pixman_image_unref(_surface);
     }
 
 private:
-    cairo_surface_t *_surface;
+    pixman_image_t *_surface;
 };
 
 class GlzDecodeSurfaceHandler: public GlzDecodeHandler {
@@ -208,10 +208,10 @@ public:
                                          int width, int height, int gross_pixels,
                                          int n_bytes_per_pixel, bool top_down)
     {
-        cairo_surface_t *surface = alloc_lz_image_surface((LzDecodeUsrData *)opaque_usr_info,
-                                                          type, width, height, gross_pixels,
-                                                          top_down);
-        uint8_t *data = cairo_image_surface_get_data(surface);
+        pixman_image_t *surface = alloc_lz_image_surface((LzDecodeUsrData *)opaque_usr_info,
+                                                         type, width, height, gross_pixels,
+                                                         top_down);
+        uint8_t *data = (uint8_t *)pixman_image_get_data(surface);
         if (!top_down) {
             data = data - (gross_pixels / height) * n_bytes_per_pixel * (height - 1);
         }
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 4b8c305..5ee7ccb 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -297,7 +297,7 @@ static inline void canvas_invers_1bpp_be(uint8_t* dest, int dest_stride, uint8_t
     }
 }
 
-static cairo_surface_t *canvas_bitmap_to_invers_surface(CairoCanvas *canvas, SpiceBitmap* bitmap,
+static pixman_image_t *canvas_bitmap_to_invers_surface(CairoCanvas *canvas, SpiceBitmap* bitmap,
                                                         SpicePalette *palette)
 {
     uint8_t* src = (uint8_t *)SPICE_GET_ADDRESS(bitmap->data);
@@ -305,21 +305,20 @@ static cairo_surface_t *canvas_bitmap_to_invers_surface(CairoCanvas *canvas, Spi
     uint8_t* end;
     uint8_t* dest;
     int dest_stride;
-    cairo_surface_t* cairo_surface;
+    pixman_image_t* surface;
 
     src_stride = bitmap->stride;
     end = src + (bitmap->y * src_stride);
     access_test(&canvas->base, src, bitmap->y * src_stride);
 
-    cairo_surface = cairo_image_surface_create((bitmap->format == SPICE_BITMAP_FMT_RGBA) ?
-                                               CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
-                                               bitmap->x, bitmap->y);
-    if (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(cairo_surface)));
+    surface = pixman_image_create_bits((bitmap->format == SPICE_BITMAP_FMT_RGBA) ?
+                                       PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
+                                       bitmap->x, bitmap->y, NULL, 0);
+    if (surface == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
-    dest = cairo_image_surface_get_data(cairo_surface);
-    dest_stride = cairo_image_surface_get_stride(cairo_surface);
+    dest = (uint8_t *)pixman_image_get_data(surface);
+    dest_stride = pixman_image_get_stride(surface);
 
     if (!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
         ASSERT(bitmap->y > 0);
@@ -347,7 +346,7 @@ static cairo_surface_t *canvas_bitmap_to_invers_surface(CairoCanvas *canvas, Spi
         canvas_invers_1bpp_be(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
         break;
     }
-    return cairo_surface;
+    return surface;
 }
 
 #if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
@@ -386,11 +385,11 @@ static void free_palette(SpiceBitmap *bitmap, SpicePalette *palette)
 
 #endif
 
-static cairo_surface_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRESS addr)
+static pixman_image_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRESS addr)
 {
     SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
-    cairo_surface_t *surface;
-    cairo_surface_t *invers = NULL;
+    pixman_image_t *surface;
+    pixman_image_t *invers = NULL;
 
     access_test(&canvas->base, descriptor, sizeof(SpiceImageDescriptor));
 
@@ -469,13 +468,13 @@ static cairo_surface_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRE
     }
 
     invers = canvas_handle_inverse_user_data(surface);
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
     return invers;
 }
 
 #else
 
-static cairo_surface_t *canvas_get_invers(CairoCanvas *canvas, SpiceBitmap *bitmap)
+static pixman_image_t *canvas_get_invers(CairoCanvas *canvas, SpiceBitmap *bitmap)
 {
     SpicePalette *palette;
 
@@ -486,7 +485,7 @@ static cairo_surface_t *canvas_get_invers(CairoCanvas *canvas, SpiceBitmap *bitm
     if (canvas->color_shift == 5) {
         int size = sizeof(SpicePalette) + (palette->num_ents << 2);
         SpicePalette *local_palette = malloc(size);
-        cairo_surface_t* surface;
+        pixman_image_t* surface;
 
         memcpy(local_palette, palette, size);
         canvas_localize_palette(canvas, palette);
@@ -498,7 +497,7 @@ static cairo_surface_t *canvas_get_invers(CairoCanvas *canvas, SpiceBitmap *bitm
     }
 }
 
-static cairo_surface_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRESS addr)
+static pixman_image_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRESS addr)
 {
     SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
 
@@ -522,28 +521,28 @@ static cairo_surface_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRE
 
 #endif
 
-static cairo_surface_t* canvas_surface_from_self(CairoCanvas *canvas, SpicePoint *pos,
-                                                 int32_t width, int32_t heigth)
+static pixman_image_t* canvas_surface_from_self(CairoCanvas *canvas, SpicePoint *pos,
+                                                int32_t width, int32_t heigth)
 {
-    cairo_surface_t *surface;
-    cairo_surface_t *src_surface;
+    pixman_image_t *surface;
+    pixman_image_t *src_surface;
     uint8_t *dest;
     int dest_stride;
     uint8_t *src;
     int src_stride;
     int i;
 
-    surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, heigth);
-    if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(surface)));
+    surface = pixman_image_create_bits(PIXMAN_x8r8g8b8, width, heigth, NULL, 0);
+    if (surface == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
-    dest = cairo_image_surface_get_data(surface);
-    dest_stride = cairo_image_surface_get_stride(surface);
 
-    src_surface = cairo_get_target(canvas->cairo);
-    src = cairo_image_surface_get_data(src_surface);
-    src_stride = cairo_image_surface_get_stride(src_surface);
+    dest = (uint8_t *)pixman_image_get_data(surface);
+    dest_stride = pixman_image_get_stride(surface);
+
+    src_surface = canvas->image;
+    src = (uint8_t *)pixman_image_get_data(src_surface);
+    src_stride = pixman_image_get_stride(src_surface);
     src += pos->y * src_stride + (pos->x << 2);
     for (i = 0; i < heigth; i++, dest += dest_stride, src += src_stride) {
         memcpy(dest, src, width << 2);
@@ -566,7 +565,8 @@ static cairo_pattern_t *canvas_get_brush(CairoCanvas *canvas, SpiceBrush *brush,
         return cairo_pattern_create_rgb(r, g, b);
     }
     case SPICE_BRUSH_TYPE_PATTERN: {
-        cairo_surface_t* surface;
+        pixman_image_t* surface;
+        cairo_surface_t* cairo_surface;
         cairo_pattern_t *pattern;
         cairo_matrix_t matrix;
 
@@ -575,13 +575,15 @@ static cairo_pattern_t *canvas_get_brush(CairoCanvas *canvas, SpiceBrush *brush,
         } else {
             surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
         }
-        pattern = cairo_pattern_create_for_surface(surface);
+        cairo_surface = surface_from_pixman_image (surface);
+        pixman_image_unref (surface);
+        pattern = cairo_pattern_create_for_surface(cairo_surface);
         if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
-            cairo_surface_destroy(surface);
+            cairo_surface_destroy(cairo_surface);
             CANVAS_ERROR("create pattern failed, %s",
                          cairo_status_to_string(cairo_pattern_status(pattern)));
         }
-        cairo_surface_destroy(surface);
+        cairo_surface_destroy(cairo_surface);
         cairo_matrix_init_translate(&matrix, -brush->u.pattern.pos.x, -brush->u.pattern.pos.y);
         cairo_pattern_set_matrix(pattern, &matrix);
         cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
@@ -666,20 +668,23 @@ static inline void canvas_draw(CairoCanvas *canvas, SpiceBrush *brush, uint16_t
 
 static cairo_pattern_t *canvas_get_mask_pattern(CairoCanvas *canvas, SpiceQMask *mask, int x, int y)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
+    cairo_surface_t *cairo_surface;
     cairo_pattern_t *pattern;
     cairo_matrix_t matrix;
 
     if (!(surface = canvas_get_mask(&canvas->base, mask))) {
         return NULL;
     }
-    pattern = cairo_pattern_create_for_surface(surface);
+    cairo_surface = surface_from_pixman_image (surface);
+    pixman_image_unref (surface);
+    pattern = cairo_pattern_create_for_surface(cairo_surface);
     if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
-        cairo_surface_destroy(surface);
+        cairo_surface_destroy(cairo_surface);
         CANVAS_ERROR("create pattern failed, %s",
                      cairo_status_to_string(cairo_pattern_status(pattern)));
     }
-    cairo_surface_destroy(surface);
+    cairo_surface_destroy(cairo_surface);
 
     cairo_matrix_init_translate(&matrix, mask->pos.x - x, mask->pos.y - y);
     cairo_pattern_set_matrix(pattern, &matrix);
@@ -724,7 +729,8 @@ static cairo_pattern_t *canvas_src_image_to_pat(CairoCanvas *canvas, SPICE_ADDRE
                                                 int scale_mode)
 {
     cairo_pattern_t *pattern;
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
+    cairo_surface_t *cairo_surface;
     cairo_matrix_t matrix;
 
     ASSERT(src_bitmap);
@@ -735,8 +741,10 @@ static cairo_pattern_t *canvas_src_image_to_pat(CairoCanvas *canvas, SPICE_ADDRE
         surface = canvas_get_image(&canvas->base, src_bitmap);
     }
 
-    pattern = cairo_pattern_create_for_surface(surface);
-    cairo_surface_destroy(surface);
+    cairo_surface = surface_from_pixman_image (surface);
+    pixman_image_unref (surface);
+    pattern = cairo_pattern_create_for_surface(cairo_surface);
+    cairo_surface_destroy(cairo_surface);
     if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
         CANVAS_ERROR("create pattern failed, %s",
                      cairo_status_to_string(cairo_pattern_status(pattern)));
@@ -1124,8 +1132,9 @@ void canvas_draw_rop3(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
 {
     cairo_t *cairo = canvas->cairo;
     cairo_pattern_t *mask;
+    pixman_image_t *dd;
     cairo_surface_t *d;
-    cairo_surface_t *s;
+    pixman_image_t *s;
     SpicePoint pos;
     int width;
     int heigth;
@@ -1142,13 +1151,13 @@ void canvas_draw_rop3(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     cairo_user_to_device(cairo, &x_pos, &y_pos);
     pos.x = (int32_t)x_pos;
     pos.y = (int32_t)y_pos;
-    d = canvas_surface_from_self(canvas, &pos, width, heigth);
+    dd = canvas_surface_from_self(canvas, &pos, width, heigth);
     s = canvas_get_image(&canvas->base, rop3->src_bitmap);
 
     if (!rect_is_same_size(bbox, &rop3->src_area)) {
-        cairo_surface_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, width, heigth,
+        pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, width, heigth,
                                                          rop3->scale_mode);
-        cairo_surface_destroy(s);
+        pixman_image_unref(s);
         s = scaled_s;
         src_pos.x = 0;
         src_pos.y = 0;
@@ -1156,26 +1165,28 @@ void canvas_draw_rop3(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
         src_pos.x = rop3->src_area.left;
         src_pos.y = rop3->src_area.top;
     }
-    if (cairo_image_surface_get_width(s) - src_pos.x < width ||
-        cairo_image_surface_get_height(s) - src_pos.y < heigth) {
+    if (pixman_image_get_width(s) - src_pos.x < width ||
+        pixman_image_get_height(s) - src_pos.y < heigth) {
         CANVAS_ERROR("bad src bitmap size");
     }
     if (rop3->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
-        cairo_surface_t *p = canvas_get_image(&canvas->base, rop3->brush.u.pattern.pat);
+        pixman_image_t *p = canvas_get_image(&canvas->base, rop3->brush.u.pattern.pat);
         SpicePoint pat_pos;
 
-        pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % cairo_image_surface_get_width(p);
-        pat_pos.y = (bbox->top - rop3->brush.u.pattern.pos.y) % cairo_image_surface_get_height(p);
-        do_rop3_with_pattern(rop3->rop3, d, s, &src_pos, p, &pat_pos);
-        cairo_surface_destroy(p);
+        pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % pixman_image_get_width(p);
+        pat_pos.y = (bbox->top - rop3->brush.u.pattern.pos.y) % pixman_image_get_height(p);
+        do_rop3_with_pattern(rop3->rop3, dd, s, &src_pos, p, &pat_pos);
+        pixman_image_unref(p);
     } else {
         uint32_t color = (canvas->base.color_shift) == 8 ? rop3->brush.u.color :
                                                          canvas_16bpp_to_32bpp(rop3->brush.u.color);
-        do_rop3_with_color(rop3->rop3, d, s, &src_pos, color);
+        do_rop3_with_color(rop3->rop3, dd, s, &src_pos, color);
     }
-    cairo_surface_destroy(s);
+    pixman_image_unref(s);
+    d = surface_from_pixman_image (dd);
     cairo_set_source_surface(cairo, d, bbox->left, bbox->top);
     cairo_surface_destroy(d);
+    pixman_image_unref (dd);
     if ((mask = canvas_get_mask_pattern(canvas, &rop3->mask, bbox->left, bbox->top))) {
         cairo_rectangle(cairo,
                         bbox->left,
@@ -1360,7 +1371,8 @@ static inline void __canvas_copy_region_bits(uint8_t *data, int stride, SpiceRec
 void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
 {
     cairo_t *cairo = canvas->cairo;
-    cairo_surface_t *surface;
+    cairo_surface_t *cairo_surface;
+    pixman_image_t *surface;
     int32_t width;
     int32_t heigth;
 
@@ -1368,22 +1380,20 @@ void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
 #ifdef FAST_COPY_BITS
     switch (clip->type) {
     case SPICE_CLIP_TYPE_NONE: {
-        surface = cairo_get_target(cairo);
-        __canvas_copy_rect_bits(cairo_image_surface_get_data(surface),
-                                cairo_image_surface_get_stride(surface),
+        __canvas_copy_rect_bits((uint8_t *)pixman_image_get_data(canvas->image),
+                                pixman_image_get_stride(canvas->image),
                                 bbox, src_pos);
         break;
     }
     case SPICE_CLIP_TYPE_RECTS: {
-        surface = cairo_get_target(cairo);
         uint32_t *n = (uint32_t *)SPICE_GET_ADDRESS(clip->data);
         access_test(&canvas->base, n, sizeof(uint32_t));
 
         SpiceRect *now = (SpiceRect *)(n + 1);
         SpiceRect *end = now + *n;
         access_test(&canvas->base, now, (unsigned long)end - (unsigned long)now);
-        uint8_t *data = cairo_image_surface_get_data(surface);
-        int stride = cairo_image_surface_get_stride(surface);
+        uint8_t *data = (uint8_t *)pixman_image_get_data(canvas->image);
+        int stride = pixman_image_get_stride(canvas->image);
 
         //using QRegion in order to sort and remove intersections
         QRegion region;
@@ -1402,11 +1412,13 @@ void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
         width = bbox->right - bbox->left;
         heigth = bbox->bottom - bbox->top;
         surface = canvas_surface_from_self(canvas, src_pos, width, heigth);
-        cairo_set_source_surface(cairo, surface, bbox->left, bbox->top);
-        cairo_surface_destroy(surface);
+        cairo_surface = surface_from_pixman_image (surface);
+        cairo_set_source_surface(cairo, cairo_surface, bbox->left, bbox->top);
         cairo_rectangle(cairo, bbox->left, bbox->top, width, heigth);
         cairo_set_operator(cairo, CAIRO_OPERATOR_RASTER_COPY);
         cairo_fill(cairo);
+        cairo_surface_destroy(cairo_surface);
+        pixman_image_unref (surface);
 #ifdef FAST_COPY_BITS
     }
 
@@ -1417,16 +1429,18 @@ void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
 static void canvas_draw_raster_str(CairoCanvas *canvas, SpiceString *str, int bpp,
                                    SpiceBrush *brush, uint16_t rop_decriptor)
 {
-    cairo_surface_t *str_mask;
+    pixman_image_t *str_mask;
+    cairo_surface_t *cairo_str_mask;
     DrawMaskData draw_data;
     cairo_matrix_t matrix;
     SpicePoint pos;
 
     str_mask = canvas_get_str_mask(&canvas->base, str, bpp, &pos);
+    cairo_str_mask = surface_from_pixman_image (str_mask);
     draw_data.cairo = canvas->cairo;
-    draw_data.mask = cairo_pattern_create_for_surface(str_mask);
+    draw_data.mask = cairo_pattern_create_for_surface(cairo_str_mask);
     if (cairo_pattern_status(draw_data.mask) != CAIRO_STATUS_SUCCESS) {
-        cairo_surface_destroy(str_mask);
+        cairo_surface_destroy(cairo_str_mask);
         CANVAS_ERROR("create pattern failed, %s",
                      cairo_status_to_string(cairo_pattern_status(draw_data.mask)));
     }
@@ -1434,7 +1448,8 @@ static void canvas_draw_raster_str(CairoCanvas *canvas, SpiceString *str, int bp
     cairo_pattern_set_matrix(draw_data.mask, &matrix);
     canvas_draw(canvas, brush, rop_decriptor, __draw_mask, &draw_data);
     cairo_pattern_destroy(draw_data.mask);
-    cairo_surface_destroy(str_mask);
+    pixman_image_unref(str_mask);
+    cairo_surface_destroy(cairo_str_mask);
 }
 
 static void canvas_draw_vector_str(CairoCanvas *canvas, SpiceString *str, SpiceBrush *brush,
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 0ddbaf4..f5a6d33 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <setjmp.h>
 #include <stdio.h>
+#include <math.h>
 
 #include <spice/draw.h>
 #include "quic.h"
@@ -76,6 +77,8 @@
 #define MAX(x, y) (((x) >= (y)) ? (x) : (y))
 #endif
 
+#define ROUND(_x) floor((_x) + 0.5)
+
 #ifdef WIN32
 typedef struct  __declspec (align(1)) LZImage {
 #else
@@ -88,12 +91,7 @@ typedef struct __attribute__ ((__packed__)) LZImage {
     };
 } LZImage;
 
-static const cairo_user_data_key_t invers_data_type = {0};
-
-#ifdef CAIRO_CANVAS_CACH_IS_SHARED
-/* should be defined and initialized once in application.cpp */
-extern mutex_t cairo_surface_user_data_mutex;
-#endif
+static const cairo_user_data_key_t pixman_data_type = {0};
 
 static inline double fix_to_double(SPICE_FIXED28_4 fixed)
 {
@@ -234,9 +232,13 @@ pixman_image_from_surface (cairo_surface_t *surface)
   pixman_image_t *image;
   cairo_format_t format;
 
-
   format = cairo_image_surface_get_format (surface);
 
+  image = (pixman_image_t *)cairo_surface_get_user_data(surface, &pixman_data_type);
+
+  if (image)
+      return pixman_image_ref (image);
+
   image = pixman_image_create_bits (pixman_format_from_cairo_format (format),
                                     cairo_image_surface_get_width (surface),
                                     cairo_image_surface_get_height (surface),
@@ -246,6 +248,44 @@ pixman_image_from_surface (cairo_surface_t *surface)
   return image;
 }
 
+static cairo_format_t
+cairo_format_from_depth (int depth)
+{
+    switch (depth) {
+    case 1:
+        return CAIRO_FORMAT_A1;
+    case 8:
+        return CAIRO_FORMAT_A8;
+    case 24:
+        return CAIRO_FORMAT_RGB24;
+    case 32:
+    default:
+        return CAIRO_FORMAT_ARGB32;
+    }
+}
+
+static cairo_surface_t *
+surface_from_pixman_image (pixman_image_t *image)
+{
+  cairo_surface_t *surface;
+  int depth;
+
+  depth = pixman_image_get_depth (image);
+
+  surface = cairo_image_surface_create_for_data ((uint8_t *)pixman_image_get_data (image),
+                                                 cairo_format_from_depth (depth),
+                                                 pixman_image_get_width (image),
+                                                 pixman_image_get_height (image),
+                                                 pixman_image_get_stride (image));
+
+
+  if (cairo_surface_set_user_data (surface, &pixman_data_type,
+                                   image, (cairo_destroy_func_t) pixman_image_unref) == CAIRO_STATUS_SUCCESS)
+      pixman_image_ref (image);
+
+  return surface;
+}
+
 #endif
 
 static inline void canvas_localize_palette(CanvasBase *canvas, SpicePalette *palette)
@@ -261,11 +301,11 @@ static inline void canvas_localize_palette(CanvasBase *canvas, SpicePalette *pal
 
 //#define DEBUG_DUMP_COMPRESS
 #ifdef DEBUG_DUMP_COMPRESS
-static void dump_surface(cairo_surface_t *surface, int cache);
+static void dump_surface(pixman_image_t *surface, int cache);
 #endif
-static cairo_surface_t *canvas_get_quic(CanvasBase *canvas, SpiceQUICImage *image, int invers)
+static pixman_image_t *canvas_get_quic(CanvasBase *canvas, SpiceQUICImage *image, int invers)
 {
-    cairo_surface_t *surface = NULL;
+    pixman_image_t *surface = NULL;
     QuicData *quic_data = &canvas->quic_data;
     QuicImageType type;
     uint8_t *dest;
@@ -279,7 +319,7 @@ static cairo_surface_t *canvas_get_quic(CanvasBase *canvas, SpiceQUICImage *imag
 #endif
 
     if (setjmp(quic_data->jmp_env)) {
-        cairo_surface_destroy(surface);
+        pixman_image_unref(surface);
         CANVAS_ERROR("quic error, %s", quic_data->message_buf);
     }
 
@@ -320,18 +360,18 @@ static cairo_surface_t *canvas_get_quic(CanvasBase *canvas, SpiceQUICImage *imag
 #ifdef WIN32
                              canvas->dc,
 #endif
-                             alpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
+                             alpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
                              width, height, FALSE);
 
-    if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(surface)));
+    if (surface == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
 
-    dest = cairo_image_surface_get_data(surface);
-    stride = cairo_image_surface_get_stride(surface);
+    dest = (uint8_t *)pixman_image_get_data(surface);
+    stride = pixman_image_get_stride(surface);
     if (quic_decode(quic_data->quic, alpha ? QUIC_IMAGE_TYPE_RGBA : QUIC_IMAGE_TYPE_RGB32,
                     dest, stride) == QUIC_ERROR) {
+        pixman_image_unref(surface);
         CANVAS_ERROR("quic decode failed");
     }
 
@@ -463,33 +503,32 @@ static inline void canvas_copy_1bpp_be(uint8_t* dest, int dest_stride, uint8_t*
     }
 }
 
-static cairo_surface_t *canvas_bitmap_to_surface(CanvasBase *canvas, SpiceBitmap* bitmap,
-                                                 SpicePalette *palette)
+static pixman_image_t *canvas_bitmap_to_surface(CanvasBase *canvas, SpiceBitmap* bitmap,
+                                                SpicePalette *palette)
 {
     uint8_t* src = (uint8_t *)SPICE_GET_ADDRESS(bitmap->data);
     int src_stride;
     uint8_t* end;
     uint8_t* dest;
     int dest_stride;
-    cairo_surface_t* cairo_surface;
+    pixman_image_t* image;
 
     src_stride = bitmap->stride;
     end = src + (bitmap->y * src_stride);
     access_test(canvas, src, bitmap->y * src_stride);
 
-    cairo_surface = surface_create(
+    image = surface_create(
 #ifdef WIN32
                                    canvas->dc,
 #endif
-                                   (bitmap->format == SPICE_BITMAP_FMT_RGBA) ? CAIRO_FORMAT_ARGB32 :
-                                                                         CAIRO_FORMAT_RGB24,
+                                   (bitmap->format == SPICE_BITMAP_FMT_RGBA) ? PIXMAN_a8r8g8b8 :
+                                                                         PIXMAN_x8r8g8b8,
                                    bitmap->x, bitmap->y, FALSE);
-    if (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(cairo_surface)));
+    if (image == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
-    dest = cairo_image_surface_get_data(cairo_surface);
-    dest_stride = cairo_image_surface_get_stride(cairo_surface);
+    dest = (uint8_t *)pixman_image_get_data(image);
+    dest_stride = pixman_image_get_stride(image);
     if (!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
         ASSERT(bitmap->y > 0);
         dest += dest_stride * ((int)bitmap->y - 1);
@@ -517,7 +556,7 @@ static cairo_surface_t *canvas_bitmap_to_surface(CanvasBase *canvas, SpiceBitmap
         canvas_copy_1bpp_be(dest, dest_stride, src, src_stride, bitmap->x, end, palette);
         break;
     }
-    return cairo_surface;
+    return image;
 }
 
 #ifdef CAIRO_CANVAS_CACHE
@@ -544,7 +583,7 @@ static inline SpicePalette *canvas_get_palett(CanvasBase *canvas, SPICE_ADDRESS
     return palette;
 }
 
-static cairo_surface_t *canvas_get_lz(CanvasBase *canvas, LZImage *image, int invers)
+static pixman_image_t *canvas_get_lz(CanvasBase *canvas, LZImage *image, int invers)
 {
     LzData *lz_data = &canvas->lz_data;
     uint8_t *comp_buf = NULL;
@@ -612,7 +651,7 @@ static cairo_surface_t *canvas_get_lz(CanvasBase *canvas, LZImage *image, int in
     alloc_lz_image_surface(&lz_data->decode_data, alpha ? LZ_IMAGE_TYPE_RGBA : LZ_IMAGE_TYPE_RGB32,
                            width, height, n_comp_pixels, top_down);
 
-    src = cairo_image_surface_get_data(lz_data->decode_data.out_surface);
+    src = (uint8_t *)pixman_image_get_data(lz_data->decode_data.out_surface);
 
     stride = (n_comp_pixels / height) * 4;
     if (!top_down) {
@@ -644,7 +683,7 @@ static cairo_surface_t *canvas_get_lz(CanvasBase *canvas, LZImage *image, int in
 
 // don't handle plts since bitmaps with plt can be decoded globaly to RGB32 (because
 // same byte sequence can be transformed to different RGB pixels by different plts)
-static cairo_surface_t *canvas_get_glz(CanvasBase *canvas, LZImage *image)
+static pixman_image_t *canvas_get_glz(CanvasBase *canvas, LZImage *image)
 {
     ASSERT(image->descriptor.type == SPICE_IMAGE_TYPE_GLZ_RGB);
 #ifdef WIN32
@@ -696,9 +735,9 @@ static void dump_bitmap(SpiceBitmap *bitmap, SpicePalette *palette)
 
 #endif
 
-static cairo_surface_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap)
+static pixman_image_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap)
 {
-    cairo_surface_t* surface;
+    pixman_image_t* surface;
     SpicePalette *palette;
 
     palette = canvas_get_palett(canvas, bitmap->palette, bitmap->flags);
@@ -720,7 +759,7 @@ static cairo_surface_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap)
 #else
 
 
-static cairo_surface_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap)
+static pixman_image_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap)
 {
     SpicePalette *palette;
 
@@ -731,7 +770,7 @@ static cairo_surface_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap)
     if (canvas->color_shift == 5) {
         int size = sizeof(SpicePalette) + (palette->num_ents << 2);
         SpicePalette *local_palette = malloc(size);
-        cairo_surface_t* surface;
+        pixman_image_t* surface;
 
         memcpy(local_palette, palette, size);
         canvas_localize_palette(canvas, local_palette);
@@ -754,21 +793,21 @@ static cairo_surface_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap)
 
 #if defined(DEBUG_DUMP_SURFACE) || defined(DEBUG_DUMP_COMPRESS)
 
-static void dump_surface(cairo_surface_t *surface, int cache)
+static void dump_surface(pixman_image_t *surface, int cache)
 {
     static uint32_t file_id = 0;
     int i, j;
     char file_str[200];
-    cairo_format_t format = cairo_image_surface_get_format(surface);
+    int depth = pixman_image_get_depth(surface);
 
-    if (format != CAIRO_FORMAT_RGB24 && format != CAIRO_FORMAT_ARGB32) {
+    if (depth != 24 && depth != 32) {
         return;
     }
 
-    uint8_t *data = cairo_image_surface_get_data(surface);
-    int width = cairo_image_surface_get_width(surface);
-    int height = cairo_image_surface_get_height(surface);
-    int stride = cairo_image_surface_get_stride(surface);
+    uint8_t *data = (uint8_t *)pixman_image_get_data(surface);
+    int width = pixman_image_get_width(surface);
+    int height = pixman_image_get_height(surface);
+    int stride = pixman_image_surface_get_stride(surface);
 
     uint32_t id = ++file_id;
 #ifdef WIN32
@@ -800,17 +839,12 @@ static void dump_surface(cairo_surface_t *surface, int cache)
 
 #if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
 
-static void __release_surface(void *inv_surf)
-{
-    cairo_surface_destroy((cairo_surface_t *)inv_surf);
-}
-
 //#define DEBUG_LZ
 
-static cairo_surface_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
+static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
 {
     SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     access_test(canvas, descriptor, sizeof(SpiceImageDescriptor));
 #ifdef DEBUG_LZ
     LOG_DEBUG("canvas_get_image image type: " << (int)descriptor->type);
@@ -872,7 +906,7 @@ static cairo_surface_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
 
 #else
 
-static cairo_surface_t *canvas_get_image(CairoCanvas *canvas, SPICE_ADDRESS addr)
+static pixman_image_t *canvas_get_image(CairoCanvas *canvas, SPICE_ADDRESS addr)
 {
     SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
 
@@ -909,9 +943,9 @@ static inline uint8_t revers_bits(uint8_t byte)
     return ret;
 }
 
-static cairo_surface_t *canvas_get_bitmap_mask(CanvasBase *canvas, SpiceBitmap* bitmap, int invers)
+static pixman_image_t *canvas_get_bitmap_mask(CanvasBase *canvas, SpiceBitmap* bitmap, int invers)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     uint8_t *src_line;
     uint8_t *end_line;
     uint8_t *dest_line;
@@ -923,10 +957,9 @@ static cairo_surface_t *canvas_get_bitmap_mask(CanvasBase *canvas, SpiceBitmap*
 #ifdef WIN32
             canvas->dc,
 #endif
-            CAIRO_FORMAT_A1, bitmap->x, bitmap->y, TRUE);
-    if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(surface)));
+            PIXMAN_a1, bitmap->x, bitmap->y, TRUE);
+    if (surface == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
 
     src_line = (uint8_t *)SPICE_GET_ADDRESS(bitmap->data);
@@ -935,8 +968,8 @@ static cairo_surface_t *canvas_get_bitmap_mask(CanvasBase *canvas, SpiceBitmap*
     access_test(canvas, src_line, end_line - src_line);
     line_size = ALIGN(bitmap->x, 8) >> 3;
 
-    dest_stride = cairo_image_surface_get_stride(surface);
-    dest_line = cairo_image_surface_get_data(surface);
+    dest_stride = pixman_image_get_stride(surface);
+    dest_line = (uint8_t *)pixman_image_get_data(surface);
 #if defined(GL_CANVAS)
     if ((bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
 #else
@@ -979,7 +1012,8 @@ static cairo_surface_t *canvas_get_bitmap_mask(CanvasBase *canvas, SpiceBitmap*
             }
             break;
         default:
-            cairo_surface_destroy(surface);
+            pixman_image_unref(surface);
+            surface = NULL;
             CANVAS_ERROR("invalid bitmap format");
         }
     } else {
@@ -1009,26 +1043,27 @@ static cairo_surface_t *canvas_get_bitmap_mask(CanvasBase *canvas, SpiceBitmap*
             }
             break;
         default:
-            cairo_surface_destroy(surface);
+            pixman_image_unref(surface);
+            surface = NULL;
             CANVAS_ERROR("invalid bitmap format");
         }
     }
     return surface;
 }
 
-static inline cairo_surface_t *canvas_A1_invers(cairo_surface_t *src_surf)
+static inline pixman_image_t *canvas_A1_invers(pixman_image_t *src_surf)
 {
-    int width = cairo_image_surface_get_width(src_surf);
-    int height = cairo_image_surface_get_height(src_surf);
+    int width = pixman_image_get_width(src_surf);
+    int height = pixman_image_get_height(src_surf);
 
-    cairo_surface_t * invers = cairo_image_surface_create(CAIRO_FORMAT_A1, width, height);
-    if (cairo_surface_status(invers) == CAIRO_STATUS_SUCCESS) {
-        uint8_t *src_line = cairo_image_surface_get_data(src_surf);
-        int src_stride = cairo_image_surface_get_stride(src_surf);
+    pixman_image_t * invers = pixman_image_create_bits(PIXMAN_a1, width, height, NULL, 0);
+    if (invers != NULL) {
+        uint8_t *src_line = (uint8_t *)pixman_image_get_data(src_surf);
+        int src_stride = pixman_image_get_stride(src_surf);
         uint8_t *end_line = src_line + (height * src_stride);
         int line_size = ALIGN(width, 8) >> 3;
-        uint8_t *dest_line = cairo_image_surface_get_data(invers);
-        int dest_stride = cairo_image_surface_get_stride(invers);
+        uint8_t *dest_line = (uint8_t *)pixman_image_get_data(invers);
+        int dest_stride = pixman_image_get_stride(invers);
 
         for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
             uint8_t *dest = dest_line;
@@ -1042,29 +1077,28 @@ static inline cairo_surface_t *canvas_A1_invers(cairo_surface_t *src_surf)
     return invers;
 }
 
-static cairo_surface_t *canvas_surf_to_invers(cairo_surface_t *surf)
+static pixman_image_t *canvas_surf_to_invers(pixman_image_t *surf)
 {
-    int width = cairo_image_surface_get_width(surf);
-    int height = cairo_image_surface_get_height(surf);
+    int width = pixman_image_get_width(surf);
+    int height = pixman_image_get_height(surf);
     uint8_t *dest_line;
     uint8_t *dest_line_end;
     uint8_t *src_line;
     int dest_stride;
     int src_stride;
 
-    ASSERT(cairo_image_surface_get_format(surf) == CAIRO_FORMAT_RGB24);
-    cairo_surface_t *invers = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
+    ASSERT(pixman_image_get_depth(surf) == 24);
+    pixman_image_t *invers = pixman_image_create_bits (PIXMAN_x8r8g8b8, width, height, NULL, 0);
 
-    if (cairo_surface_status(invers) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(invers)));
+    if (invers == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
 
-    dest_line = cairo_image_surface_get_data(invers);
-    dest_stride = cairo_image_surface_get_stride(invers);
+    dest_line = (uint8_t *)pixman_image_get_data(invers);
+    dest_stride = pixman_image_get_stride(invers);
     dest_line_end = dest_line + dest_stride * height;
-    src_line = cairo_image_surface_get_data(surf);
-    src_stride = cairo_image_surface_get_stride(surf);
+    src_line = (uint8_t *)pixman_image_get_data(surf);
+    src_stride = pixman_image_get_stride(surf);
 
     for (; dest_line != dest_line_end; dest_line += dest_stride, src_line += src_stride) {
         uint32_t *src = (uint32_t *)src_line;
@@ -1083,53 +1117,27 @@ static cairo_surface_t *canvas_surf_to_invers(cairo_surface_t *surf)
 * the returned reference, you must call cairo_surface_destroy.
 * Thread safe with respect to the user data.
 */
-static inline cairo_surface_t* canvas_handle_inverse_user_data(cairo_surface_t* surface)
+static inline pixman_image_t* canvas_handle_inverse_user_data(pixman_image_t* surface)
 {
-    cairo_surface_t *inv_surf = NULL;
-#ifdef CAIRO_CANVAS_CACH_IS_SHARED
-    MUTEX_LOCK(cairo_surface_user_data_mutex);
-#endif
-    inv_surf = (cairo_surface_t *)cairo_surface_get_user_data(surface, &invers_data_type);
-#ifdef CAIRO_CANVAS_CACH_IS_SHARED
-    MUTEX_UNLOCK(cairo_surface_user_data_mutex);
-#endif
-    if (!inv_surf) {
-        if (cairo_image_surface_get_format(surface) == CAIRO_FORMAT_A1) {
-            inv_surf = canvas_A1_invers(surface);
-        } else {
-            inv_surf = canvas_surf_to_invers(surface);
-        }
+    pixman_image_t *inv_surf = NULL;
 
-        if (cairo_surface_status(inv_surf) != CAIRO_STATUS_SUCCESS) {
-            cairo_surface_destroy(inv_surf);
-            CANVAS_ERROR("create surface failed, %s",
-                         cairo_status_to_string(cairo_surface_status(surface)));
-        }
-#ifdef CAIRO_CANVAS_CACH_IS_SHARED
-        MUTEX_LOCK(cairo_surface_user_data_mutex);
-
-        // checking if other thread has already assigned the user data
-        if (!cairo_surface_get_user_data(surface, &invers_data_type)) {
-#endif
-            if (cairo_surface_set_user_data(surface, &invers_data_type, inv_surf,
-                                            __release_surface) == CAIRO_STATUS_SUCCESS) {
-                cairo_surface_reference(inv_surf);
-            }
-#ifdef CAIRO_CANVAS_CACH_IS_SHARED
-        }
-        MUTEX_UNLOCK(cairo_surface_user_data_mutex);
-#endif
+    if (pixman_image_get_depth(surface) == 1) {
+        inv_surf = canvas_A1_invers(surface);
     } else {
-        cairo_surface_reference(inv_surf);
+        inv_surf = canvas_surf_to_invers(surface);
+    }
+
+    if (inv_surf == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
 
     return inv_surf;
 }
 
-static cairo_surface_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask)
+static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask)
 {
     SpiceImageDescriptor *descriptor;
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     int need_invers;
     int is_invers;
     int cache_me;
@@ -1172,11 +1180,11 @@ static cairo_surface_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask)
     }
 
     if (need_invers && !is_invers) { // surface is in cache
-        cairo_surface_t *inv_surf;
+        pixman_image_t *inv_surf;
 
         inv_surf = canvas_handle_inverse_user_data(surface);
 
-        cairo_surface_destroy(surface);
+        pixman_image_unref(surface);
         surface = inv_surf;
     }
 #endif
@@ -1332,12 +1340,12 @@ static void canvas_put_glyph_bits(SpiceRasterGlyph *glyph, int bpp, uint8_t *des
     }
 }
 
-static cairo_surface_t *canvas_get_str_mask(CanvasBase *canvas, SpiceString *str, int bpp, SpicePoint *pos)
+static pixman_image_t *canvas_get_str_mask(CanvasBase *canvas, SpiceString *str, int bpp, SpicePoint *pos)
 {
     SpiceRasterGlyph *glyph = (SpiceRasterGlyph *)str->data;
     SpiceRasterGlyph *next_glyph;
     SpiceRect bounds;
-    cairo_surface_t *str_mask;
+    pixman_image_t *str_mask;
     uint8_t *dest;
     int dest_stride;
     int i;
@@ -1360,15 +1368,14 @@ static cairo_surface_t *canvas_get_str_mask(CanvasBase *canvas, SpiceString *str
         rect_union(&bounds, &glyph_box);
     }
 
-    str_mask = cairo_image_surface_create((bpp == 1) ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8,
-                                          bounds.right - bounds.left,
-                                          bounds.bottom - bounds.top);
-    if (cairo_surface_status(str_mask) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(str_mask)));
+    str_mask = pixman_image_create_bits((bpp == 1) ? PIXMAN_a1 : PIXMAN_a8,
+                                        bounds.right - bounds.left,
+                                        bounds.bottom - bounds.top, NULL, 0);
+    if (str_mask == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
-    dest = cairo_image_surface_get_data(str_mask);
-    dest_stride = cairo_image_surface_get_stride(str_mask);
+    dest = (uint8_t *)pixman_image_get_data(str_mask);
+    dest_stride = pixman_image_get_stride(str_mask);
     glyph = (SpiceRasterGlyph *)str->data;
     for (i = 0; i < str->length; i++) {
 #if defined(GL_CANVAS)
@@ -1390,47 +1397,39 @@ static inline SpiceVectorGlyph *canvas_next_vector_glyph(const SpiceVectorGlyph
     return (SpiceVectorGlyph *)((uint8_t *)(glyph + 1) + glyph->data_size);
 }
 
-static cairo_surface_t *canvas_scale_surface(cairo_surface_t *src, const SpiceRect *src_area, int width,
-                                             int hight, int scale_mode)
+static pixman_image_t *canvas_scale_surface(pixman_image_t *src, const SpiceRect *src_area, int width,
+                                            int height, int scale_mode)
 {
-    cairo_t *cairo;
-    cairo_surface_t *surface;
-    cairo_pattern_t *pattern;
-    cairo_matrix_t matrix;
+    pixman_image_t *surface;
+    pixman_transform_t transform;
     double sx, sy;
 
-    surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, hight);
-    if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(surface)));
-    }
-
-    cairo = cairo_create(surface);
-    if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s", cairo_status_to_string(cairo_status(cairo)));
-    }
-
-    pattern = cairo_pattern_create_for_surface(src);
-    if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create pattern failed, %s",
-                     cairo_status_to_string(cairo_pattern_status(pattern)));
+    surface = pixman_image_create_bits(PIXMAN_x8r8g8b8, width, height, NULL, 0);
+    if (surface == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
 
     sx = (double)(src_area->right - src_area->left) / width;
-    sy = (double)(src_area->bottom - src_area->top) / hight;
+    sy = (double)(src_area->bottom - src_area->top) / height;
 
-    cairo_matrix_init_translate(&matrix, src_area->left, src_area->top);
-    cairo_matrix_scale(&matrix, sx, sy);
+    pixman_transform_init_scale(&transform, pixman_double_to_fixed(sx), pixman_double_to_fixed(sy));
 
-    cairo_pattern_set_matrix(pattern, &matrix);
+    pixman_image_set_transform (src, &transform);
+    pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
     ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE || scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
-    cairo_pattern_set_filter(pattern, (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
-                                                          CAIRO_FILTER_NEAREST : CAIRO_FILTER_GOOD);
+    pixman_image_set_filter(src,
+                            (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
+                            NULL, 0);
+
+    pixman_image_composite32(PIXMAN_OP_SRC,
+                             src, NULL, surface,
+                             ROUND(src_area->left / sx), ROUND (src_area->top / sy),
+                             0, 0, /* mask */
+                             0, 0, /* dst */
+                             width, height);
+
+    pixman_image_set_transform(src, NULL);
 
-    cairo_set_source(cairo, pattern);
-    cairo_pattern_destroy(pattern);
-    cairo_paint(cairo);
-    cairo_destroy(cairo);
     return surface;
 }
 
diff --git a/common/canvas_base.h b/common/canvas_base.h
index cc75087..78ece62 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -20,7 +20,7 @@
 #define _H_CANVAS_BASE
 
 
-#include "cairo.h"
+#include "pixman_utils.h"
 #include "lz.h"
 #include <spice/draw.h>
 
@@ -30,9 +30,9 @@ typedef struct _SpicePaletteCache SpicePaletteCache;
 typedef struct {
     void (*put)(SpiceImageCache *cache,
                 uint64_t id,
-                cairo_surface_t *surface);
-    cairo_surface_t *(*get)(SpiceImageCache *cache,
-                            uint64_t id);
+                pixman_image_t *surface);
+    pixman_image_t *(*get)(SpiceImageCache *cache,
+                           uint64_t id);
 } SpiceImageCacheOps;
 
 struct _SpiceImageCache {
diff --git a/common/canvas_utils.c b/common/canvas_utils.c
index f6470ca..b70b17b 100644
--- a/common/canvas_utils.c
+++ b/common/canvas_utils.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
@@ -45,31 +46,50 @@ extern int gdi_handlers;
 #define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
 #endif
 
-const cairo_user_data_key_t bitmap_data_type = {0};
-const cairo_user_data_key_t bitmap_withstride_data_type = {0};
-
-#ifdef WIN32
-static void release_bitmap(void *bitmap_cache)
+static void release_data(pixman_image_t *image, void *release_data)
 {
-    DeleteObject((HBITMAP)((BitmapCache *)bitmap_cache)->bitmap);
-    CloseHandle(((BitmapCache *)bitmap_cache)->mutex);
-    free(bitmap_cache);
-    gdi_handlers--;
-}
+    PixmanData *data = (PixmanData *)release_data;
 
+#ifdef WIN32
+    if (data->bitmap) {
+        DeleteObject((HBITMAP)data->bitmap);
+        CloseHandle(data->mutex);
+        gdi_handlers--;
+    }
 #endif
+    if (data->data) {
+        free(data->data);
+    }
 
-static void release_withstride_bitmap(void *data)
-{
     free(data);
 }
 
-static inline cairo_surface_t *__surface_create_stride(cairo_format_t format, int width, int height,
-                                                       int stride)
+static PixmanData *
+pixman_image_add_data(pixman_image_t *image)
+{
+    PixmanData *data;
+
+    data = (PixmanData *)pixman_image_get_destroy_data(image);
+    if (data == NULL) {
+        data = (PixmanData *)calloc(1, sizeof(PixmanData));
+        if (data == NULL) {
+            CANVAS_ERROR("out of memory");
+        }
+        pixman_image_set_destroy_function(image,
+                                          release_data,
+                                          data);
+    }
+
+    return data;
+}
+
+static inline pixman_image_t *__surface_create_stride(pixman_format_code_t format, int width, int height,
+                                                      int stride)
 {
     uint8_t *data;
     uint8_t *stride_data;
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
+    PixmanData *pixman_data;
 
     data = (uint8_t *)malloc(abs(stride) * height);
     if (stride < 0) {
@@ -78,30 +98,24 @@ static inline cairo_surface_t *__surface_create_stride(cairo_format_t format, in
         stride_data = data;
     }
 
-    surface = cairo_image_surface_create_for_data(stride_data, format, width, height, stride);
+    surface = pixman_image_create_bits(format, width, height, (uint32_t *)stride_data, stride);
 
-    if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+    if (surface == NULL) {
         free(data);
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(surface)));
+        CANVAS_ERROR("create surface failed, out of memory");
     }
 
-    if (cairo_surface_set_user_data(surface, &bitmap_withstride_data_type, data,
-                                    release_withstride_bitmap) != CAIRO_STATUS_SUCCESS) {
-        free(data);
-        cairo_surface_destroy(surface);
-        CANVAS_ERROR("set_user_data surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(surface)));
-    }
+    pixman_data = pixman_image_add_data(surface);
+    pixman_data->data = data;
 
     return surface;
 }
 
 #ifdef WIN32
-cairo_surface_t *surface_create(HDC dc, cairo_format_t format,
+pixman_image_t *surface_create(HDC dc, pixman_format_code_t format,
                                 int width, int height, int top_down)
 #else
-cairo_surface_t * surface_create(cairo_format_t format, int width, int height, int top_down)
+pixman_image_t * surface_create(pixman_format_code_t format, int width, int height, int top_down)
 #endif
 {
 #ifdef WIN32
@@ -120,8 +134,10 @@ cairo_surface_t * surface_create(cairo_format_t format, int width, int height, i
             RGBQUAD palette[255];
         } bitmap_info;
         int nstride;
-        cairo_surface_t *surface;
-        BitmapCache *bitmap_cache;
+        pixman_image_t *surface;
+        PixmanData *pixman_data;
+        HBITMAP bitmap;
+        HANDLE mutex;
 
         memset(&bitmap_info, 0, sizeof(bitmap_info));
         bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
@@ -131,16 +147,16 @@ cairo_surface_t * surface_create(cairo_format_t format, int width, int height, i
 
         bitmap_info.inf.bmiHeader.biPlanes = 1;
         switch (format) {
-        case CAIRO_FORMAT_ARGB32:
-        case CAIRO_FORMAT_RGB24:
+        case PIXMAN_a8r8g8b8:
+        case PIXMAN_x8r8g8b8:
             bitmap_info.inf.bmiHeader.biBitCount = 32;
             nstride = width * 4;
             break;
-        case CAIRO_FORMAT_A8:
+        case PIXMAN_a8:
             bitmap_info.inf.bmiHeader.biBitCount = 8;
             nstride = ALIGN(width, 4);
             break;
-        case CAIRO_FORMAT_A1:
+        case PIXMAN_a1:
             bitmap_info.inf.bmiHeader.biBitCount = 1;
             nstride = ALIGN(width, 32) / 8;
             break;
@@ -150,25 +166,15 @@ cairo_surface_t * surface_create(cairo_format_t format, int width, int height, i
 
         bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
 
-        bitmap_cache = (BitmapCache *)malloc(sizeof(*bitmap_cache));
-        if (!bitmap_cache) {
-            CANVAS_ERROR("malloc failed");
-            return NULL;
-        }
-
-        bitmap_cache->mutex = CreateMutex(NULL, 0, NULL);
-        if (!bitmap_cache->mutex) {
-            free(bitmap_cache);
+        mutex = CreateMutex(NULL, 0, NULL);
+        if (!mutex) {
             CANVAS_ERROR("Unable to CreateMutex");
-            return NULL;
         }
 
-        bitmap_cache->bitmap = CreateDIBSection(dc, &bitmap_info.inf, 0, (VOID **)&data, NULL, 0);
-        if (!bitmap_cache->bitmap) {
+        bitmap = CreateDIBSection(dc, &bitmap_info.inf, 0, (VOID **)&data, NULL, 0);
+        if (!bitmap) {
             CloseHandle(bitmap_cache->mutex);
-            free(bitmap_cache);
             CANVAS_ERROR("Unable to CreateDIBSection");
-            return NULL;
         }
 
         if (top_down) {
@@ -178,41 +184,33 @@ cairo_surface_t * surface_create(cairo_format_t format, int width, int height, i
             nstride = -nstride;
         }
 
-        surface = cairo_image_surface_create_for_data(src, format, width, height, nstride);
-        if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
-            CloseHandle(bitmap_cache->mutex);
-            DeleteObject((HBITMAP)bitmap_cache->bitmap);
-            free(bitmap_cache);
-            CANVAS_ERROR("create surface failed, %s",
-                         cairo_status_to_string(cairo_surface_status(surface)));
-        }
-        if (cairo_surface_set_user_data(surface, &bitmap_data_type, bitmap_cache,
-                                        release_bitmap) != CAIRO_STATUS_SUCCESS) {
-            CloseHandle(bitmap_cache->mutex);
-            cairo_surface_destroy(surface);
-            DeleteObject((HBITMAP)bitmap_cache->bitmap);
-            free(bitmap_cache);
-            CANVAS_ERROR("set_user_data surface failed, %s",
-                         cairo_status_to_string(cairo_surface_status(surface)));
+        surface = pixman_image_create_bits(format, width, height, (uint32_t *)src, stride);
+        if (surface == NULL) {
+            CloseHandle(mutex);
+            DeleteObject(bitmap);
+            CANVAS_ERROR("create surface failed, out of memory");
         }
+        pixman_data = pixman_image_add_data(surface);
+        pixman_data.bitmap = bitmap;
+        pixman_data.mutex = mutex;
         gdi_handlers++;
         return surface;
     } else {
 #endif
     if (top_down) {
-        return cairo_image_surface_create(format, width, height);
+        return pixman_image_create_bits(format, width, height, NULL, 0);
     } else {
         // NOTE: we assume here that the lz decoders always decode to RGB32.
         int stride = 0;
         switch (format) {
-        case CAIRO_FORMAT_ARGB32:
-        case CAIRO_FORMAT_RGB24:
+        case PIXMAN_a8r8g8b8:
+        case PIXMAN_x8r8g8b8:
             stride = width * 4;
             break;
-        case CAIRO_FORMAT_A8:
+        case PIXMAN_a8:
             stride = ALIGN(width, 4);
             break;
-        case CAIRO_FORMAT_A1:
+        case PIXMAN_a1:
             stride = ALIGN(width, 32) / 8;
             break;
         default:
@@ -228,11 +226,11 @@ cairo_surface_t * surface_create(cairo_format_t format, int width, int height, i
 }
 
 #ifdef WIN32
-cairo_surface_t *surface_create_stride(HDC dc, cairo_format_t format, int width, int height,
-                                       int stride)
+pixman_image_t *surface_create_stride(HDC dc, pixman_format_code_t format, int width, int height,
+                                      int stride)
 #else
-cairo_surface_t *surface_create_stride(cairo_format_t format, int width, int height,
-                                        int stride)
+pixman_image_t *surface_create_stride(pixman_format_code_t format, int width, int height,
+                                      int stride)
 #endif
 {
 #ifdef WIN32
@@ -246,12 +244,12 @@ cairo_surface_t *surface_create_stride(cairo_format_t format, int width, int hei
     return __surface_create_stride(format, width, height, stride);
 }
 
-cairo_surface_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data, LzImageType type, int width,
-                                        int height, int gross_pixels, int top_down)
+pixman_image_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data, LzImageType type, int width,
+                                       int height, int gross_pixels, int top_down)
 {
     int stride;
     int alpha;
-    cairo_surface_t *surface = NULL;
+    pixman_image_t *surface = NULL;
 
     stride = (gross_pixels / height) * 4;
 
@@ -270,7 +268,7 @@ cairo_surface_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data, LzImageTyp
 #ifdef WIN32
             canvas_data->dc,
 #endif
-            alpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, width, height, stride);
+            alpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8, width, height, stride);
     canvas_data->out_surface = surface;
     return surface;
 }
diff --git a/common/canvas_utils.h b/common/canvas_utils.h
index 10f2d64..4fdbe6a 100644
--- a/common/canvas_utils.h
+++ b/common/canvas_utils.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
@@ -20,31 +21,30 @@
 
 #include <spice/types.h>
 
-#include "cairo.h"
+#include "pixman_utils.h"
 #include "lz.h"
 
+typedef struct PixmanData {
 #ifdef WIN32
-typedef struct BitmapCache {
     HBITMAP bitmap;
     HANDLE mutex;
-} BitmapCache;
 #endif
-
-extern const cairo_user_data_key_t bitmap_data_type;
+    uint8_t *data;
+} PixmanData;
 
 #ifdef WIN32
-cairo_surface_t *surface_create(HDC dc, cairo_format_t format,
-                                int width, int height, int top_down);
+pixman_image_t *surface_create(HDC dc, pixman_format_code_t format,
+                               int width, int height, int top_down);
 #else
-cairo_surface_t *surface_create(cairo_format_t format, int width, int height, int top_down);
+pixman_image_t *surface_create(pixman_format_code_t format, int width, int height, int top_down);
 #endif
 
 #ifdef WIN32
-cairo_surface_t *surface_create_stride(HDC dc, cairo_format_t format, int width, int height,
-                                       int stride);
+pixman_image_t *surface_create_stride(HDC dc, pixman_format_code_t format, int width, int height,
+                                      int stride);
 #else
-cairo_surface_t *surface_create_stride(cairo_format_t format, int width, int height,
-                                       int stride);
+pixman_image_t *surface_create_stride(pixman_format_code_t format, int width, int height,
+                                      int stride);
 #endif
 
 
@@ -52,10 +52,10 @@ typedef struct LzDecodeUsrData {
 #ifdef WIN32
     HDC dc;
 #endif
-    cairo_surface_t       *out_surface;
+    pixman_image_t       *out_surface;
 } LzDecodeUsrData;
 
 
-cairo_surface_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data, LzImageType type, int width,
-                                        int height, int gross_pixels, int top_down);
+pixman_image_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data, LzImageType type, int width,
+                                       int height, int gross_pixels, int top_down);
 #endif
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index bc9f8b2..66f8a02 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -305,15 +305,15 @@ uint32_t raster_ops[] = {
     0x00FF0062 // 1 WHITENESS
 };
 
-static inline void surface_to_image(cairo_surface_t *surface, GdiImage *image)
+static inline void surface_to_image(pixman_image_t *surface, GdiImage *image)
 {
-    cairo_format_t format = cairo_image_surface_get_format(surface);
+    int depth = pixman_image_surface_get_depth(surface);
 
-    ASSERT(format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24);
-    image->width = cairo_image_surface_get_width(surface);
-    image->height = cairo_image_surface_get_height(surface);
-    image->stride = cairo_image_surface_get_stride(surface);
-    image->pixels = cairo_image_surface_get_data(surface);
+    ASSERT(depth == 32 || depth == 24);
+    image->width = pixman_image_get_width(surface);
+    image->height = pixman_image_get_height(surface);
+    image->stride = pixman_image_get_stride(surface);
+    image->pixels = pixman_image_get_data(surface);
 }
 
 static void set_path(GdiCanvas *canvas, void *addr)
@@ -645,7 +645,7 @@ static HBRUSH get_brush(GdiCanvas *canvas, SpiceBrush *brush)
     case SPICE_BRUSH_TYPE_PATTERN: {
         GdiImage image;
         HBRUSH hbrush;
-        cairo_surface_t *surface;
+        pixman_image_t *surface;
         HDC dc;
         HBITMAP bitmap;
         HBITMAP prev_bitmap;
@@ -664,7 +664,7 @@ static HBRUSH get_brush(GdiCanvas *canvas, SpiceBrush *brush)
         }
 
         release_bitmap(dc, bitmap, prev_bitmap, 0);
-        cairo_surface_destroy(surface);
+        pixman_image_unref(surface);
         return hbrush;
     }
     case SPICE_BRUSH_TYPE_NONE:
@@ -779,27 +779,27 @@ uint8_t calc_rop3_src_brush(uint16_t rop3_bits)
 
 static struct BitmapData get_mask_bitmap(struct GdiCanvas *canvas, struct SpiceQMask *mask)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     struct BitmapData bitmap;
-    BitmapCache *bitmap_cache;
+    PixmanData *pixman_data;
 
     bitmap.hbitmap = NULL;
     if (!(surface = canvas_get_mask(&canvas->base, mask))) {
         return bitmap;
     }
 
-    bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
-    if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+    pixman_data = pixman_image_get_destroy_data (surface);
+    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
         bitmap.dc = create_compatible_dc();
-        bitmap.prev_hbitmap = (HBITMAP)SelectObject(bitmap.dc, bitmap_cache->bitmap);
-        bitmap.hbitmap = bitmap_cache->bitmap;
-        ReleaseMutex(bitmap_cache->mutex);
+        bitmap.prev_hbitmap = (HBITMAP)SelectObject(bitmap.dc, pixman_data->bitmap);
+        bitmap.hbitmap = pixman_data->bitmap;
+        ReleaseMutex(pixman_data->mutex);
         bitmap.cache = 1;
     } else if (!create_bitmap(&bitmap.hbitmap, &bitmap.prev_hbitmap, &bitmap.dc,
-                              cairo_image_surface_get_data(surface),
-                              cairo_image_surface_get_width(surface),
-                              cairo_image_surface_get_height(surface),
-                              cairo_image_surface_get_stride(surface), 1, 0)) {
+                              pixman_image_get_data(surface),
+                              pixman_image_get_width(surface),
+                              pixman_image_get_height(surface),
+                              pixman_image_get_stride(surface), 1, 0)) {
         bitmap.hbitmap = NULL;
     } else {
         bitmap.cache = 0;
@@ -865,7 +865,7 @@ static void draw_str_mask_bitmap(struct GdiCanvas *canvas,
                                  SpiceString *str, int n, SpiceRect *dest,
                                  SpiceRect *src, SpiceBrush *brush)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     struct BitmapData bitmap;
     SpicePoint pos;
     int dest_stride;
@@ -882,9 +882,9 @@ static void draw_str_mask_bitmap(struct GdiCanvas *canvas,
     bitmap.cache = 0;
     bitmap_data = create_bitmap(&bitmap.hbitmap, &bitmap.prev_hbitmap,
                                 &bitmap.dc, NULL,
-                                cairo_image_surface_get_width(surface),
-                                cairo_image_surface_get_height(surface),
-                                cairo_image_surface_get_stride(surface), 32, 0);
+                                pixman_image_get_width(surface),
+                                pixman_image_get_height(surface),
+                                pixman_image_get_stride(surface), 32, 0);
 
     if (!bitmap_data) {
         return;
@@ -896,14 +896,14 @@ static void draw_str_mask_bitmap(struct GdiCanvas *canvas,
 
     dest->left = pos.x;
     dest->top = pos.y;
-    dest->right = pos.x + cairo_image_surface_get_width(surface);
-    dest->bottom = pos.y + cairo_image_surface_get_height(surface);
+    dest->right = pos.x + pixman_image_get_width(surface);
+    dest->bottom = pos.y + pixman_image_get_height(surface);
     src->left = 0;
     src->top = 0;
-    src->right = cairo_image_surface_get_width(surface);
-    src->bottom = cairo_image_surface_get_height(surface);
+    src->right = pixman_image_get_width(surface);
+    src->bottom = pixman_image_get_height(surface);
 
-    dest_stride = cairo_image_surface_get_width(surface);
+    dest_stride = pixman_image_get_width(surface);
     switch (n) {
     case 1:
         dest_stride = dest_stride / 8;
@@ -926,10 +926,10 @@ static void draw_str_mask_bitmap(struct GdiCanvas *canvas,
 
     unset_brush(bitmap.dc, prev_hbrush);
 
-    copy_bitmap_alpha(cairo_image_surface_get_data(surface),
-                      cairo_image_surface_get_height(surface),
-                      cairo_image_surface_get_width(surface),
-                      cairo_image_surface_get_stride(surface),
+    copy_bitmap_alpha(pixman_image_get_data(surface),
+                      pixman_image_get_height(surface),
+                      pixman_image_get_width(surface),
+                      pixman_image_get_stride(surface),
                       bitmap_data, dest_stride, n);
 
     BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
@@ -1000,30 +1000,30 @@ void gdi_canvas_draw_fill(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
 
 void gdi_canvas_draw_copy(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     GdiImage image;
     struct BitmapData bitmapmask;
-    BitmapCache *bitmap_cache;
+    PixmanData *pixman_data;
 
     bitmapmask = get_mask_bitmap(canvas, &copy->mask);
     surface = canvas_get_image(&canvas->base, copy->src_bitmap);
-    bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
 
     Lock lock(*canvas->lock);
     set_scale_mode(canvas, copy->scale_mode);
     set_clip(canvas, clip);
 
-    if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
         HDC dc;
         HBITMAP prev_bitmap;
 
         dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
         gdi_draw_bitmap_redrop(canvas->dc, &copy->src_area, bbox, dc,
                                &bitmapmask, copy->rop_decriptor, 0);
         SelectObject(dc, prev_bitmap);
         DeleteObject(dc);
-        ReleaseMutex(bitmap_cache->mutex);
+        ReleaseMutex(pixman_data->mutex);
     } else {
         surface_to_image(surface, &image);
         gdi_draw_image(canvas->dc, &copy->src_area, bbox, image.pixels,
@@ -1033,7 +1033,7 @@ void gdi_canvas_draw_copy(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
 
     free_mask(&bitmapmask);
 
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
 }
 
 void gdi_canvas_put_image(GdiCanvas *canvas, HDC dc, const SpiceRect *dest, const uint8_t *src_data,
@@ -1127,26 +1127,26 @@ static void gdi_draw_image_transparent(GdiCanvas *canvas, HDC dest_dc, const Spi
 void gdi_canvas_draw_transparent(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
                                  SpiceTransparent* transparent)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     GdiImage image;
-    BitmapCache *bitmap_cache;
+    PixmanData *pixman_data;
 
     surface = canvas_get_image(&canvas->base, transparent->src_bitmap);
-    bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
     Lock lock(*canvas->lock);
     set_clip(canvas, clip);
-    if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
         HDC dc;
         HBITMAP prev_bitmap;
 
         dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
         gdi_draw_bitmap_transparent(canvas, canvas->dc, &transparent->src_area, bbox, dc,
                                     transparent->true_color);
 
         SelectObject(dc, prev_bitmap);
         DeleteObject(dc);
-        ReleaseMutex(bitmap_cache->mutex);
+        ReleaseMutex(pixman_data->mutex);
     } else {
         surface_to_image(surface, &image);
         gdi_draw_image_transparent(canvas, canvas->dc, &transparent->src_area, bbox, image.pixels,
@@ -1154,7 +1154,7 @@ void gdi_canvas_draw_transparent(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *
                                    transparent->true_color, 0);
     }
 
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
 }
 
 static void gdi_draw_bitmap_alpha(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
@@ -1198,28 +1198,28 @@ static void gdi_draw_image_alpha(HDC dest_dc, const SpiceRect *src, const SpiceR
 
 void gdi_canvas_draw_alpha_blend(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     GdiImage image;
-    BitmapCache *bitmap_cache;
+    PixmanData *pixman_data;
     int use_bitmap_alpha;
 
     surface = canvas_get_image(&canvas->base, alpha_blend->src_bitmap);
-    use_bitmap_alpha = cairo_image_surface_get_format(surface) == CAIRO_FORMAT_ARGB32;
-    bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+    use_bitmap_alpha = pixman_image_get_depth(surface) == 32;
+    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
 
     Lock lock(*canvas->lock);
     set_clip(canvas, clip);
-    if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
         HDC dc;
         HBITMAP prev_bitmap;
 
         dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
         gdi_draw_bitmap_alpha(canvas->dc, &alpha_blend->src_area, bbox, dc, alpha_blend->alpha,
                               use_bitmap_alpha);
         SelectObject(dc, prev_bitmap);
         DeleteObject(dc);
-        ReleaseMutex(bitmap_cache->mutex);
+        ReleaseMutex(pixman_data->mutex);
     } else {
         surface_to_image(surface, &image);
         gdi_draw_image_alpha(canvas->dc, &alpha_blend->src_area, bbox, image.pixels,
@@ -1227,21 +1227,21 @@ void gdi_canvas_draw_alpha_blend(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *
                              alpha_blend->alpha, 0, use_bitmap_alpha);
     }
 
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
 }
 
 void gdi_canvas_draw_opaque(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     GdiImage image;
     struct BitmapData bitmapmask;
-    BitmapCache *bitmap_cache;
+    PixmanData *pixman_data;
     HBRUSH prev_hbrush;
     HBRUSH hbrush;
     uint8_t rop3;
 
     surface = canvas_get_image(&canvas->base, opaque->src_bitmap);
-    bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
     bitmapmask = get_mask_bitmap(canvas, &opaque->mask);
     rop3 = calc_rop3_src_brush(opaque->rop_decriptor);
     hbrush = get_brush(canvas, &opaque->brush);
@@ -1252,16 +1252,16 @@ void gdi_canvas_draw_opaque(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
     set_clip(canvas, clip);
     prev_hbrush = set_brush(canvas->dc, hbrush, &opaque->brush);
 
-    if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
         HDC dc;
         HBITMAP prev_bitmap;
 
         dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
         gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, dc, &bitmapmask, rop3);
         SelectObject(dc, prev_bitmap);
         DeleteObject(dc);
-        ReleaseMutex(bitmap_cache->mutex);
+        ReleaseMutex(pixman_data->mutex);
     } else {
         surface_to_image(surface, &image);
         gdi_draw_image_rop3(canvas->dc, &opaque->src_area, bbox, image.pixels,
@@ -1272,35 +1272,35 @@ void gdi_canvas_draw_opaque(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
 
     free_mask(&bitmapmask);
 
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
 }
 
 void gdi_canvas_draw_blend(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     GdiImage image;
     struct BitmapData bitmapmask;
-    BitmapCache *bitmap_cache;
+    PixmanData *pixman_data;
 
     bitmapmask = get_mask_bitmap(canvas, &blend->mask);
     surface = canvas_get_image(&canvas->base, blend->src_bitmap);
-    bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
 
     Lock lock(*canvas->lock);
     set_scale_mode(canvas, blend->scale_mode);
     set_clip(canvas, clip);
 
-    if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
         HDC dc;
         HBITMAP prev_bitmap;
 
         dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
         gdi_draw_bitmap_redrop(canvas->dc, &blend->src_area, bbox, dc,
                                &bitmapmask, blend->rop_decriptor, 0);
         SelectObject(dc, prev_bitmap);
         DeleteObject(dc);
-        ReleaseMutex(bitmap_cache->mutex);
+        ReleaseMutex(pixman_data->mutex);
     } else {
         surface_to_image(surface, &image);
         gdi_draw_image(canvas->dc, &blend->src_area, bbox, image.pixels, image.stride, image.width,
@@ -1309,7 +1309,7 @@ void gdi_canvas_draw_blend(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
 
     free_mask(&bitmapmask);
 
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
 }
 
 void gdi_canvas_draw_blackness(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
@@ -1353,16 +1353,16 @@ void gdi_canvas_draw_whiteness(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *cl
 
 void gdi_canvas_draw_rop3(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     GdiImage image;
     struct BitmapData bitmapmask;
     HBRUSH prev_hbrush;
     HBRUSH hbrush;
-    BitmapCache *bitmap_cache;
+    PixmanData *pixman_data;
 
     hbrush = get_brush(canvas, &rop3->brush);
     surface = canvas_get_image(&canvas->base, rop3->src_bitmap);
-    bitmap_cache = (BitmapCache *)cairo_surface_get_user_data(surface, &bitmap_data_type);
+    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
     bitmapmask = get_mask_bitmap(canvas, &rop3->mask);
 
     Lock lock(*canvas->lock);
@@ -1370,17 +1370,17 @@ void gdi_canvas_draw_rop3(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     set_clip(canvas, clip);
     prev_hbrush = set_brush(canvas->dc, hbrush, &rop3->brush);
 
-    if (bitmap_cache && (WaitForSingleObject(bitmap_cache->mutex, INFINITE) != WAIT_FAILED)) {
+    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
         HDC dc;
         HBITMAP prev_bitmap;
 
         dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, bitmap_cache->bitmap);
+        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
         gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, dc,
                         &bitmapmask, rop3->rop3);
         SelectObject(dc, prev_bitmap);
         DeleteObject(dc);
-        ReleaseMutex(bitmap_cache->mutex);
+        ReleaseMutex(pixman_data->mutex);
     } else {
         surface_to_image(surface, &image);
         gdi_draw_image_rop3(canvas->dc, &rop3->src_area, bbox, image.pixels,
@@ -1390,7 +1390,7 @@ void gdi_canvas_draw_rop3(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     unset_brush(canvas->dc, prev_hbrush);
     free_mask(&bitmapmask);
 
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
 }
 
 void gdi_canvas_copy_bits(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
@@ -1517,7 +1517,7 @@ void gdi_canvas_draw_stroke(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
     int ps_join = 0;
     int line_cap = 0;
     uint32_t *user_style = NULL;
-    cairo_surface_t *surface = NULL;
+    pixman_image_t *surface = NULL;
 
     if (stroke->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
         surface = canvas_get_image(&canvas->base, stroke->brush.u.pattern.pat);
@@ -1630,7 +1630,7 @@ void gdi_canvas_draw_stroke(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
         logbrush.lbStyle = BS_DIBPATTERN | DIB_RGB_COLORS;
         logbrush.lbColor = 0;
 #endif
-        cairo_surface_destroy(surface);
+        pixman_image_unref(surface);
     }
 
 #if 0
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
index 18a4843..febd967 100644
--- a/common/gdi_canvas.h
+++ b/common/gdi_canvas.h
@@ -22,7 +22,7 @@
 #include <stdint.h>
 
 #include <spice/draw.h>
-#include "cairo.h"
+#include "pixman_utils.h"
 #include "canvas_base.h"
 #include "region.h"
 
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index 329d97b..b6d584f 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -71,8 +71,8 @@ static inline uint8_t *copy_opposite_image(GLCanvas *canvas, void *data, int str
     return (uint8_t *)canvas->private_data;
 }
 
-static cairo_surface_t *canvas_surf_to_trans_surf(GLCImage *image,
-                                                  uint32_t trans_color)
+static pixman_image_t *canvas_surf_to_trans_surf(GLCImage *image,
+                                                 uint32_t trans_color)
 {
     int width = image->width;
     int height = image->height;
@@ -81,21 +81,20 @@ static cairo_surface_t *canvas_surf_to_trans_surf(GLCImage *image,
     int src_stride;
     uint8_t *dest_line;
     int dest_stride;
-    cairo_surface_t *ret;
+    pixman_image_t *ret;
     int i;
 
-    ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
-    if (cairo_surface_status(ret) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(ret)));
+    ret = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, NULL, 0);
+    if (ret == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
 
     src_line = image->pixels;
     src_stride = image->stride;
     end_src_line = src_line + src_stride * height;
 
-    dest_line = cairo_image_surface_get_data(ret);
-    dest_stride = cairo_image_surface_get_stride(ret);
+    dest_line = (uint8_t *)pixman_image_get_data(ret);
+    dest_stride = pixman_image_get_stride(ret);
 
     for (; src_line < end_src_line; src_line += src_stride, dest_line += dest_stride) {
         for (i = 0; i < width; i++) {
@@ -209,32 +208,32 @@ static void set_clip(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip)
 
 static void set_mask(GLCanvas *canvas, SpiceQMask *mask, int x, int y)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *image;
 
-    if (!(surface = canvas_get_mask(&canvas->base, mask))) {
+    if (!(image = canvas_get_mask(&canvas->base, mask))) {
         glc_clear_mask(canvas->glc, GLC_MASK_A);
         return;
     }
 
 
     glc_set_mask(canvas->glc, x - mask->pos.x, y - mask->pos.y,
-                 cairo_image_surface_get_width(surface),
-                 cairo_image_surface_get_height(surface),
-                 cairo_image_surface_get_stride(surface),
-                 cairo_image_surface_get_data(surface), GLC_MASK_A);
+                 pixman_image_get_width(image),
+                 pixman_image_get_height(image),
+                 pixman_image_get_stride(image),
+                 (uint8_t *)pixman_image_get_data(image), GLC_MASK_A);
 }
 
-static inline void surface_to_image(GLCanvas *canvas, cairo_surface_t *surface, GLCImage *image,
+static inline void surface_to_image(GLCanvas *canvas, pixman_image_t *surface, GLCImage *image,
                                     int ignore_stride)
 {
-    cairo_format_t format = cairo_image_surface_get_format(surface);
+    int depth = pixman_image_get_depth(surface);
 
-    ASSERT(format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24);
-    image->format = (format == CAIRO_FORMAT_RGB24) ? GLC_IMAGE_RGB32 : GLC_IMAGE_ARGB32;
-    image->width = cairo_image_surface_get_width(surface);
-    image->height = cairo_image_surface_get_height(surface);
-    image->stride = cairo_image_surface_get_stride(surface);
-    image->pixels = cairo_image_surface_get_data(surface);
+    ASSERT(depth == 32 || depth == 24);
+    image->format = (depth == 24) ? GLC_IMAGE_RGB32 : GLC_IMAGE_ARGB32;
+    image->width = pixman_image_get_width(surface);
+    image->height = pixman_image_get_height(surface);
+    image->stride = pixman_image_get_stride(surface);
+    image->pixels = (uint8_t *)pixman_image_get_data(surface);
     image->pallet = NULL;
     if (ignore_stride) {
         return;
@@ -265,7 +264,7 @@ static void set_brush(GLCanvas *canvas, SpiceBrush *brush)
     case SPICE_BRUSH_TYPE_PATTERN: {
         GLCImage image;
         GLCPattern pattern;
-        cairo_surface_t *surface;
+        pixman_image_t *surface;
 
         surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
         surface_to_image(canvas, surface, &image, 0);
@@ -275,6 +274,7 @@ static void set_brush(GLCanvas *canvas, SpiceBrush *brush)
 
         glc_set_pattern(canvas->glc, pattern);
         glc_pattern_destroy(pattern);
+        pixman_image_unref (surface);
     }
     case SPICE_BRUSH_TYPE_NONE:
         return;
@@ -358,7 +358,7 @@ void gl_canvas_draw_fill(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
 
 void gl_canvas_draw_copy(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     GLCRecti src;
     GLCRecti dest;
     GLCImage image;
@@ -374,13 +374,13 @@ void gl_canvas_draw_copy(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     SET_GLC_RECT(&src, &copy->src_area);
     glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
 
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
     glc_flush(canvas->glc);
 }
 
 void gl_canvas_draw_opaque(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     GLCRecti src;
     GLCRecti dest;
     GLCRect fill_rect;
@@ -396,7 +396,7 @@ void gl_canvas_draw_opaque(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     SET_GLC_RECT(&dest, bbox);
     SET_GLC_RECT(&src, &opaque->src_area);
     glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
 
     set_brush(canvas, &opaque->brush);
     set_op(canvas, opaque->rop_decriptor & ~SPICE_ROPD_INVERS_SRC);
@@ -408,7 +408,7 @@ void gl_canvas_draw_opaque(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
 
 void gl_canvas_draw_alpha_blend(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd *alpha_blend)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     GLCRecti src;
     GLCRecti dest;
     GLCImage image;
@@ -423,13 +423,13 @@ void gl_canvas_draw_alpha_blend(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *cl
     SET_GLC_RECT(&src, &alpha_blend->src_area);
     glc_draw_image(canvas->glc, &dest, &src, &image, 0, (double)alpha_blend->alpha / 0xff);
 
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
     glc_flush(canvas->glc);
 }
 
 void gl_canvas_draw_blend(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
 {
-    cairo_surface_t *surface;
+    pixman_image_t *surface;
     GLCRecti src;
     GLCRecti dest;
     GLCImage image;
@@ -444,14 +444,14 @@ void gl_canvas_draw_blend(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Sp
     surface_to_image(canvas, surface, &image, 0);
     glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
 
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
     glc_flush(canvas->glc);
 }
 
 void gl_canvas_draw_transparent(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent *transparent)
 {
-    cairo_surface_t *surface;
-    cairo_surface_t *trans_surf;
+    pixman_image_t *surface;
+    pixman_image_t *trans_surf;
     GLCImage image;
     GLCRecti src;
     GLCRecti dest;
@@ -464,14 +464,14 @@ void gl_canvas_draw_transparent(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *cl
     surface_to_image(canvas, surface, &image, 0);
 
     trans_surf = canvas_surf_to_trans_surf(&image, transparent->true_color);
-    cairo_surface_destroy(surface);
+    pixman_image_unref(surface);
 
     surface_to_image(canvas, trans_surf, &image, 1);
     SET_GLC_RECT(&dest, bbox);
     SET_GLC_RECT(&src, &transparent->src_area);
     glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
 
-    cairo_surface_destroy(trans_surf);
+    pixman_image_unref(trans_surf);
     glc_flush(canvas->glc);
 }
 
@@ -503,8 +503,8 @@ void gl_canvas_draw_invers(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
 
 void gl_canvas_draw_rop3(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
 {
-    cairo_surface_t *d;
-    cairo_surface_t *s;
+    pixman_image_t *d;
+    pixman_image_t *s;
     GLCImage image;
     SpicePoint src_pos;
     uint8_t *data_opp;
@@ -521,34 +521,33 @@ void gl_canvas_draw_rop3(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
 
     image.pallet = NULL;
 
-    d = cairo_image_surface_create(CAIRO_FORMAT_RGB24, image.width, image.height);
-    if (cairo_surface_status(d) != CAIRO_STATUS_SUCCESS) {
-        CANVAS_ERROR("create surface failed, %s",
-                     cairo_status_to_string(cairo_surface_status(d)));
+    d = pixman_image_create_bits(PIXMAN_x8r8g8b8, image.width, image.height, NULL, 0);
+    if (d == NULL) {
+        CANVAS_ERROR("create surface failed");
     }
-    image.pixels = cairo_image_surface_get_data(d);
-    image.stride = cairo_image_surface_get_stride(d);
+    image.pixels = (uint8_t *)pixman_image_get_data(d);
+    image.stride = pixman_image_get_stride(d);
 
     glc_read_pixels(canvas->glc, bbox->left, bbox->top, &image);
     data_opp = copy_opposite_image(canvas, image.pixels,
-                                   cairo_image_surface_get_stride(d),
-                                   cairo_image_surface_get_height(d));
+                                   image.stride,
+                                   pixman_image_get_height(d));
     memcpy(image.pixels, data_opp,
-           cairo_image_surface_get_stride(d) * cairo_image_surface_get_height(d));
+           image.stride * pixman_image_get_height(d));
 
     s = canvas_get_image(&canvas->base, rop3->src_bitmap);
-    src_stride = cairo_image_surface_get_stride(s);
+    src_stride = pixman_image_get_stride(s);
     if (src_stride > 0) {
-        data_opp = copy_opposite_image(canvas, cairo_image_surface_get_data(s),
-                                       src_stride, cairo_image_surface_get_height(s));
-        memcpy(cairo_image_surface_get_data(s), data_opp,
-               src_stride * cairo_image_surface_get_height(s));
+        data_opp = copy_opposite_image(canvas, (uint8_t *)pixman_image_get_data(s),
+                                       src_stride, pixman_image_get_height(s));
+        memcpy((uint8_t *)pixman_image_get_data(s), data_opp,
+               src_stride * pixman_image_get_height(s));
     }
 
     if (!rect_is_same_size(bbox, &rop3->src_area)) {
-        cairo_surface_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, image.width,
-                                                         image.height, rop3->scale_mode);
-        cairo_surface_destroy(s);
+        pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, image.width,
+                                                        image.height, rop3->scale_mode);
+        pixman_image_unref(s);
         s = scaled_s;
         src_pos.x = 0;
         src_pos.y = 0;
@@ -557,49 +556,49 @@ void gl_canvas_draw_rop3(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
         src_pos.y = rop3->src_area.top;
     }
 
-    if (cairo_image_surface_get_width(s) - src_pos.x < image.width ||
-                                  cairo_image_surface_get_height(s) - src_pos.y < image.height) {
+    if (pixman_image_get_width(s) - src_pos.x < image.width ||
+        pixman_image_get_height(s) - src_pos.y < image.height) {
         CANVAS_ERROR("bad src bitmap size");
     }
 
     if (rop3->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
-        cairo_surface_t *p = canvas_get_image(&canvas->base, rop3->brush.u.pattern.pat);
+        pixman_image_t *p = canvas_get_image(&canvas->base, rop3->brush.u.pattern.pat);
         SpicePoint pat_pos;
 
-        pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % cairo_image_surface_get_width(p);
+        pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % pixman_image_get_width(p);
 
-        pat_pos.y = (bbox->top - rop3->brush.u.pattern.pos.y) % cairo_image_surface_get_height(p);
+        pat_pos.y = (bbox->top - rop3->brush.u.pattern.pos.y) % pixman_image_get_height(p);
 
         //for now (bottom-top)
         if (pat_pos.y < 0) {
-            pat_pos.y = cairo_image_surface_get_height(p) + pat_pos.y;
+            pat_pos.y = pixman_image_get_height(p) + pat_pos.y;
         }
-        pat_pos.y = (image.height + pat_pos.y) % cairo_image_surface_get_height(p);
-        pat_pos.y = cairo_image_surface_get_height(p) - pat_pos.y;
+        pat_pos.y = (image.height + pat_pos.y) % pixman_image_get_height(p);
+        pat_pos.y = pixman_image_get_height(p) - pat_pos.y;
 
         do_rop3_with_pattern(rop3->rop3, d, s, &src_pos, p, &pat_pos);
-        cairo_surface_destroy(p);
+        pixman_image_unref(p);
     } else {
         uint32_t color = (canvas->base.color_shift) == 8 ? rop3->brush.u.color :
                                                          canvas_16bpp_to_32bpp(rop3->brush.u.color);
         do_rop3_with_color(rop3->rop3, d, s, &src_pos, color);
     }
 
-    cairo_surface_destroy(s);
+    pixman_image_unref(s);
 
     GLCRecti dest;
     GLCRecti src;
     dest.x = bbox->left;
     dest.y = bbox->top;
 
-    image.pixels = copy_opposite_image(canvas, image.pixels, cairo_image_surface_get_stride(d),
-                                       cairo_image_surface_get_height(d));
+    image.pixels = copy_opposite_image(canvas, image.pixels, pixman_image_get_stride(d),
+                                       pixman_image_get_height(d));
 
     src.x = src.y = 0;
     dest.width = src.width = image.width;
     dest.height = src.height = image.height;
     glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
-    cairo_surface_destroy(d);
+    pixman_image_unref(d);
 }
 
 void gl_canvas_draw_stroke(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
@@ -641,34 +640,34 @@ void gl_canvas_draw_text(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     set_op(canvas, text->fore_mode);
     if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) {
         SpicePoint pos;
-        cairo_surface_t *mask = canvas_get_str_mask(&canvas->base, str, 1, &pos);
+        pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 1, &pos);
         _glc_fill_mask(canvas->glc, pos.x, pos.y,
-                       cairo_image_surface_get_width(mask),
-                       cairo_image_surface_get_height(mask),
-                       cairo_image_surface_get_stride(mask),
-                       cairo_image_surface_get_data(mask));
-        cairo_surface_destroy(mask);
+                       pixman_image_get_width(mask),
+                       pixman_image_get_height(mask),
+                       pixman_image_get_stride(mask),
+                       (uint8_t *)pixman_image_get_data(mask));
+        pixman_image_unref(mask);
     } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) {
         SpicePoint pos;
-        cairo_surface_t *mask = canvas_get_str_mask(&canvas->base, str, 4, &pos);
+        pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 4, &pos);
         glc_fill_alpha(canvas->glc, pos.x, pos.y,
-                       cairo_image_surface_get_width(mask),
-                       cairo_image_surface_get_height(mask),
-                       cairo_image_surface_get_stride(mask),
-                       cairo_image_surface_get_data(mask));
+                       pixman_image_get_width(mask),
+                       pixman_image_get_height(mask),
+                       pixman_image_get_stride(mask),
+                       (uint8_t *)pixman_image_get_data(mask));
 
-        cairo_surface_destroy(mask);
+        pixman_image_unref(mask);
     } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A8) {
         WARN("untested path A8 glyphs, doing nothing");
         if (0) {
             SpicePoint pos;
-            cairo_surface_t *mask = canvas_get_str_mask(&canvas->base, str, 8, &pos);
+            pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 8, &pos);
             glc_fill_alpha(canvas->glc, pos.x, pos.y,
-                           cairo_image_surface_get_width(mask),
-                           cairo_image_surface_get_height(mask),
-                           cairo_image_surface_get_stride(mask),
-                           cairo_image_surface_get_data(mask));
-            cairo_surface_destroy(mask);
+                           pixman_image_get_width(mask),
+                           pixman_image_get_height(mask),
+                           pixman_image_get_stride(mask),
+                           (uint8_t *)pixman_image_get_data(mask));
+            pixman_image_unref(mask);
         }
     } else {
         WARN("untested path vector glyphs, doing nothing");
diff --git a/common/rop3.c b/common/rop3.c
index 64baba8..014109f 100644
--- a/common/rop3.c
+++ b/common/rop3.c
@@ -24,11 +24,11 @@
 #define WARN(x) printf("warning: %s\n", x)
 #endif
 
-typedef void (*rop3_with_pattern_handler_t)(cairo_surface_t *d, cairo_surface_t *s,
-                                            SpicePoint *src_pos, cairo_surface_t *p,
+typedef void (*rop3_with_pattern_handler_t)(pixman_image_t *d, pixman_image_t *s,
+                                            SpicePoint *src_pos, pixman_image_t *p,
                                             SpicePoint *pat_pos);
 
-typedef void (*rop3_with_color_handler_t)(cairo_surface_t *d, cairo_surface_t *s,
+typedef void (*rop3_with_color_handler_t)(pixman_image_t *d, pixman_image_t *s,
                                           SpicePoint *src_pos, uint32_t rgb);
 
 typedef void (*rop3_test_handler_t)();
@@ -40,14 +40,14 @@ static rop3_with_color_handler_t rop3_with_color_handlers[ROP3_NUM_OPS];
 static rop3_test_handler_t rop3_test_handlers[ROP3_NUM_OPS];
 
 
-static void default_rop3_with_pattern_handler(cairo_surface_t *d, cairo_surface_t *s,
-                                              SpicePoint *src_pos, cairo_surface_t *p,
+static void default_rop3_with_pattern_handler(pixman_image_t *d, pixman_image_t *s,
+                                              SpicePoint *src_pos, pixman_image_t *p,
                                               SpicePoint *pat_pos)
 {
     WARN("not implemented 0x%x");
 }
 
-static void default_rop3_withe_color_handler(cairo_surface_t *d, cairo_surface_t *s, SpicePoint *src_pos,
+static void default_rop3_withe_color_handler(pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
                                              uint32_t rgb)
 {
     WARN("not implemented 0x%x");
@@ -58,24 +58,24 @@ static void default_rop3_test_handler()
 }
 
 #define ROP3_HANDLERS(name, formula, index)                                                     \
-static void rop3_handle_p_##name(cairo_surface_t *d, cairo_surface_t *s, SpicePoint *src_pos,        \
-                                      cairo_surface_t *p, SpicePoint *pat_pos)                       \
+static void rop3_handle_p_##name(pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,     \
+                                      pixman_image_t *p, SpicePoint *pat_pos)                   \
 {                                                                                               \
-    int width = cairo_image_surface_get_width(d);                                               \
-    int height = cairo_image_surface_get_height(d);                                             \
-    uint8_t *dest_line = cairo_image_surface_get_data(d);                                       \
-    int dest_stride = cairo_image_surface_get_stride(d);                                        \
+    int width = pixman_image_get_width(d);                                                      \
+    int height = pixman_image_get_height(d);                                                    \
+    uint8_t *dest_line = (uint8_t *)pixman_image_get_data(d);		                        \
+    int dest_stride = pixman_image_get_stride(d);                                               \
     uint8_t *end_line = dest_line + height * dest_stride;                                       \
                                                                                                 \
-    int pat_width = cairo_image_surface_get_width(p);                                           \
-    int pat_height = cairo_image_surface_get_height(p);                                         \
-    uint8_t *pat_base = cairo_image_surface_get_data(p);                                        \
-    int pat_stride = cairo_image_surface_get_stride(p);                                         \
+    int pat_width = pixman_image_get_width(p);                                                  \
+    int pat_height = pixman_image_get_height(p);                                                \
+    uint8_t *pat_base = (uint8_t *)pixman_image_get_data(p);                                    \
+    int pat_stride = pixman_image_get_stride(p);                                                \
     int pat_v_offset = pat_pos->y;                                                              \
                                                                                                 \
-    int src_stride = cairo_image_surface_get_stride(s);                                         \
+    int src_stride = pixman_image_get_stride(s);                                                \
     uint8_t *src_line;                                                                          \
-    src_line = cairo_image_surface_get_data(s) + src_pos->y * src_stride + (src_pos->x << 2);   \
+    src_line = (uint8_t *)pixman_image_get_data(s) + src_pos->y * src_stride + (src_pos->x << 2); \
                                                                                                 \
     for (; dest_line < end_line; dest_line += dest_stride, src_line += src_stride) {            \
         uint32_t *dest = (uint32_t *)dest_line;                                                 \
@@ -95,19 +95,19 @@ static void rop3_handle_p_##name(cairo_surface_t *d, cairo_surface_t *s, SpicePo
     }                                                                                           \
 }                                                                                               \
                                                                                                 \
-static void rop3_handle_c_##name(cairo_surface_t *d, cairo_surface_t *s, SpicePoint *src_pos,        \
+static void rop3_handle_c_##name(pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,     \
                                    uint32_t rgb)                                                \
 {                                                                                               \
-    int width = cairo_image_surface_get_width(d);                                               \
-    int height = cairo_image_surface_get_height(d);                                             \
-    uint8_t *dest_line = cairo_image_surface_get_data(d);                                       \
-    int dest_stride = cairo_image_surface_get_stride(d);                                        \
+    int width = pixman_image_get_width(d);                                                      \
+    int height = pixman_image_get_height(d);                                                    \
+    uint8_t *dest_line = (uint8_t *)pixman_image_get_data(d);                                   \
+    int dest_stride = pixman_image_get_stride(d);                                               \
     uint8_t *end_line = dest_line + height * dest_stride;                                       \
     uint32_t *pat = &rgb;                                                                       \
                                                                                                 \
-    int src_stride = cairo_image_surface_get_stride(s);                                         \
+    int src_stride = pixman_image_get_stride(s);                                                \
     uint8_t *src_line;                                                                          \
-    src_line = cairo_image_surface_get_data(s) + src_pos->y * src_stride + (src_pos->x << 2);   \
+    src_line = (uint8_t *)pixman_image_get_data(s) + src_pos->y * src_stride + (src_pos->x << 2); \
                                                                                                 \
     for (; dest_line < end_line; dest_line += dest_stride, src_line += src_stride) {            \
         uint32_t *dest = (uint32_t *)dest_line;                                                 \
@@ -600,13 +600,13 @@ void rop3_init()
     }
 }
 
-void do_rop3_with_pattern(uint8_t rop3, cairo_surface_t *d, cairo_surface_t *s, SpicePoint *src_pos,
-                          cairo_surface_t *p, SpicePoint *pat_pos)
+void do_rop3_with_pattern(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
+                          pixman_image_t *p, SpicePoint *pat_pos)
 {
     rop3_with_pattern_handlers[rop3](d, s, src_pos, p, pat_pos);
 }
 
-void do_rop3_with_color(uint8_t rop3, cairo_surface_t *d, cairo_surface_t *s, SpicePoint *src_pos,
+void do_rop3_with_color(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
                         uint32_t rgb)
 {
     rop3_with_color_handlers[rop3](d, s, src_pos, rgb);
diff --git a/common/rop3.h b/common/rop3.h
index 32a6203..832ac51 100644
--- a/common/rop3.h
+++ b/common/rop3.h
@@ -22,11 +22,11 @@
 #include <stdint.h>
 
 #include <spice/draw.h>
-#include "cairo.h"
+#include "pixman_utils.h"
 
-void do_rop3_with_pattern(uint8_t rop3, cairo_surface_t *d, cairo_surface_t *s, SpicePoint *src_pos,
-                          cairo_surface_t *p, SpicePoint *pat_pos);
-void do_rop3_with_color(uint8_t rop3, cairo_surface_t *d, cairo_surface_t *s, SpicePoint *src_pos,
+void do_rop3_with_pattern(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
+                          pixman_image_t *p, SpicePoint *pat_pos);
+void do_rop3_with_color(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
                         uint32_t rgb);
 
 void rop3_init();
diff --git a/server/red_worker.c b/server/red_worker.c
index 85964ce..b2cb1f6 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -722,7 +722,7 @@ typedef struct ImageCacheItem {
     uint32_t age;
 #endif
     struct ImageCacheItem *next;
-    cairo_surface_t *surf;
+    pixman_image_t *image;
 } ImageCacheItem;
 
 #define IMAGE_CACHE_HASH_SIZE 1024
@@ -3890,7 +3890,7 @@ static void image_cache_remove(ImageCache *cache, ImageCacheItem *item)
         now = &(*now)->next;
     }
     ring_remove(&item->lru_link);
-    cairo_surface_destroy(item->surf);
+    pixman_image_unref(item->image);
     free(item);
 #ifndef IMAGE_CACHE_AGE
     cache->num_items--;
@@ -3899,7 +3899,7 @@ static void image_cache_remove(ImageCache *cache, ImageCacheItem *item)
 
 #define IMAGE_CACHE_MAX_ITEMS 2
 
-static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, cairo_surface_t *surface)
+static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, pixman_image_t *image)
 {
     ImageCache *cache = (ImageCache *)spice_cache;
     ImageCacheItem *item;
@@ -3921,7 +3921,7 @@ static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, cairo_sur
 #else
     cache->num_items++;
 #endif
-    item->surf = cairo_surface_reference(surface);
+    item->image = pixman_image_ref(image);
     ring_item_init(&item->lru_link);
 
     item->next = cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
@@ -3930,7 +3930,7 @@ static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, cairo_sur
     ring_add(&cache->lru, &item->lru_link);
 }
 
-static cairo_surface_t *image_cache_get(SpiceImageCache *spice_cache, uint64_t id)
+static pixman_image_t *image_cache_get(SpiceImageCache *spice_cache, uint64_t id)
 {
     ImageCache *cache = (ImageCache *)spice_cache;
 
@@ -3938,7 +3938,7 @@ static cairo_surface_t *image_cache_get(SpiceImageCache *spice_cache, uint64_t i
     if (!item) {
         red_error("not found");
     }
-    return cairo_surface_reference(item->surf);
+    return pixman_image_ref(item->image);
 }
 
 static void image_cache_init(ImageCache *cache)
commit 0b0342ee7ece8ea5a811cfb05c70f03ca4e3bde3
Author: Alexander Larsson <alexl at redhat.com>
Date:   Mon Feb 8 15:38:24 2010 +0100

    Turn image and palette cache into c style dynamic interface
    
    Instead of passing a bunch of function pointer and an opaque
    pointer we make a real type and add a vtable pointer to it.
    This means we can simplify all the canvas constructors, etc.

diff --git a/client/canvas.cpp b/client/canvas.cpp
index 04b0ce6..24ebd01 100644
--- a/client/canvas.cpp
+++ b/client/canvas.cpp
@@ -213,36 +213,6 @@ void Canvas::draw_stroke(SpiceMsgDisplayDrawStroke& stroke, int size)
     draw_stroke(&stroke.base.box, &stroke.base.clip, &stroke.data);
 }
 
-void Canvas::bits_cache_put(void *opaque, uint64_t id, cairo_surface_t *surface)
-{
-    PixmapCache* cache = static_cast<PixmapCache*>(opaque);
-    cache->add(id, surface);
-}
-
-cairo_surface_t* Canvas::bits_cache_get(void *opaque, uint64_t id)
-{
-    PixmapCache* cache = static_cast<PixmapCache*>(opaque);
-    return cache->get(id);
-}
-
-void Canvas::palette_cache_put(void *opaque, SpicePalette *palette)
-{
-    PaletteCache* cache = static_cast<PaletteCache*>(opaque);
-    AutoRef<CachedPalette> cached_palette(new CachedPalette(palette));
-    cache->add(palette->unique, *cached_palette);
-}
-
-SpicePalette* Canvas::palette_cache_get(void *opaque, uint64_t id)
-{
-    PaletteCache* cache = static_cast<PaletteCache*>(opaque);
-    return cache->get(id)->palette();
-}
-
-void Canvas::palette_cache_release(SpicePalette* palette)
-{
-    CachedPalette::unref(palette);
-}
-
 void Canvas::glz_decode(void *opaque, uint8_t *data, SpicePalette *plt, void *usr_data)
 {
     GlzDecoder* decoder = static_cast<GlzDecoder*>(opaque);
diff --git a/client/canvas.h b/client/canvas.h
index 84a654a..3074d0d 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -24,6 +24,7 @@
 #include <spice/protocol.h>
 #include "cache.hpp"
 #include "shared_cache.hpp"
+#include "canvas_base.h"
 #include "canvas_utils.h"
 #include "glz_decoded_image.h"
 #include "glz_decoder.h"
@@ -52,7 +53,35 @@ public:
     static const char* name() { return "pixmap";}
 };
 
-typedef SharedCache<cairo_surface_t, PixmapCacheTreat, 1024> PixmapCache;
+class SpiceImageCacheBase;
+
+typedef SharedCache<cairo_surface_t, PixmapCacheTreat, 1024, SpiceImageCacheBase> PixmapCache;
+
+class SpiceImageCacheBase {
+public:
+    SpiceImageCache base;
+
+    static void op_put(SpiceImageCache *c, uint64_t id, cairo_surface_t *surface)
+    {
+        PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
+        cache->add(id, surface);
+    }
+
+    static cairo_surface_t* op_get(SpiceImageCache *c, uint64_t id)
+    {
+        PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
+        return cache->get(id);
+    }
+
+    SpiceImageCacheBase()
+    {
+        static SpiceImageCacheOps cache_ops = {
+            op_put,
+            op_get
+        };
+        base.ops = &cache_ops;
+    }
+};
 
 class CachedPalette {
 public:
@@ -113,7 +142,43 @@ public:
     static const char* name() { return "palette";}
 };
 
-typedef Cache<CachedPalette, PaletteCacheTreat, 1024> PaletteCache;
+class SpicePaletteCacheBase;
+typedef Cache<CachedPalette, PaletteCacheTreat, 1024, SpicePaletteCacheBase> PaletteCache;
+
+class SpicePaletteCacheBase {
+public:
+    SpicePaletteCache base;
+
+    static void op_put(SpicePaletteCache *c, SpicePalette *palette)
+    {
+        PaletteCache* cache = reinterpret_cast<PaletteCache*>(c);
+        AutoRef<CachedPalette> cached_palette(new CachedPalette(palette));
+        cache->add(palette->unique, *cached_palette);
+    }
+
+    static SpicePalette* op_get(SpicePaletteCache *c, uint64_t id)
+    {
+        PaletteCache* cache = reinterpret_cast<PaletteCache*>(c);
+        return cache->get(id)->palette();
+    }
+
+    static void op_release (SpicePaletteCache *c,
+                            SpicePalette *palette)
+    {
+        CachedPalette::unref(palette);
+    }
+
+    SpicePaletteCacheBase()
+    {
+        static SpicePaletteCacheOps cache_ops = {
+            op_put,
+            op_get,
+            op_release
+        };
+        base.ops = &cache_ops;
+    }
+};
+
 
 /* Lz decoder related classes */
 
@@ -230,11 +295,6 @@ protected:
 
     PixmapCache& pixmap_cache() { return _pixmap_cache;}
     PaletteCache& palette_cache() { return _palette_cache;}
-    static void bits_cache_put(void *opaque, uint64_t id, cairo_surface_t *surface);
-    static cairo_surface_t* bits_cache_get(void *opaque, uint64_t id);
-    static void palette_cache_put(void *opaque, SpicePalette *palette);
-    static SpicePalette* palette_cache_get(void *opaque, uint64_t id);
-    static void palette_cache_release(SpicePalette* palette);
 
     GlzDecoder& glz_decoder() {return _glz_decoder;}
     static void glz_decode(void *opaque, uint8_t *data, SpicePalette *plt, void *usr_data);
diff --git a/client/red_cairo_canvas.cpp b/client/red_cairo_canvas.cpp
index 08880ea..80a89b7 100644
--- a/client/red_cairo_canvas.cpp
+++ b/client/red_cairo_canvas.cpp
@@ -99,9 +99,8 @@ void CCanvas::set_mode(int width, int height, int depth, RedWindow *win)
         THROW("create cairo failed, %s", cairo_status_to_string(cairo_status(cairo)));
     }
     if (!(_canvas = canvas_create(cairo, depth,
-                                  &pixmap_cache(), bits_cache_put, bits_cache_get,
-                                  &palette_cache(), palette_cache_put, palette_cache_get,
-                                  palette_cache_release,
+                                  &pixmap_cache().base,
+                                  &palette_cache().base,
                                   &glz_decoder(),
                                   glz_decode))) {
         THROW("create canvas failed");
diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp
index 8e50a06..e30845e 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -81,10 +81,8 @@ void GDICanvas::set_mode(int width, int height, int depth)
     create_pixmap(width, height);
     if (!(_canvas = gdi_canvas_create(_pixmap->get_dc(),
                                       &_pixmap->get_mutex(),
-                                      depth, &pixmap_cache(), bits_cache_put,
-                                      bits_cache_get, &palette_cache(),
-                                      palette_cache_put, palette_cache_get,
-                                      palette_cache_release,
+                                      depth, &pixmap_cache().base,
+                                      &palette_cache().base,
                                       &glz_decoder(),
                                       glz_decode))) {
         THROW("create canvas failed");
diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp
index 830de03..43bf424 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -87,13 +87,8 @@ void GCanvas::set_mode(int width, int height, int depth, RedWindow *win,
 
     create_pixmap(width, height, win, rendertype);
     if (!(_canvas = gl_canvas_create(NULL, width, height, depth,
-                                     &pixmap_cache(),
-                                     bits_cache_put,
-                                     bits_cache_get,
-                                     &palette_cache(),
-                                     palette_cache_put,
-                                     palette_cache_get,
-                                     palette_cache_release,
+                                     &pixmap_cache().base,
+                                     &palette_cache().base,
                                      &glz_decoder(),
                                      glz_decode))) {
         THROW("create canvas failed");
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index c40693b..4b8c305 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -438,7 +438,7 @@ static cairo_surface_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRE
     }
 #endif
     case SPICE_IMAGE_TYPE_FROM_CACHE:
-        surface = canvas->base.bits_cache_get(canvas->base.bits_cache_opaque, descriptor->id);
+        surface = canvas->base.bits_cache->ops->get(canvas->base.bits_cache, descriptor->id);
         break;
     case SPICE_IMAGE_TYPE_BITMAP: {
         SpiceBitmapImage *bitmap = (SpiceBitmapImage *)descriptor;
@@ -465,7 +465,7 @@ static cairo_surface_t *canvas_get_invers_image(CairoCanvas *canvas, SPICE_ADDRE
     }
 
     if (cache_me) {
-        canvas->base.bits_cache_put(canvas->base.bits_cache_opaque, descriptor->id, surface);
+        canvas->base.bits_cache->ops->put(canvas->base.bits_cache, descriptor->id, surface);
     }
 
     invers = canvas_handle_inverse_user_data(surface);
@@ -1596,15 +1596,11 @@ static int need_init = 1;
 
 #ifdef CAIRO_CANVAS_CACHE
 CairoCanvas *canvas_create(cairo_t *cairo, int bits,
-                           void *bits_cache_opaque,
-                           bits_cache_put_fn_t bits_cache_put, bits_cache_get_fn_t bits_cache_get,
-                           void *palette_cache_opaque, palette_cache_put_fn_t palette_cache_put,
-                           palette_cache_get_fn_t palette_cache_get,
-                           palette_cache_release_fn_t palette_cache_release
+                           SpiceImageCache *bits_cache,
+                           SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
 CairoCanvas *canvas_create(cairo_t *cairo, int bits,
-                           void *bits_cache_opaque,
-                           bits_cache_put_fn_t bits_cache_put, bits_cache_get_fn_t bits_cache_get
+                           SpiceImageCache *bits_cache
 #else
 CairoCanvas *canvas_create(cairo_t *cairo, int bits
 #endif
@@ -1626,18 +1622,11 @@ CairoCanvas *canvas_create(cairo_t *cairo, int bits
     memset(canvas, 0, sizeof(CairoCanvas));
 #ifdef CAIRO_CANVAS_CACHE
     init_ok = canvas_base_init(&canvas->base, bits,
-                               bits_cache_opaque,
-                               bits_cache_put,
-                               bits_cache_get,
-                               palette_cache_opaque,
-                               palette_cache_put,
-                               palette_cache_get,
-                               palette_cache_release
+                               bits_cache,
+                               palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
     init_ok = canvas_base_init(&canvas->base, bits,
-                               bits_cache_opaque,
-                               bits_cache_put,
-                               bits_cache_get
+                               bits_cache
 #else
     init_ok = canvas_base_init(&canvas->base, bits
 #endif
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
index bf6ff90..b0c0820 100644
--- a/common/cairo_canvas.h
+++ b/common/cairo_canvas.h
@@ -62,14 +62,12 @@ void canvas_set_access_params(CairoCanvas *canvas, unsigned long base, unsigned
 cairo_t *canvas_get_cairo(CairoCanvas *canvas);
 
 #ifdef CAIRO_CANVAS_CACHE
-CairoCanvas *canvas_create(cairo_t *cairo, int bits, void *bits_cache_opaque,
-                           bits_cache_put_fn_t bits_cache_put, bits_cache_get_fn_t bits_cache_get,
-                           void *palette_cache_opaque, palette_cache_put_fn_t palette_cache_put,
-                           palette_cache_get_fn_t palette_cache_get,
-                           palette_cache_release_fn_t palette_cache_release
+CairoCanvas *canvas_create(cairo_t *cairo, int bits,
+                           SpiceImageCache *bits_cache,
+                           SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
-CairoCanvas *canvas_create(cairo_t *cairo, int bits, void *bits_cache_opaque,
-                           bits_cache_put_fn_t bits_cache_put, bits_cache_get_fn_t bits_cache_get
+CairoCanvas *canvas_create(cairo_t *cairo, int bits,
+                           SpiceImageCache *bits_cache
 #else
 CairoCanvas *canvas_create(cairo_t *cairo, int bits
 #endif
diff --git a/common/canvas_base.c b/common/canvas_base.c
index ebb9846..0ddbaf4 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -171,15 +171,10 @@ typedef struct CanvasBase {
 #endif
 
 #if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
-    void *bits_cache_opaque;
-    bits_cache_put_fn_t bits_cache_put;
-    bits_cache_get_fn_t bits_cache_get;
+    SpiceImageCache *bits_cache;
 #endif
 #ifdef CAIRO_CANVAS_CACHE
-    void *palette_cache_opaque;
-    palette_cache_put_fn_t palette_cache_put;
-    palette_cache_get_fn_t palette_cache_get;
-    palette_cache_release_fn_t palette_cache_release;
+    SpicePaletteCache *palette_cache;
 #endif
 #ifdef WIN32
     HDC dc;
@@ -535,13 +530,13 @@ static inline SpicePalette *canvas_get_palett(CanvasBase *canvas, SPICE_ADDRESS
     }
 
     if (flags & SPICE_BITMAP_FLAGS_PAL_FROM_CACHE) {
-        palette = canvas->palette_cache_get(canvas->palette_cache_opaque, base_palette);
+        palette = canvas->palette_cache->ops->get(canvas->palette_cache, base_palette);
     } else if (flags & SPICE_BITMAP_FLAGS_PAL_CACHE_ME) {
         palette = (SpicePalette *)SPICE_GET_ADDRESS(base_palette);
         access_test(canvas, palette, sizeof(SpicePalette));
         access_test(canvas, palette, sizeof(SpicePalette) + palette->num_ents * sizeof(uint32_t));
         canvas_localize_palette(canvas, palette);
-        canvas->palette_cache_put(canvas->palette_cache_opaque, palette);
+        canvas->palette_cache->ops->put(canvas->palette_cache, palette);
     } else {
         palette = (SpicePalette *)SPICE_GET_ADDRESS(base_palette);
         canvas_localize_palette(canvas, palette);
@@ -716,7 +711,7 @@ static cairo_surface_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap)
     surface = canvas_bitmap_to_surface(canvas, bitmap, palette);
 
     if (palette && (bitmap->flags & SPICE_BITMAP_FLAGS_PAL_FROM_CACHE)) {
-        canvas->palette_cache_release(palette);
+        canvas->palette_cache->ops->release(canvas->palette_cache, palette);
     }
 
     return surface;
@@ -851,7 +846,7 @@ static cairo_surface_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
     }
 #endif
     case SPICE_IMAGE_TYPE_FROM_CACHE:
-        return canvas->bits_cache_get(canvas->bits_cache_opaque, descriptor->id);
+        return canvas->bits_cache->ops->get(canvas->bits_cache, descriptor->id);
     case SPICE_IMAGE_TYPE_BITMAP: {
         SpiceBitmapImage *bitmap = (SpiceBitmapImage *)descriptor;
         access_test(canvas, descriptor, sizeof(SpiceBitmapImage));
@@ -863,7 +858,7 @@ static cairo_surface_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
     }
 
     if (descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_ME) {
-        canvas->bits_cache_put(canvas->bits_cache_opaque, descriptor->id, surface);
+        canvas->bits_cache->ops->put(canvas->bits_cache, descriptor->id, surface);
 #ifdef DEBUG_DUMP_SURFACE
         dump_surface(surface, 1);
 #endif
@@ -1163,7 +1158,7 @@ static cairo_surface_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask)
     }
 #if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
     case SPICE_IMAGE_TYPE_FROM_CACHE:
-        surface = canvas->bits_cache_get(canvas->bits_cache_opaque, descriptor->id);
+        surface = canvas->bits_cache->ops->get(canvas->bits_cache, descriptor->id);
         is_invers = 0;
         break;
 #endif
@@ -1173,7 +1168,7 @@ static cairo_surface_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask)
 
 #if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
     if (cache_me) {
-        canvas->bits_cache_put(canvas->bits_cache_opaque, descriptor->id, surface);
+        canvas->bits_cache->ops->put(canvas->bits_cache, descriptor->id, surface);
     }
 
     if (need_invers && !is_invers) { // surface is in cache
@@ -1570,18 +1565,11 @@ static void canvas_base_destroy(CanvasBase *canvas)
 
 #ifdef CAIRO_CANVAS_CACHE
 static int canvas_base_init(CanvasBase *canvas, int depth,
-                            void *bits_cache_opaque,
-                            bits_cache_put_fn_t bits_cache_put,
-                            bits_cache_get_fn_t bits_cache_get,
-                            void *palette_cache_opaque,
-                            palette_cache_put_fn_t palette_cache_put,
-                            palette_cache_get_fn_t palette_cache_get,
-                            palette_cache_release_fn_t palette_cache_release
+                            SpiceImageCache *bits_cache,
+                            SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
 static int canvas_base_init(CanvasBase *canvas, int depth,
-                            void *bits_cache_opaque,
-                            bits_cache_put_fn_t bits_cache_put,
-                            bits_cache_get_fn_t bits_cache_get
+                            SpiceImageCache *bits_cache
 #else
 static int canvas_base_init(CanvasBase *canvas, int depth
 #endif
@@ -1637,15 +1625,10 @@ static int canvas_base_init(CanvasBase *canvas, int depth
 
 
 #if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
-    canvas->bits_cache_opaque = bits_cache_opaque;
-    canvas->bits_cache_put = bits_cache_put;
-    canvas->bits_cache_get = bits_cache_get;
+    canvas->bits_cache = bits_cache;
 #endif
 #ifdef CAIRO_CANVAS_CACHE
-    canvas->palette_cache_opaque = palette_cache_opaque;
-    canvas->palette_cache_put = palette_cache_put;
-    canvas->palette_cache_get = palette_cache_get;
-    canvas->palette_cache_release = palette_cache_release;
+    canvas->palette_cache = palette_cache;
 #endif
 
 #ifdef WIN32
diff --git a/common/canvas_base.h b/common/canvas_base.h
index 15cf869..cc75087 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -24,15 +24,33 @@
 #include "lz.h"
 #include <spice/draw.h>
 
-#if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
-typedef void (*bits_cache_put_fn_t)(void *bits_cache_opaque, uint64_t id, cairo_surface_t *surface);
-typedef cairo_surface_t *(*bits_cache_get_fn_t)(void *bits_cache_opaque, uint64_t id);
-#endif
-#ifdef CAIRO_CANVAS_CACHE
-typedef void (*palette_cache_put_fn_t)(void *palette_cache_opaque, SpicePalette *palette);
-typedef SpicePalette *(*palette_cache_get_fn_t)(void *palette_cache_opaque, uint64_t id);
-typedef void (*palette_cache_release_fn_t)(SpicePalette *palette);
-#endif
+typedef struct _SpiceImageCache SpiceImageCache;
+typedef struct _SpicePaletteCache SpicePaletteCache;
+
+typedef struct {
+    void (*put)(SpiceImageCache *cache,
+                uint64_t id,
+                cairo_surface_t *surface);
+    cairo_surface_t *(*get)(SpiceImageCache *cache,
+                            uint64_t id);
+} SpiceImageCacheOps;
+
+struct _SpiceImageCache {
+  SpiceImageCacheOps *ops;
+};
+
+typedef struct {
+    void (*put)(SpicePaletteCache *cache,
+                SpicePalette *palette);
+    SpicePalette *(*get)(SpicePaletteCache *cache,
+                         uint64_t id);
+    void (*release)(SpicePaletteCache *cache,
+                    SpicePalette *palette);
+} SpicePaletteCacheOps;
+
+struct _SpicePaletteCache {
+  SpicePaletteCacheOps *ops;
+};
 
 typedef void (*glz_decode_fn_t)(void *glz_decoder_opaque, uint8_t *data,
                                 SpicePalette *plt, void *usr_data);
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index 9e29cb8..bc9f8b2 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -1696,14 +1696,11 @@ static int need_init = 1;
 
 #ifdef CAIRO_CANVAS_CACHE
 GdiCanvas *gdi_canvas_create(HDC dc, Mutex* lock, int bits, void *bits_cache_opaque,
-                             bits_cache_put_fn_t bits_cache_put, bits_cache_get_fn_t bits_cache_get,
-                             void *palette_cache_opaque, palette_cache_put_fn_t palette_cache_put,
-                             palette_cache_get_fn_t palette_cache_get,
-                             palette_cache_release_fn_t palette_cache_release
+                             SpiceImageCache *bits_cache,
+                             SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
 GdiCanvas *gdi_canvas_create(HDC dc, int bits,
-                             void *bits_cache_opaque,
-                             bits_cache_put_fn_t bits_cache_put, bits_cache_get_fn_t bits_cache_get
+                             SpiceImageCache *bits_cache
 #else
 GdiCanvas *gdi_canvas_create(HDC dc, int bits
 #endif
@@ -1721,18 +1718,11 @@ GdiCanvas *gdi_canvas_create(HDC dc, int bits
     memset(canvas, 0, sizeof(GdiCanvas));
 #ifdef CAIRO_CANVAS_CACHE
     init_ok = canvas_base_init(&canvas->base, bits,
-                               bits_cache_opaque,
-                               bits_cache_put,
-                               bits_cache_get,
-                               palette_cache_opaque,
-                               palette_cache_put,
-                               palette_cache_get,
-                               palette_cache_release
+                               bits_cache,
+                               palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
     init_ok = gdi_canvas_base_init(&canvas->base, bits,
-                                   bits_cache_opaque,
-                                   bits_cache_put,
-                                   bits_cache_get
+                                   bits_cache
 #else
     init_ok = gdi_canvas_base_init(&canvas->base, bits
 #endif
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
index 606e71e..18a4843 100644
--- a/common/gdi_canvas.h
+++ b/common/gdi_canvas.h
@@ -60,10 +60,8 @@ void gdi_canvas_set_access_params(GdiCanvas *canvas, unsigned long base, unsigne
 
 
 GdiCanvas *gdi_canvas_create(HDC dc, class Mutex *lock, int bits, void *bits_cache_opaque,
-                             bits_cache_put_fn_t bits_cache_put, bits_cache_get_fn_t bits_cache_get,
-                             void *palette_cache_opaque, palette_cache_put_fn_t palette_cache_put,
-                             palette_cache_get_fn_t palette_cache_get,
-                             palette_cache_release_fn_t palette_cache_release,
+                             SpiceImageCache *bits_cache,
+                             SpicePaletteCache *palette_cache
                              void *glz_decoder_opaque,
                              glz_decode_fn_t glz_decode);
 
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index 124bcfe..329d97b 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -787,18 +787,11 @@ static int need_init = 1;
 
 #ifdef CAIRO_CANVAS_CACHE
 GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
-                           void *bits_cache_opaque,
-                           bits_cache_put_fn_t bits_cache_put,
-                           bits_cache_get_fn_t bits_cache_get,
-                           void *palette_cache_opaque,
-                           palette_cache_put_fn_t palette_cache_put,
-                           palette_cache_get_fn_t palette_cache_get,
-                           palette_cache_release_fn_t palette_cache_release
+                           SpiceImageCache *bits_cache,
+                           SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
 GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
-                           void *bits_cache_opaque,
-                           bits_cache_put_fn_t bits_cache_put,
-                           bits_cache_get_fn_t bits_cache_get
+                           SpiceImageCache *bits_cache
 #else
 GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #endif
@@ -826,18 +819,11 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
     canvas->private_data = NULL;
 #ifdef CAIRO_CANVAS_CACHE
     init_ok = canvas_base_init(&canvas->base, depth,
-                               bits_cache_opaque,
-                               bits_cache_put,
-                               bits_cache_get,
-                               palette_cache_opaque,
-                               palette_cache_put,
-                               palette_cache_get,
-                               palette_cache_release
+                               bits_cache,
+                               palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
     init_ok = canvas_base_init(&canvas->base, depth,
-                               bits_cache_opaque,
-                               bits_cache_put,
-                               bits_cache_get
+                               bits_cache
 #else
     init_ok = canvas_base_init(&canvas->base, depth
 #endif
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index 74c8913..794b1ea 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -56,18 +56,11 @@ void *gl_canvas_get_usr_data(GLCanvas *canvas);
 
 #ifdef CAIRO_CANVAS_CACHE
 GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
-                           void *bits_cache_opaque,
-                           bits_cache_put_fn_t bits_cache_put,
-                           bits_cache_get_fn_t bits_cache_get,
-                           void *palette_cache_opaque,
-                           palette_cache_put_fn_t palette_cache_put,
-                           palette_cache_get_fn_t palette_cache_get,
-                           palette_cache_release_fn_t palette_cache_release
+                           SpiceImageCache *bits_cache,
+                           SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
 GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
-                           void *bits_cache_opaque,
-                           bits_cache_put_fn_t bits_cache_put,
-                           bits_cache_get_fn_t bits_cache_get
+                           SpiceImageCache *bits_cache
 #else
 GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #endif
diff --git a/server/red_worker.c b/server/red_worker.c
index 2b50b9f..85964ce 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -728,6 +728,7 @@ typedef struct ImageCacheItem {
 #define IMAGE_CACHE_HASH_SIZE 1024
 
 typedef struct ImageCache {
+    SpiceImageCache base;
     ImageCacheItem *hash_table[IMAGE_CACHE_HASH_SIZE];
     Ring lru;
 #ifdef IMAGE_CACHE_AGE
@@ -3898,9 +3899,9 @@ static void image_cache_remove(ImageCache *cache, ImageCacheItem *item)
 
 #define IMAGE_CACHE_MAX_ITEMS 2
 
-static void image_cache_put(void *opaque, uint64_t id, cairo_surface_t *surface)
+static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, cairo_surface_t *surface)
 {
-    ImageCache *cache = (ImageCache *)opaque;
+    ImageCache *cache = (ImageCache *)spice_cache;
     ImageCacheItem *item;
 
 #ifndef IMAGE_CACHE_AGE
@@ -3929,9 +3930,9 @@ static void image_cache_put(void *opaque, uint64_t id, cairo_surface_t *surface)
     ring_add(&cache->lru, &item->lru_link);
 }
 
-static cairo_surface_t *image_cache_get(void *opaque, uint64_t id)
+static cairo_surface_t *image_cache_get(SpiceImageCache *spice_cache, uint64_t id)
 {
-    ImageCache *cache = (ImageCache *)opaque;
+    ImageCache *cache = (ImageCache *)spice_cache;
 
     ImageCacheItem *item = image_cache_find(cache, id);
     if (!item) {
@@ -3942,6 +3943,12 @@ static cairo_surface_t *image_cache_get(void *opaque, uint64_t id)
 
 static void image_cache_init(ImageCache *cache)
 {
+    static SpiceImageCacheOps image_cache_ops = {
+        image_cache_put,
+        image_cache_get,
+    };
+
+    cache->base.ops = &image_cache_ops;
     memset(cache->hash_table, 0, sizeof(cache->hash_table));
     ring_init(&cache->lru);
 #ifdef IMAGE_CACHE_AGE
@@ -7488,7 +7495,7 @@ static CairoCanvas *create_cairo_context(RedWorker *worker, uint32_t width, uint
                   cairo_status_to_string(cairo_status(cairo)));
     }
 
-    return canvas_create(cairo, depth, &worker->image_cache, image_cache_put, image_cache_get,
+    return canvas_create(cairo, depth, &worker->image_cache.base,
                          worker, cb_get_virt_preload_group, worker,
                          cb_validate_virt_preload_group);
 }
@@ -7550,9 +7557,8 @@ static GLCanvas *create_ogl_context_common(RedWorker *worker, OGLCtx *ctx, uint3
     GLCanvas *canvas;
 
     oglctx_make_current(ctx);
-    if (!(canvas = gl_canvas_create(ctx, width, height, depth, &worker->image_cache,
-                                    image_cache_put, image_cache_get, worker,
-                                    cb_get_virt_preload_group,
+    if (!(canvas = gl_canvas_create(ctx, width, height, depth, &worker->image_cache.base,
+                                    worker, cb_get_virt_preload_group,
                                     worker, cb_validate_virt_preload_group))) {
         return NULL;
     }
commit 7537acd630e8973c92bb769b56d78836372b30c2
Author: Alexander Larsson <alexl at redhat.com>
Date:   Mon Feb 8 15:35:12 2010 +0100

    Add optional templated base class to Cache and SharedCache
    
    We want this for integration with C-style classes.

diff --git a/client/cache.hpp b/client/cache.hpp
index 72340e1..0b53676 100644
--- a/client/cache.hpp
+++ b/client/cache.hpp
@@ -26,8 +26,8 @@
     const char* name();
 };*/
 
-template <class T, class Treat, int HASH_SIZE>
-class Cache {
+template <class T, class Treat, int HASH_SIZE, class Base = EmptyBase>
+class Cache : public Base {
 public:
     Cache()
     {
diff --git a/client/shared_cache.hpp b/client/shared_cache.hpp
index ee96638..96a476f 100644
--- a/client/shared_cache.hpp
+++ b/client/shared_cache.hpp
@@ -27,8 +27,8 @@
     const char* name();
 };*/
 
-template <class T, class Treat, int HASH_SIZE>
-class SharedCache {
+template <class T, class Treat, int HASH_SIZE, class Base = EmptyBase>
+class SharedCache : public Base {
 public:
     SharedCache()
         : _aborting (false)
diff --git a/client/utils.h b/client/utils.h
index 9a29cfb..6ed3eea 100644
--- a/client/utils.h
+++ b/client/utils.h
@@ -149,5 +149,8 @@ private:
     T* _array;
 };
 
+class EmptyBase {
+};
+
 #endif
 
commit 79d8c5c6a48e3b10d93d67dfd777b626dadafab9
Author: Alexander Larsson <alexl at redhat.com>
Date:   Mon Feb 8 12:14:45 2010 +0100

    Add pixman_image_t referencing the cairo_canvas bits
    
    This references the same data as the cairo surface and can be used
    for drawing to the surface using direct pixman calls instead.

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index a5d4146..c40693b 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -17,16 +17,19 @@
 */
 
 #include "cairo_canvas.h"
+#define CANVAS_USE_PIXMAN
 #include "canvas_base.c"
 #include "rop3.h"
 #include "rect.h"
 #include "region.h"
+#include "pixman_utils.h"
 
 struct CairoCanvas {
     CanvasBase base;
     cairo_t *cairo;
     uint32_t *private_data;
     int private_data_size;
+    pixman_image_t *image;
 };
 
 static void canvas_set_path(CairoCanvas *canvas, void *addr)
@@ -1581,6 +1584,7 @@ void canvas_destroy(CairoCanvas *canvas)
     if (!canvas) {
         return;
     }
+    pixman_image_unref(canvas->image);
     canvas_base_destroy(&canvas->base);
     if (canvas->private_data) {
         free(canvas->private_data);
@@ -1654,6 +1658,9 @@ CairoCanvas *canvas_create(cairo_t *cairo, int bits
     canvas->private_data = NULL;
     canvas->private_data_size = 0;
     cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
+
+    canvas->image = pixman_image_from_surface (cairo_get_target (cairo));
+
     return canvas;
 }
 
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 356ef9d..ebb9846 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -26,6 +26,7 @@
 #include "quic.h"
 #include "lz.h"
 #include "canvas_base.h"
+#include "pixman_utils.h"
 #include "canvas_utils.h"
 #include "rect.h"
 
@@ -214,6 +215,43 @@ typedef struct ATTR_PACKED DataChunk {
 
 #endif
 
+#ifdef CANVAS_USE_PIXMAN
+
+static pixman_format_code_t
+pixman_format_from_cairo_format (cairo_format_t format)
+{
+    switch (format) {
+    case CAIRO_FORMAT_A1:
+        return PIXMAN_a1;
+    case CAIRO_FORMAT_A8:
+        return PIXMAN_a8;
+    case CAIRO_FORMAT_RGB24:
+        return PIXMAN_x8r8g8b8;
+    case CAIRO_FORMAT_ARGB32:
+    default:
+        return PIXMAN_a8r8g8b8;
+    }
+}
+
+static pixman_image_t *
+pixman_image_from_surface (cairo_surface_t *surface)
+{
+  pixman_image_t *image;
+  cairo_format_t format;
+
+
+  format = cairo_image_surface_get_format (surface);
+
+  image = pixman_image_create_bits (pixman_format_from_cairo_format (format),
+                                    cairo_image_surface_get_width (surface),
+                                    cairo_image_surface_get_height (surface),
+                                    (uint32_t *)cairo_image_surface_get_data (surface),
+                                    cairo_image_surface_get_stride (surface));
+
+  return image;
+}
+
+#endif
 
 static inline void canvas_localize_palette(CanvasBase *canvas, SpicePalette *palette)
 {
commit 60a189f2503dec00556656988835948016aff389
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Feb 17 15:50:44 2010 +0100

    Add line rasterizer

diff --git a/client/Makefile.am b/client/Makefile.am
index c59b6f3..d01d03c 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -11,6 +11,7 @@ RED_COMMON_SRCS =	 		\
 	cache.hpp			\
 	cairo_canvas.cpp		\
 	pixman_utils.cpp		\
+	lines.cpp			\
 	canvas.cpp			\
 	canvas.h			\
 	canvas_utils.cpp		\
diff --git a/client/lines.cpp b/client/lines.cpp
new file mode 100644
index 0000000..c9e619c
--- /dev/null
+++ b/client/lines.cpp
@@ -0,0 +1,24 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "common.h"
+#include "utils.h"
+
+
+#define CANVAS_ERROR(format, ...) THROW(format, ## __VA_ARGS__)
+
+#include "../common/lines.c"
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index e50c4ae..47cb698 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -66,6 +66,7 @@ RED_COMMON_SRCS = 					\
 	$(top_srcdir)/client/inputs_channel.h		\
 	$(top_srcdir)/client/inputs_handler.h		\
 	$(top_srcdir)/client/lz.cpp			\
+	$(top_srcdir)/client/lines.cpp			\
 	$(top_srcdir)/client/monitor.cpp		\
 	$(top_srcdir)/client/monitor.h			\
 	$(top_srcdir)/client/menu.cpp			\
diff --git a/common/Makefile.am b/common/Makefile.am
index 2d18440..e5f8290 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -34,6 +34,8 @@ COMMON_SRCS = 				\
 	ring.h				\
 	rop3.h				\
 	rop3.c				\
+	lines.h				\
+	lines.c				\
 	lz.c				\
 	lz_compress_tmpl.c		\
 	lz_config.h			\
diff --git a/common/lines.c b/common/lines.c
new file mode 100644
index 0000000..8e44ac3
--- /dev/null
+++ b/common/lines.c
@@ -0,0 +1,3632 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/***********************************************************
+
+Copyright 1989, 1998  The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1989 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+
+#include <stdio.h>
+#ifdef _XOPEN_SOURCE
+#include <math.h>
+#else
+#define _XOPEN_SOURCE           /* to get prototype for hypot on some systems */
+#include <math.h>
+#undef _XOPEN_SOURCE
+#endif
+#include "lines.h"
+
+#ifndef FALSE
+#   define FALSE 0
+#endif
+
+#ifndef TRUE
+#   define TRUE 1
+#endif
+
+#ifndef MIN
+#  define MIN(a, b) ((a < b) ? a : b)
+#endif
+
+#ifndef MAX
+#  define MAX(a, b) ((a > b) ? a : b)
+#endif
+
+#define xalloc(i) malloc(i)
+#define xrealloc(a,b) realloc(a,b)
+#define xfree(i) free(i)
+
+typedef uint32_t CARD32;
+typedef int Boolean;
+typedef pixman_rectangle32_t xRectangle;
+typedef SpicePoint DDXPointRec;
+typedef DDXPointRec *DDXPointPtr;
+typedef struct lineGC *GCPtr;
+
+/* largest positive value that can fit into a component of a point.
+ * Assumes that the point structure is {type x, y;} where type is
+ * a signed type.
+ */
+#define MAX_COORDINATE 2147483647
+#define MIN_COORDINATE -2147483648
+
+#define miZeroLine spice_canvas_zero_line
+#define miZeroDashLine spice_canvas_zero_dash_line
+#define miWideDash spice_canvas_wide_dash_line
+#define miWideLine spice_canvas_wide_line
+
+static int inline
+ICEIL (double x)
+{
+    int _cTmp = x;
+    return ((x == _cTmp) || (x < 0.0)) ? _cTmp : _cTmp + 1;
+}
+
+typedef struct {
+    int count;                  /* number of spans                  */
+    DDXPointPtr points;         /* pointer to list of start points  */
+    int *widths;                /* pointer to list of widths        */
+} Spans;
+
+typedef struct {
+    int size;                   /* Total number of *Spans allocated     */
+    int count;                  /* Number of *Spans actually in group   */
+    Spans *group;               /* List of Spans                        */
+    int ymin, ymax;             /* Min, max y values encountered        */
+} SpanGroup;
+
+/* Initialize SpanGroup.  MUST BE DONE before use. */
+static void miInitSpanGroup (SpanGroup *        /*spanGroup */
+    );
+
+/* Add a Spans to a SpanGroup. The spans MUST BE in y-sorted order */
+static void miAppendSpans (SpanGroup * /*spanGroup */ ,
+                           SpanGroup * /*otherGroup */ ,
+                           Spans *      /*spans */
+    );
+
+/* Paint a span group, insuring that each pixel is painted at most once */
+static void miFillUniqueSpanGroup (GCPtr /*pGC */ ,
+                                   SpanGroup * /*spanGroup */ ,
+                                   Boolean /* foreground */
+    );
+
+/* Free up data in a span group.  MUST BE DONE or you'll suffer memory leaks */
+static void miFreeSpanGroup (SpanGroup *        /*spanGroup */
+    );
+
+/* Rops which must use span groups */
+#define miSpansCarefulRop(rop)  (((rop) & 0xc) == 0x8 || ((rop) & 0x3) == 0x2)
+#define miSpansEasyRop(rop)     (!miSpansCarefulRop(rop))
+
+/*
+ * Public definitions used for configuring basic pixelization aspects
+ * of the sample implementation line-drawing routines provided in
+ * {mfb,mi,cfb*} at run-time.
+ */
+
+#define XDECREASING     4
+#define YDECREASING     2
+#define YMAJOR          1
+
+#define OCTANT1                 (1 << (YDECREASING))
+#define OCTANT2                 (1 << (YDECREASING|YMAJOR))
+#define OCTANT3                 (1 << (XDECREASING|YDECREASING|YMAJOR))
+#define OCTANT4                 (1 << (XDECREASING|YDECREASING))
+#define OCTANT5                 (1 << (XDECREASING))
+#define OCTANT6                 (1 << (XDECREASING|YMAJOR))
+#define OCTANT7                 (1 << (YMAJOR))
+#define OCTANT8                 (1 << (0))
+
+#define XMAJOROCTANTS           (OCTANT1 | OCTANT4 | OCTANT5 | OCTANT8)
+
+#define DEFAULTZEROLINEBIAS     (OCTANT2 | OCTANT3 | OCTANT4 | OCTANT5)
+
+/*
+ * Devices can configure the rendering of routines in mi, mfb, and cfb*
+ * by specifying a thin line bias to be applied to a particular screen
+ * using the following function.  The bias parameter is an OR'ing of
+ * the appropriate OCTANT constants defined above to indicate which
+ * octants to bias a line to prefer an axial step when the Bresenham
+ * error term is exactly zero.  The octants are mapped as follows:
+ *
+ *   \    |    /
+ *    \ 3 | 2 /
+ *     \  |  /
+ *    4 \ | / 1
+ *       \|/
+ *   -----------
+ *       /|\
+ *    5 / | \ 8
+ *     /  |  \
+ *    / 6 | 7 \
+ *   /    |    \
+ *
+ * For more information, see "Ambiguities in Incremental Line Rastering,"
+ * Jack E. Bresenham, IEEE CG&A, May 1987.
+ */
+
+/*
+ * Private definitions needed for drawing thin (zero width) lines
+ * Used by the mi, mfb, and all cfb* components.
+ */
+
+#define X_AXIS  0
+#define Y_AXIS  1
+
+#define OUT_LEFT  0x08
+#define OUT_RIGHT 0x04
+#define OUT_ABOVE 0x02
+#define OUT_BELOW 0x01
+
+#define OUTCODES(_result, _x, _y, _pbox) \
+    if      ( (_x) <  (_pbox)->x1) (_result) |= OUT_LEFT; \
+    else if ( (_x) >= (_pbox)->x2) (_result) |= OUT_RIGHT; \
+    if      ( (_y) <  (_pbox)->y1) (_result) |= OUT_ABOVE; \
+    else if ( (_y) >= (_pbox)->y2) (_result) |= OUT_BELOW;
+
+#define MIOUTCODES(outcode, x, y, xmin, ymin, xmax, ymax) \
+{\
+     if (x < xmin) outcode |= OUT_LEFT;\
+     if (x > xmax) outcode |= OUT_RIGHT;\
+     if (y < ymin) outcode |= OUT_ABOVE;\
+     if (y > ymax) outcode |= OUT_BELOW;\
+}
+
+#define SWAPINT(i, j) \
+{  int _t = i;  i = j;  j = _t; }
+
+#define SWAPPT(i, j) \
+{  DDXPointRec _t; _t = i;  i = j; j = _t; }
+
+#define SWAPINT_PAIR(x1, y1, x2, y2)\
+{   int t = x1;  x1 = x2;  x2 = t;\
+        t = y1;  y1 = y2;  y2 = t;\
+}
+
+#define miGetZeroLineBias(_pScreen) (DEFAULTZEROLINEBIAS)
+
+#define CalcLineDeltas(_x1,_y1,_x2,_y2,_adx,_ady,_sx,_sy,_SX,_SY,_octant) \
+    (_octant) = 0;                              \
+    (_sx) = (_SX);                              \
+    if (((_adx) = (_x2) - (_x1)) < 0) {                 \
+        (_adx) = -(_adx);                       \
+        (_sx = -(_sx));                                 \
+        (_octant) |= XDECREASING;               \
+    }                                           \
+    (_sy) = (_SY);                              \
+    if (((_ady) = (_y2) - (_y1)) < 0) {                 \
+        (_ady) = -(_ady);                       \
+        (_sy = -(_sy));                                 \
+        (_octant) |= YDECREASING;               \
+    }
+
+#define SetYMajorOctant(_octant)        ((_octant) |= YMAJOR)
+
+#define FIXUP_ERROR(_e, _octant, _bias) \
+    (_e) -= (((_bias) >> (_octant)) & 1)
+
+#define IsXMajorOctant(_octant)                 (!((_octant) & YMAJOR))
+#define IsYMajorOctant(_octant)                 ((_octant) & YMAJOR)
+#define IsXDecreasingOctant(_octant)    ((_octant) & XDECREASING)
+#define IsYDecreasingOctant(_octant)    ((_octant) & YDECREASING)
+
+static int miZeroClipLine (int /*xmin */ ,
+                           int /*ymin */ ,
+                           int /*xmax */ ,
+                           int /*ymax */ ,
+                           int * /*new_x1 */ ,
+                           int * /*new_y1 */ ,
+                           int * /*new_x2 */ ,
+                           int * /*new_y2 */ ,
+                           unsigned int /*adx */ ,
+                           unsigned int /*ady */ ,
+                           int * /*pt1_clipped */ ,
+                           int * /*pt2_clipped */ ,
+                           int /*octant */ ,
+                           unsigned int /*bias */ ,
+                           int /*oc1 */ ,
+                           int  /*oc2 */
+    );
+
+/*
+ * interface data to span-merging polygon filler
+ */
+
+typedef struct _SpanData {
+    SpanGroup fgGroup, bgGroup;
+} SpanDataRec, *SpanDataPtr;
+
+#define AppendSpanGroup(pGC, foreground, spanPtr, spanData) { \
+        SpanGroup   *group, *othergroup = NULL; \
+        if (foreground) \
+        { \
+            group = &spanData->fgGroup; \
+            if (pGC->lineStyle == LineDoubleDash) \
+                othergroup = &spanData->bgGroup; \
+        } \
+        else \
+        { \
+            group = &spanData->bgGroup; \
+            othergroup = &spanData->fgGroup; \
+        } \
+        miAppendSpans (group, othergroup, spanPtr); \
+}
+
+/*
+ * Polygon edge description for integer wide-line routines
+ */
+
+typedef struct _PolyEdge {
+    int height;                 /* number of scanlines to process */
+    int x;                      /* starting x coordinate */
+    int stepx;                  /* fixed integral dx */
+    int signdx;                 /* variable dx sign */
+    int e;                      /* initial error term */
+    int dy;
+    int dx;
+} PolyEdgeRec, *PolyEdgePtr;
+
+#define SQSECANT 108.856472512142       /* 1/sin^2(11/2) - miter limit constant */
+
+/*
+ * types for general polygon routines
+ */
+
+typedef struct _PolyVertex {
+    double x, y;
+} PolyVertexRec, *PolyVertexPtr;
+
+typedef struct _PolySlope {
+    int dx, dy;
+    double k;                   /* x0 * dy - y0 * dx */
+} PolySlopeRec, *PolySlopePtr;
+
+/*
+ * Line face description for caps/joins
+ */
+
+typedef struct _LineFace {
+    double xa, ya;
+    int dx, dy;
+    int x, y;
+    double k;
+} LineFaceRec, *LineFacePtr;
+
+/*
+ * macros for polygon fillers
+ */
+
+#define MIPOLYRELOADLEFT    if (!left_height && left_count) { \
+                                left_height = left->height; \
+                                left_x = left->x; \
+                                left_stepx = left->stepx; \
+                                left_signdx = left->signdx; \
+                                left_e = left->e; \
+                                left_dy = left->dy; \
+                                left_dx = left->dx; \
+                                --left_count; \
+                                ++left; \
+                            }
+
+#define MIPOLYRELOADRIGHT   if (!right_height && right_count) { \
+                                right_height = right->height; \
+                                right_x = right->x; \
+                                right_stepx = right->stepx; \
+                                right_signdx = right->signdx; \
+                                right_e = right->e; \
+                                right_dy = right->dy; \
+                                right_dx = right->dx; \
+                                --right_count; \
+                                ++right; \
+                        }
+
+#define MIPOLYSTEPLEFT  left_x += left_stepx; \
+                        left_e += left_dx; \
+                        if (left_e > 0) \
+                        { \
+                            left_x += left_signdx; \
+                            left_e -= left_dy; \
+                        }
+
+#define MIPOLYSTEPRIGHT right_x += right_stepx; \
+                        right_e += right_dx; \
+                        if (right_e > 0) \
+                        { \
+                            right_x += right_signdx; \
+                            right_e -= right_dy; \
+                        }
+
+static void miRoundJoinClip (LineFacePtr /*pLeft */ ,
+                             LineFacePtr /*pRight */ ,
+                             PolyEdgePtr /*edge1 */ ,
+                             PolyEdgePtr /*edge2 */ ,
+                             int * /*y1 */ ,
+                             int * /*y2 */ ,
+                             Boolean * /*left1 */ ,
+                             Boolean *     /*left2 */
+    );
+
+static int miRoundCapClip (LineFacePtr /*face */ ,
+                           Boolean /*isInt */ ,
+                           PolyEdgePtr /*edge */ ,
+                           Boolean *       /*leftEdge */
+    );
+
+static int miPolyBuildEdge (double x0, double y0, double k, int dx, int dy,
+                            int xi, int yi, int left, PolyEdgePtr edge);
+static int miPolyBuildPoly (PolyVertexPtr vertices, PolySlopePtr slopes,
+                            int count, int xi, int yi, PolyEdgePtr left,
+                            PolyEdgePtr right, int *pnleft, int *pnright, int *h);
+
+
+static void
+miStepDash (int dist,           /* distance to step */
+            int *pDashIndex,    /* current dash */
+            unsigned char *pDash,       /* dash list */
+            int numInDashList,  /* total length of dash list */
+            int *pDashOffset    /* offset into current dash */
+    )
+{
+    int dashIndex, dashOffset;
+    int totallen;
+    int i;
+
+    dashIndex = *pDashIndex;
+    dashOffset = *pDashOffset;
+    if (dist < pDash[dashIndex] - dashOffset) {
+        *pDashOffset = dashOffset + dist;
+        return;
+    }
+    dist -= pDash[dashIndex] - dashOffset;
+    if (++dashIndex == numInDashList)
+        dashIndex = 0;
+    totallen = 0;
+    for (i = 0; i < numInDashList; i++)
+        totallen += pDash[i];
+    if (totallen <= dist)
+        dist = dist % totallen;
+    while (dist >= pDash[dashIndex]) {
+        dist -= pDash[dashIndex];
+        if (++dashIndex == numInDashList)
+            dashIndex = 0;
+    }
+    *pDashIndex = dashIndex;
+    *pDashOffset = dist;
+}
+
+/*
+
+These routines maintain lists of Spans, in order to implement the
+``touch-each-pixel-once'' rules of wide lines and arcs.
+
+Written by Joel McCormack, Summer 1989.
+
+*/
+
+
+static void
+miInitSpanGroup (SpanGroup * spanGroup)
+{
+    spanGroup->size = 0;
+    spanGroup->count = 0;
+    spanGroup->group = NULL;
+    spanGroup->ymin = MAX_COORDINATE;
+    spanGroup->ymax = MIN_COORDINATE;
+}                               /* InitSpanGroup */
+
+#define YMIN(spans) (spans->points[0].y)
+#define YMAX(spans)  (spans->points[spans->count-1].y)
+
+static void
+miSubtractSpans (SpanGroup * spanGroup, Spans * sub)
+{
+    int i, subCount, spansCount;
+    int ymin, ymax, xmin, xmax;
+    Spans *spans;
+    DDXPointPtr subPt, spansPt;
+    int *subWid, *spansWid;
+    int extra;
+
+    ymin = YMIN (sub);
+    ymax = YMAX (sub);
+    spans = spanGroup->group;
+    for (i = spanGroup->count; i; i--, spans++) {
+        if (YMIN (spans) <= ymax && ymin <= YMAX (spans)) {
+            subCount = sub->count;
+            subPt = sub->points;
+            subWid = sub->widths;
+            spansCount = spans->count;
+            spansPt = spans->points;
+            spansWid = spans->widths;
+            extra = 0;
+            for (;;) {
+                while (spansCount && spansPt->y < subPt->y) {
+                    spansPt++;
+                    spansWid++;
+                    spansCount--;
+                }
+                if (!spansCount)
+                    break;
+                while (subCount && subPt->y < spansPt->y) {
+                    subPt++;
+                    subWid++;
+                    subCount--;
+                }
+                if (!subCount)
+                    break;
+                if (subPt->y == spansPt->y) {
+                    xmin = subPt->x;
+                    xmax = xmin + *subWid;
+                    if (xmin >= spansPt->x + *spansWid || spansPt->x >= xmax) {
+                        ;
+                    } else if (xmin <= spansPt->x) {
+                        if (xmax >= spansPt->x + *spansWid) {
+                            memmove (spansPt, spansPt + 1, sizeof *spansPt * (spansCount - 1));
+                            memmove (spansWid, spansWid + 1, sizeof *spansWid * (spansCount - 1));
+                            spansPt--;
+                            spansWid--;
+                            spans->count--;
+                            extra++;
+                        } else {
+                            *spansWid = *spansWid - (xmax - spansPt->x);
+                            spansPt->x = xmax;
+                        }
+                    } else {
+                        if (xmax >= spansPt->x + *spansWid) {
+                            *spansWid = xmin - spansPt->x;
+                        } else {
+                            if (!extra) {
+                                DDXPointPtr newPt;
+                                int *newwid;
+
+#define EXTRA 8
+                                newPt =
+                                    (DDXPointPtr) xrealloc (spans->points,
+                                                            (spans->count +
+                                                             EXTRA) * sizeof (DDXPointRec));
+                                if (!newPt)
+                                    break;
+                                spansPt = newPt + (spansPt - spans->points);
+                                spans->points = newPt;
+                                newwid =
+                                    (int *) xrealloc (spans->widths,
+                                                      (spans->count + EXTRA) * sizeof (int));
+                                if (!newwid)
+                                    break;
+                                spansWid = newwid + (spansWid - spans->widths);
+                                spans->widths = newwid;
+                                extra = EXTRA;
+                            }
+                            memmove (spansPt + 1, spansPt, sizeof *spansPt * (spansCount));
+                            memmove (spansWid + 1, spansWid, sizeof *spansWid * (spansCount));
+                            spans->count++;
+                            extra--;
+                            *spansWid = xmin - spansPt->x;
+                            spansWid++;
+                            spansPt++;
+                            *spansWid = *spansWid - (xmax - spansPt->x);
+                            spansPt->x = xmax;
+                        }
+                    }
+                }
+                spansPt++;
+                spansWid++;
+                spansCount--;
+            }
+        }
+    }
+}
+
+static void
+miAppendSpans (SpanGroup * spanGroup, SpanGroup * otherGroup, Spans * spans)
+{
+    int ymin, ymax;
+    int spansCount;
+
+    spansCount = spans->count;
+    if (spansCount > 0) {
+        if (spanGroup->size == spanGroup->count) {
+            spanGroup->size = (spanGroup->size + 8) * 2;
+            spanGroup->group = (Spans *)
+                xrealloc (spanGroup->group, sizeof (Spans) * spanGroup->size);
+        }
+
+        spanGroup->group[spanGroup->count] = *spans;
+        (spanGroup->count)++;
+        ymin = spans->points[0].y;
+        if (ymin < spanGroup->ymin)
+            spanGroup->ymin = ymin;
+        ymax = spans->points[spansCount - 1].y;
+        if (ymax > spanGroup->ymax)
+            spanGroup->ymax = ymax;
+        if (otherGroup && otherGroup->ymin < ymax && ymin < otherGroup->ymax) {
+            miSubtractSpans (otherGroup, spans);
+        }
+    } else {
+        xfree (spans->points);
+        xfree (spans->widths);
+    }
+}                               /* AppendSpans */
+
+static void
+miFreeSpanGroup (SpanGroup * spanGroup)
+{
+    if (spanGroup->group != NULL)
+        xfree (spanGroup->group);
+}
+
+static void
+QuickSortSpansX (DDXPointRec points[], int widths[], int numSpans)
+{
+    int x;
+    int i, j, m;
+    DDXPointPtr r;
+
+/* Always called with numSpans > 1 */
+/* Sorts only by x, as all y should be the same */
+
+#define ExchangeSpans(a, b)                                 \
+{                                                           \
+    DDXPointRec         tpt;                                \
+    int                 tw;                                 \
+                                                            \
+    tpt = points[a]; points[a] = points[b]; points[b] = tpt;    \
+    tw = widths[a]; widths[a] = widths[b]; widths[b] = tw;  \
+}
+
+    do {
+        if (numSpans < 9) {
+            /* Do insertion sort */
+            int xprev;
+
+            xprev = points[0].x;
+            i = 1;
+            do {                /* while i != numSpans */
+                x = points[i].x;
+                if (xprev > x) {
+                    /* points[i] is out of order.  Move into proper location. */
+                    DDXPointRec tpt;
+                    int tw, k;
+
+                    for (j = 0; x >= points[j].x; j++) {
+                    }
+                    tpt = points[i];
+                    tw = widths[i];
+                    for (k = i; k != j; k--) {
+                        points[k] = points[k - 1];
+                        widths[k] = widths[k - 1];
+                    }
+                    points[j] = tpt;
+                    widths[j] = tw;
+                    x = points[i].x;
+                }               /* if out of order */
+                xprev = x;
+                i++;
+            } while (i != numSpans);
+            return;
+        }
+
+        /* Choose partition element, stick in location 0 */
+        m = numSpans / 2;
+        if (points[m].x > points[0].x)
+            ExchangeSpans (m, 0);
+        if (points[m].x > points[numSpans - 1].x)
+            ExchangeSpans (m, numSpans - 1);
+        if (points[m].x > points[0].x)
+            ExchangeSpans (m, 0);
+        x = points[0].x;
+
+        /* Partition array */
+        i = 0;
+        j = numSpans;
+        do {
+            r = &(points[i]);
+            do {
+                r++;
+                i++;
+            } while (i != numSpans && r->x < x);
+            r = &(points[j]);
+            do {
+                r--;
+                j--;
+            } while (x < r->x);
+            if (i < j)
+                ExchangeSpans (i, j);
+        } while (i < j);
+
+        /* Move partition element back to middle */
+        ExchangeSpans (0, j);
+
+        /* Recurse */
+        if (numSpans - j - 1 > 1)
+            QuickSortSpansX (&points[j + 1], &widths[j + 1], numSpans - j - 1);
+        numSpans = j;
+    } while (numSpans > 1);
+}                               /* QuickSortSpans */
+
+
+static int
+UniquifySpansX (Spans * spans, DDXPointRec * newPoints, int *newWidths)
+{
+    int newx1, newx2, oldpt, i, y;
+    DDXPointRec *oldPoints;
+    int *oldWidths;
+    int *startNewWidths;
+
+/* Always called with numSpans > 1 */
+/* Uniquify the spans, and stash them into newPoints and newWidths.  Return the
+   number of unique spans. */
+
+
+    startNewWidths = newWidths;
+
+    oldPoints = spans->points;
+    oldWidths = spans->widths;
+
+    y = oldPoints->y;
+    newx1 = oldPoints->x;
+    newx2 = newx1 + *oldWidths;
+
+    for (i = spans->count - 1; i != 0; i--) {
+        oldPoints++;
+        oldWidths++;
+        oldpt = oldPoints->x;
+        if (oldpt > newx2) {
+            /* Write current span, start a new one */
+            newPoints->x = newx1;
+            newPoints->y = y;
+            *newWidths = newx2 - newx1;
+            newPoints++;
+            newWidths++;
+            newx1 = oldpt;
+            newx2 = oldpt + *oldWidths;
+        } else {
+            /* extend current span, if old extends beyond new */
+            oldpt = oldpt + *oldWidths;
+            if (oldpt > newx2)
+                newx2 = oldpt;
+        }
+    }                           /* for */
+
+    /* Write final span */
+    newPoints->x = newx1;
+    *newWidths = newx2 - newx1;
+    newPoints->y = y;
+
+    return (newWidths - startNewWidths) + 1;
+}                               /* UniquifySpansX */
+
+static void
+miDisposeSpanGroup (SpanGroup * spanGroup)
+{
+    int i;
+    Spans *spans;
+
+    for (i = 0; i < spanGroup->count; i++) {
+        spans = spanGroup->group + i;
+        xfree (spans->points);
+        xfree (spans->widths);
+    }
+}
+
+static void
+miFillUniqueSpanGroup (GCPtr pGC, SpanGroup * spanGroup, Boolean foreground)
+{
+    int i;
+    Spans *spans;
+    Spans *yspans;
+    int *ysizes;
+    int ymin, ylength;
+
+    /* Outgoing spans for one big call to FillSpans */
+    DDXPointPtr points;
+    int *widths;
+    int count;
+
+    if (spanGroup->count == 0)
+        return;
+
+    if (spanGroup->count == 1) {
+        /* Already should be sorted, unique */
+        spans = spanGroup->group;
+        (*pGC->ops->FillSpans)
+            (pGC, spans->count, spans->points, spans->widths, TRUE, foreground);
+        xfree (spans->points);
+        xfree (spans->widths);
+    } else {
+        /* Yuck.  Gross.  Radix sort into y buckets, then sort x and uniquify */
+        /* This seems to be the fastest thing to do.  I've tried sorting on
+           both x and y at the same time rather than creating into all those
+           y buckets, but it was somewhat slower. */
+
+        ymin = spanGroup->ymin;
+        ylength = spanGroup->ymax - ymin + 1;
+
+        /* Allocate Spans for y buckets */
+        yspans = (Spans*)xalloc (ylength * sizeof (Spans));
+        ysizes = (int *)xalloc (ylength * sizeof (int));
+
+        if (!yspans || !ysizes) {
+            if (yspans)
+                xfree (yspans);
+            if (ysizes)
+                xfree (ysizes);
+            miDisposeSpanGroup (spanGroup);
+            return;
+        }
+
+        for (i = 0; i != ylength; i++) {
+            ysizes[i] = 0;
+            yspans[i].count = 0;
+            yspans[i].points = NULL;
+            yspans[i].widths = NULL;
+        }
+
+        /* Go through every single span and put it into the correct bucket */
+        count = 0;
+        for (i = 0, spans = spanGroup->group; i != spanGroup->count; i++, spans++) {
+            int index;
+            int j;
+
+            for (j = 0, points = spans->points, widths = spans->widths;
+                 j != spans->count; j++, points++, widths++) {
+                index = points->y - ymin;
+                if (index >= 0 && index < ylength) {
+                    Spans *newspans = &(yspans[index]);
+                    if (newspans->count == ysizes[index]) {
+                        DDXPointPtr newpoints;
+                        int *newwidths;
+                        ysizes[index] = (ysizes[index] + 8) * 2;
+                        newpoints = (DDXPointPtr) xrealloc (newspans->points,
+                                                            ysizes[index] * sizeof (DDXPointRec));
+                        newwidths = (int *) xrealloc (newspans->widths,
+                                                      ysizes[index] * sizeof (int));
+                        if (!newpoints || !newwidths) {
+                            int i;
+
+                            for (i = 0; i < ylength; i++) {
+                                xfree (yspans[i].points);
+                                xfree (yspans[i].widths);
+                            }
+                            xfree (yspans);
+                            xfree (ysizes);
+                            miDisposeSpanGroup (spanGroup);
+                            return;
+                        }
+                        newspans->points = newpoints;
+                        newspans->widths = newwidths;
+                    }
+                    newspans->points[newspans->count] = *points;
+                    newspans->widths[newspans->count] = *widths;
+                    (newspans->count)++;
+                }               /* if y value of span in range */
+            }                   /* for j through spans */
+            count += spans->count;
+            xfree (spans->points);
+            spans->points = NULL;
+            xfree (spans->widths);
+            spans->widths = NULL;
+        }                       /* for i thorough Spans */
+
+        /* Now sort by x and uniquify each bucket into the final array */
+        points = (DDXPointRec*)xalloc (count * sizeof (DDXPointRec));
+        widths = (int *)xalloc (count * sizeof (int));
+        if (!points || !widths) {
+            int i;
+
+            for (i = 0; i < ylength; i++) {
+                xfree (yspans[i].points);
+                xfree (yspans[i].widths);
+            }
+            xfree (yspans);
+            xfree (ysizes);
+            if (points)
+                xfree (points);
+            if (widths)
+                xfree (widths);
+            return;
+        }
+        count = 0;
+        for (i = 0; i != ylength; i++) {
+            int ycount = yspans[i].count;
+            if (ycount > 0) {
+                if (ycount > 1) {
+                    QuickSortSpansX (yspans[i].points, yspans[i].widths, ycount);
+                    count += UniquifySpansX (&(yspans[i]), &(points[count]), &(widths[count]));
+                } else {
+                    points[count] = yspans[i].points[0];
+                    widths[count] = yspans[i].widths[0];
+                    count++;
+                }
+                xfree (yspans[i].points);
+                xfree (yspans[i].widths);
+            }
+        }
+
+        (*pGC->ops->FillSpans) (pGC, count, points, widths, TRUE, foreground);
+        xfree (points);
+        xfree (widths);
+        xfree (yspans);
+        xfree (ysizes);         /* use (DE)xalloc for these? */
+    }
+
+    spanGroup->count = 0;
+    spanGroup->ymin = MAX_COORDINATE;
+    spanGroup->ymax = MIN_COORDINATE;
+}
+
+/*
+
+The bresenham error equation used in the mi/mfb/cfb line routines is:
+
+        e = error
+        dx = difference in raw X coordinates
+        dy = difference in raw Y coordinates
+        M = # of steps in X direction
+        N = # of steps in Y direction
+        B = 0 to prefer diagonal steps in a given octant,
+            1 to prefer axial steps in a given octant
+
+        For X major lines:
+                e = 2Mdy - 2Ndx - dx - B
+                -2dx <= e < 0
+
+        For Y major lines:
+                e = 2Ndx - 2Mdy - dy - B
+                -2dy <= e < 0
+
+At the start of the line, we have taken 0 X steps and 0 Y steps,
+so M = 0 and N = 0:
+
+        X major         e = 2Mdy - 2Ndx - dx - B
+                  = -dx - B
+
+        Y major         e = 2Ndx - 2Mdy - dy - B
+                  = -dy - B
+
+At the end of the line, we have taken dx X steps and dy Y steps,
+so M = dx and N = dy:
+
+        X major         e = 2Mdy - 2Ndx - dx - B
+                  = 2dxdy - 2dydx - dx - B
+                  = -dx - B
+        Y major e = 2Ndx - 2Mdy - dy - B
+                  = 2dydx - 2dxdy - dy - B
+                  = -dy - B
+
+Thus, the error term is the same at the start and end of the line.
+
+Let us consider clipping an X coordinate.  There are 4 cases which
+represent the two independent cases of clipping the start vs. the
+end of the line and an X major vs. a Y major line.  In any of these
+cases, we know the number of X steps (M) and we wish to find the
+number of Y steps (N).  Thus, we will solve our error term equation.
+If we are clipping the start of the line, we will find the smallest
+N that satisfies our error term inequality.  If we are clipping the
+end of the line, we will find the largest number of Y steps that
+satisfies the inequality.  In that case, since we are representing
+the Y steps as (dy - N), we will actually want to solve for the
+smallest N in that equation.
+
+Case 1:  X major, starting X coordinate moved by M steps
+
+                -2dx <= 2Mdy - 2Ndx - dx - B < 0
+        2Ndx <= 2Mdy - dx - B + 2dx     2Ndx > 2Mdy - dx - B
+        2Ndx <= 2Mdy + dx - B           N > (2Mdy - dx - B) / 2dx
+        N <= (2Mdy + dx - B) / 2dx
+
+Since we are trying to find the smallest N that satisfies these
+equations, we should use the > inequality to find the smallest:
+
+        N = floor((2Mdy - dx - B) / 2dx) + 1
+          = floor((2Mdy - dx - B + 2dx) / 2dx)
+          = floor((2Mdy + dx - B) / 2dx)
+
+Case 1b: X major, ending X coordinate moved to M steps
+
+Same derivations as Case 1, but we want the largest N that satisfies
+the equations, so we use the <= inequality:
+
+        N = floor((2Mdy + dx - B) / 2dx)
+
+Case 2: X major, ending X coordinate moved by M steps
+
+                -2dx <= 2(dx - M)dy - 2(dy - N)dx - dx - B < 0
+                -2dx <= 2dxdy - 2Mdy - 2dxdy + 2Ndx - dx - B < 0
+                -2dx <= 2Ndx - 2Mdy - dx - B < 0
+        2Ndx >= 2Mdy + dx + B - 2dx     2Ndx < 2Mdy + dx + B
+        2Ndx >= 2Mdy - dx + B           N < (2Mdy + dx + B) / 2dx
+        N >= (2Mdy - dx + B) / 2dx
+
+Since we are trying to find the highest number of Y steps that
+satisfies these equations, we need to find the smallest N, so
+we should use the >= inequality to find the smallest:
+
+        N = ceiling((2Mdy - dx + B) / 2dx)
+          = floor((2Mdy - dx + B + 2dx - 1) / 2dx)
+          = floor((2Mdy + dx + B - 1) / 2dx)
+
+Case 2b: X major, starting X coordinate moved to M steps from end
+
+Same derivations as Case 2, but we want the smallest number of Y
+steps, so we want the highest N, so we use the < inequality:
+
+        N = ceiling((2Mdy + dx + B) / 2dx) - 1
+          = floor((2Mdy + dx + B + 2dx - 1) / 2dx) - 1
+          = floor((2Mdy + dx + B + 2dx - 1 - 2dx) / 2dx)
+          = floor((2Mdy + dx + B - 1) / 2dx)
+
+Case 3: Y major, starting X coordinate moved by M steps
+
+                -2dy <= 2Ndx - 2Mdy - dy - B < 0
+        2Ndx >= 2Mdy + dy + B - 2dy     2Ndx < 2Mdy + dy + B
+        2Ndx >= 2Mdy - dy + B           N < (2Mdy + dy + B) / 2dx
+        N >= (2Mdy - dy + B) / 2dx
+
+Since we are trying to find the smallest N that satisfies these
+equations, we should use the >= inequality to find the smallest:
+
+        N = ceiling((2Mdy - dy + B) / 2dx)
+          = floor((2Mdy - dy + B + 2dx - 1) / 2dx)
+          = floor((2Mdy - dy + B - 1) / 2dx) + 1
+
+Case 3b: Y major, ending X coordinate moved to M steps
+
+Same derivations as Case 3, but we want the largest N that satisfies
+the equations, so we use the < inequality:
+
+        N = ceiling((2Mdy + dy + B) / 2dx) - 1
+          = floor((2Mdy + dy + B + 2dx - 1) / 2dx) - 1
+          = floor((2Mdy + dy + B + 2dx - 1 - 2dx) / 2dx)
+          = floor((2Mdy + dy + B - 1) / 2dx)
+
+Case 4: Y major, ending X coordinate moved by M steps
+
+                -2dy <= 2(dy - N)dx - 2(dx - M)dy - dy - B < 0
+                -2dy <= 2dxdy - 2Ndx - 2dxdy + 2Mdy - dy - B < 0
+                -2dy <= 2Mdy - 2Ndx - dy - B < 0
+        2Ndx <= 2Mdy - dy - B + 2dy     2Ndx > 2Mdy - dy - B
+        2Ndx <= 2Mdy + dy - B           N > (2Mdy - dy - B) / 2dx
+        N <= (2Mdy + dy - B) / 2dx
+
+Since we are trying to find the highest number of Y steps that
+satisfies these equations, we need to find the smallest N, so
+we should use the > inequality to find the smallest:
+
+        N = floor((2Mdy - dy - B) / 2dx) + 1
+
+Case 4b: Y major, starting X coordinate moved to M steps from end
+
+Same analysis as Case 4, but we want the smallest number of Y steps
+which means the largest N, so we use the <= inequality:
+
+        N = floor((2Mdy + dy - B) / 2dx)
+
+Now let's try the Y coordinates, we have the same 4 cases.
+
+Case 5: X major, starting Y coordinate moved by N steps
+
+                -2dx <= 2Mdy - 2Ndx - dx - B < 0
+        2Mdy >= 2Ndx + dx + B - 2dx     2Mdy < 2Ndx + dx + B
+        2Mdy >= 2Ndx - dx + B           M < (2Ndx + dx + B) / 2dy
+        M >= (2Ndx - dx + B) / 2dy
+
+Since we are trying to find the smallest M, we use the >= inequality:
+
+        M = ceiling((2Ndx - dx + B) / 2dy)
+          = floor((2Ndx - dx + B + 2dy - 1) / 2dy)
+          = floor((2Ndx - dx + B - 1) / 2dy) + 1
+
+Case 5b: X major, ending Y coordinate moved to N steps
+
+Same derivations as Case 5, but we want the largest M that satisfies
+the equations, so we use the < inequality:
+
+        M = ceiling((2Ndx + dx + B) / 2dy) - 1
+          = floor((2Ndx + dx + B + 2dy - 1) / 2dy) - 1
+          = floor((2Ndx + dx + B + 2dy - 1 - 2dy) / 2dy)
+          = floor((2Ndx + dx + B - 1) / 2dy)
+
+Case 6: X major, ending Y coordinate moved by N steps
+
+                -2dx <= 2(dx - M)dy - 2(dy - N)dx - dx - B < 0
+                -2dx <= 2dxdy - 2Mdy - 2dxdy + 2Ndx - dx - B < 0
+                -2dx <= 2Ndx - 2Mdy - dx - B < 0
+        2Mdy <= 2Ndx - dx - B + 2dx     2Mdy > 2Ndx - dx - B
+        2Mdy <= 2Ndx + dx - B           M > (2Ndx - dx - B) / 2dy
+        M <= (2Ndx + dx - B) / 2dy
+
+Largest # of X steps means smallest M, so use the > inequality:
+
+        M = floor((2Ndx - dx - B) / 2dy) + 1
+
+Case 6b: X major, starting Y coordinate moved to N steps from end
+
+Same derivations as Case 6, but we want the smallest # of X steps
+which means the largest M, so use the <= inequality:
+
+        M = floor((2Ndx + dx - B) / 2dy)
+
+Case 7: Y major, starting Y coordinate moved by N steps
+
+                -2dy <= 2Ndx - 2Mdy - dy - B < 0
+        2Mdy <= 2Ndx - dy - B + 2dy     2Mdy > 2Ndx - dy - B
+        2Mdy <= 2Ndx + dy - B           M > (2Ndx - dy - B) / 2dy
+        M <= (2Ndx + dy - B) / 2dy
+
+To find the smallest M, use the > inequality:
+
+        M = floor((2Ndx - dy - B) / 2dy) + 1
+          = floor((2Ndx - dy - B + 2dy) / 2dy)
+          = floor((2Ndx + dy - B) / 2dy)
+
+Case 7b: Y major, ending Y coordinate moved to N steps
+
+Same derivations as Case 7, but we want the largest M that satisfies
+the equations, so use the <= inequality:
+
+        M = floor((2Ndx + dy - B) / 2dy)
+
+Case 8: Y major, ending Y coordinate moved by N steps
+
+                -2dy <= 2(dy - N)dx - 2(dx - M)dy - dy - B < 0
+                -2dy <= 2dxdy - 2Ndx - 2dxdy + 2Mdy - dy - B < 0
+                -2dy <= 2Mdy - 2Ndx - dy - B < 0
+        2Mdy >= 2Ndx + dy + B - 2dy     2Mdy < 2Ndx + dy + B
+        2Mdy >= 2Ndx - dy + B           M < (2Ndx + dy + B) / 2dy
+        M >= (2Ndx - dy + B) / 2dy
+
+To find the highest X steps, find the smallest M, use the >= inequality:
+
+        M = ceiling((2Ndx - dy + B) / 2dy)
+          = floor((2Ndx - dy + B + 2dy - 1) / 2dy)
+          = floor((2Ndx + dy + B - 1) / 2dy)
+
+Case 8b: Y major, starting Y coordinate moved to N steps from the end
+
+Same derivations as Case 8, but we want to find the smallest # of X
+steps which means the largest M, so we use the < inequality:
+
+        M = ceiling((2Ndx + dy + B) / 2dy) - 1
+          = floor((2Ndx + dy + B + 2dy - 1) / 2dy) - 1
+          = floor((2Ndx + dy + B + 2dy - 1 - 2dy) / 2dy)
+          = floor((2Ndx + dy + B - 1) / 2dy)
+
+So, our equations are:
+
+        1:  X major move x1 to x1+M     floor((2Mdy + dx - B) / 2dx)
+        1b: X major move x2 to x1+M     floor((2Mdy + dx - B) / 2dx)
+        2:  X major move x2 to x2-M     floor((2Mdy + dx + B - 1) / 2dx)
+        2b: X major move x1 to x2-M     floor((2Mdy + dx + B - 1) / 2dx)
+
+        3:  Y major move x1 to x1+M     floor((2Mdy - dy + B - 1) / 2dx) + 1
+        3b: Y major move x2 to x1+M     floor((2Mdy + dy + B - 1) / 2dx)
+        4:  Y major move x2 to x2-M     floor((2Mdy - dy - B) / 2dx) + 1
+        4b: Y major move x1 to x2-M     floor((2Mdy + dy - B) / 2dx)
+
+        5:  X major move y1 to y1+N     floor((2Ndx - dx + B - 1) / 2dy) + 1
+        5b: X major move y2 to y1+N     floor((2Ndx + dx + B - 1) / 2dy)
+        6:  X major move y2 to y2-N     floor((2Ndx - dx - B) / 2dy) + 1
+        6b: X major move y1 to y2-N     floor((2Ndx + dx - B) / 2dy)
+
+        7:  Y major move y1 to y1+N     floor((2Ndx + dy - B) / 2dy)
+        7b: Y major move y2 to y1+N     floor((2Ndx + dy - B) / 2dy)
+        8:  Y major move y2 to y2-N     floor((2Ndx + dy + B - 1) / 2dy)
+        8b: Y major move y1 to y2-N     floor((2Ndx + dy + B - 1) / 2dy)
+
+We have the following constraints on all of the above terms:
+
+        0 < M,N <= 2^15          2^15 can be imposed by miZeroClipLine
+        0 <= dx/dy <= 2^16 - 1
+        0 <= B <= 1
+
+The floor in all of the above equations can be accomplished with a
+simple C divide operation provided that both numerator and denominator
+are positive.
+
+Since dx,dy >= 0 and since moving an X coordinate implies that dx != 0
+and moving a Y coordinate implies dy != 0, we know that the denominators
+are all > 0.
+
+For all lines, (-B) and (B-1) are both either 0 or -1, depending on the
+bias.  Thus, we have to show that the 2MNdxy +/- dxy terms are all >= 1
+or > 0 to prove that the numerators are positive (or zero).
+
+For X Major lines we know that dx > 0 and since 2Mdy is >= 0 due to the
+constraints, the first four equations all have numerators >= 0.
+
+For the second four equations, M > 0, so 2Mdy >= 2dy so (2Mdy - dy) >= dy
+So (2Mdy - dy) > 0, since they are Y major lines.  Also, (2Mdy + dy) >= 3dy
+or (2Mdy + dy) > 0.  So all of their numerators are >= 0.
+
+For the third set of four equations, N > 0, so 2Ndx >= 2dx so (2Ndx - dx)
+>= dx > 0.  Similarly (2Ndx + dx) >= 3dx > 0.  So all numerators >= 0.
+
+For the fourth set of equations, dy > 0 and 2Ndx >= 0, so all numerators
+are > 0.
+
+To consider overflow, consider the case of 2 * M,N * dx,dy + dx,dy.  This
+is bounded <= 2 * 2^15 * (2^16 - 1) + (2^16 - 1)
+           <= 2^16 * (2^16 - 1) + (2^16 - 1)
+           <= 2^32 - 2^16 + 2^16 - 1
+           <= 2^32 - 1
+Since the (-B) and (B-1) terms are all 0 or -1, the maximum value of
+the numerator is therefore (2^32 - 1), which does not overflow an unsigned
+32 bit variable.
+
+*/
+
+/* Bit codes for the terms of the 16 clipping equations defined below. */
+
+#define T_2NDX          (1 << 0)
+#define T_2MDY          (0)     /* implicit term */
+#define T_DXNOTY        (1 << 1)
+#define T_DYNOTX        (0)     /* implicit term */
+#define T_SUBDXORY      (1 << 2)
+#define T_ADDDX                 (T_DXNOTY)      /* composite term */
+#define T_SUBDX                 (T_DXNOTY | T_SUBDXORY) /* composite term */
+#define T_ADDDY                 (T_DYNOTX)      /* composite term */
+#define T_SUBDY                 (T_DYNOTX | T_SUBDXORY) /* composite term */
+#define T_BIASSUBONE    (1 << 3)
+#define T_SUBBIAS       (0)     /* implicit term */
+#define T_DIV2DX        (1 << 4)
+#define T_DIV2DY        (0)     /* implicit term */
+#define T_ADDONE        (1 << 5)
+
+/* Bit masks defining the 16 equations used in miZeroClipLine. */
+
+#define EQN1    (T_2MDY | T_ADDDX | T_SUBBIAS    | T_DIV2DX)
+#define EQN1B   (T_2MDY | T_ADDDX | T_SUBBIAS    | T_DIV2DX)
+#define EQN2    (T_2MDY | T_ADDDX | T_BIASSUBONE | T_DIV2DX)
+#define EQN2B   (T_2MDY | T_ADDDX | T_BIASSUBONE | T_DIV2DX)
+
+#define EQN3    (T_2MDY | T_SUBDY | T_BIASSUBONE | T_DIV2DX | T_ADDONE)
+#define EQN3B   (T_2MDY | T_ADDDY | T_BIASSUBONE | T_DIV2DX)
+#define EQN4    (T_2MDY | T_SUBDY | T_SUBBIAS    | T_DIV2DX | T_ADDONE)
+#define EQN4B   (T_2MDY | T_ADDDY | T_SUBBIAS    | T_DIV2DX)
+
+#define EQN5    (T_2NDX | T_SUBDX | T_BIASSUBONE | T_DIV2DY | T_ADDONE)
+#define EQN5B   (T_2NDX | T_ADDDX | T_BIASSUBONE | T_DIV2DY)
+#define EQN6    (T_2NDX | T_SUBDX | T_SUBBIAS    | T_DIV2DY | T_ADDONE)
+#define EQN6B   (T_2NDX | T_ADDDX | T_SUBBIAS    | T_DIV2DY)
+
+#define EQN7    (T_2NDX | T_ADDDY | T_SUBBIAS    | T_DIV2DY)
+#define EQN7B   (T_2NDX | T_ADDDY | T_SUBBIAS    | T_DIV2DY)
+#define EQN8    (T_2NDX | T_ADDDY | T_BIASSUBONE | T_DIV2DY)
+#define EQN8B   (T_2NDX | T_ADDDY | T_BIASSUBONE | T_DIV2DY)
+
+/* miZeroClipLine
+ *
+ * returns:  1 for partially clipped line
+ *          -1 for completely clipped line
+ *
+ */
+static int
+miZeroClipLine (int xmin, int ymin, int xmax, int ymax,
+                int *new_x1, int *new_y1, int *new_x2, int *new_y2,
+                unsigned int adx, unsigned int ady,
+                int *pt1_clipped, int *pt2_clipped, int octant, unsigned int bias, int oc1, int oc2)
+{
+    int swapped = 0;
+    int clipDone = 0;
+    CARD32 utmp = 0;
+    int clip1, clip2;
+    int x1, y1, x2, y2;
+    int x1_orig, y1_orig, x2_orig, y2_orig;
+    int xmajor;
+    int negslope = 0, anchorval = 0;
+    unsigned int eqn = 0;
+
+    x1 = x1_orig = *new_x1;
+    y1 = y1_orig = *new_y1;
+    x2 = x2_orig = *new_x2;
+    y2 = y2_orig = *new_y2;
+
+    clip1 = 0;
+    clip2 = 0;
+
+    xmajor = IsXMajorOctant (octant);
+    bias = ((bias >> octant) & 1);
+
+    while (1) {
+        if ((oc1 & oc2) != 0) { /* trivial reject */
+            clipDone = -1;
+            clip1 = oc1;
+            clip2 = oc2;
+            break;
+        } else if ((oc1 | oc2) == 0) {  /* trivial accept */
+            clipDone = 1;
+            if (swapped) {
+                SWAPINT_PAIR (x1, y1, x2, y2);
+                SWAPINT (clip1, clip2);
+            }
+            break;
+        } else {                /* have to clip */
+
+            /* only clip one point at a time */
+            if (oc1 == 0) {
+                SWAPINT_PAIR (x1, y1, x2, y2);
+                SWAPINT_PAIR (x1_orig, y1_orig, x2_orig, y2_orig);
+                SWAPINT (oc1, oc2);
+                SWAPINT (clip1, clip2);
+                swapped = !swapped;
+            }
+
+            clip1 |= oc1;
+            if (oc1 & OUT_LEFT) {
+                negslope = IsYDecreasingOctant (octant);
+                utmp = xmin - x1_orig;
+                if (utmp <= 32767) {    /* clip based on near endpt */
+                    if (xmajor)
+                        eqn = (swapped) ? EQN2 : EQN1;
+                    else
+                        eqn = (swapped) ? EQN4 : EQN3;
+                    anchorval = y1_orig;
+                } else {        /* clip based on far endpt */
+
+                    utmp = x2_orig - xmin;
+                    if (xmajor)
+                        eqn = (swapped) ? EQN1B : EQN2B;
+                    else
+                        eqn = (swapped) ? EQN3B : EQN4B;
+                    anchorval = y2_orig;
+                    negslope = !negslope;
+                }
+                x1 = xmin;
+            } else if (oc1 & OUT_ABOVE) {
+                negslope = IsXDecreasingOctant (octant);
+                utmp = ymin - y1_orig;
+                if (utmp <= 32767) {    /* clip based on near endpt */
+                    if (xmajor)
+                        eqn = (swapped) ? EQN6 : EQN5;
+                    else
+                        eqn = (swapped) ? EQN8 : EQN7;
+                    anchorval = x1_orig;
+                } else {        /* clip based on far endpt */
+
+                    utmp = y2_orig - ymin;
+                    if (xmajor)
+                        eqn = (swapped) ? EQN5B : EQN6B;
+                    else
+                        eqn = (swapped) ? EQN7B : EQN8B;
+                    anchorval = x2_orig;
+                    negslope = !negslope;
+                }
+                y1 = ymin;
+            } else if (oc1 & OUT_RIGHT) {
+                negslope = IsYDecreasingOctant (octant);
+                utmp = x1_orig - xmax;
+                if (utmp <= 32767) {    /* clip based on near endpt */
+                    if (xmajor)
+                        eqn = (swapped) ? EQN2 : EQN1;
+                    else
+                        eqn = (swapped) ? EQN4 : EQN3;
+                    anchorval = y1_orig;
+                } else {        /* clip based on far endpt */
+
+                    /*
+                     * Technically since the equations can handle
+                     * utmp == 32768, this overflow code isn't
+                     * needed since X11 protocol can't generate
+                     * a line which goes more than 32768 pixels
+                     * to the right of a clip rectangle.
+                     */
+                    utmp = xmax - x2_orig;
+                    if (xmajor)
+                        eqn = (swapped) ? EQN1B : EQN2B;
+                    else
+                        eqn = (swapped) ? EQN3B : EQN4B;
+                    anchorval = y2_orig;
+                    negslope = !negslope;
+                }
+                x1 = xmax;
+            } else if (oc1 & OUT_BELOW) {
+                negslope = IsXDecreasingOctant (octant);
+                utmp = y1_orig - ymax;
+                if (utmp <= 32767) {    /* clip based on near endpt */
+                    if (xmajor)
+                        eqn = (swapped) ? EQN6 : EQN5;
+                    else
+                        eqn = (swapped) ? EQN8 : EQN7;
+                    anchorval = x1_orig;
+                } else {        /* clip based on far endpt */
+
+                    /*
+                     * Technically since the equations can handle
+                     * utmp == 32768, this overflow code isn't
+                     * needed since X11 protocol can't generate
+                     * a line which goes more than 32768 pixels
+                     * below the bottom of a clip rectangle.
+                     */
+                    utmp = ymax - y2_orig;
+                    if (xmajor)
+                        eqn = (swapped) ? EQN5B : EQN6B;
+                    else
+                        eqn = (swapped) ? EQN7B : EQN8B;
+                    anchorval = x2_orig;
+                    negslope = !negslope;
+                }
+                y1 = ymax;
+            }
+
+            if (swapped)
+                negslope = !negslope;
+
+            utmp <<= 1;         /* utmp = 2N or 2M */
+            if (eqn & T_2NDX)
+                utmp = (utmp * adx);
+            else                /* (eqn & T_2MDY) */
+                utmp = (utmp * ady);
+            if (eqn & T_DXNOTY)
+                if (eqn & T_SUBDXORY)
+                    utmp -= adx;
+                else
+                    utmp += adx;
+            else /* (eqn & T_DYNOTX) */ if (eqn & T_SUBDXORY)
+                utmp -= ady;
+            else
+                utmp += ady;
+            if (eqn & T_BIASSUBONE)
+                utmp += bias - 1;
+            else                /* (eqn & T_SUBBIAS) */
+                utmp -= bias;
+            if (eqn & T_DIV2DX)
+                utmp /= (adx << 1);
+            else                /* (eqn & T_DIV2DY) */
+                utmp /= (ady << 1);
+            if (eqn & T_ADDONE)
+                utmp++;
+
+            if (negslope)
+                utmp = -utmp;
+
+            if (eqn & T_2NDX)   /* We are calculating X steps */
+                x1 = anchorval + utmp;
+            else                /* else, Y steps */
+                y1 = anchorval + utmp;
+
+            oc1 = 0;
+            MIOUTCODES (oc1, x1, y1, xmin, ymin, xmax, ymax);
+        }
+    }
+
+    *new_x1 = x1;
+    *new_y1 = y1;
+    *new_x2 = x2;
+    *new_y2 = y2;
+
+    *pt1_clipped = clip1;
+    *pt2_clipped = clip2;
+
+    return clipDone;
+}
+
+/* Draw lineSolid, fillStyle-independent zero width lines.
+ *
+ * Must keep X and Y coordinates in "ints" at least until after they're
+ * translated and clipped to accomodate CoordModePrevious lines with very
+ * large coordinates.
+ *
+ * Draws the same pixels regardless of sign(dx) or sign(dy).
+ *
+ * Ken Whaley
+ *
+ */
+
+#define MI_OUTPUT_POINT(xx, yy)\
+{\
+    if ( !new_span && yy == current_y)\
+    {\
+        if (xx < spans->x)\
+            spans->x = xx;\
+        ++*widths;\
+    }\
+    else\
+    {\
+        ++Nspans;\
+        ++spans;\
+        ++widths;\
+        spans->x = xx;\
+        spans->y = yy;\
+        *widths = 1;\
+        current_y = yy;\
+        new_span = FALSE;\
+    }\
+}
+
+void
+miZeroLine (GCPtr pGC, int mode,        /* Origin or Previous */
+            int npt,            /* number of points */
+            DDXPointPtr pptInit)
+{
+    int Nspans, current_y = 0;
+    DDXPointPtr ppt;
+    DDXPointPtr pspanInit, spans;
+    int *pwidthInit, *widths, list_len;
+    int xleft, ytop, xright, ybottom;
+    int new_x1, new_y1, new_x2, new_y2;
+    int x = 0, y = 0, x1, y1, x2, y2, xstart, ystart;
+    int oc1, oc2;
+    int result;
+    int pt1_clipped, pt2_clipped = 0;
+    Boolean new_span;
+    int signdx, signdy;
+    int clipdx, clipdy;
+    int width, height;
+    int adx, ady;
+    int octant;
+    unsigned int bias = miGetZeroLineBias (screen);
+    int e, e1, e2, e3;          /* Bresenham error terms */
+    int length;                 /* length of lines == # of pixels on major axis */
+
+    xleft = 0;
+    ytop = 0;
+    xright = pGC->width - 1;
+    ybottom = pGC->height - 1;
+
+    /* it doesn't matter whether we're in drawable or screen coordinates,
+     * FillSpans simply cannot take starting coordinates outside of the
+     * range of a DDXPointRec component.
+     */
+    if (xright > MAX_COORDINATE)
+        xright = MAX_COORDINATE;
+    if (ybottom > MAX_COORDINATE)
+        ybottom = MAX_COORDINATE;
+
+    /* since we're clipping to the drawable's boundaries & coordinate
+     * space boundaries, we're guaranteed that the larger of width/height
+     * is the longest span we'll need to output
+     */
+    width = xright - xleft + 1;
+    height = ybottom - ytop + 1;
+    list_len = (height >= width) ? height : width;
+    pspanInit = (DDXPointRec *)xalloc (list_len * sizeof (DDXPointRec));
+    pwidthInit = (int *)xalloc (list_len * sizeof (int));
+    if (!pspanInit || !pwidthInit)
+        return;
+
+    Nspans = 0;
+    new_span = TRUE;
+    spans = pspanInit - 1;
+    widths = pwidthInit - 1;
+    ppt = pptInit;
+
+    xstart = ppt->x;
+    ystart = ppt->y;
+
+    /* x2, y2, oc2 copied to x1, y1, oc1 at top of loop to simplify
+     * iteration logic
+     */
+    x2 = xstart;
+    y2 = ystart;
+    oc2 = 0;
+    MIOUTCODES (oc2, x2, y2, xleft, ytop, xright, ybottom);
+
+    while (--npt > 0) {
+        if (Nspans > 0)
+            (*pGC->ops->FillSpans) (pGC, Nspans, pspanInit, pwidthInit, FALSE, TRUE);
+        Nspans = 0;
+        new_span = TRUE;
+        spans = pspanInit - 1;
+        widths = pwidthInit - 1;
+
+        x1 = x2;
+        y1 = y2;
+        oc1 = oc2;
+        ++ppt;
+
+        x2 = ppt->x;
+        y2 = ppt->y;
+        if (mode == CoordModePrevious) {
+            x2 += x1;
+            y2 += y1;
+        }
+
+        oc2 = 0;
+        MIOUTCODES (oc2, x2, y2, xleft, ytop, xright, ybottom);
+
+        CalcLineDeltas (x1, y1, x2, y2, adx, ady, signdx, signdy, 1, 1, octant);
+
+        if (adx > ady) {
+            e1 = ady << 1;
+            e2 = e1 - (adx << 1);
+            e = e1 - adx;
+            length = adx;       /* don't draw endpoint in main loop */
+
+            FIXUP_ERROR (e, octant, bias);
+
+            new_x1 = x1;
+            new_y1 = y1;
+            new_x2 = x2;
+            new_y2 = y2;
+            pt1_clipped = 0;
+            pt2_clipped = 0;
+
+            if ((oc1 | oc2) != 0) {
+                result = miZeroClipLine (xleft, ytop, xright, ybottom,
+                                         &new_x1, &new_y1, &new_x2, &new_y2,
+                                         adx, ady,
+                                         &pt1_clipped, &pt2_clipped, octant, bias, oc1, oc2);
+                if (result == -1)
+                    continue;
+
+                length = abs (new_x2 - new_x1);
+
+                /* if we've clipped the endpoint, always draw the full length
+                 * of the segment, because then the capstyle doesn't matter
+                 */
+                if (pt2_clipped)
+                    length++;
+
+                if (pt1_clipped) {
+                    /* must calculate new error terms */
+                    clipdx = abs (new_x1 - x1);
+                    clipdy = abs (new_y1 - y1);
+                    e += (clipdy * e2) + ((clipdx - clipdy) * e1);
+                }
+            }
+
+            /* draw the segment */
+
+            x = new_x1;
+            y = new_y1;
+
+            e3 = e2 - e1;
+            e = e - e1;
+
+            while (length--) {
+                MI_OUTPUT_POINT (x, y);
+                e += e1;
+                if (e >= 0) {
+                    y += signdy;
+                    e += e3;
+                }
+                x += signdx;
+            }
+        } else {                /* Y major line */
+
+            e1 = adx << 1;
+            e2 = e1 - (ady << 1);
+            e = e1 - ady;
+            length = ady;       /* don't draw endpoint in main loop */
+
+            SetYMajorOctant (octant);
+            FIXUP_ERROR (e, octant, bias);
+
+            new_x1 = x1;
+            new_y1 = y1;
+            new_x2 = x2;
+            new_y2 = y2;
+            pt1_clipped = 0;
+            pt2_clipped = 0;
+
+            if ((oc1 | oc2) != 0) {
+                result = miZeroClipLine (xleft, ytop, xright, ybottom,
+                                         &new_x1, &new_y1, &new_x2, &new_y2,
+                                         adx, ady,
+                                         &pt1_clipped, &pt2_clipped, octant, bias, oc1, oc2);
+                if (result == -1)
+                    continue;
+
+                length = abs (new_y2 - new_y1);
+
+                /* if we've clipped the endpoint, always draw the full length
+                 * of the segment, because then the capstyle doesn't matter
+                 */
+                if (pt2_clipped)
+                    length++;
+
+                if (pt1_clipped) {
+                    /* must calculate new error terms */
+                    clipdx = abs (new_x1 - x1);
+                    clipdy = abs (new_y1 - y1);
+                    e += (clipdx * e2) + ((clipdy - clipdx) * e1);
+                }
+            }
+
+            /* draw the segment */
+
+            x = new_x1;
+            y = new_y1;
+
+            e3 = e2 - e1;
+            e = e - e1;
+
+            while (length--) {
+                MI_OUTPUT_POINT (x, y);
+                e += e1;
+                if (e >= 0) {
+                    x += signdx;
+                    e += e3;
+                }
+                y += signdy;
+            }
+        }
+    }
+
+    /* only do the capnotlast check on the last segment
+     * and only if the endpoint wasn't clipped.  And then, if the last
+     * point is the same as the first point, do not draw it, unless the
+     * line is degenerate
+     */
+    if ((!pt2_clipped) && (pGC->capStyle != CapNotLast) &&
+        (((xstart != x2) || (ystart != y2)) || (ppt == pptInit + 1))) {
+        MI_OUTPUT_POINT (x, y);
+    }
+
+    if (Nspans > 0)
+        (*pGC->ops->FillSpans) (pGC, Nspans, pspanInit, pwidthInit, FALSE, TRUE);
+
+    xfree (pwidthInit);
+    xfree (pspanInit);
+}
+
+void
+miZeroDashLine (GCPtr pgc, int mode, int nptInit,       /* number of points in polyline */
+                DDXPointRec * pptInit   /* points in the polyline */
+    )
+{
+    /* XXX kludge until real zero-width dash code is written */
+    pgc->lineWidth = 1;
+    miWideDash (pgc, mode, nptInit, pptInit);
+    pgc->lineWidth = 0;
+}
+
+static void miLineArc (GCPtr pGC,
+                       Boolean foreground, SpanDataPtr spanData,
+                       LineFacePtr leftFace,
+                       LineFacePtr rightFace, double xorg, double yorg, Boolean isInt);
+
+
+/*
+ * spans-based polygon filler
+ */
+
+static void
+miFillPolyHelper (GCPtr pGC, Boolean foreground,
+                  SpanDataPtr spanData, int y, int overall_height,
+                  PolyEdgePtr left, PolyEdgePtr right, int left_count, int right_count)
+{
+    int left_x = 0, left_e = 0;
+    int left_stepx = 0;
+    int left_signdx = 0;
+    int left_dy = 0, left_dx = 0;
+
+    int right_x = 0, right_e = 0;
+    int right_stepx = 0;
+    int right_signdx = 0;
+    int right_dy = 0, right_dx = 0;
+
+    int height = 0;
+    int left_height = 0, right_height = 0;
+
+    DDXPointPtr ppt;
+    DDXPointPtr pptInit = NULL;
+    int *pwidth;
+    int *pwidthInit = NULL;
+    int xorg;
+    Spans spanRec;
+
+    left_height = 0;
+    right_height = 0;
+
+    if (!spanData) {
+        pptInit = (DDXPointRec *)xalloc (overall_height * sizeof (*ppt));
+        if (!pptInit)
+            return;
+        pwidthInit = (int *)xalloc (overall_height * sizeof (*pwidth));
+        if (!pwidthInit) {
+            xfree (pptInit);
+            return;
+        }
+        ppt = pptInit;
+        pwidth = pwidthInit;
+    } else {
+        spanRec.points = (DDXPointRec *)xalloc (overall_height * sizeof (*ppt));
+        if (!spanRec.points)
+            return;
+        spanRec.widths = (int *)xalloc (overall_height * sizeof (int));
+        if (!spanRec.widths) {
+            xfree (spanRec.points);
+            return;
+        }
+        ppt = spanRec.points;
+        pwidth = spanRec.widths;
+    }
+
+    xorg = 0;
+    while ((left_count || left_height) && (right_count || right_height)) {
+        MIPOLYRELOADLEFT MIPOLYRELOADRIGHT height = left_height;
+        if (height > right_height)
+            height = right_height;
+
+        left_height -= height;
+        right_height -= height;
+
+        while (--height >= 0) {
+            if (right_x >= left_x) {
+                ppt->y = y;
+                ppt->x = left_x + xorg;
+                ppt++;
+                *pwidth++ = right_x - left_x + 1;
+            }
+            y++;
+
+        MIPOLYSTEPLEFT MIPOLYSTEPRIGHT}
+    }
+    if (!spanData) {
+        (*pGC->ops->FillSpans) (pGC, ppt - pptInit, pptInit, pwidthInit, TRUE, foreground);
+        xfree (pwidthInit);
+        xfree (pptInit);
+    } else {
+        spanRec.count = ppt - spanRec.points;
+        AppendSpanGroup (pGC, foreground, &spanRec, spanData)
+    }
+}
+
+static void
+miFillRectPolyHelper (GCPtr pGC, Boolean foreground, SpanDataPtr spanData, int x, int y, int w, int h)
+{
+    DDXPointPtr ppt;
+    int *pwidth;
+    Spans spanRec;
+    xRectangle rect;
+
+    if (!spanData) {
+        rect.x = x;
+        rect.y = y;
+        rect.width = w;
+        rect.height = h;
+        (*pGC->ops->FillRects) (pGC, 1, &rect, foreground);
+    } else {
+        spanRec.points = (DDXPointRec *)xalloc (h * sizeof (*ppt));
+        if (!spanRec.points)
+            return;
+        spanRec.widths = (int *)xalloc (h * sizeof (int));
+        if (!spanRec.widths) {
+            xfree (spanRec.points);
+            return;
+        }
+        ppt = spanRec.points;
+        pwidth = spanRec.widths;
+
+        while (h--) {
+            ppt->x = x;
+            ppt->y = y;
+            ppt++;
+            *pwidth++ = w;
+            y++;
+        }
+        spanRec.count = ppt - spanRec.points;
+        AppendSpanGroup (pGC, foreground, &spanRec, spanData)
+    }
+}
+
+static int
+miPolyBuildEdge (double x0, double y0, double k,        /* x0 * dy - y0 * dx */
+                 int dx, int dy, int xi, int yi, int left, PolyEdgePtr edge)
+{
+    int x, y, e;
+    int xady;
+
+    if (dy < 0) {
+        dy = -dy;
+        dx = -dx;
+        k = -k;
+    }
+#ifdef NOTDEF
+    {
+        double realk, kerror;
+        realk = x0 * dy - y0 * dx;
+        kerror = Fabs (realk - k);
+        if (kerror > .1)
+            printf ("realk: %g k: %g\n", realk, k);
+    }
+#endif
+    y = ICEIL (y0);
+    xady = ICEIL (k) + y * dx;
+
+    if (xady <= 0)
+        x = -(-xady / dy) - 1;
+    else
+        x = (xady - 1) / dy;
+
+    e = xady - x * dy;
+
+    if (dx >= 0) {
+        edge->signdx = 1;
+        edge->stepx = dx / dy;
+        edge->dx = dx % dy;
+    } else {
+        edge->signdx = -1;
+        edge->stepx = -(-dx / dy);
+        edge->dx = -dx % dy;
+        e = dy - e + 1;
+    }
+    edge->dy = dy;
+    edge->x = x + left + xi;
+    edge->e = e - dy;           /* bias to compare against 0 instead of dy */
+    return y + yi;
+}
+
+#define StepAround(v, incr, max) (((v) + (incr) < 0) ? (max - 1) : ((v) + (incr) == max) ? 0 : ((v) + (incr)))
+
+static int
+miPolyBuildPoly (PolyVertexPtr vertices,
+                 PolySlopePtr slopes,
+                 int count,
+                 int xi,
+                 int yi, PolyEdgePtr left, PolyEdgePtr right, int *pnleft, int *pnright, int *h)
+{
+    int top, bottom;
+    double miny, maxy;
+    int i;
+    int j;
+    int clockwise;
+    int slopeoff;
+    int s;
+    int nright, nleft;
+    int y, lasty = 0, bottomy, topy = 0;
+
+    /* find the top of the polygon */
+    maxy = miny = vertices[0].y;
+    bottom = top = 0;
+    for (i = 1; i < count; i++) {
+        if (vertices[i].y < miny) {
+            top = i;
+            miny = vertices[i].y;
+        }
+        if (vertices[i].y >= maxy) {
+            bottom = i;
+            maxy = vertices[i].y;
+        }
+    }
+    clockwise = 1;
+    slopeoff = 0;
+
+    i = top;
+    j = StepAround (top, -1, count);
+
+    if (slopes[j].dy * slopes[i].dx > slopes[i].dy * slopes[j].dx) {
+        clockwise = -1;
+        slopeoff = -1;
+    }
+
+    bottomy = ICEIL (maxy) + yi;
+
+    nright = 0;
+
+    s = StepAround (top, slopeoff, count);
+    i = top;
+    while (i != bottom) {
+        if (slopes[s].dy != 0) {
+            y = miPolyBuildEdge (vertices[i].x, vertices[i].y,
+                                 slopes[s].k,
+                                 slopes[s].dx, slopes[s].dy, xi, yi, 0, &right[nright]);
+            if (nright != 0)
+                right[nright - 1].height = y - lasty;
+            else
+                topy = y;
+            nright++;
+            lasty = y;
+        }
+
+        i = StepAround (i, clockwise, count);
+        s = StepAround (s, clockwise, count);
+    }
+    if (nright != 0)
+        right[nright - 1].height = bottomy - lasty;
+
+    if (slopeoff == 0)
+        slopeoff = -1;
+    else
+        slopeoff = 0;
+
+    nleft = 0;
+    s = StepAround (top, slopeoff, count);
+    i = top;
+    while (i != bottom) {
+        if (slopes[s].dy != 0) {
+            y = miPolyBuildEdge (vertices[i].x, vertices[i].y,
+                                 slopes[s].k, slopes[s].dx, slopes[s].dy, xi, yi, 1, &left[nleft]);
+
+            if (nleft != 0)
+                left[nleft - 1].height = y - lasty;
+            nleft++;
+            lasty = y;
+        }
+        i = StepAround (i, -clockwise, count);
+        s = StepAround (s, -clockwise, count);
+    }
+    if (nleft != 0)
+        left[nleft - 1].height = bottomy - lasty;
+    *pnleft = nleft;
+    *pnright = nright;
+    *h = bottomy - topy;
+    return topy;
+}
+
+static void
+miLineOnePoint (GCPtr pGC, Boolean foreground, SpanDataPtr spanData, int x, int y)
+{
+    DDXPointRec pt;
+    int wid;
+
+    wid = 1;
+    pt.x = x;
+    pt.y = y;
+    (*pGC->ops->FillSpans) (pGC, 1, &pt, &wid, TRUE, foreground);
+}
+
+static void
+miLineJoin (GCPtr pGC, Boolean foreground, SpanDataPtr spanData, LineFacePtr pLeft, LineFacePtr pRight)
+{
+    double mx = 0, my = 0;
+    double denom = 0.0;
+    PolyVertexRec vertices[4];
+    PolySlopeRec slopes[4];
+    int edgecount;
+    PolyEdgeRec left[4], right[4];
+    int nleft, nright;
+    int y, height;
+    int swapslopes;
+    int joinStyle = pGC->joinStyle;
+    int lw = pGC->lineWidth;
+
+    if (lw == 1 && !spanData) {
+        /* See if one of the lines will draw the joining pixel */
+        if (pLeft->dx > 0 || (pLeft->dx == 0 && pLeft->dy > 0))
+            return;
+        if (pRight->dx > 0 || (pRight->dx == 0 && pRight->dy > 0))
+            return;
+        if (joinStyle != JoinRound) {
+            denom = -pLeft->dx * (double) pRight->dy + pRight->dx * (double) pLeft->dy;
+            if (denom == 0)
+                return;         /* no join to draw */
+        }
+        if (joinStyle != JoinMiter) {
+            miLineOnePoint (pGC, foreground, spanData, pLeft->x, pLeft->y);
+            return;
+        }
+    } else {
+        if (joinStyle == JoinRound) {
+            miLineArc (pGC, foreground, spanData, pLeft, pRight, (double) 0.0, (double) 0.0, TRUE);
+            return;
+        }
+        denom = -pLeft->dx * (double) pRight->dy + pRight->dx * (double) pLeft->dy;
+        if (denom == 0.0)
+            return;             /* no join to draw */
+    }
+
+    swapslopes = 0;
+    if (denom > 0) {
+        pLeft->xa = -pLeft->xa;
+        pLeft->ya = -pLeft->ya;
+        pLeft->dx = -pLeft->dx;
+        pLeft->dy = -pLeft->dy;
+    } else {
+        swapslopes = 1;
+        pRight->xa = -pRight->xa;
+        pRight->ya = -pRight->ya;
+        pRight->dx = -pRight->dx;
+        pRight->dy = -pRight->dy;
+    }
+
+    vertices[0].x = pRight->xa;
+    vertices[0].y = pRight->ya;
+    slopes[0].dx = -pRight->dy;
+    slopes[0].dy = pRight->dx;
+    slopes[0].k = 0;
+
+    vertices[1].x = 0;
+    vertices[1].y = 0;
+    slopes[1].dx = pLeft->dy;
+    slopes[1].dy = -pLeft->dx;
+    slopes[1].k = 0;
+
+    vertices[2].x = pLeft->xa;
+    vertices[2].y = pLeft->ya;
+
+    if (joinStyle == JoinMiter) {
+        my = (pLeft->dy * (pRight->xa * pRight->dy - pRight->ya * pRight->dx) -
+              pRight->dy * (pLeft->xa * pLeft->dy - pLeft->ya * pLeft->dx)) / denom;
+        if (pLeft->dy != 0) {
+            mx = pLeft->xa + (my - pLeft->ya) * (double) pLeft->dx / (double) pLeft->dy;
+        } else {
+            mx = pRight->xa + (my - pRight->ya) * (double) pRight->dx / (double) pRight->dy;
+        }
+        /* check miter limit */
+        if ((mx * mx + my * my) * 4 > SQSECANT * lw * lw)
+            joinStyle = JoinBevel;
+    }
+
+    if (joinStyle == JoinMiter) {
+        slopes[2].dx = pLeft->dx;
+        slopes[2].dy = pLeft->dy;
+        slopes[2].k = pLeft->k;
+        if (swapslopes) {
+            slopes[2].dx = -slopes[2].dx;
+            slopes[2].dy = -slopes[2].dy;
+            slopes[2].k = -slopes[2].k;
+        }
+        vertices[3].x = mx;
+        vertices[3].y = my;
+        slopes[3].dx = pRight->dx;
+        slopes[3].dy = pRight->dy;
+        slopes[3].k = pRight->k;
+        if (swapslopes) {
+            slopes[3].dx = -slopes[3].dx;
+            slopes[3].dy = -slopes[3].dy;
+            slopes[3].k = -slopes[3].k;
+        }
+        edgecount = 4;
+    } else {
+        double scale, dx, dy, adx, ady;
+
+        adx = dx = pRight->xa - pLeft->xa;
+        ady = dy = pRight->ya - pLeft->ya;
+        if (adx < 0)
+            adx = -adx;
+        if (ady < 0)
+            ady = -ady;
+        scale = ady;
+        if (adx > ady)
+            scale = adx;
+        slopes[2].dx = (dx * 65536) / scale;
+        slopes[2].dy = (dy * 65536) / scale;
+        slopes[2].k = ((pLeft->xa + pRight->xa) * slopes[2].dy -
+                       (pLeft->ya + pRight->ya) * slopes[2].dx) / 2.0;
+        edgecount = 3;
+    }
+
+    y = miPolyBuildPoly (vertices, slopes, edgecount, pLeft->x, pLeft->y,
+                         left, right, &nleft, &nright, &height);
+    miFillPolyHelper (pGC, foreground, spanData, y, height, left, right, nleft, nright);
+}
+
+static int
+miLineArcI (GCPtr pGC, int xorg, int yorg, DDXPointPtr points, int *widths)
+{
+    DDXPointPtr tpts, bpts;
+    int *twids, *bwids;
+    int x, y, e, ex, slw;
+
+    tpts = points;
+    twids = widths;
+    slw = pGC->lineWidth;
+    if (slw == 1) {
+        tpts->x = xorg;
+        tpts->y = yorg;
+        *twids = 1;
+        return 1;
+    }
+    bpts = tpts + slw;
+    bwids = twids + slw;
+    y = (slw >> 1) + 1;
+    if (slw & 1)
+        e = -((y << 2) + 3);
+    else
+        e = -(y << 3);
+    ex = -4;
+    x = 0;
+    while (y) {
+        e += (y << 3) - 4;
+        while (e >= 0) {
+            x++;
+            e += (ex = -((x << 3) + 4));
+        }
+        y--;
+        slw = (x << 1) + 1;
+        if ((e == ex) && (slw > 1))
+            slw--;
+        tpts->x = xorg - x;
+        tpts->y = yorg - y;
+        tpts++;
+        *twids++ = slw;
+        if ((y != 0) && ((slw > 1) || (e != ex))) {
+            bpts--;
+            bpts->x = xorg - x;
+            bpts->y = yorg + y;
+            *--bwids = slw;
+        }
+    }
+    return (pGC->lineWidth);
+}
+
+#define CLIPSTEPEDGE(edgey,edge,edgeleft) \
+    if (ybase == edgey) \
+    { \
+        if (edgeleft) \
+        { \
+            if (edge->x > xcl) \
+                xcl = edge->x; \
+        } \
+        else \
+        { \
+            if (edge->x < xcr) \
+                xcr = edge->x; \
+        } \
+        edgey++; \
+        edge->x += edge->stepx; \
+        edge->e += edge->dx; \
+        if (edge->e > 0) \
+        { \
+            edge->x += edge->signdx; \
+            edge->e -= edge->dy; \
+        } \
+    }
+
+static int
+miLineArcD (GCPtr pGC,
+            double xorg,
+            double yorg,
+            DDXPointPtr points,
+            int *widths,
+            PolyEdgePtr edge1,
+            int edgey1, Boolean edgeleft1, PolyEdgePtr edge2, int edgey2, Boolean edgeleft2)
+{
+    DDXPointPtr pts;
+    int *wids;
+    double radius, x0, y0, el, er, yk, xlk, xrk, k;
+    int xbase, ybase, y, boty, xl, xr, xcl, xcr;
+    int ymin, ymax;
+    Boolean edge1IsMin, edge2IsMin;
+    int ymin1, ymin2;
+
+    pts = points;
+    wids = widths;
+    xbase = floor (xorg);
+    x0 = xorg - xbase;
+    ybase = ICEIL (yorg);
+    y0 = yorg - ybase;
+    xlk = x0 + x0 + 1.0;
+    xrk = x0 + x0 - 1.0;
+    yk = y0 + y0 - 1.0;
+    radius = ((double) pGC->lineWidth) / 2.0;
+    y = floor (radius - y0 + 1.0);
+    ybase -= y;
+    ymin = ybase;
+    ymax = 65536;
+    edge1IsMin = FALSE;
+    ymin1 = edgey1;
+    if (edge1->dy >= 0) {
+        if (!edge1->dy) {
+            if (edgeleft1)
+                edge1IsMin = TRUE;
+            else
+                ymax = edgey1;
+            edgey1 = 65536;
+        } else {
+            if ((edge1->signdx < 0) == edgeleft1)
+                edge1IsMin = TRUE;
+        }
+    }
+    edge2IsMin = FALSE;
+    ymin2 = edgey2;
+    if (edge2->dy >= 0) {
+        if (!edge2->dy) {
+            if (edgeleft2)
+                edge2IsMin = TRUE;
+            else
+                ymax = edgey2;
+            edgey2 = 65536;
+        } else {
+            if ((edge2->signdx < 0) == edgeleft2)
+                edge2IsMin = TRUE;
+        }
+    }
+    if (edge1IsMin) {
+        ymin = ymin1;
+        if (edge2IsMin && ymin1 > ymin2)
+            ymin = ymin2;
+    } else if (edge2IsMin)
+        ymin = ymin2;
+    el = radius * radius - ((y + y0) * (y + y0)) - (x0 * x0);
+    er = el + xrk;
+    xl = 1;
+    xr = 0;
+    if (x0 < 0.5) {
+        xl = 0;
+        el -= xlk;
+    }
+    boty = (y0 < -0.5) ? 1 : 0;
+    if (ybase + y - boty > ymax)
+        boty = ymax - ybase - y;
+    while (y > boty) {
+        k = (y << 1) + yk;
+        er += k;
+        while (er > 0.0) {
+            xr++;
+            er += xrk - (xr << 1);
+        }
+        el += k;
+        while (el >= 0.0) {
+            xl--;
+            el += (xl << 1) - xlk;
+        }
+        y--;
+        ybase++;
+        if (ybase < ymin)
+            continue;
+        xcl = xl + xbase;
+        xcr = xr + xbase;
+        CLIPSTEPEDGE (edgey1, edge1, edgeleft1);
+        CLIPSTEPEDGE (edgey2, edge2, edgeleft2);
+        if (xcr >= xcl) {
+            pts->x = xcl;
+            pts->y = ybase;
+            pts++;
+            *wids++ = xcr - xcl + 1;
+        }
+    }
+    er = xrk - (xr << 1) - er;
+    el = (xl << 1) - xlk - el;
+    boty = floor (-y0 - radius + 1.0);
+    if (ybase + y - boty > ymax)
+        boty = ymax - ybase - y;
+    while (y > boty) {
+        k = (y << 1) + yk;
+        er -= k;
+        while ((er >= 0.0) && (xr >= 0)) {
+            xr--;
+            er += xrk - (xr << 1);
+        }
+        el -= k;
+        while ((el > 0.0) && (xl <= 0)) {
+            xl++;
+            el += (xl << 1) - xlk;
+        }
+        y--;
+        ybase++;
+        if (ybase < ymin)
+            continue;
+        xcl = xl + xbase;
+        xcr = xr + xbase;
+        CLIPSTEPEDGE (edgey1, edge1, edgeleft1);
+        CLIPSTEPEDGE (edgey2, edge2, edgeleft2);
+        if (xcr >= xcl) {
+            pts->x = xcl;
+            pts->y = ybase;
+            pts++;
+            *wids++ = xcr - xcl + 1;
+        }
+    }
+    return (pts - points);
+}
+
+static int
+miRoundJoinFace (LineFacePtr face, PolyEdgePtr edge, Boolean * leftEdge)
+{
+    int y;
+    int dx, dy;
+    double xa, ya;
+    Boolean left;
+
+    dx = -face->dy;
+    dy = face->dx;
+    xa = face->xa;
+    ya = face->ya;
+    left = 1;
+    if (ya > 0) {
+        ya = 0.0;
+        xa = 0.0;
+    }
+    if (dy < 0 || (dy == 0 && dx > 0)) {
+        dx = -dx;
+        dy = -dy;
+        left = !left;
+    }
+    if (dx == 0 && dy == 0)
+        dy = 1;
+    if (dy == 0) {
+        y = ICEIL (face->ya) + face->y;
+        edge->x = -32767;
+        edge->stepx = 0;
+        edge->signdx = 0;
+        edge->e = -1;
+        edge->dy = 0;
+        edge->dx = 0;
+        edge->height = 0;
+    } else {
+        y = miPolyBuildEdge (xa, ya, 0.0, dx, dy, face->x, face->y, !left, edge);
+        edge->height = 32767;
+    }
+    *leftEdge = !left;
+    return y;
+}
+
+static void
+miRoundJoinClip (LineFacePtr pLeft, LineFacePtr pRight,
+                 PolyEdgePtr edge1, PolyEdgePtr edge2, int *y1, int *y2, Boolean * left1, Boolean * left2)
+{
+    double denom;
+
+    denom = -pLeft->dx * (double) pRight->dy + pRight->dx * (double) pLeft->dy;
+
+    if (denom >= 0) {
+        pLeft->xa = -pLeft->xa;
+        pLeft->ya = -pLeft->ya;
+    } else {
+        pRight->xa = -pRight->xa;
+        pRight->ya = -pRight->ya;
+    }
+    *y1 = miRoundJoinFace (pLeft, edge1, left1);
+    *y2 = miRoundJoinFace (pRight, edge2, left2);
+}
+
+static int
+miRoundCapClip (LineFacePtr face, Boolean isInt, PolyEdgePtr edge, Boolean * leftEdge)
+{
+    int y;
+    int dx, dy;
+    double xa, ya, k;
+    Boolean left;
+
+    dx = -face->dy;
+    dy = face->dx;
+    xa = face->xa;
+    ya = face->ya;
+    k = 0.0;
+    if (!isInt)
+        k = face->k;
+    left = 1;
+    if (dy < 0 || (dy == 0 && dx > 0)) {
+        dx = -dx;
+        dy = -dy;
+        xa = -xa;
+        ya = -ya;
+        left = !left;
+    }
+    if (dx == 0 && dy == 0)
+        dy = 1;
+    if (dy == 0) {
+        y = ICEIL (face->ya) + face->y;
+        edge->x = -32767;
+        edge->stepx = 0;
+        edge->signdx = 0;
+        edge->e = -1;
+        edge->dy = 0;
+        edge->dx = 0;
+        edge->height = 0;
+    } else {
+        y = miPolyBuildEdge (xa, ya, k, dx, dy, face->x, face->y, !left, edge);
+        edge->height = 32767;
+    }
+    *leftEdge = !left;
+    return y;
+}
+
+static void
+miLineArc (GCPtr pGC,
+           Boolean foreground,
+           SpanDataPtr spanData,
+           LineFacePtr leftFace, LineFacePtr rightFace, double xorg, double yorg, Boolean isInt)
+{
+    DDXPointPtr points;
+    int *widths;
+    int xorgi = 0, yorgi = 0;
+    Spans spanRec;
+    int n;
+    PolyEdgeRec edge1, edge2;
+    int edgey1, edgey2;
+    Boolean edgeleft1, edgeleft2;
+
+    if (isInt) {
+        xorgi = leftFace ? leftFace->x : rightFace->x;
+        yorgi = leftFace ? leftFace->y : rightFace->y;
+    }
+    edgey1 = 65536;
+    edgey2 = 65536;
+    edge1.x = 0;                /* not used, keep memory checkers happy */
+    edge1.dy = -1;
+    edge2.x = 0;                /* not used, keep memory checkers happy */
+    edge2.dy = -1;
+    edgeleft1 = FALSE;
+    edgeleft2 = FALSE;
+    if ((pGC->lineStyle != LineSolid || pGC->lineWidth > 2) &&
+        ((pGC->capStyle == CapRound && pGC->joinStyle != JoinRound) ||
+         (pGC->joinStyle == JoinRound && pGC->capStyle == CapButt))) {
+        if (isInt) {
+            xorg = (double) xorgi;
+            yorg = (double) yorgi;
+        }
+        if (leftFace && rightFace) {
+            miRoundJoinClip (leftFace, rightFace, &edge1, &edge2,
+                             &edgey1, &edgey2, &edgeleft1, &edgeleft2);
+        } else if (leftFace) {
+            edgey1 = miRoundCapClip (leftFace, isInt, &edge1, &edgeleft1);
+        } else if (rightFace) {
+            edgey2 = miRoundCapClip (rightFace, isInt, &edge2, &edgeleft2);
+        }
+        isInt = FALSE;
+    }
+    if (!spanData) {
+        points = (DDXPointRec *)xalloc (sizeof (DDXPointRec) * pGC->lineWidth);
+        if (!points)
+            return;
+        widths = (int *)xalloc (sizeof (int) * pGC->lineWidth);
+        if (!widths) {
+            xfree (points);
+            return;
+        }
+    } else {
+        points = (DDXPointRec *)xalloc (pGC->lineWidth * sizeof (DDXPointRec));
+        if (!points)
+            return;
+        widths = (int *)xalloc (pGC->lineWidth * sizeof (int));
+        if (!widths) {
+            xfree (points);
+            return;
+        }
+        spanRec.points = points;
+        spanRec.widths = widths;
+    }
+    if (isInt)
+        n = miLineArcI (pGC, xorgi, yorgi, points, widths);
+    else
+        n = miLineArcD (pGC, xorg, yorg, points, widths,
+                        &edge1, edgey1, edgeleft1, &edge2, edgey2, edgeleft2);
+
+    if (!spanData) {
+        (*pGC->ops->FillSpans) (pGC, n, points, widths, TRUE, foreground);
+        xfree (widths);
+        xfree (points);
+    } else {
+        spanRec.count = n;
+        AppendSpanGroup (pGC, foreground, &spanRec, spanData)
+    }
+}
+
+static void
+miLineProjectingCap (GCPtr pGC, Boolean foreground,
+                     SpanDataPtr spanData, LineFacePtr face, Boolean isLeft,
+                     double xorg, double yorg, Boolean isInt)
+{
+    int xorgi = 0, yorgi = 0;
+    int lw;
+    PolyEdgeRec lefts[2], rights[2];
+    int lefty, righty, topy, bottomy;
+    PolyEdgePtr left, right;
+    PolyEdgePtr top, bottom;
+    double xa, ya;
+    double k;
+    double xap, yap;
+    int dx, dy;
+    double projectXoff, projectYoff;
+    double maxy;
+    int finaly;
+
+    if (isInt) {
+        xorgi = face->x;
+        yorgi = face->y;
+    }
+    lw = pGC->lineWidth;
+    dx = face->dx;
+    dy = face->dy;
+    k = face->k;
+    if (dy == 0) {
+        lefts[0].height = lw;
+        lefts[0].x = xorgi;
+        if (isLeft)
+            lefts[0].x -= (lw >> 1);
+        lefts[0].stepx = 0;
+        lefts[0].signdx = 1;
+        lefts[0].e = -lw;
+        lefts[0].dx = 0;
+        lefts[0].dy = lw;
+        rights[0].height = lw;
+        rights[0].x = xorgi;
+        if (!isLeft)
+            rights[0].x += ((lw + 1) >> 1);
+        rights[0].stepx = 0;
+        rights[0].signdx = 1;
+        rights[0].e = -lw;
+        rights[0].dx = 0;
+        rights[0].dy = lw;
+        miFillPolyHelper (pGC, foreground, spanData, yorgi - (lw >> 1), lw, lefts, rights, 1, 1);
+    } else if (dx == 0) {
+        if (dy < 0) {
+            dy = -dy;
+            isLeft = !isLeft;
+        }
+        topy = yorgi;
+        bottomy = yorgi + dy;
+        if (isLeft)
+            topy -= (lw >> 1);
+        else
+            bottomy += (lw >> 1);
+        lefts[0].height = bottomy - topy;
+        lefts[0].x = xorgi - (lw >> 1);
+        lefts[0].stepx = 0;
+        lefts[0].signdx = 1;
+        lefts[0].e = -dy;
+        lefts[0].dx = dx;
+        lefts[0].dy = dy;
+
+        rights[0].height = bottomy - topy;
+        rights[0].x = lefts[0].x + (lw - 1);
+        rights[0].stepx = 0;
+        rights[0].signdx = 1;
+        rights[0].e = -dy;
+        rights[0].dx = dx;
+        rights[0].dy = dy;
+        miFillPolyHelper (pGC, foreground, spanData, topy, bottomy - topy, lefts, rights, 1, 1);
+    } else {
+        xa = face->xa;
+        ya = face->ya;
+        projectXoff = -ya;
+        projectYoff = xa;
+        if (dx < 0) {
+            right = &rights[1];
+            left = &lefts[0];
+            top = &rights[0];
+            bottom = &lefts[1];
+        } else {
+            right = &rights[0];
+            left = &lefts[1];
+            top = &lefts[0];
+            bottom = &rights[1];
+        }
+        if (isLeft) {
+            righty = miPolyBuildEdge (xa, ya, k, dx, dy, xorgi, yorgi, 0, right);
+
+            xa = -xa;
+            ya = -ya;
+            k = -k;
+            lefty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
+                                     k, dx, dy, xorgi, yorgi, 1, left);
+            if (dx > 0) {
+                ya = -ya;
+                xa = -xa;
+            }
+            xap = xa - projectXoff;
+            yap = ya - projectYoff;
+            topy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy,
+                                    -dy, dx, xorgi, yorgi, dx > 0, top);
+            bottomy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, xorgi, yorgi, dx < 0, bottom);
+            maxy = -ya;
+        } else {
+            righty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
+                                      k, dx, dy, xorgi, yorgi, 0, right);
+
+            xa = -xa;
+            ya = -ya;
+            k = -k;
+            lefty = miPolyBuildEdge (xa, ya, k, dx, dy, xorgi, yorgi, 1, left);
+            if (dx > 0) {
+                ya = -ya;
+                xa = -xa;
+            }
+            xap = xa - projectXoff;
+            yap = ya - projectYoff;
+            topy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, xorgi, xorgi, dx > 0, top);
+            bottomy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy,
+                                       -dy, dx, xorgi, xorgi, dx < 0, bottom);
+            maxy = -ya + projectYoff;
+        }
+        finaly = ICEIL (maxy) + yorgi;
+        if (dx < 0) {
+            left->height = bottomy - lefty;
+            right->height = finaly - righty;
+            top->height = righty - topy;
+        } else {
+            right->height = bottomy - righty;
+            left->height = finaly - lefty;
+            top->height = lefty - topy;
+        }
+        bottom->height = finaly - bottomy;
+        miFillPolyHelper (pGC, foreground, spanData, topy,
+                          bottom->height + bottomy - topy, lefts, rights, 2, 2);
+    }
+}
+
+static void
+miWideSegment (GCPtr pGC,
+               Boolean foreground,
+               SpanDataPtr spanData,
+               int x1,
+               int y1,
+               int x2,
+               int y2,
+               Boolean projectLeft, Boolean projectRight, LineFacePtr leftFace, LineFacePtr rightFace)
+{
+    double l, L, r;
+    double xa, ya;
+    double projectXoff = 0.0, projectYoff = 0.0;
+    double k;
+    double maxy;
+    int x, y;
+    int dx, dy;
+    int finaly;
+    PolyEdgePtr left, right;
+    PolyEdgePtr top, bottom;
+    int lefty, righty, topy, bottomy;
+    int signdx;
+    PolyEdgeRec lefts[2], rights[2];
+    LineFacePtr tface;
+    int lw = pGC->lineWidth;
+
+    /* draw top-to-bottom always */
+    if (y2 < y1 || (y2 == y1 && x2 < x1)) {
+        x = x1;
+        x1 = x2;
+        x2 = x;
+
+        y = y1;
+        y1 = y2;
+        y2 = y;
+
+        x = projectLeft;
+        projectLeft = projectRight;
+        projectRight = x;
+
+        tface = leftFace;
+        leftFace = rightFace;
+        rightFace = tface;
+    }
+
+    dy = y2 - y1;
+    signdx = 1;
+    dx = x2 - x1;
+    if (dx < 0)
+        signdx = -1;
+
+    leftFace->x = x1;
+    leftFace->y = y1;
+    leftFace->dx = dx;
+    leftFace->dy = dy;
+
+    rightFace->x = x2;
+    rightFace->y = y2;
+    rightFace->dx = -dx;
+    rightFace->dy = -dy;
+
+    if (dy == 0) {
+        rightFace->xa = 0;
+        rightFace->ya = (double) lw / 2.0;
+        rightFace->k = -(double) (lw * dx) / 2.0;
+        leftFace->xa = 0;
+        leftFace->ya = -rightFace->ya;
+        leftFace->k = rightFace->k;
+        x = x1;
+        if (projectLeft)
+            x -= (lw >> 1);
+        y = y1 - (lw >> 1);
+        dx = x2 - x;
+        if (projectRight)
+            dx += ((lw + 1) >> 1);
+        dy = lw;
+        miFillRectPolyHelper (pGC, foreground, spanData, x, y, dx, dy);
+    } else if (dx == 0) {
+        leftFace->xa = (double) lw / 2.0;
+        leftFace->ya = 0;
+        leftFace->k = (double) (lw * dy) / 2.0;
+        rightFace->xa = -leftFace->xa;
+        rightFace->ya = 0;
+        rightFace->k = leftFace->k;
+        y = y1;
+        if (projectLeft)
+            y -= lw >> 1;
+        x = x1 - (lw >> 1);
+        dy = y2 - y;
+        if (projectRight)
+            dy += ((lw + 1) >> 1);
+        dx = lw;
+        miFillRectPolyHelper (pGC, foreground, spanData, x, y, dx, dy);
+    } else {
+        l = ((double) lw) / 2.0;
+        L = hypot ((double) dx, (double) dy);
+
+        if (dx < 0) {
+            right = &rights[1];
+            left = &lefts[0];
+            top = &rights[0];
+            bottom = &lefts[1];
+        } else {
+            right = &rights[0];
+            left = &lefts[1];
+            top = &lefts[0];
+            bottom = &rights[1];
+        }
+        r = l / L;
+
+        /* coord of upper bound at integral y */
+        ya = -r * dx;
+        xa = r * dy;
+
+        if (projectLeft | projectRight) {
+            projectXoff = -ya;
+            projectYoff = xa;
+        }
+
+        /* xa * dy - ya * dx */
+        k = l * L;
+
+        leftFace->xa = xa;
+        leftFace->ya = ya;
+        leftFace->k = k;
+        rightFace->xa = -xa;
+        rightFace->ya = -ya;
+        rightFace->k = k;
+
+        if (projectLeft)
+            righty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
+                                      k, dx, dy, x1, y1, 0, right);
+        else
+            righty = miPolyBuildEdge (xa, ya, k, dx, dy, x1, y1, 0, right);
+
+        /* coord of lower bound at integral y */
+        ya = -ya;
+        xa = -xa;
+
+        /* xa * dy - ya * dx */
+        k = -k;
+
+        if (projectLeft)
+            lefty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
+                                     k, dx, dy, x1, y1, 1, left);
+        else
+            lefty = miPolyBuildEdge (xa, ya, k, dx, dy, x1, y1, 1, left);
+
+        /* coord of top face at integral y */
+
+        if (signdx > 0) {
+            ya = -ya;
+            xa = -xa;
+        }
+
+        if (projectLeft) {
+            double xap = xa - projectXoff;
+            double yap = ya - projectYoff;
+            topy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy, -dy, dx, x1, y1, dx > 0, top);
+        } else
+            topy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, x1, y1, dx > 0, top);
+
+        /* coord of bottom face at integral y */
+
+        if (projectRight) {
+            double xap = xa + projectXoff;
+            double yap = ya + projectYoff;
+            bottomy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy,
+                                       -dy, dx, x2, y2, dx < 0, bottom);
+            maxy = -ya + projectYoff;
+        } else {
+            bottomy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, x2, y2, dx < 0, bottom);
+            maxy = -ya;
+        }
+
+        finaly = ICEIL (maxy) + y2;
+
+        if (dx < 0) {
+            left->height = bottomy - lefty;
+            right->height = finaly - righty;
+            top->height = righty - topy;
+        } else {
+            right->height = bottomy - righty;
+            left->height = finaly - lefty;
+            top->height = lefty - topy;
+        }
+        bottom->height = finaly - bottomy;
+        miFillPolyHelper (pGC, foreground, spanData, topy,
+                          bottom->height + bottomy - topy, lefts, rights, 2, 2);
+    }
+}
+
+static SpanDataPtr
+miSetupSpanData (GCPtr pGC, SpanDataPtr spanData, int npt)
+{
+    if ((npt < 3 && pGC->capStyle != CapRound) || miSpansEasyRop (pGC->alu))
+        return (SpanDataPtr) NULL;
+    if (pGC->lineStyle == LineDoubleDash)
+        miInitSpanGroup (&spanData->bgGroup);
+    miInitSpanGroup (&spanData->fgGroup);
+    return spanData;
+}
+
+static void
+miCleanupSpanData (GCPtr pGC, SpanDataPtr spanData)
+{
+    if (pGC->lineStyle == LineDoubleDash) {
+        miFillUniqueSpanGroup (pGC, &spanData->bgGroup, FALSE);
+        miFreeSpanGroup (&spanData->bgGroup);
+    }
+    miFillUniqueSpanGroup (pGC, &spanData->fgGroup, TRUE);
+    miFreeSpanGroup (&spanData->fgGroup);
+}
+
+void
+miWideLine (GCPtr pGC, int mode, int npt, DDXPointPtr pPts)
+{
+    int x1, y1, x2, y2;
+    SpanDataRec spanDataRec;
+    SpanDataPtr spanData;
+    Boolean projectLeft, projectRight;
+    LineFaceRec leftFace, rightFace, prevRightFace;
+    LineFaceRec firstFace;
+    int first;
+    Boolean somethingDrawn = FALSE;
+    Boolean selfJoin;
+
+    spanData = miSetupSpanData (pGC, &spanDataRec, npt);
+    x2 = pPts->x;
+    y2 = pPts->y;
+    first = TRUE;
+    selfJoin = FALSE;
+    if (npt > 1) {
+        if (mode == CoordModePrevious) {
+            int nptTmp;
+            DDXPointPtr pPtsTmp;
+
+            x1 = x2;
+            y1 = y2;
+            nptTmp = npt;
+            pPtsTmp = pPts + 1;
+            while (--nptTmp) {
+                x1 += pPtsTmp->x;
+                y1 += pPtsTmp->y;
+                ++pPtsTmp;
+            }
+            if (x2 == x1 && y2 == y1)
+                selfJoin = TRUE;
+        } else if (x2 == pPts[npt - 1].x && y2 == pPts[npt - 1].y) {
+            selfJoin = TRUE;
+        }
+    }
+    projectLeft = pGC->capStyle == CapProjecting && !selfJoin;
+    projectRight = FALSE;
+    while (--npt) {
+        x1 = x2;
+        y1 = y2;
+        ++pPts;
+        x2 = pPts->x;
+        y2 = pPts->y;
+        if (mode == CoordModePrevious) {
+            x2 += x1;
+            y2 += y1;
+        }
+        if (x1 != x2 || y1 != y2) {
+            somethingDrawn = TRUE;
+            if (npt == 1 && pGC->capStyle == CapProjecting && !selfJoin)
+                projectRight = TRUE;
+            miWideSegment (pGC, TRUE, spanData, x1, y1, x2, y2,
+                           projectLeft, projectRight, &leftFace, &rightFace);
+            if (first) {
+                if (selfJoin)
+                    firstFace = leftFace;
+                else if (pGC->capStyle == CapRound) {
+                    if (pGC->lineWidth == 1 && !spanData)
+                        miLineOnePoint (pGC, TRUE, spanData, x1, y1);
+                    else
+                        miLineArc (pGC, TRUE, spanData,
+                                   &leftFace, (LineFacePtr) NULL, (double) 0.0, (double) 0.0, TRUE);
+                }
+            } else {
+                miLineJoin (pGC, TRUE, spanData, &leftFace, &prevRightFace);
+            }
+            prevRightFace = rightFace;
+            first = FALSE;
+            projectLeft = FALSE;
+        }
+        if (npt == 1 && somethingDrawn) {
+            if (selfJoin)
+                miLineJoin (pGC, TRUE, spanData, &firstFace, &rightFace);
+            else if (pGC->capStyle == CapRound) {
+                if (pGC->lineWidth == 1 && !spanData)
+                    miLineOnePoint (pGC, TRUE, spanData, x2, y2);
+                else
+                    miLineArc (pGC, TRUE, spanData,
+                               (LineFacePtr) NULL, &rightFace, (double) 0.0, (double) 0.0, TRUE);
+            }
+        }
+    }
+    /* handle crock where all points are coincedent */
+    if (!somethingDrawn) {
+        projectLeft = pGC->capStyle == CapProjecting;
+        miWideSegment (pGC, TRUE, spanData,
+                       x2, y2, x2, y2, projectLeft, projectLeft, &leftFace, &rightFace);
+        if (pGC->capStyle == CapRound) {
+            miLineArc (pGC, TRUE, spanData,
+                       &leftFace, (LineFacePtr) NULL, (double) 0.0, (double) 0.0, TRUE);
+            rightFace.dx = -1;  /* sleezy hack to make it work */
+            miLineArc (pGC, TRUE, spanData,
+                       (LineFacePtr) NULL, &rightFace, (double) 0.0, (double) 0.0, TRUE);
+        }
+    }
+    if (spanData)
+        miCleanupSpanData (pGC, spanData);
+}
+
+#define V_TOP       0
+#define V_RIGHT     1
+#define V_BOTTOM    2
+#define V_LEFT      3
+
+static void
+miWideDashSegment (GCPtr pGC,
+                   SpanDataPtr spanData,
+                   int *pDashOffset,
+                   int *pDashIndex,
+                   int x1,
+                   int y1,
+                   int x2,
+                   int y2,
+                   Boolean projectLeft, Boolean projectRight, LineFacePtr leftFace, LineFacePtr rightFace)
+{
+    int dashIndex, dashRemain;
+    unsigned char *pDash;
+    double L, l;
+    double k;
+    PolyVertexRec vertices[4];
+    PolyVertexRec saveRight, saveBottom;
+    PolySlopeRec slopes[4];
+    PolyEdgeRec left[2], right[2];
+    LineFaceRec lcapFace, rcapFace;
+    int nleft, nright;
+    int h;
+    int y;
+    int dy, dx;
+    Boolean foreground;
+    double LRemain;
+    double r;
+    double rdx, rdy;
+    double dashDx, dashDy;
+    double saveK = 0.0;
+    Boolean first = TRUE;
+    double lcenterx, lcentery, rcenterx = 0.0, rcentery = 0.0;
+
+    dx = x2 - x1;
+    dy = y2 - y1;
+    dashIndex = *pDashIndex;
+    pDash = pGC->dash;
+    dashRemain = pDash[dashIndex] - *pDashOffset;
+
+    l = ((double) pGC->lineWidth) / 2.0;
+    if (dx == 0) {
+        L = dy;
+        rdx = 0;
+        rdy = l;
+        if (dy < 0) {
+            L = -dy;
+            rdy = -l;
+        }
+    } else if (dy == 0) {
+        L = dx;
+        rdx = l;
+        rdy = 0;
+        if (dx < 0) {
+            L = -dx;
+            rdx = -l;
+        }
+    } else {
+        L = hypot ((double) dx, (double) dy);
+        r = l / L;
+
+        rdx = r * dx;
+        rdy = r * dy;
+    }
+    k = l * L;
+    LRemain = L;
+    /* All position comments are relative to a line with dx and dy > 0,
+     * but the code does not depend on this */
+    /* top */
+    slopes[V_TOP].dx = dx;
+    slopes[V_TOP].dy = dy;
+    slopes[V_TOP].k = k;
+    /* right */
+    slopes[V_RIGHT].dx = -dy;
+    slopes[V_RIGHT].dy = dx;
+    slopes[V_RIGHT].k = 0;
+    /* bottom */
+    slopes[V_BOTTOM].dx = -dx;
+    slopes[V_BOTTOM].dy = -dy;
+    slopes[V_BOTTOM].k = k;
+    /* left */
+    slopes[V_LEFT].dx = dy;
+    slopes[V_LEFT].dy = -dx;
+    slopes[V_LEFT].k = 0;
+
+    /* preload the start coordinates */
+    vertices[V_RIGHT].x = vertices[V_TOP].x = rdy;
+    vertices[V_RIGHT].y = vertices[V_TOP].y = -rdx;
+
+    vertices[V_BOTTOM].x = vertices[V_LEFT].x = -rdy;
+    vertices[V_BOTTOM].y = vertices[V_LEFT].y = rdx;
+
+    if (projectLeft) {
+        vertices[V_TOP].x -= rdx;
+        vertices[V_TOP].y -= rdy;
+
+        vertices[V_LEFT].x -= rdx;
+        vertices[V_LEFT].y -= rdy;
+
+        slopes[V_LEFT].k = rdx * dx + rdy * dy;
+    }
+
+    lcenterx = x1;
+    lcentery = y1;
+
+    if (pGC->capStyle == CapRound) {
+        lcapFace.dx = dx;
+        lcapFace.dy = dy;
+        lcapFace.x = x1;
+        lcapFace.y = y1;
+
+        rcapFace.dx = -dx;
+        rcapFace.dy = -dy;
+        rcapFace.x = x1;
+        rcapFace.y = y1;
+    }
+    while (LRemain > dashRemain) {
+        dashDx = (dashRemain * dx) / L;
+        dashDy = (dashRemain * dy) / L;
+
+        rcenterx = lcenterx + dashDx;
+        rcentery = lcentery + dashDy;
+
+        vertices[V_RIGHT].x += dashDx;
+        vertices[V_RIGHT].y += dashDy;
+
+        vertices[V_BOTTOM].x += dashDx;
+        vertices[V_BOTTOM].y += dashDy;
+
+        slopes[V_RIGHT].k = vertices[V_RIGHT].x * dx + vertices[V_RIGHT].y * dy;
+
+        if (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1)) {
+            if (pGC->lineStyle == LineOnOffDash && pGC->capStyle == CapProjecting) {
+                saveRight = vertices[V_RIGHT];
+                saveBottom = vertices[V_BOTTOM];
+                saveK = slopes[V_RIGHT].k;
+
+                if (!first) {
+                    vertices[V_TOP].x -= rdx;
+                    vertices[V_TOP].y -= rdy;
+
+                    vertices[V_LEFT].x -= rdx;
+                    vertices[V_LEFT].y -= rdy;
+
+                    slopes[V_LEFT].k = vertices[V_LEFT].x *
+                        slopes[V_LEFT].dy - vertices[V_LEFT].y * slopes[V_LEFT].dx;
+                }
+
+                vertices[V_RIGHT].x += rdx;
+                vertices[V_RIGHT].y += rdy;
+
+                vertices[V_BOTTOM].x += rdx;
+                vertices[V_BOTTOM].y += rdy;
+
+                slopes[V_RIGHT].k = vertices[V_RIGHT].x *
+                    slopes[V_RIGHT].dy - vertices[V_RIGHT].y * slopes[V_RIGHT].dx;
+            }
+            y = miPolyBuildPoly (vertices, slopes, 4, x1, y1, left, right, &nleft, &nright, &h);
+            foreground = (dashIndex & 1) == 0;
+            miFillPolyHelper (pGC, foreground, spanData, y, h, left, right, nleft, nright);
+
+            if (pGC->lineStyle == LineOnOffDash) {
+                switch (pGC->capStyle) {
+                case CapProjecting:
+                    vertices[V_BOTTOM] = saveBottom;
+                    vertices[V_RIGHT] = saveRight;
+                    slopes[V_RIGHT].k = saveK;
+                    break;
+                case CapRound:
+                    if (!first) {
+                        if (dx < 0) {
+                            lcapFace.xa = -vertices[V_LEFT].x;
+                            lcapFace.ya = -vertices[V_LEFT].y;
+                            lcapFace.k = slopes[V_LEFT].k;
+                        } else {
+                            lcapFace.xa = vertices[V_TOP].x;
+                            lcapFace.ya = vertices[V_TOP].y;
+                            lcapFace.k = -slopes[V_LEFT].k;
+                        }
+                        miLineArc (pGC, foreground, spanData,
+                                   &lcapFace, (LineFacePtr) NULL, lcenterx, lcentery, FALSE);
+                    }
+                    if (dx < 0) {
+                        rcapFace.xa = vertices[V_BOTTOM].x;
+                        rcapFace.ya = vertices[V_BOTTOM].y;
+                        rcapFace.k = slopes[V_RIGHT].k;
+                    } else {
+                        rcapFace.xa = -vertices[V_RIGHT].x;
+                        rcapFace.ya = -vertices[V_RIGHT].y;
+                        rcapFace.k = -slopes[V_RIGHT].k;
+                    }
+                    miLineArc (pGC, foreground, spanData,
+                               (LineFacePtr) NULL, &rcapFace, rcenterx, rcentery, FALSE);
+                    break;
+                }
+            }
+        }
+        LRemain -= dashRemain;
+        ++dashIndex;
+        if (dashIndex == pGC->numInDashList)
+            dashIndex = 0;
+        dashRemain = pDash[dashIndex];
+
+        lcenterx = rcenterx;
+        lcentery = rcentery;
+
+        vertices[V_TOP] = vertices[V_RIGHT];
+        vertices[V_LEFT] = vertices[V_BOTTOM];
+        slopes[V_LEFT].k = -slopes[V_RIGHT].k;
+        first = FALSE;
+    }
+
+    if (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1)) {
+        vertices[V_TOP].x -= dx;
+        vertices[V_TOP].y -= dy;
+
+        vertices[V_LEFT].x -= dx;
+        vertices[V_LEFT].y -= dy;
+
+        vertices[V_RIGHT].x = rdy;
+        vertices[V_RIGHT].y = -rdx;
+
+        vertices[V_BOTTOM].x = -rdy;
+        vertices[V_BOTTOM].y = rdx;
+
+
+        if (projectRight) {
+            vertices[V_RIGHT].x += rdx;
+            vertices[V_RIGHT].y += rdy;
+
+            vertices[V_BOTTOM].x += rdx;
+            vertices[V_BOTTOM].y += rdy;
+            slopes[V_RIGHT].k = vertices[V_RIGHT].x *
+                slopes[V_RIGHT].dy - vertices[V_RIGHT].y * slopes[V_RIGHT].dx;
+        } else
+            slopes[V_RIGHT].k = 0;
+
+        if (!first && pGC->lineStyle == LineOnOffDash && pGC->capStyle == CapProjecting) {
+            vertices[V_TOP].x -= rdx;
+            vertices[V_TOP].y -= rdy;
+
+            vertices[V_LEFT].x -= rdx;
+            vertices[V_LEFT].y -= rdy;
+            slopes[V_LEFT].k = vertices[V_LEFT].x *
+                slopes[V_LEFT].dy - vertices[V_LEFT].y * slopes[V_LEFT].dx;
+        } else
+            slopes[V_LEFT].k += dx * dx + dy * dy;
+
+
+        y = miPolyBuildPoly (vertices, slopes, 4, x2, y2, left, right, &nleft, &nright, &h);
+
+        foreground = (dashIndex & 1) == 0;
+        miFillPolyHelper (pGC, foreground, spanData, y, h, left, right, nleft, nright);
+        if (!first && pGC->lineStyle == LineOnOffDash && pGC->capStyle == CapRound) {
+            lcapFace.x = x2;
+            lcapFace.y = y2;
+            if (dx < 0) {
+                lcapFace.xa = -vertices[V_LEFT].x;
+                lcapFace.ya = -vertices[V_LEFT].y;
+                lcapFace.k = slopes[V_LEFT].k;
+            } else {
+                lcapFace.xa = vertices[V_TOP].x;
+                lcapFace.ya = vertices[V_TOP].y;
+                lcapFace.k = -slopes[V_LEFT].k;
+            }
+            miLineArc (pGC, foreground, spanData,
+                       &lcapFace, (LineFacePtr) NULL, rcenterx, rcentery, FALSE);
+        }
+    }
+    dashRemain = ((double) dashRemain) - LRemain;
+    if (dashRemain == 0) {
+        dashIndex++;
+        if (dashIndex == pGC->numInDashList)
+            dashIndex = 0;
+        dashRemain = pDash[dashIndex];
+    }
+
+    leftFace->x = x1;
+    leftFace->y = y1;
+    leftFace->dx = dx;
+    leftFace->dy = dy;
+    leftFace->xa = rdy;
+    leftFace->ya = -rdx;
+    leftFace->k = k;
+
+    rightFace->x = x2;
+    rightFace->y = y2;
+    rightFace->dx = -dx;
+    rightFace->dy = -dy;
+    rightFace->xa = -rdy;
+    rightFace->ya = rdx;
+    rightFace->k = k;
+
+    *pDashIndex = dashIndex;
+    *pDashOffset = pDash[dashIndex] - dashRemain;
+}
+
+void
+miWideDash (GCPtr pGC, int mode, int npt, DDXPointPtr pPts)
+{
+    int x1, y1, x2, y2;
+    Boolean foreground;
+    Boolean projectLeft, projectRight;
+    LineFaceRec leftFace, rightFace, prevRightFace;
+    LineFaceRec firstFace;
+    int first;
+    int dashIndex, dashOffset;
+    int prevDashIndex;
+    SpanDataRec spanDataRec;
+    SpanDataPtr spanData;
+    Boolean somethingDrawn = FALSE;
+    Boolean selfJoin;
+    Boolean endIsFg = FALSE, startIsFg = FALSE;
+    Boolean firstIsFg = FALSE, prevIsFg = FALSE;
+
+    if (npt == 0)
+        return;
+    spanData = miSetupSpanData (pGC, &spanDataRec, npt);
+    x2 = pPts->x;
+    y2 = pPts->y;
+    first = TRUE;
+    selfJoin = FALSE;
+    if (mode == CoordModePrevious) {
+        int nptTmp;
+        DDXPointPtr pPtsTmp;
+
+        x1 = x2;
+        y1 = y2;
+        nptTmp = npt;
+        pPtsTmp = pPts + 1;
+        while (--nptTmp) {
+            x1 += pPtsTmp->x;
+            y1 += pPtsTmp->y;
+            ++pPtsTmp;
+        }
+        if (x2 == x1 && y2 == y1)
+            selfJoin = TRUE;
+    } else if (x2 == pPts[npt - 1].x && y2 == pPts[npt - 1].y) {
+        selfJoin = TRUE;
+    }
+    projectLeft = pGC->capStyle == CapProjecting && !selfJoin;
+    projectRight = FALSE;
+    dashIndex = 0;
+    dashOffset = 0;
+    miStepDash ((int) pGC->dashOffset, &dashIndex,
+                pGC->dash, (int) pGC->numInDashList, &dashOffset);
+    while (--npt) {
+        x1 = x2;
+        y1 = y2;
+        ++pPts;
+        x2 = pPts->x;
+        y2 = pPts->y;
+        if (mode == CoordModePrevious) {
+            x2 += x1;
+            y2 += y1;
+        }
+        if (x1 != x2 || y1 != y2) {
+            somethingDrawn = TRUE;
+            if (npt == 1 && pGC->capStyle == CapProjecting && (!selfJoin || !firstIsFg))
+                projectRight = TRUE;
+            prevDashIndex = dashIndex;
+            miWideDashSegment (pGC, spanData, &dashOffset, &dashIndex,
+                               x1, y1, x2, y2, projectLeft, projectRight, &leftFace, &rightFace);
+            startIsFg = !(prevDashIndex & 1);
+            endIsFg = (dashIndex & 1) ^ (dashOffset != 0);
+            if (pGC->lineStyle == LineDoubleDash || startIsFg) {
+                foreground = startIsFg;
+                if (first || (pGC->lineStyle == LineOnOffDash && !prevIsFg)) {
+                    if (first && selfJoin) {
+                        firstFace = leftFace;
+                        firstIsFg = startIsFg;
+                    } else if (pGC->capStyle == CapRound)
+                        miLineArc (pGC, foreground, spanData,
+                                   &leftFace, (LineFacePtr) NULL, (double) 0.0, (double) 0.0, TRUE);
+                } else {
+                    miLineJoin (pGC, foreground, spanData, &leftFace, &prevRightFace);
+                }
+            }
+            prevRightFace = rightFace;
+            prevIsFg = endIsFg;
+            first = FALSE;
+            projectLeft = FALSE;
+        }
+        if (npt == 1 && somethingDrawn) {
+            if (pGC->lineStyle == LineDoubleDash || endIsFg) {
+                foreground = endIsFg;
+                if (selfJoin && (pGC->lineStyle == LineDoubleDash || firstIsFg)) {
+                    miLineJoin (pGC, foreground, spanData, &firstFace, &rightFace);
+                } else {
+                    if (pGC->capStyle == CapRound)
+                        miLineArc (pGC, foreground, spanData,
+                                   (LineFacePtr) NULL, &rightFace,
+                                   (double) 0.0, (double) 0.0, TRUE);
+                }
+            } else {
+                /* glue a cap to the start of the line if
+                 * we're OnOffDash and ended on odd dash
+                 */
+                if (selfJoin && firstIsFg) {
+                    foreground = TRUE;
+                    if (pGC->capStyle == CapProjecting)
+                        miLineProjectingCap (pGC, foreground, spanData,
+                                             &firstFace, TRUE, (double) 0.0, (double) 0.0, TRUE);
+                    else if (pGC->capStyle == CapRound)
+                        miLineArc (pGC, foreground, spanData,
+                                   &firstFace, (LineFacePtr) NULL,
+                                   (double) 0.0, (double) 0.0, TRUE);
+                }
+            }
+        }
+    }
+    /* handle crock where all points are coincident */
+    if (!somethingDrawn && (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1))) {
+        /* not the same as endIsFg computation above */
+        foreground = (dashIndex & 1) == 0;
+        switch (pGC->capStyle) {
+        case CapRound:
+            miLineArc (pGC, foreground, spanData,
+                       (LineFacePtr) NULL, (LineFacePtr) NULL, (double) x2, (double) y2, FALSE);
+            break;
+        case CapProjecting:
+            x1 = pGC->lineWidth;
+            miFillRectPolyHelper (pGC, foreground, spanData,
+                                  x2 - (x1 >> 1), y2 - (x1 >> 1), x1, x1);
+            break;
+        }
+    }
+    if (spanData)
+        miCleanupSpanData (pGC, spanData);
+}
+
+#undef ExchangeSpans
+#define ExchangeSpans(a, b)                                 \
+{                                                           \
+    DDXPointRec tpt;                                        \
+    int         tw;                                         \
+                                                            \
+    tpt = spans[a]; spans[a] = spans[b]; spans[b] = tpt;    \
+    tw = widths[a]; widths[a] = widths[b]; widths[b] = tw;  \
+}
+
+static void QuickSortSpans(
+    DDXPointRec spans[],
+    int         widths[],
+    int         numSpans)
+{
+    int     y;
+    int     i, j, m;
+    DDXPointPtr    r;
+
+    /* Always called with numSpans > 1 */
+    /* Sorts only by y, doesn't bother to sort by x */
+
+    do
+    {
+        if (numSpans < 9)
+        {
+            /* Do insertion sort */
+            int yprev;
+
+            yprev = spans[0].y;
+            i = 1;
+            do
+            { /* while i != numSpans */
+                y = spans[i].y;
+                if (yprev > y)
+                {
+                    /* spans[i] is out of order.  Move into proper location. */
+                    DDXPointRec tpt;
+                    int     tw, k;
+
+                    for (j = 0; y >= spans[j].y; j++) {}
+                    tpt = spans[i];
+                    tw  = widths[i];
+                    for (k = i; k != j; k--)
+                    {
+                        spans[k] = spans[k-1];
+                        widths[k] = widths[k-1];
+                    }
+                    spans[j] = tpt;
+                    widths[j] = tw;
+                    y = spans[i].y;
+                } /* if out of order */
+                yprev = y;
+                i++;
+            } while (i != numSpans);
+            return;
+        }
+
+        /* Choose partition element, stick in location 0 */
+        m = numSpans / 2;
+        if (spans[m].y > spans[0].y)            ExchangeSpans(m, 0);
+        if (spans[m].y > spans[numSpans-1].y)   ExchangeSpans(m, numSpans-1);
+        if (spans[m].y > spans[0].y)            ExchangeSpans(m, 0);
+        y = spans[0].y;
+
+        /* Partition array */
+        i = 0;
+        j = numSpans;
+        do
+        {
+            r = &(spans[i]);
+            do
+            {
+                r++;
+                i++;
+            } while (i != numSpans && r->y < y);
+            r = &(spans[j]);
+            do
+            {
+                r--;
+                j--;
+            } while (y < r->y);
+            if (i < j)
+                ExchangeSpans(i, j);
+        } while (i < j);
+
+        /* Move partition element back to middle */
+        ExchangeSpans(0, j);
+
+        /* Recurse */
+        if (numSpans-j-1 > 1)
+            QuickSortSpans(&spans[j+1], &widths[j+1], numSpans-j-1);
+        numSpans = j;
+    } while (numSpans > 1);
+}
+
+#define NextBand()                                                  \
+{                                                                   \
+    clipy1 = pboxBandStart->y1;                                     \
+    clipy2 = pboxBandStart->y2;                                     \
+    pboxBandEnd = pboxBandStart + 1;                                \
+    while (pboxBandEnd != pboxLast && pboxBandEnd->y1 == clipy1) {  \
+        pboxBandEnd++;                                              \
+    }                                                               \
+    for (; ppt != pptLast && ppt->y < clipy1; ppt++, pwidth++) {} \
+}
+
+/*
+    Clip a list of scanlines to a region.  The caller has allocated the
+    space.  FSorted is non-zero if the scanline origins are in ascending
+    order.
+    returns the number of new, clipped scanlines.
+*/
+
+int spice_canvas_clip_spans(pixman_region32_t *prgnDst,
+                            DDXPointPtr ppt,
+                            int         *pwidth,
+                            int                 nspans,
+                            DDXPointPtr         pptNew,
+                            int                 *pwidthNew,
+                            int                 fSorted)
+{
+    DDXPointPtr pptLast;
+    int         *pwidthNewStart;        /* the vengeance of Xerox! */
+    int         y, x1, x2;
+    int         numRects;
+    pixman_box32_t *pboxBandStart;
+
+    pptLast = ppt + nspans;
+    pwidthNewStart = pwidthNew;
+
+    pboxBandStart = pixman_region32_rectangles (prgnDst, &numRects);
+
+    if (numRects == 1) {
+        /* Do special fast code with clip boundaries in registers(?) */
+        /* It doesn't pay much to make use of fSorted in this case,
+           so we lump everything together. */
+
+        int clipx1, clipx2, clipy1, clipy2;
+
+        clipx1 = pboxBandStart->x1;
+        clipy1 = pboxBandStart->y1;
+        clipx2 = pboxBandStart->x2;
+        clipy2 = pboxBandStart->y2;
+
+        for (; ppt != pptLast; ppt++, pwidth++) {
+            y = ppt->y;
+            x1 = ppt->x;
+            if (clipy1 <= y && y < clipy2) {
+                x2 = x1 + *pwidth;
+                if (x1 < clipx1)
+                    x1 = clipx1;
+                if (x2 > clipx2)
+                    x2 = clipx2;
+                if (x1 < x2) {
+                    /* part of span in clip rectangle */
+                    pptNew->x = x1;
+                    pptNew->y = y;
+                    *pwidthNew = x2 - x1;
+                    pptNew++;
+                    pwidthNew++;
+                }
+            }
+        } /* end for */
+    } else if (numRects != 0) {
+        /* Have to clip against many boxes */
+        pixman_box32_t *pboxBandEnd, *pbox, *pboxLast;
+        int clipy1, clipy2;
+
+        /* In this case, taking advantage of sorted spans gains more than
+           the sorting costs. */
+        if ((! fSorted) && (nspans > 1))
+            QuickSortSpans(ppt, pwidth, nspans);
+
+        pboxLast = pboxBandStart + numRects;
+
+        NextBand();
+
+        for (; ppt != pptLast; ) {
+            y = ppt->y;
+            if (y < clipy2) {
+                /* span is in the current band */
+                pbox = pboxBandStart;
+                x1 = ppt->x;
+                x2 = x1 + *pwidth;
+                do { /* For each box in band */
+                    int newx1, newx2;
+
+                    newx1 = x1;
+                    newx2 = x2;
+                    if (newx1 < pbox->x1)
+                        newx1 = pbox->x1;
+                    if (newx2 > pbox->x2)
+                        newx2 = pbox->x2;
+                    if (newx1 < newx2) {
+                        /* Part of span in clip rectangle */
+                        pptNew->x = newx1;
+                        pptNew->y = y;
+                        *pwidthNew = newx2 - newx1;
+                        pptNew++;
+                        pwidthNew++;
+                    }
+                    pbox++;
+                } while (pbox != pboxBandEnd);
+                ppt++;
+                pwidth++;
+            } else {
+                /* Move to next band, adjust ppt as needed */
+                pboxBandStart = pboxBandEnd;
+                if (pboxBandStart == pboxLast)
+                    break; /* We're completely done */
+                NextBand();
+            }
+        }
+    }
+    return (pwidthNew - pwidthNewStart);
+}
diff --git a/common/lines.h b/common/lines.h
new file mode 100644
index 0000000..b5f7725
--- /dev/null
+++ b/common/lines.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/***********************************************************
+
+Copyright 1987, 1998  The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#ifndef LINES_H
+#define LINES_H
+
+#include <pixman.h>
+#include <stdlib.h>
+#include <string.h>
+#include <spice/draw.h>
+
+typedef struct lineGC lineGC;
+
+typedef struct {
+    void (*FillSpans)(lineGC * pGC,
+                      int num_spans, SpicePoint * points, int *widths,
+                      int sorted, int foreground);
+    void (*FillRects)(lineGC * pGC,
+                      int nun_rects, pixman_rectangle32_t * rects,
+                      int foreground);
+} lineGCOps;
+
+struct lineGC {
+    int width;
+    int height;
+    unsigned char alu;
+    unsigned short lineWidth;
+    unsigned short dashOffset;
+    unsigned short numInDashList;
+    unsigned char *dash;
+    unsigned int lineStyle:2;
+    unsigned int capStyle:2;
+    unsigned int joinStyle:2;
+    lineGCOps *ops;
+};
+
+/* CoordinateMode for drawing routines */
+
+#define CoordModeOrigin         0       /* relative to the origin */
+#define CoordModePrevious       1       /* relative to previous point */
+
+/* LineStyle */
+
+#define LineSolid               0
+#define LineOnOffDash           1
+#define LineDoubleDash          2
+
+/* capStyle */
+
+#define CapNotLast              0
+#define CapButt                 1
+#define CapRound                2
+#define CapProjecting           3
+
+/* joinStyle */
+
+#define JoinMiter               0
+#define JoinRound               1
+#define JoinBevel               2
+
+extern void spice_canvas_zero_line(lineGC *pgc,
+                                   int mode,
+                                   int num_points,
+                                   SpicePoint * points);
+extern void spice_canvas_zero_dash_line(lineGC * pgc,
+                                        int mode,
+                                        int n_points,
+                                        SpicePoint * points);
+extern void spice_canvas_wide_dash_line(lineGC * pGC,
+                                        int mode,
+                                        int num_points,
+                                        SpicePoint * points);
+extern void spice_canvas_wide_line(lineGC *pGC,
+                                   int mode,
+                                   int num_points,
+                                   SpicePoint * points);
+extern int spice_canvas_clip_spans(pixman_region32_t *clip_region,
+                                   SpicePoint *points,
+                                   int *widths,
+                                   int num_spans,
+                                   SpicePoint *new_points,
+                                   int *new_widths,
+                                   int sorted);
+
+#endif /* LINES_H */
diff --git a/server/Makefile.am b/server/Makefile.am
index b2bea00..aaef6a7 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -21,6 +21,7 @@ INCLUDES = \
 COMMON_SRCS = 						\
 	$(top_srcdir)/common/cairo_canvas.c		\
 	$(top_srcdir)/common/pixman_utils.c		\
+	$(top_srcdir)/common/lines.c			\
 	$(top_srcdir)/common/gl_canvas.c		\
 	$(top_srcdir)/common/region.c			\
 	$(top_srcdir)/common/glc.c			\
commit 9091e763a80d368114100d8452e26af67c5829b3
Author: Alexander Larsson <alexl at redhat.com>
Date:   Mon Feb 8 11:48:12 2010 +0100

    Add pixman utilities
    
    This includes:
     * pixman region from SpiceRects
     * rop2 enum
     * solid fill
     * solid fill with rop
     * tiled fill
     * tiled fill with rop
     * blit
     * blit with rop
     * copy rect

diff --git a/client/Makefile.am b/client/Makefile.am
index b453e94..c59b6f3 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -10,6 +10,7 @@ RED_COMMON_SRCS =	 		\
 	audio_devices.h			\
 	cache.hpp			\
 	cairo_canvas.cpp		\
+	pixman_utils.cpp		\
 	canvas.cpp			\
 	canvas.h			\
 	canvas_utils.cpp		\
diff --git a/client/pixman_utils.cpp b/client/pixman_utils.cpp
new file mode 100644
index 0000000..337e07c
--- /dev/null
+++ b/client/pixman_utils.cpp
@@ -0,0 +1,25 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "common.h"
+#include "utils.h"
+
+
+#define CANVAS_ERROR(format, ...) THROW(format, ## __VA_ARGS__)
+
+#include "../common/pixman_utils.c"
+
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index c5efd86..e50c4ae 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -71,6 +71,7 @@ RED_COMMON_SRCS = 					\
 	$(top_srcdir)/client/menu.cpp			\
 	$(top_srcdir)/client/menu.h			\
 	$(top_srcdir)/client/pixels_source.h		\
+	$(top_srcdir)/client/pixman_utils.cpp		\
 	$(top_srcdir)/client/platform.h			\
 	$(top_srcdir)/client/playback_channel.cpp	\
 	$(top_srcdir)/client/process_loop.cpp		\
diff --git a/common/Makefile.am b/common/Makefile.am
index 7fcdfe9..2d18440 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -3,6 +3,8 @@ NULL =
 COMMON_SRCS = 				\
 	cairo_canvas.h			\
 	cairo_canvas.c			\
+	pixman_utils.h			\
+	pixman_utils.c			\
 	canvas_base.h			\
 	canvas_base.c			\
 	canvas_utils.h			\
diff --git a/common/pixman_utils.c b/common/pixman_utils.c
new file mode 100644
index 0000000..27257f7
--- /dev/null
+++ b/common/pixman_utils.c
@@ -0,0 +1,938 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "pixman_utils.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef FALSE
+#   define FALSE 0
+#endif
+
+#ifndef TRUE
+#   define TRUE 1
+#endif
+
+#ifndef ASSERT
+#define ASSERT(x) if (!(x)) {                               \
+    printf("%s: ASSERT %s failed\n", __FUNCTION__, #x);     \
+    abort();                                                \
+}
+#endif
+
+#define SOLID_RASTER_OP(_name, _size, _type, _equation)  \
+static void                                        \
+solid_rop_ ## _name ## _ ## _size (_type *ptr, int len, _type src)  \
+{                                                  \
+    while (len--) {                                \
+        _type dst = *ptr;                          \
+        if (dst) /* avoid unused warning */;       \
+        *ptr = (_type)(_equation);                 \
+        ptr++;                                     \
+    }                                              \
+}                                                  \
+
+#define TILED_RASTER_OP(_name, _size, _type, _equation) \
+static void                                        \
+tiled_rop_ ## _name ## _ ## _size (_type *ptr, int len, _type *tile, _type *tile_end, int tile_width)   \
+{                                                  \
+    while (len--) {                                \
+        _type src = *tile;                         \
+        _type dst = *ptr;                          \
+        if (src) /* avoid unused warning */;       \
+        if (dst) /* avoid unused warning */;       \
+        *ptr = (_type)(_equation);                 \
+        ptr++;                                     \
+        tile++;                                    \
+        if (tile == tile_end)                      \
+            tile -= tile_width;                    \
+    }                                              \
+}                                                  \
+
+#define COPY_RASTER_OP(_name, _size, _type, _equation) \
+static void                                        \
+ copy_rop_ ## _name ## _ ## _size (_type *ptr, _type *src_line, int len)        \
+{                                                  \
+    while (len--) {                                \
+        _type src = *src_line;                     \
+        _type dst = *ptr;                          \
+        if (src) /* avoid unused warning */;       \
+        if (dst) /* avoid unused warning */;       \
+        *ptr = (_type)(_equation);                 \
+        ptr++;                                     \
+        src_line++;                                \
+    }                                              \
+}                                                  \
+
+#define RASTER_OP(name, equation) \
+    SOLID_RASTER_OP(name, 8, uint8_t, equation) \
+    SOLID_RASTER_OP(name, 16, uint16_t, equation) \
+    SOLID_RASTER_OP(name, 32, uint32_t, equation) \
+    TILED_RASTER_OP(name, 8, uint8_t, equation) \
+    TILED_RASTER_OP(name, 16, uint16_t, equation) \
+    TILED_RASTER_OP(name, 32, uint32_t, equation) \
+    COPY_RASTER_OP(name, 8, uint8_t, equation) \
+    COPY_RASTER_OP(name, 16, uint16_t, equation) \
+    COPY_RASTER_OP(name, 32, uint32_t, equation)
+
+RASTER_OP(clear, 0x0)
+RASTER_OP(and, src & dst)
+RASTER_OP(and_reverse, src & ~dst)
+RASTER_OP(copy, src)
+RASTER_OP(and_inverted, ~src & dst)
+RASTER_OP(noop, dst)
+RASTER_OP(xor, src ^ dst)
+RASTER_OP(or, src | dst)
+RASTER_OP(nor, ~src & ~dst)
+RASTER_OP(equiv, ~src ^ dst)
+RASTER_OP(invert, ~dst)
+RASTER_OP(or_reverse, src | ~dst)
+RASTER_OP(copy_inverted, ~src)
+RASTER_OP(or_inverted, ~src | dst)
+RASTER_OP(nand, ~src | ~dst)
+RASTER_OP(set, 0xffffffff)
+
+typedef void (*solid_rop_8_func_t)(uint8_t *ptr, int len, uint8_t src);
+typedef void (*solid_rop_16_func_t)(uint16_t *ptr, int len, uint16_t src);
+typedef void (*solid_rop_32_func_t)(uint32_t *ptr, int len, uint32_t src);
+typedef void (*tiled_rop_8_func_t)(uint8_t *ptr, int len,
+                                   uint8_t *tile, uint8_t *tile_end, int tile_width);
+typedef void (*tiled_rop_16_func_t)(uint16_t *ptr, int len,
+                                    uint16_t *tile, uint16_t *tile_end, int tile_width);
+typedef void (*tiled_rop_32_func_t)(uint32_t *ptr, int len,
+                                    uint32_t *tile, uint32_t *tile_end, int tile_width);
+typedef void (*copy_rop_8_func_t)(uint8_t *ptr, uint8_t *src, int len);
+typedef void (*copy_rop_16_func_t)(uint16_t *ptr, uint16_t *src, int len);
+typedef void (*copy_rop_32_func_t)(uint32_t *ptr, uint32_t *src, int len);
+
+#define ROP_TABLE(_type, _size)                                                 \
+static void (*solid_rops_ ## _size[16]) (_type *ptr, int len, _type src) = { \
+    solid_rop_clear_ ## _size,  \
+    solid_rop_and_ ## _size,    \
+    solid_rop_and_reverse_ ## _size,    \
+    solid_rop_copy_ ## _size,    \
+    solid_rop_and_inverted_ ## _size,    \
+    solid_rop_noop_ ## _size,    \
+    solid_rop_xor_ ## _size,    \
+    solid_rop_or_ ## _size,    \
+    solid_rop_nor_ ## _size,    \
+    solid_rop_equiv_ ## _size,    \
+    solid_rop_invert_ ## _size,    \
+    solid_rop_or_reverse_ ## _size,    \
+    solid_rop_copy_inverted_ ## _size,    \
+    solid_rop_or_inverted_ ## _size,    \
+    solid_rop_nand_ ## _size,    \
+    solid_rop_set_ ## _size    \
+};                          \
+static void (*tiled_rops_ ## _size[16]) (_type *ptr, int len, _type *tile, _type *tile_end, int tile_width) = { \
+    tiled_rop_clear_ ## _size,  \
+    tiled_rop_and_ ## _size,    \
+    tiled_rop_and_reverse_ ## _size,    \
+    tiled_rop_copy_ ## _size,    \
+    tiled_rop_and_inverted_ ## _size,    \
+    tiled_rop_noop_ ## _size,    \
+    tiled_rop_xor_ ## _size,    \
+    tiled_rop_or_ ## _size,    \
+    tiled_rop_nor_ ## _size,    \
+    tiled_rop_equiv_ ## _size,    \
+    tiled_rop_invert_ ## _size,    \
+    tiled_rop_or_reverse_ ## _size,    \
+    tiled_rop_copy_inverted_ ## _size,    \
+    tiled_rop_or_inverted_ ## _size,    \
+    tiled_rop_nand_ ## _size,    \
+    tiled_rop_set_ ## _size    \
+}; \
+static void (*copy_rops_ ## _size[16]) (_type *ptr, _type *tile, int len) = { \
+    copy_rop_clear_ ## _size,  \
+    copy_rop_and_ ## _size,    \
+    copy_rop_and_reverse_ ## _size,    \
+    copy_rop_copy_ ## _size,    \
+    copy_rop_and_inverted_ ## _size,    \
+    copy_rop_noop_ ## _size,    \
+    copy_rop_xor_ ## _size,    \
+    copy_rop_or_ ## _size,    \
+    copy_rop_nor_ ## _size,    \
+    copy_rop_equiv_ ## _size,    \
+    copy_rop_invert_ ## _size,    \
+    copy_rop_or_reverse_ ## _size,    \
+    copy_rop_copy_inverted_ ## _size,    \
+    copy_rop_or_inverted_ ## _size,    \
+    copy_rop_nand_ ## _size,    \
+    copy_rop_set_ ## _size    \
+};
+
+ROP_TABLE(uint8_t, 8)
+ROP_TABLE(uint16_t, 16)
+ROP_TABLE(uint32_t, 32)
+
+void spice_pixman_fill_rect(pixman_image_t *dest,
+                            int x, int y,
+                            int width, int height,
+                            uint32_t value)
+{
+    uint32_t *bits;
+    int stride, depth;
+    uint32_t byte_width;
+    uint8_t *byte_line;
+
+    bits = pixman_image_get_data(dest);
+    stride = pixman_image_get_stride(dest);
+    depth = pixman_image_get_depth(dest);
+    /* stride is in bytes, depth in bits */
+
+    ASSERT(x >= 0);
+    ASSERT(y >= 0);
+    ASSERT(width > 0);
+    ASSERT(height > 0);
+    ASSERT(x + width <= pixman_image_get_width(dest));
+    ASSERT(y + height <= pixman_image_get_height(dest));
+
+    if (depth == 24) {
+        depth = 32; /* Needed for pixman_fill */
+    }
+
+    if (pixman_fill(bits,
+                    stride / 4,
+                    depth,
+                    x, y,
+                    width, height,
+                    value)) {
+        return;
+    }
+
+    if (depth == 8) {
+        byte_line = ((uint8_t *)bits) + stride * y + x;
+        byte_width = width;
+        value = (value & 0xff) * 0x01010101;
+    } else if (depth == 16) {
+        byte_line = ((uint8_t *)bits) + stride * y + x * 2;
+        byte_width = 2 * width;
+        value = (value & 0xffff) * 0x00010001;
+    } else {
+        ASSERT (depth == 32 || depth == 24)
+        byte_line = ((uint8_t *)bits) + stride * y + x * 4;
+        byte_width = 4 * width;
+    }
+
+    while (height--) {
+        int w;
+        uint8_t *d = byte_line;
+
+        byte_line += stride;
+        w = byte_width;
+
+        while (w >= 1 && ((unsigned long)d & 1)) {
+            *(uint8_t *)d = (value & 0xff);
+            w--;
+            d++;
+        }
+
+        while (w >= 2 && ((unsigned long)d & 3)) {
+            *(uint16_t *)d = value;
+            w -= 2;
+            d += 2;
+        }
+
+        while (w >= 4 && ((unsigned long)d & 7)) {
+            *(uint32_t *)d = value;
+
+            w -= 4;
+            d += 4;
+        }
+
+        while (w >= 4) {
+            *(uint32_t *)d = value;
+
+            w -= 4;
+            d += 4;
+        }
+
+        while (w >= 2) {
+            *(uint16_t *)d = value;
+            w -= 2;
+            d += 2;
+        }
+
+        while (w >= 1) {
+            *(uint8_t *)d = (value & 0xff);
+            w--;
+            d++;
+        }
+    }
+}
+
+void spice_pixman_fill_rect_rop(pixman_image_t *dest,
+                                int x, int y,
+                                int width, int height,
+                                uint32_t value,
+                                SpiceROP rop)
+{
+    uint32_t *bits;
+    int stride, depth;
+    uint8_t *byte_line;
+
+    bits = pixman_image_get_data(dest);
+    stride = pixman_image_get_stride(dest);
+    depth = pixman_image_get_depth(dest);
+    /* stride is in bytes, depth in bits */
+
+    ASSERT(x >= 0);
+    ASSERT(y >= 0);
+    ASSERT(width > 0);
+    ASSERT(height > 0);
+    ASSERT(x + width <= pixman_image_get_width(dest));
+    ASSERT(y + height <= pixman_image_get_height(dest));
+    ASSERT(rop >= 0 && rop < 16);
+
+    if (depth == 8) {
+        solid_rop_8_func_t rop_func = solid_rops_8[rop];
+
+        byte_line = ((uint8_t *)bits) + stride * y + x;
+        while (height--) {
+            rop_func((uint8_t *)byte_line, width, (uint8_t)value);
+            byte_line += stride;
+        }
+
+    } else if (depth == 16) {
+        solid_rop_16_func_t rop_func = solid_rops_16[rop];
+
+        byte_line = ((uint8_t *)bits) + stride * y + x * 2;
+        while (height--) {
+            rop_func((uint16_t *)byte_line, width, (uint16_t)value);
+            byte_line += stride;
+        }
+    }  else {
+        solid_rop_32_func_t rop_func = solid_rops_32[rop];
+
+        ASSERT (depth == 32 || depth == 24);
+
+        byte_line = ((uint8_t *)bits) + stride * y + x * 4;
+        while (height--) {
+            rop_func((uint32_t *)byte_line, width, (uint32_t)value);
+            byte_line += stride;
+        }
+    }
+}
+
+void spice_pixman_tile_rect(pixman_image_t *dest,
+                            int x, int y,
+                            int width, int height,
+                            pixman_image_t *tile,
+                            int offset_x,
+                            int offset_y)
+{
+    uint32_t *bits, *tile_bits;
+    int stride, depth;
+    int tile_width, tile_height, tile_stride;
+    uint8_t *byte_line;
+    uint8_t *tile_line;
+    int tile_start_x, tile_start_y, tile_end_dx;
+
+    bits = pixman_image_get_data(dest);
+    stride = pixman_image_get_stride(dest);
+    depth = pixman_image_get_depth(dest);
+    /* stride is in bytes, depth in bits */
+
+    tile_bits = pixman_image_get_data(tile);
+    tile_stride = pixman_image_get_stride(tile);
+    tile_width = pixman_image_get_width(tile);
+    tile_height = pixman_image_get_height(tile);
+
+    ASSERT(x >= 0);
+    ASSERT(y >= 0);
+    ASSERT(width > 0);
+    ASSERT(height > 0);
+    ASSERT(x + width <= pixman_image_get_width(dest));
+    ASSERT(y + height <= pixman_image_get_height(dest));
+    ASSERT(depth == pixman_image_get_depth(tile));
+
+    tile_start_x = (x - offset_x) % tile_width;
+    if (tile_start_x < 0) {
+        tile_start_x += tile_width;
+    }
+    tile_start_y = (y - offset_y) % tile_height;
+    if (tile_start_y < 0) {
+        tile_start_y += tile_height;
+    }
+    tile_end_dx = tile_width - tile_start_x;
+
+    if (depth == 8) {
+        byte_line = ((uint8_t *)bits) + stride * y + x;
+        tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x;
+        while (height--) {
+            tiled_rop_copy_8((uint8_t *)byte_line, width,
+                             (uint8_t *)tile_line, (uint8_t *)tile_line + tile_end_dx,
+                             tile_width);
+            byte_line += stride;
+            tile_line += tile_stride;
+            if (++tile_start_y == tile_height) {
+                tile_line -= tile_height * tile_stride;
+                tile_start_y = 0;
+            }
+        }
+
+    } else if (depth == 16) {
+        byte_line = ((uint8_t *)bits) + stride * y + x * 2;
+        tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x * 2;
+        while (height--) {
+            tiled_rop_copy_16((uint16_t *)byte_line, width,
+                              (uint16_t *)tile_line, (uint16_t *)tile_line + tile_end_dx,
+                              tile_width);
+            byte_line += stride;
+            tile_line += tile_stride;
+            if (++tile_start_y == tile_height) {
+                tile_line -= tile_height * tile_stride;
+                tile_start_y = 0;
+            }
+        }
+    }  else {
+        ASSERT (depth == 32 || depth == 24);
+
+        byte_line = ((uint8_t *)bits) + stride * y + x * 4;
+        tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x * 4;
+        while (height--) {
+            tiled_rop_copy_32((uint32_t *)byte_line, width,
+                              (uint32_t *)tile_line, (uint32_t *)tile_line + tile_end_dx,
+                              tile_width);
+            byte_line += stride;
+            tile_line += tile_stride;
+            if (++tile_start_y == tile_height) {
+                tile_line -= tile_height * tile_stride;
+                tile_start_y = 0;
+            }
+        }
+    }
+}
+
+void spice_pixman_tile_rect_rop(pixman_image_t *dest,
+                                int x, int y,
+                                int width, int height,
+                                pixman_image_t *tile,
+                                int offset_x,
+                                int offset_y,
+                                SpiceROP rop)
+{
+    uint32_t *bits, *tile_bits;
+    int stride, depth;
+    int tile_width, tile_height, tile_stride;
+    uint8_t *byte_line;
+    uint8_t *tile_line;
+    int tile_start_x, tile_start_y, tile_end_dx;
+
+    bits = pixman_image_get_data(dest);
+    stride = pixman_image_get_stride(dest);
+    depth = pixman_image_get_depth(dest);
+    /* stride is in bytes, depth in bits */
+
+    tile_bits = pixman_image_get_data(tile);
+    tile_stride = pixman_image_get_stride(tile);
+    tile_width = pixman_image_get_width(tile);
+    tile_height = pixman_image_get_height(tile);
+
+    ASSERT(x >= 0);
+    ASSERT(y >= 0);
+    ASSERT(width > 0);
+    ASSERT(height > 0);
+    ASSERT(x + width <= pixman_image_get_width(dest));
+    ASSERT(y + height <= pixman_image_get_height(dest));
+    ASSERT(rop >= 0 && rop < 16);
+    ASSERT(depth == pixman_image_get_depth(tile));
+
+    tile_start_x = (x - offset_x) % tile_width;
+    if (tile_start_x < 0) {
+        tile_start_x += tile_width;
+    }
+    tile_start_y = (y - offset_y) % tile_height;
+    if (tile_start_y < 0) {
+        tile_start_y += tile_height;
+    }
+    tile_end_dx = tile_width - tile_start_x;
+
+    if (depth == 8) {
+        tiled_rop_8_func_t rop_func = tiled_rops_8[rop];
+
+        byte_line = ((uint8_t *)bits) + stride * y + x;
+        tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x;
+        while (height--) {
+            rop_func((uint8_t *)byte_line, width,
+                     (uint8_t *)tile_line, (uint8_t *)tile_line + tile_end_dx,
+                     tile_width);
+            byte_line += stride;
+            tile_line += tile_stride;
+            if (++tile_start_y == tile_height) {
+                tile_line -= tile_height * tile_stride;
+                tile_start_y = 0;
+            }
+        }
+
+    } else if (depth == 16) {
+        tiled_rop_16_func_t rop_func = tiled_rops_16[rop];
+
+        byte_line = ((uint8_t *)bits) + stride * y + x * 2;
+        tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x * 2;
+        while (height--) {
+            rop_func((uint16_t *)byte_line, width,
+                     (uint16_t *)tile_line, (uint16_t *)tile_line + tile_end_dx,
+                     tile_width);
+            byte_line += stride;
+            tile_line += tile_stride;
+            if (++tile_start_y == tile_height) {
+                tile_line -= tile_height * tile_stride;
+                tile_start_y = 0;
+            }
+        }
+    }  else {
+        tiled_rop_32_func_t rop_func = tiled_rops_32[rop];
+
+        ASSERT (depth == 32 || depth == 24);
+
+        byte_line = ((uint8_t *)bits) + stride * y + x * 4;
+        tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x * 4;
+        while (height--) {
+            rop_func((uint32_t *)byte_line, width,
+                     (uint32_t *)tile_line, (uint32_t *)tile_line + tile_end_dx,
+                     tile_width);
+            byte_line += stride;
+            tile_line += tile_stride;
+            if (++tile_start_y == tile_height) {
+                tile_line -= tile_height * tile_stride;
+                tile_start_y = 0;
+            }
+        }
+    }
+}
+
+
+void spice_pixman_blit(pixman_image_t *dest,
+                       pixman_image_t *src,
+                       int src_x, int src_y,
+                       int dest_x, int dest_y,
+                       int width, int height)
+{
+    uint32_t *bits, *src_bits;
+    int stride, depth;
+    int src_width, src_height, src_stride;
+    uint8_t *byte_line;
+    uint8_t *src_line;
+    int byte_width;
+
+    bits = pixman_image_get_data(dest);
+    stride = pixman_image_get_stride(dest);
+    depth = pixman_image_get_depth(dest);
+    /* stride is in bytes, depth in bits */
+
+    src_bits = pixman_image_get_data(src);
+    src_stride = pixman_image_get_stride(src);
+    src_width = pixman_image_get_width(src);
+    src_height = pixman_image_get_height(src);
+
+    /* Clip source */
+    if (src_x < 0) {
+        width += src_x;
+        dest_x -= src_x;
+        src_x = 0;
+    }
+    if (src_y < 0) {
+        height += src_y;
+        dest_y -= src_y;
+        src_y = 0;
+    }
+    if (src_x + width > src_width) {
+        width = src_width - src_x;
+    }
+    if (src_y + height > src_height) {
+        height = src_height - src_y;
+    }
+
+    if (width <= 0 || height <= 0) {
+        return;
+    }
+
+    ASSERT(src_x >= 0);
+    ASSERT(src_y >= 0);
+    ASSERT(dest_x >= 0);
+    ASSERT(dest_y >= 0);
+    ASSERT(width > 0);
+    ASSERT(height > 0);
+    ASSERT(dest_x + width <= pixman_image_get_width(dest));
+    ASSERT(dest_y + height <= pixman_image_get_height(dest));
+    ASSERT(src_x + width <= pixman_image_get_width(src));
+    ASSERT(src_y + height <= pixman_image_get_height(src));
+    ASSERT(depth == pixman_image_get_depth(src));
+
+    if (depth == 24) {
+        depth = 32; /* Needed for pixman_blt */
+    }
+
+    if (pixman_blt(src_bits,
+                   bits,
+                   src_stride / 4,
+                   stride / 4,
+                   depth, depth,
+                   src_x, src_y,
+                   dest_x, dest_y,
+                   width, height)) {
+        return;
+    }
+
+    if (depth == 8) {
+        byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x;
+        byte_width = width;
+        src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x;
+    } else if (depth == 16) {
+        byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 2;
+        byte_width = width * 2;
+        src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 2;
+    }  else {
+        ASSERT (depth == 32 || depth == 24);
+        byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 4;
+        byte_width = width * 4;
+        src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 4;
+    }
+
+    while (height--) {
+        memcpy(byte_line, src_line, byte_width);
+        byte_line += stride;
+        src_line += src_stride;
+    }
+}
+
+void spice_pixman_blit_rop (pixman_image_t *dest,
+                            pixman_image_t *src,
+                            int src_x, int src_y,
+                            int dest_x, int dest_y,
+                            int width, int height,
+                            SpiceROP rop)
+{
+    uint32_t *bits, *src_bits;
+    int stride, depth;
+    int src_width, src_height, src_stride;
+    uint8_t *byte_line;
+    uint8_t *src_line;
+
+    bits = pixman_image_get_data(dest);
+    stride = pixman_image_get_stride(dest);
+    depth = pixman_image_get_depth(dest);
+    /* stride is in bytes, depth in bits */
+
+    src_bits = pixman_image_get_data(src);
+    src_stride = pixman_image_get_stride(src);
+    src_width = pixman_image_get_width(src);
+    src_height = pixman_image_get_height(src);
+
+    /* Clip source */
+    if (src_x < 0) {
+        width += src_x;
+        dest_x -= src_x;
+        src_x = 0;
+    }
+    if (src_y < 0) {
+        height += src_y;
+        dest_y -= src_y;
+        src_y = 0;
+    }
+    if (src_x + width > src_width) {
+        width = src_width - src_x;
+    }
+    if (src_y + height > src_height) {
+        height = src_height - src_y;
+    }
+
+    if (width <= 0 || height <= 0) {
+        return;
+    }
+
+    ASSERT(src_x >= 0);
+    ASSERT(src_y >= 0);
+    ASSERT(dest_x >= 0);
+    ASSERT(dest_y >= 0);
+    ASSERT(width > 0);
+    ASSERT(height > 0);
+    ASSERT(dest_x + width <= pixman_image_get_width(dest));
+    ASSERT(dest_y + height <= pixman_image_get_height(dest));
+    ASSERT(src_x + width <= pixman_image_get_width(src));
+    ASSERT(src_y + height <= pixman_image_get_height(src));
+    ASSERT(depth == pixman_image_get_depth(src));
+
+    if (depth == 8) {
+        copy_rop_8_func_t rop_func = copy_rops_8[rop];
+
+        byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x;
+        src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x;
+
+        while (height--) {
+            rop_func((uint8_t *)byte_line, (uint8_t *)src_line, width);
+            byte_line += stride;
+            src_line += src_stride;
+        }
+    } else if (depth == 16) {
+        copy_rop_16_func_t rop_func = copy_rops_16[rop];
+
+        byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 2;
+        src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 2;
+
+        while (height--) {
+            rop_func((uint16_t *)byte_line, (uint16_t *)src_line, width);
+            byte_line += stride;
+            src_line += src_stride;
+        }
+    }  else {
+        copy_rop_32_func_t rop_func = copy_rops_32[rop];
+
+        ASSERT (depth == 32 || depth == 24);
+        byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 4;
+        src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 4;
+
+        while (height--) {
+            rop_func((uint32_t *)byte_line, (uint32_t *)src_line, width);
+            byte_line += stride;
+            src_line += src_stride;
+        }
+    }
+
+}
+
+void spice_pixman_blit_colorkey (pixman_image_t *dest,
+                                 pixman_image_t *src,
+                                 int src_x, int src_y,
+                                 int dest_x, int dest_y,
+                                 int width, int height,
+                                 uint32_t transparent_color)
+{
+    uint32_t *bits, *src_bits;
+    int stride, depth;
+    int src_width, src_height, src_stride;
+    uint8_t *byte_line;
+    uint8_t *src_line;
+    int x;
+
+    bits = pixman_image_get_data(dest);
+    stride = pixman_image_get_stride(dest);
+    depth = pixman_image_get_depth(dest);
+    /* stride is in bytes, depth in bits */
+
+    src_bits = pixman_image_get_data(src);
+    src_stride = pixman_image_get_stride(src);
+    src_width = pixman_image_get_width(src);
+    src_height = pixman_image_get_height(src);
+
+    /* Clip source */
+    if (src_x < 0) {
+        width += src_x;
+        dest_x -= src_x;
+        src_x = 0;
+    }
+    if (src_y < 0) {
+        height += src_y;
+        dest_y -= src_y;
+        src_y = 0;
+    }
+    if (src_x + width > src_width) {
+        width = src_width - src_x;
+    }
+    if (src_y + height > src_height) {
+        height = src_height - src_y;
+    }
+
+    if (width <= 0 || height <= 0) {
+        return;
+    }
+
+    ASSERT(src_x >= 0);
+    ASSERT(src_y >= 0);
+    ASSERT(dest_x >= 0);
+    ASSERT(dest_y >= 0);
+    ASSERT(width > 0);
+    ASSERT(height > 0);
+    ASSERT(dest_x + width <= pixman_image_get_width(dest));
+    ASSERT(dest_y + height <= pixman_image_get_height(dest));
+    ASSERT(src_x + width <= pixman_image_get_width(src));
+    ASSERT(src_y + height <= pixman_image_get_height(src));
+    ASSERT(depth == pixman_image_get_depth(src));
+
+    if (depth == 8) {
+        byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x;
+        src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x;
+
+        while (height--) {
+            uint8_t *d = (uint8_t *)byte_line;
+            uint8_t *s = (uint8_t *)byte_line;
+
+            s = (uint8_t *)src_line;
+            for (x = 0; x < width; x++) {
+                uint8_t val = *s;
+                if (val != (uint8_t)transparent_color) {
+                    *d = val;
+                }
+                s++; d++;
+            }
+
+            byte_line += stride;
+            src_line += src_stride;
+        }
+    } else if (depth == 16) {
+        byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 2;
+        src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 2;
+
+        while (height--) {
+            uint16_t *d = (uint16_t *)byte_line;
+            uint16_t *s = (uint16_t *)byte_line;
+
+            s = (uint16_t *)src_line;
+            for (x = 0; x < width; x++) {
+                uint16_t val = *s;
+                if (val != (uint16_t)transparent_color) {
+                    *d = val;
+                }
+                s++; d++;
+            }
+
+            byte_line += stride;
+            src_line += src_stride;
+        }
+    }  else {
+        ASSERT (depth == 32 || depth == 24);
+        byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 4;
+        src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 4;
+
+        while (height--) {
+            uint32_t *d = (uint32_t *)byte_line;
+            uint32_t *s = (uint32_t *)byte_line;
+
+            s = (uint32_t *)src_line;
+            for (x = 0; x < width; x++) {
+                uint32_t val = *s;
+                if (val != (0xffffff & transparent_color)) {
+                    *d = val;
+                }
+                s++; d++;
+            }
+
+            byte_line += stride;
+            src_line += src_stride;
+        }
+    }
+}
+
+static void copy_bits_up(uint8_t *data, const int stride,
+                         const int src_x, const int src_y,
+                         const int width, const int height,
+                         const int dest_x, const int dest_y)
+{
+    uint8_t *src = data + src_y * stride + src_x * sizeof(uint32_t);
+    uint8_t *dest = data + dest_y * stride + dest_x * sizeof(uint32_t);
+    uint8_t *end = dest + height * stride;
+    for (; dest != end; dest += stride, src += stride) {
+        memcpy(dest, src, width * sizeof(uint32_t));
+    }
+}
+
+static void copy_bits_down(uint8_t *data, const int stride,
+                           const int src_x, const int src_y,
+                           const int width, const int height,
+                           const int dest_x, const int dest_y)
+{
+    uint8_t *src = data + (src_y + height - 1) * stride + src_x * sizeof(uint32_t);
+    uint8_t *end = data + (dest_y - 1) * stride + dest_x * sizeof(uint32_t);
+    uint8_t *dest = end + height * stride;
+
+    for (; dest != end; dest -= stride, src -= stride) {
+        memcpy(dest, src, width * sizeof(uint32_t));
+    }
+}
+
+static void copy_bits_same_line(uint8_t *data, const int stride,
+                                const int src_x, const int src_y,
+                                const int width, const int height,
+                                const int dest_x, const int dest_y)
+{
+    uint8_t *src = data + src_y * stride + src_x * sizeof(uint32_t);
+    uint8_t *dest = data + dest_y * stride + dest_x * sizeof(uint32_t);
+    uint8_t *end = dest + height * stride;
+    for (; dest != end; dest += stride, src += stride) {
+        memmove(dest, src, width * sizeof(uint32_t));
+    }
+}
+
+void spice_pixman_copy_rect (pixman_image_t *image,
+                             int src_x, int src_y,
+                             int width, int height,
+                             int dest_x, int dest_y)
+{
+    uint8_t *data;
+    int stride;
+
+    data = (uint8_t *)pixman_image_get_data(image);
+    stride = pixman_image_get_stride(image);
+
+    ASSERT(pixman_image_get_depth(image) == 24 ||
+           pixman_image_get_depth(image) == 32);
+
+    if (dest_y > src_y) {
+        copy_bits_down(data, stride,
+                       src_x, src_y,
+                       width, height,
+                       dest_x, dest_y);
+    } else if (dest_y < src_y) {
+        copy_bits_up(data, stride,
+                     src_x, src_y,
+                     width, height,
+                     dest_x, dest_y);
+    } else {
+        copy_bits_same_line(data, stride,
+                            src_x, src_y,
+                            width, height,
+                            dest_x, dest_y);
+    }
+}
+
+pixman_bool_t spice_pixman_region32_init_rects (pixman_region32_t *region,
+                                                const SpiceRect   *rects,
+                                                int                count)
+{
+    pixman_box32_t boxes_array[10];
+    pixman_box32_t *boxes;
+    pixman_bool_t res;
+    int i;
+
+    if (count < 10) {
+        boxes = boxes_array;
+    } else {
+        boxes = (pixman_box32_t *)malloc(sizeof(pixman_box32_t) * count);
+        if (boxes == NULL) {
+            return FALSE;
+        }
+    }
+
+    for (i = 0; i < count; i++) {
+        boxes[i].x1 = rects[i].left;
+        boxes[i].y1 = rects[i].top;
+        boxes[i].x2 = rects[i].right;
+        boxes[i].y2 = rects[i].bottom;
+    }
+
+    res = pixman_region32_init_rects(region, boxes, count);
+
+    if (count >= 10) {
+        free(boxes);
+    }
+
+    return res;
+}
diff --git a/common/pixman_utils.h b/common/pixman_utils.h
new file mode 100644
index 0000000..fb8dd02
--- /dev/null
+++ b/common/pixman_utils.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H__PIXMAN_UTILS
+#define _H__PIXMAN_UTILS
+
+#include <pixman.h>
+
+#include <spice/draw.h>
+
+/* This lists all possible 2 argument binary raster ops.
+ * This enum has the same values as the X11 GXcopy type
+ * and same as the GL constants (GL_AND etc) if you
+ * or it with 0x1500. However it is not exactly the
+ * same as the win32 ROP2 type (they use another order).
+ */
+typedef enum {
+    SPICE_ROP_CLEAR,         /* 0x0    0 */
+    SPICE_ROP_AND,           /* 0x1    src AND dst */
+    SPICE_ROP_AND_REVERSE,   /* 0x2    src AND NOT dst */
+    SPICE_ROP_COPY,          /* 0x3    src */
+    SPICE_ROP_AND_INVERTED,  /* 0x4    (NOT src) AND dst */
+    SPICE_ROP_NOOP,          /* 0x5    dst */
+    SPICE_ROP_XOR,           /* 0x6    src XOR dst */
+    SPICE_ROP_OR,            /* 0x7    src OR dst */
+    SPICE_ROP_NOR,           /* 0x8    (NOT src) AND (NOT dst) */
+    SPICE_ROP_EQUIV,         /* 0x9    (NOT src) XOR dst */
+    SPICE_ROP_INVERT,        /* 0xa    NOT dst */
+    SPICE_ROP_OR_REVERSE,    /* 0xb    src OR (NOT dst) */
+    SPICE_ROP_COPY_INVERTED, /* 0xc    NOT src */
+    SPICE_ROP_OR_INVERTED,   /* 0xd    (NOT src) OR dst */
+    SPICE_ROP_NAND,          /* 0xe    (NOT src) OR (NOT dst) */
+    SPICE_ROP_SET            /* 0xf    1 */
+} SpiceROP;
+
+
+void spice_pixman_region32_init_from_bitmap(pixman_region32_t *region,
+                                            uint32_t *data,
+                                            int width, int height,
+                                            int stride);
+pixman_bool_t spice_pixman_region32_init_rects(pixman_region32_t *region,
+                                               const SpiceRect   *rects,
+                                               int                count);
+void spice_pixman_fill_rect(pixman_image_t *dest,
+                            int x, int y,
+                            int w, int h,
+                            uint32_t value);
+void spice_pixman_fill_rect_rop(pixman_image_t *dest,
+                                int x, int y,
+                                int w, int h,
+                                uint32_t value,
+                                SpiceROP rop);
+void spice_pixman_tile_rect(pixman_image_t *dest,
+                            int x, int y,
+                            int w, int h,
+                            pixman_image_t *tile,
+                            int offset_x,
+                            int offset_y);
+void spice_pixman_tile_rect_rop(pixman_image_t *dest,
+                                int x, int y,
+                                int w, int h,
+                                pixman_image_t *tile,
+                                int offset_x,
+                                int offset_y,
+                                SpiceROP rop);
+void spice_pixman_blit(pixman_image_t *dest,
+                       pixman_image_t *src,
+                       int src_x, int src_y,
+                       int dest_x, int dest_y,
+                       int w, int h);
+void spice_pixman_blit_rop(pixman_image_t *dest,
+                           pixman_image_t *src,
+                           int src_x, int src_y,
+                           int dest_x, int dest_y,
+                           int w, int h,
+                           SpiceROP rop);
+void spice_pixman_blit_colorkey(pixman_image_t *dest,
+                                pixman_image_t *src,
+                                int src_x, int src_y,
+                                int dest_x, int dest_y,
+                                int width, int height,
+                                uint32_t transparent_color);
+void spice_pixman_copy_rect(pixman_image_t *image,
+                            int src_x, int src_y,
+                            int w, int h,
+                            int dest_x, int dest_y);
+
+#endif /* _H__PIXMAN_UTILS */
diff --git a/server/Makefile.am b/server/Makefile.am
index 03f15a3..b2bea00 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -20,6 +20,7 @@ INCLUDES = \
 
 COMMON_SRCS = 						\
 	$(top_srcdir)/common/cairo_canvas.c		\
+	$(top_srcdir)/common/pixman_utils.c		\
 	$(top_srcdir)/common/gl_canvas.c		\
 	$(top_srcdir)/common/region.c			\
 	$(top_srcdir)/common/glc.c			\
commit 2233dd02a191200b5d33f0b37c32ebf05880c126
Author: Alexander Larsson <alexl at redhat.com>
Date:   Mon Feb 8 11:54:02 2010 +0100

    Add emacs settings for indent according to spice styleguide

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index f198878..a5d4146 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
index 72abcb2..bf6ff90 100644
--- a/common/cairo_canvas.h
+++ b/common/cairo_canvas.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/canvas_base.c b/common/canvas_base.c
index de467c7..356ef9d 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/canvas_base.h b/common/canvas_base.h
index ad73bb9..15cf869 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index df5ae64..9e29cb8 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
index 8ba3f20..606e71e 100644
--- a/common/gdi_canvas.h
+++ b/common/gdi_canvas.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index fd1a2c9..124bcfe 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index 442c9f4..74c8913 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/gl_utils.h b/common/gl_utils.h
index 4126677..eeb9f02 100644
--- a/common/gl_utils.h
+++ b/common/gl_utils.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/glc.c b/common/glc.c
index 5300ebd..090dc03 100644
--- a/common/glc.c
+++ b/common/glc.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/glc.h b/common/glc.h
index 6213c37..a6b8579 100644
--- a/common/glc.h
+++ b/common/glc.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/lookup3.c b/common/lookup3.c
index f23461a..57274db 100644
--- a/common/lookup3.c
+++ b/common/lookup3.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
 -------------------------------------------------------------------------------
 lookup3.c, by Bob Jenkins, May 2006, Public Domain.
diff --git a/common/lookup3.h b/common/lookup3.h
index 00bc4eb..a0d6ec8 100644
--- a/common/lookup3.h
+++ b/common/lookup3.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/lz.c b/common/lz.c
index e0e112e..f99aa34 100644
--- a/common/lz.c
+++ b/common/lz.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
 
  Copyright 2009 Red Hat, Inc. and/or its affiliates.
diff --git a/common/lz.h b/common/lz.h
index 0444dc8..3ad6caa 100644
--- a/common/lz.h
+++ b/common/lz.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
         dictionary compression for images based on fastlz (http://www.fastlz.org/)
         (Distributed under MIT license).
diff --git a/common/lz_common.h b/common/lz_common.h
index 75c32e2..d5019fd 100644
--- a/common/lz_common.h
+++ b/common/lz_common.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/lz_compress_tmpl.c b/common/lz_compress_tmpl.c
index be1b941..b74a256 100644
--- a/common/lz_compress_tmpl.c
+++ b/common/lz_compress_tmpl.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
 
  Copyright 2009 Red Hat, Inc. and/or its affiliates.
diff --git a/common/lz_config.h b/common/lz_config.h
index 748d79d..4db6dc3 100644
--- a/common/lz_config.h
+++ b/common/lz_config.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/lz_decompress_tmpl.c b/common/lz_decompress_tmpl.c
index 36502c6..2fbcfba 100644
--- a/common/lz_decompress_tmpl.c
+++ b/common/lz_decompress_tmpl.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
 
  Copyright 2009 Red Hat, Inc. and/or its affiliates.
diff --git a/common/mutex.h b/common/mutex.h
index 373d54e..2cf29e9 100644
--- a/common/mutex.h
+++ b/common/mutex.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/ogl_ctx.c b/common/ogl_ctx.c
index 6b17511..df58a81 100644
--- a/common/ogl_ctx.c
+++ b/common/ogl_ctx.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/ogl_ctx.h b/common/ogl_ctx.h
index 85a551a..c460b9a 100644
--- a/common/ogl_ctx.h
+++ b/common/ogl_ctx.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/quic.c b/common/quic.c
index 7568f9e..507c450 100644
--- a/common/quic.c
+++ b/common/quic.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/quic.h b/common/quic.h
index 8824db7..e8057e4 100644
--- a/common/quic.h
+++ b/common/quic.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/quic_config.h b/common/quic_config.h
index c9eb8fa..424a34d 100644
--- a/common/quic_config.h
+++ b/common/quic_config.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/quic_family_tmpl.c b/common/quic_family_tmpl.c
index 58ae693..3c45af3 100644
--- a/common/quic_family_tmpl.c
+++ b/common/quic_family_tmpl.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/quic_rgb_tmpl.c b/common/quic_rgb_tmpl.c
index 2007df6..af3cda9 100644
--- a/common/quic_rgb_tmpl.c
+++ b/common/quic_rgb_tmpl.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/quic_tmpl.c b/common/quic_tmpl.c
index 5a0a8e5..8a600d3 100644
--- a/common/quic_tmpl.c
+++ b/common/quic_tmpl.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/rect.h b/common/rect.h
index 61de99c..43e7f21 100644
--- a/common/rect.h
+++ b/common/rect.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/region.h b/common/region.h
index e685bfb..f981ef3 100644
--- a/common/region.h
+++ b/common/region.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/ring.h b/common/ring.h
index 4c17713..76c28a8 100644
--- a/common/ring.h
+++ b/common/ring.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/rop3.c b/common/rop3.c
index 6d2efa2..64baba8 100644
--- a/common/rop3.c
+++ b/common/rop3.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
diff --git a/common/rop3.h b/common/rop3.h
index 3a24ece..32a6203 100644
--- a/common/rop3.h
+++ b/common/rop3.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
commit b82f9ed2b3723f468cda2a8f1ffa7918756a290f
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Feb 9 13:18:54 2010 +0100

    Fix delete vs delete[] mismatches
    
    This fixes a bunch of valgrind warnings.

diff --git a/client/glz_decoder_window.cpp b/client/glz_decoder_window.cpp
index 9cd64ab..aca2485 100644
--- a/client/glz_decoder_window.cpp
+++ b/client/glz_decoder_window.cpp
@@ -50,7 +50,7 @@ GlzDecoderWindow::GlzDecoderWindow(int pixels_capacity, GlzDecoderDebug &debug_c
 GlzDecoderWindow::~GlzDecoderWindow()
 {
     clear();
-    delete _images;
+    delete[] _images;
 }
 
 DecodedImageWinId GlzDecoderWindow::pre_decode(uint64_t image_id, uint64_t relative_head_id)
@@ -246,7 +246,7 @@ void GlzDecoderWindow::realloc(int size)
     for (int i = 0; i < _n_images; i++) {
         new_images[i] = _images[(i + _head_idx) % _images_capacity];
     }
-    delete _images;
+    delete[] _images;
 
     _images = new_images;
     _head_idx = 0;
diff --git a/client/x11/red_pixmap_cairo.cpp b/client/x11/red_pixmap_cairo.cpp
index 795c8a0..7ca9315 100644
--- a/client/x11/red_pixmap_cairo.cpp
+++ b/client/x11/red_pixmap_cairo.cpp
@@ -181,7 +181,7 @@ RedPixmapCairo::~RedPixmapCairo()
     cairo_destroy(((RedDrawable_p*)get_opaque())->cairo);
     if (((PixelsSource_p*)get_opaque())->type == PIXELS_SOURCE_TYPE_PIXMAP) {
         delete ((PixelsSource_p*)get_opaque())->pixmap.x_image;
-        delete _data;
+        delete[] _data;
     } else {
         XShmSegmentInfo *shminfo = ((PixelsSource_p*)get_opaque())->x_shm_drawable.shminfo;
         XShmDetach(XPlatform::get_display(), shminfo);


More information about the Spice-commits mailing list