[Spice-commits] 8 commits - client/canvas.cpp client/canvas.h client/glz_decoder.cpp client/glz_decoder.h client/red_cairo_canvas.cpp client/red_cairo_canvas.h client/red_gdi_canvas.cpp client/red_gdi_canvas.h client/red_gl_canvas.cpp client/red_gl_canvas.h common/cairo_canvas.c common/cairo_canvas.h common/canvas_base.c common/canvas_base.h common/gdi_canvas.c common/gdi_canvas.h common/gl_canvas.c common/gl_canvas.h server/red_worker.c

Alexander Larsson alexl at kemper.freedesktop.org
Mon Mar 8 11:05:23 PST 2010


 client/canvas.cpp           |   69 +
 client/canvas.h             |   30 
 client/glz_decoder.cpp      |   14 
 client/glz_decoder.h        |    6 
 client/red_cairo_canvas.cpp |   99 --
 client/red_cairo_canvas.h   |   25 
 client/red_gdi_canvas.cpp   |   89 --
 client/red_gdi_canvas.h     |   20 
 client/red_gl_canvas.cpp    |  104 --
 client/red_gl_canvas.h      |   20 
 common/cairo_canvas.c       | 1617 +++-----------------------------------------
 common/cairo_canvas.h       |   52 -
 common/canvas_base.c        | 1521 ++++++++++++++++++++++++++++++++++++++++-
 common/canvas_base.h        |  152 +++-
 common/gdi_canvas.c         |  120 ++-
 common/gdi_canvas.h         |   38 -
 common/gl_canvas.c          |  156 ++--
 common/gl_canvas.h          |   54 -
 server/red_worker.c         |  315 ++------
 19 files changed, 2139 insertions(+), 2362 deletions(-)

New commits:
commit 5cb99e12c6817592f49ca153a3081dd6b5995cf2
Author: Alexander Larsson <alexl at redhat.com>
Date:   Mon Mar 8 17:49:18 2010 +0100

    Move draw_rop3 to canvas_base

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 14d8bd7..b66301d 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -21,7 +21,6 @@
 #define CANVAS_USE_PIXMAN
 #define CANVAS_SINGLE_INSTANCE
 #include "canvas_base.c"
-#include "rop3.h"
 #include "rect.h"
 #include "region.h"
 #include "pixman_utils.h"
@@ -35,36 +34,6 @@ struct CairoCanvas {
     pixman_image_t *image;
 };
 
-static pixman_image_t* canvas_surface_from_self(CairoCanvas *canvas,
-                                                int x, int y,
-                                                int32_t width, int32_t heigth)
-{
-    pixman_image_t *surface;
-    pixman_image_t *src_surface;
-    uint8_t *dest;
-    int dest_stride;
-    uint8_t *src;
-    int src_stride;
-    int i;
-
-    surface = pixman_image_create_bits(PIXMAN_x8r8g8b8, width, heigth, 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);
-
-    src_surface = canvas->image;
-    src = (uint8_t *)pixman_image_get_data(src_surface);
-    src_stride = pixman_image_get_stride(src_surface);
-    src += y * src_stride + (x << 2);
-    for (i = 0; i < heigth; i++, dest += dest_stride, src += src_stride) {
-        memcpy(dest, src, width << 2);
-    }
-    return surface;
-}
-
 static pixman_image_t *canvas_get_pixman_brush(CairoCanvas *canvas,
                                                SpiceBrush *brush)
 {
@@ -630,70 +599,6 @@ static void colorkey_scale_image(SpiceCanvas *spice_canvas,
 
     pixman_image_unref(scaled);
 }
-static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    pixman_region32_t dest_region;
-    pixman_image_t *d;
-    pixman_image_t *s;
-    SpicePoint src_pos;
-    int width;
-    int heigth;
-
-    pixman_region32_init_rect(&dest_region,
-                              bbox->left, bbox->top,
-                              bbox->right - bbox->left,
-                              bbox->bottom - bbox->top);
-
-
-    canvas_clip_pixman(&canvas->base, &dest_region, clip);
-    canvas_mask_pixman(&canvas->base, &dest_region, &rop3->mask,
-                       bbox->left, bbox->top);
-
-    width = bbox->right - bbox->left;
-    heigth = bbox->bottom - bbox->top;
-
-    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);
-        pixman_image_unref(s);
-        s = scaled_s;
-        src_pos.x = 0;
-        src_pos.y = 0;
-    } else {
-        src_pos.x = rop3->src_area.left;
-        src_pos.y = rop3->src_area.top;
-    }
-    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) {
-        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) % 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, 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, d, s, &src_pos, color);
-    }
-    pixman_image_unref(s);
-
-    blit_image(spice_canvas, &dest_region, d,
-               bbox->left,
-               bbox->top);
-
-    pixman_image_unref(d);
-
-    pixman_region32_fini(&dest_region);
-}
 
 static void canvas_put_image(SpiceCanvas *spice_canvas,
 #ifdef WIN32
@@ -943,7 +848,6 @@ void cairo_canvas_init() //unsafe global function
 
     canvas_base_init_ops(&cairo_canvas_ops);
     cairo_canvas_ops.draw_text = canvas_draw_text;
-    cairo_canvas_ops.draw_rop3 = canvas_draw_rop3;
     cairo_canvas_ops.put_image = canvas_put_image;
     cairo_canvas_ops.clear = canvas_clear;
     cairo_canvas_ops.read_bits = canvas_read_bits;
diff --git a/common/canvas_base.c b/common/canvas_base.c
index a3a1253..01df479 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -30,6 +30,7 @@
 #include "canvas_utils.h"
 #include "rect.h"
 #include "lines.h"
+#include "rop3.h"
 
 #include "mutex.h"
 
@@ -1084,6 +1085,33 @@ static void canvas_touch_image(CanvasBase *canvas, SPICE_ADDRESS addr)
     canvas_get_image_internal(canvas, addr, FALSE);
 }
 
+static pixman_image_t* canvas_get_image_from_self(SpiceCanvas *canvas,
+                                                  int x, int y,
+                                                  int32_t width, int32_t height)
+{
+    pixman_image_t *surface;
+    uint8_t *dest;
+    int dest_stride;
+    SpiceRect area;
+
+    surface = pixman_image_create_bits(PIXMAN_x8r8g8b8, width, height, 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);
+
+    area.left = x;
+    area.top = y;
+    area.right = x + width;
+    area.bottom = y + height;
+
+    canvas->ops->read_bits(canvas, dest, dest_stride, &area);
+
+    return surface;
+}
+
 static inline uint8_t revers_bits(uint8_t byte)
 {
     uint8_t ret = 0;
@@ -2804,6 +2832,71 @@ static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox,
     pixman_region32_fini(&gc.dest_region);
 }
 
+static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox,
+                             SpiceClip *clip, SpiceRop3 *rop3)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_region32_t dest_region;
+    pixman_image_t *d;
+    pixman_image_t *s;
+    SpicePoint src_pos;
+    int width;
+    int heigth;
+
+    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;
+
+    d = canvas_get_image_from_self(spice_canvas, bbox->left, bbox->top, width, heigth);
+    s = canvas_get_image(canvas, 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);
+        pixman_image_unref(s);
+        s = scaled_s;
+        src_pos.x = 0;
+        src_pos.y = 0;
+    } else {
+        src_pos.x = rop3->src_area.left;
+        src_pos.y = rop3->src_area.top;
+    }
+    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) {
+        pixman_image_t *p = canvas_get_image(canvas, rop3->brush.u.pattern.pat);
+        SpicePoint pat_pos;
+
+        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, d, s, &src_pos, p, &pat_pos);
+        pixman_image_unref(p);
+    } else {
+        uint32_t color = (canvas->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);
+    }
+    pixman_image_unref(s);
+
+    spice_canvas->ops->blit_image(spice_canvas, &dest_region, d,
+                                  bbox->left,
+                                  bbox->top);
+
+    pixman_image_unref(d);
+
+    pixman_region32_fini(&dest_region);
+}
+
 static void canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
@@ -2890,6 +2983,7 @@ inline static void canvas_base_init_ops(SpiceCanvasOps *ops)
     ops->draw_transparent = canvas_draw_transparent;
     ops->draw_alpha_blend = canvas_draw_alpha_blend;
     ops->draw_stroke = canvas_draw_stroke;
+    ops->draw_rop3 = canvas_draw_rop3;
     ops->group_start = canvas_base_group_start;
     ops->group_end = canvas_base_group_end;
 }
commit a0c6344c6a99312ad203c13b5d4e64202a6c2495
Author: Alexander Larsson <alexl at redhat.com>
Date:   Mon Mar 8 17:29:59 2010 +0100

    Move canvas_draw_stroke to canvas_base

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index c5ef3b2..14d8bd7 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -24,7 +24,6 @@
 #include "rop3.h"
 #include "rect.h"
 #include "region.h"
-#include "lines.h"
 #include "pixman_utils.h"
 
 typedef struct CairoCanvas CairoCanvas;
@@ -837,459 +836,6 @@ static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
     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;
-    }
-}
-
-
-static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    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;
-
-    pixman_region32_init_rect(&gc.dest_region,
-                              bbox->left, bbox->top,
-                              bbox->right - bbox->left,
-                              bbox->bottom - bbox->top);
-
-    canvas_clip_pixman(&canvas->base, &gc.dest_region, clip);
-
-    if (pixman_region32_n_rects(&gc.dest_region) == 0) {
-        touch_brush(&canvas->base, &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);
-}
-
 static void canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
@@ -1397,7 +943,6 @@ void cairo_canvas_init() //unsafe global function
 
     canvas_base_init_ops(&cairo_canvas_ops);
     cairo_canvas_ops.draw_text = canvas_draw_text;
-    cairo_canvas_ops.draw_stroke = canvas_draw_stroke;
     cairo_canvas_ops.draw_rop3 = canvas_draw_rop3;
     cairo_canvas_ops.put_image = canvas_put_image;
     cairo_canvas_ops.clear = canvas_clear;
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 231b5ed..a3a1253 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -29,6 +29,7 @@
 #include "pixman_utils.h"
 #include "canvas_utils.h"
 #include "rect.h"
+#include "lines.h"
 
 #include "mutex.h"
 
@@ -2355,6 +2356,454 @@ static void canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, Spice
     pixman_region32_fini(&dest_region);
 }
 
+typedef struct {
+    lineGC base;
+    SpiceCanvas *canvas;
+    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)
+{
+    SpiceCanvas *canvas;
+    StrokeGC *strokeGC;
+    int i;
+    SpiceROP rop;
+
+    strokeGC = (StrokeGC *)pGC;
+    canvas = strokeGC->canvas;
+
+    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;
+    }
+
+    if (strokeGC->solid) {
+        if (rop == SPICE_ROP_COPY) {
+            canvas->ops->fill_solid_spans(canvas, points, widths, num_spans,
+                                          strokeGC->color);
+        } else {
+            for (i = 0; i < num_spans; i++) {
+                pixman_box32_t r;
+                r.x1 = points[i].x;
+                r.y1 = points[i].y;
+                r.x2 = points[i].x + widths[i];
+                canvas->ops->fill_solid_rects_rop(canvas, &r, 1,
+                                                  strokeGC->color, rop);
+            }
+        }
+    } else {
+        if (rop == SPICE_ROP_COPY) {
+            for (i = 0; i < num_spans; i++) {
+                pixman_box32_t r;
+                r.x1 = points[i].x;
+                r.y1 = points[i].y;
+                r.x2 = points[i].x + widths[i];
+                canvas->ops->fill_tiled_rects(canvas, &r, 1,
+                                              strokeGC->tile,
+                                              strokeGC->tile_offset_x,
+                                              strokeGC->tile_offset_y);
+            }
+        } else {
+            for (i = 0; i < num_spans; i++) {
+                pixman_box32_t r;
+                r.x1 = points[i].x;
+                r.y1 = points[i].y;
+                r.x2 = points[i].x + widths[i];
+                canvas->ops->fill_tiled_rects_rop(canvas, &r, 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)
+{
+    SpiceCanvas *canvas;
+    pixman_region32_t area;
+    pixman_box32_t *boxes;
+    StrokeGC *strokeGC;
+    SpiceROP rop;
+    int i;
+    pixman_box32_t *area_rects;
+    int n_area_rects;
+
+    strokeGC = (StrokeGC *)pGC;
+    canvas = strokeGC->canvas;
+
+    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);
+
+    if (strokeGC->solid) {
+        if (rop == SPICE_ROP_COPY) {
+            canvas->ops->fill_solid_rects(canvas, area_rects, n_area_rects,
+                                          strokeGC->color);
+        } else {
+            canvas->ops->fill_solid_rects_rop(canvas, area_rects, n_area_rects,
+                                              strokeGC->color, rop);
+        }
+    } else {
+        if (rop == SPICE_ROP_COPY) {
+            canvas->ops->fill_tiled_rects(canvas, area_rects, n_area_rects,
+                                          strokeGC->tile,
+                                          strokeGC->tile_offset_x,
+                                          strokeGC->tile_offset_y);
+        } else {
+            canvas->ops->fill_tiled_rects_rop(canvas, area_rects, n_area_rects,
+                                              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;
+    }
+}
+
+
+static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox,
+                               SpiceClip *clip, SpiceStroke *stroke)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    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;
+
+    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.canvas = spice_canvas;
+    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.base.width = canvas->width;
+    gc.base.height = canvas->height;
+    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, 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,
+                                   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, data_size, sizeof(uint32_t));
+    more = *data_size;
+    seg = (SpicePathSeg*)(data_size + 1);
+
+    stroke_lines_init(&lines);
+
+    do {
+        access_test(canvas, seg, sizeof(SpicePathSeg));
+
+        uint32_t flags = seg->flags;
+        SpicePointFix* point = (SpicePointFix*)seg->data;
+        SpicePointFix* end_point = point + seg->count;
+        access_test(canvas, 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);
+}
+
 static void canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
@@ -2440,6 +2889,7 @@ inline static void canvas_base_init_ops(SpiceCanvasOps *ops)
     ops->draw_invers = canvas_draw_invers;
     ops->draw_transparent = canvas_draw_transparent;
     ops->draw_alpha_blend = canvas_draw_alpha_blend;
+    ops->draw_stroke = canvas_draw_stroke;
     ops->group_start = canvas_base_group_start;
     ops->group_end = canvas_base_group_end;
 }
commit e00bce8e25d3aaebd83a2f9bc4191bad6aff329d
Author: Alexander Larsson <alexl at redhat.com>
Date:   Thu Mar 4 14:23:08 2010 +0100

    Move most of the shared draw_xyz() methods from CairoCanvas to CanvasBase
    
    This adds a set of virtual methods for low-level operations. A subclass
    can choose to implement those and let the default CanvasBase implementations
    handle the highlevel stuff.

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 2c5b0d7..c5ef3b2 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -36,304 +36,6 @@ struct CairoCanvas {
     pixman_image_t *image;
 };
 
-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->base.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 pixman_image_t* canvas_surface_from_self(CairoCanvas *canvas,
                                                 int x, int y,
                                                 int32_t width, int32_t heigth)
@@ -401,10 +103,11 @@ static pixman_image_t *canvas_get_pixman_brush(CairoCanvas *canvas,
 }
 
 
-static void copy_region(CairoCanvas *canvas,
+static void copy_region(SpiceCanvas *spice_canvas,
                         pixman_region32_t *dest_region,
                         int dx, int dy)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_box32_t *dest_rects;
     int n_rects;
     int i, j, end_line;
@@ -470,17 +173,33 @@ static void copy_region(CairoCanvas *canvas,
     }
 }
 
-static void fill_solid_rects(CairoCanvas *canvas,
-                             pixman_region32_t *region,
+static void fill_solid_spans(SpiceCanvas *spice_canvas,
+                             SpicePoint *points,
+                             int *widths,
+                             int n_spans,
                              uint32_t color)
 {
-    pixman_box32_t *rects;
-    int n_rects;
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     int i;
 
-    rects = pixman_region32_rectangles(region, &n_rects);
+   for (i = 0; i < n_spans; i++) {
+        spice_pixman_fill_rect(canvas->image,
+                               points[i].x, points[i].y,
+                               widths[i],
+                               1,
+                               color);
+    }
+}
 
-    for (i = 0; i < n_rects; i++) {
+static void fill_solid_rects(SpiceCanvas *spice_canvas,
+                             pixman_box32_t *rects,
+                             int n_rects,
+                             uint32_t color)
+{
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
+    int i;
+
+   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,
@@ -489,18 +208,16 @@ static void fill_solid_rects(CairoCanvas *canvas,
     }
 }
 
-static void fill_solid_rects_rop(CairoCanvas *canvas,
-                                 pixman_region32_t *region,
+static void fill_solid_rects_rop(SpiceCanvas *spice_canvas,
+                                 pixman_box32_t *rects,
+                                 int n_rects,
                                  uint32_t color,
                                  SpiceROP rop)
 {
-    pixman_box32_t *rects;
-    int n_rects;
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     int i;
 
-    rects = pixman_region32_rectangles(region, &n_rects);
-
-    for (i = 0; i < n_rects; i++) {
+   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,
@@ -509,51 +226,35 @@ static void fill_solid_rects_rop(CairoCanvas *canvas,
     }
 }
 
-static void fill_tiled_rects(CairoCanvas *canvas,
-                             pixman_region32_t *region,
-                             SpicePattern *pattern)
+static void fill_tiled_rects(SpiceCanvas *spice_canvas,
+                             pixman_box32_t *rects,
+                             int n_rects,
+                             pixman_image_t *tile,
+                             int offset_x, int offset_y)
 {
-    pixman_image_t *tile;
-    int offset_x, offset_y;
-    pixman_box32_t *rects;
-    int n_rects;
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     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++) {
+   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,
+static void fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
+                                 pixman_box32_t *rects,
+                                 int n_rects,
+                                 pixman_image_t *tile,
+                                 int offset_x, int offset_y,
                                  SpiceROP rop)
 {
-    pixman_image_t *tile;
-    int offset_x, offset_y;
-    pixman_box32_t *rects;
-    int n_rects;
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     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++) {
+   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,
@@ -561,16 +262,14 @@ static void fill_tiled_rects_rop(CairoCanvas *canvas,
                                    tile, offset_x, offset_y,
                                    rop);
     }
-
-    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)
+static void blit_image(SpiceCanvas *spice_canvas,
+                       pixman_region32_t *region,
+                       pixman_image_t *src_image,
+                       int offset_x, int offset_y)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_box32_t *rects;
     int n_rects, i;
 
@@ -595,32 +294,18 @@ static void blit_with_region(CairoCanvas *canvas,
     }
 }
 
-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 blit_image_rop(CairoCanvas *canvas,
+static void blit_image_rop(SpiceCanvas *spice_canvas,
                            pixman_region32_t *region,
-                           SPICE_ADDRESS src_bitmap,
+                           pixman_image_t *src_image,
                            int offset_x, int offset_y,
                            SpiceROP rop)
 {
-    pixman_image_t *src_image;
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     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;
 
@@ -638,27 +323,24 @@ static void blit_image_rop(CairoCanvas *canvas,
                               dest_x, dest_y,
                               width, height, rop);
     }
-    pixman_image_unref(src_image);
 }
 
-static void scale_image(CairoCanvas *canvas,
+static void scale_image(SpiceCanvas *spice_canvas,
                         pixman_region32_t *region,
-                        SPICE_ADDRESS src_bitmap,
+                        pixman_image_t *src,
                         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)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     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,
@@ -685,20 +367,19 @@ static void scale_image(CairoCanvas *canvas,
     pixman_image_set_transform(src, &transform);
 
     pixman_image_set_clip_region32(canvas->image, NULL);
-    pixman_image_unref(src);
 }
 
-static void scale_image_rop(CairoCanvas *canvas,
+static void scale_image_rop(SpiceCanvas *spice_canvas,
                             pixman_region32_t *region,
-                            SPICE_ADDRESS src_bitmap,
+                            pixman_image_t *src,
                             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)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
-    pixman_image_t *src;
     pixman_image_t *scaled;
     pixman_box32_t *rects;
     int n_rects, i;
@@ -707,8 +388,6 @@ static void scale_image_rop(CairoCanvas *canvas,
     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,
@@ -758,20 +437,18 @@ static void scale_image_rop(CairoCanvas *canvas,
     }
 
     pixman_image_unref(scaled);
-    pixman_image_unref(src);
 }
 
-static void blend_image(CairoCanvas *canvas,
+static void blend_image(SpiceCanvas *spice_canvas,
                         pixman_region32_t *region,
-                        SPICE_ADDRESS src_bitmap,
+                        pixman_image_t *src,
                         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);
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
+    pixman_image_t *mask;
 
     pixman_image_set_clip_region32(canvas->image, region);
 
@@ -795,14 +472,13 @@ static void blend_image(CairoCanvas *canvas,
     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,
+static void blend_scale_image(SpiceCanvas *spice_canvas,
                               pixman_region32_t *region,
-                              SPICE_ADDRESS src_bitmap,
+                              pixman_image_t *src,
                               int src_x, int src_y,
                               int src_width, int src_height,
                               int dest_x, int dest_y,
@@ -810,15 +486,14 @@ static void blend_scale_image(CairoCanvas *canvas,
                               int scale_mode,
                               int overall_alpha)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
-    pixman_image_t *src, *mask;
+    pixman_image_t *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,
@@ -854,24 +529,22 @@ static void blend_scale_image(CairoCanvas *canvas,
     if (mask) {
         pixman_image_unref(mask);
     }
-    pixman_image_unref(src);
 
     pixman_image_set_clip_region32(canvas->image, NULL);
 }
 
-static void colorkey_image(CairoCanvas *canvas,
+static void colorkey_image(SpiceCanvas *spice_canvas,
                            pixman_region32_t *region,
-                           SPICE_ADDRESS src_bitmap,
+                           pixman_image_t *src_image,
                            int offset_x, int offset_y,
                            uint32_t transparent_color)
 {
-    pixman_image_t *src_image;
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     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;
 
@@ -890,20 +563,19 @@ static void colorkey_image(CairoCanvas *canvas,
                                    width, height,
                                    transparent_color);
     }
-    pixman_image_unref(src_image);
 }
 
-static void colorkey_scale_image(CairoCanvas *canvas,
+static void colorkey_scale_image(SpiceCanvas *spice_canvas,
                                  pixman_region32_t *region,
-                                 SPICE_ADDRESS src_bitmap,
+                                 pixman_image_t *src,
                                  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)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
-    pixman_image_t *src;
     pixman_image_t *scaled;
     pixman_box32_t *rects;
     int n_rects, i;
@@ -912,8 +584,6 @@ static void colorkey_scale_image(CairoCanvas *canvas,
     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,
@@ -960,60 +630,16 @@ static void colorkey_scale_image(CairoCanvas *canvas,
     }
 
     pixman_image_unref(scaled);
-    pixman_image_unref(src);
 }
-
-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);
-    }
-}
-
-static void canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
+static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
-    SpiceROP rop;
+    pixman_image_t *d;
+    pixman_image_t *s;
+    SpicePoint src_pos;
+    int width;
+    int heigth;
 
     pixman_region32_init_rect(&dest_region,
                               bbox->left, bbox->top,
@@ -1021,91 +647,51 @@ static void canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
                               bbox->bottom - bbox->top);
 
 
-    canvas_clip_pixman(canvas, &dest_region, clip);
-    canvas_mask_pixman(canvas, &dest_region, &fill->mask,
+    canvas_clip_pixman(&canvas->base, &dest_region, clip);
+    canvas_mask_pixman(&canvas->base, &dest_region, &rop3->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;
-    }
-
-    draw_brush(canvas, &dest_region, &fill->brush, rop);
-
-    pixman_region32_fini(&dest_region);
-}
-
-static void canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    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);
-
-    canvas_clip_pixman(canvas, &dest_region, clip);
-    canvas_mask_pixman(canvas, &dest_region, &copy->mask,
-                       bbox->left, bbox->top);
+    width = bbox->right - bbox->left;
+    heigth = bbox->bottom - bbox->top;
 
-    rop = ropd_descriptor_to_rop(copy->rop_decriptor,
-                                 ROP_INPUT_SRC,
-                                 ROP_INPUT_DEST);
+    d = canvas_surface_from_self(canvas, bbox->left, bbox->top, width, heigth);
+    s = canvas_get_image(&canvas->base, rop3->src_bitmap);
 
-    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, &rop3->src_area)) {
+        pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, width, heigth,
+                                                        rop3->scale_mode);
+        pixman_image_unref(s);
+        s = scaled_s;
+        src_pos.x = 0;
+        src_pos.y = 0;
+    } else {
+        src_pos.x = rop3->src_area.left;
+        src_pos.y = rop3->src_area.top;
     }
+    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) {
+        pixman_image_t *p = canvas_get_image(&canvas->base, rop3->brush.u.pattern.pat);
+        SpicePoint pat_pos;
 
-    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);
-        }
+        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, d, s, &src_pos, p, &pat_pos);
+        pixman_image_unref(p);
     } else {
-        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);
-        }
+        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);
     }
+    pixman_image_unref(s);
+
+    blit_image(spice_canvas, &dest_region, d,
+               bbox->left,
+               bbox->top);
+
+    pixman_image_unref(d);
 
     pixman_region32_fini(&dest_region);
 }
@@ -1168,390 +754,6 @@ static void canvas_put_image(SpiceCanvas *spice_canvas,
     pixman_image_unref(src);
 }
 
-static void canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    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);
-
-    if (pixman_region32_n_rects (&dest_region) == 0) {
-        canvas_touch_image(&canvas->base, transparent->src_bitmap);
-        pixman_region32_fini(&dest_region);
-        return;
-    }
-
-    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);
-    }
-
-    pixman_region32_fini(&dest_region);
-}
-
-static void canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    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);
-
-    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,
-                    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);
-    }
-
-    pixman_region32_fini(&dest_region);
-}
-
-static void canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    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);
-
-    canvas_clip_pixman(canvas, &dest_region, clip);
-    canvas_mask_pixman(canvas, &dest_region, &opaque->mask,
-                       bbox->left, bbox->top);
-
-    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 {
-        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);
-    }
-
-    draw_brush(canvas, &dest_region, &opaque->brush, rop);
-
-    pixman_region32_fini(&dest_region);
-}
-
-static void canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    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);
-
-    canvas_clip_pixman(canvas, &dest_region, clip);
-    canvas_mask_pixman(canvas, &dest_region, &blend->mask,
-                       bbox->left, bbox->top);
-
-    rop = ropd_descriptor_to_rop(blend->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, blend->src_bitmap);
-        pixman_region32_fini(&dest_region);
-        return;
-    }
-
-    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 {
-        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);
-        }
-    }
-
-    pixman_region32_fini(&dest_region);
-}
-
-static void canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    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);
-}
-
-static void canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    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);
-}
-
-static void canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    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);
-}
-
-static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    pixman_region32_t dest_region;
-    pixman_image_t *d;
-    pixman_image_t *s;
-    SpicePoint src_pos;
-    int width;
-    int heigth;
-
-    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;
-
-    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);
-        pixman_image_unref(s);
-        s = scaled_s;
-        src_pos.x = 0;
-        src_pos.y = 0;
-    } else {
-        src_pos.x = rop3->src_area.left;
-        src_pos.y = rop3->src_area.top;
-    }
-    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) {
-        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) % 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, 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, d, s, &src_pos, color);
-    }
-    pixman_image_unref(s);
-
-    blit_with_region(canvas, &dest_region, d,
-                     bbox->left,
-                     bbox->top);
-
-    pixman_image_unref(d);
-
-    pixman_region32_fini(&dest_region);
-}
-
-static void canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    pixman_region32_t dest_region;
-    int dx, dy;
-
-    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);
-
-    dx = bbox->left - src_pos->x;
-    dy = bbox->top - src_pos->y;
-
-    if (dx != 0 || dy != 0) {
-        pixman_region32_t src_region;
-
-        /* 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);
-
-        copy_region(canvas, &dest_region, dx, dy);
-    }
-
-    pixman_region32_fini(&dest_region);
-}
 
 static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
 {
@@ -1567,11 +769,11 @@ static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
                               bbox->right - bbox->left,
                               bbox->bottom - bbox->top);
 
-    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_clip_pixman(&canvas->base, &dest_region, clip);
 
     if (pixman_region32_n_rects(&dest_region) == 0) {
-        touch_brush(canvas, &text->fore_brush);
-        touch_brush(canvas, &text->back_brush);
+        touch_brush(&canvas->base, &text->fore_brush);
+        touch_brush(&canvas->base, &text->back_brush);
         pixman_region32_fini(&dest_region);
         return;
     }
@@ -1592,7 +794,7 @@ static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
         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);
+            draw_brush(spice_canvas, &back_region, &text->back_brush, SPICE_ROP_COPY);
         }
 
         pixman_region32_fini(&back_region);
@@ -1942,10 +1144,10 @@ static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, Spice
                               bbox->right - bbox->left,
                               bbox->bottom - bbox->top);
 
-    canvas_clip_pixman(canvas, &gc.dest_region, clip);
+    canvas_clip_pixman(&canvas->base, &gc.dest_region, clip);
 
     if (pixman_region32_n_rects(&gc.dest_region) == 0) {
-        touch_brush(canvas, &stroke->brush);
+        touch_brush(&canvas->base, &stroke->brush);
         pixman_region32_fini(&gc.dest_region);
         return;
     }
@@ -2194,25 +1396,28 @@ void cairo_canvas_init() //unsafe global function
     need_init = 0;
 
     canvas_base_init_ops(&cairo_canvas_ops);
-    cairo_canvas_ops.draw_fill = canvas_draw_fill;
-    cairo_canvas_ops.draw_copy = canvas_draw_copy;
-    cairo_canvas_ops.draw_opaque = canvas_draw_opaque;
-    cairo_canvas_ops.copy_bits = canvas_copy_bits;
     cairo_canvas_ops.draw_text = canvas_draw_text;
     cairo_canvas_ops.draw_stroke = canvas_draw_stroke;
     cairo_canvas_ops.draw_rop3 = canvas_draw_rop3;
-    cairo_canvas_ops.draw_blend = canvas_draw_blend;
-    cairo_canvas_ops.draw_blackness = canvas_draw_blackness;
-    cairo_canvas_ops.draw_whiteness = canvas_draw_whiteness;
-    cairo_canvas_ops.draw_invers = canvas_draw_invers;
-    cairo_canvas_ops.draw_transparent = canvas_draw_transparent;
-    cairo_canvas_ops.draw_alpha_blend = canvas_draw_alpha_blend;
     cairo_canvas_ops.put_image = canvas_put_image;
     cairo_canvas_ops.clear = canvas_clear;
     cairo_canvas_ops.read_bits = canvas_read_bits;
     cairo_canvas_ops.set_access_params = canvas_set_access_params;
     cairo_canvas_ops.destroy = canvas_destroy;
 
+    cairo_canvas_ops.fill_solid_spans = fill_solid_spans;
+    cairo_canvas_ops.fill_solid_rects = fill_solid_rects;
+    cairo_canvas_ops.fill_solid_rects_rop = fill_solid_rects_rop;
+    cairo_canvas_ops.fill_tiled_rects = fill_tiled_rects;
+    cairo_canvas_ops.fill_tiled_rects_rop = fill_tiled_rects_rop;
+    cairo_canvas_ops.blit_image = blit_image;
+    cairo_canvas_ops.blit_image_rop = blit_image_rop;
+    cairo_canvas_ops.scale_image = scale_image;
+    cairo_canvas_ops.scale_image_rop = scale_image_rop;
+    cairo_canvas_ops.blend_image = blend_image;
+    cairo_canvas_ops.blend_scale_image = blend_scale_image;
+    cairo_canvas_ops.colorkey_image = colorkey_image;
+    cairo_canvas_ops.colorkey_scale_image = colorkey_scale_image;
+    cairo_canvas_ops.copy_region = copy_region;
     rop3_init();
 }
-
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 4bf2be7..231b5ed 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -231,6 +231,176 @@ typedef struct ATTR_PACKED DataChunk {
 
 #endif
 
+typedef enum {
+    ROP_INPUT_SRC,
+    ROP_INPUT_BRUSH,
+    ROP_INPUT_DEST
+} ROPInput;
+
+static 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 inline void canvas_localize_palette(CanvasBase *canvas, SpicePalette *palette)
 {
     if (canvas->color_shift == 5) {
@@ -874,7 +1044,7 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
 
 #else
 
-static pixman_image_t *canvas_get_image_internal(CairoCanvas *canvas, SPICE_ADDRESS addr, int real_get)
+static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRESS addr, int real_get)
 {
     SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
 
@@ -908,12 +1078,10 @@ 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)
 {
@@ -1582,6 +1750,646 @@ void *spice_canvas_get_usr_data(SpiceCanvas *spice_canvas)
 }
 #endif
 
+
+static void canvas_clip_pixman(CanvasBase *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, n, sizeof(uint32_t));
+
+        SpiceRect *now = (SpiceRect *)(n + 1);
+        access_test(canvas, 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(CanvasBase *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,
+                            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 draw_brush(SpiceCanvas *canvas,
+                       pixman_region32_t *region,
+                       SpiceBrush *brush,
+                       SpiceROP rop)
+{
+    CanvasBase *canvas_base = (CanvasBase *)canvas;
+    uint32_t color;
+    SpicePattern *pattern;
+    pixman_image_t *tile;
+    int offset_x, offset_y;
+    pixman_box32_t *rects;
+    int n_rects;
+
+    rects = pixman_region32_rectangles(region, &n_rects);
+
+   switch (brush->type) {
+    case SPICE_BRUSH_TYPE_SOLID:
+        color = brush->u.color;
+        if (rop == SPICE_ROP_COPY) {
+            canvas->ops->fill_solid_rects(canvas, rects, n_rects, color);
+        } else {
+            canvas->ops->fill_solid_rects_rop(canvas, rects, n_rects, color, rop);
+        }
+        break;
+    case SPICE_BRUSH_TYPE_PATTERN:
+        pattern = &brush->u.pattern;
+        tile = canvas_get_image(canvas_base, pattern->pat);
+        offset_x = pattern->pos.x;
+        offset_y = pattern->pos.y;
+
+        if (rop == SPICE_ROP_COPY) {
+            canvas->ops->fill_tiled_rects(canvas, rects, n_rects, tile, offset_x, offset_y);
+        } else {
+            canvas->ops->fill_tiled_rects_rop(canvas, rects, n_rects,
+                                              tile, offset_x, offset_y, rop);
+        }
+        pixman_image_unref(tile);
+        break;
+    case SPICE_BRUSH_TYPE_NONE:
+        /* Still need to do *something* here, because rop could be e.g invert dest */
+        canvas->ops->fill_solid_rects_rop(canvas, rects, n_rects, 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(CanvasBase *canvas, SpiceBrush *brush)
+{
+    SpicePattern *pattern;
+
+    if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
+        pattern = &brush->u.pattern;
+        canvas_touch_image(canvas, pattern->pat);
+    }
+}
+
+static void canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    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);
+
+
+    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;
+    }
+
+    draw_brush(spice_canvas, &dest_region, &fill->brush, rop);
+
+    pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_region32_t dest_region;
+    pixman_image_t *src_image;
+    SpiceROP rop;
+
+    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, &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, copy->src_bitmap);
+        pixman_region32_fini(&dest_region);
+        return;
+    }
+
+    src_image = canvas_get_image(canvas, copy->src_bitmap);
+
+    if (rect_is_same_size(bbox, &copy->src_area)) {
+        if (rop == SPICE_ROP_COPY) {
+            spice_canvas->ops->blit_image(spice_canvas, &dest_region,
+                                          src_image,
+                                          bbox->left - copy->src_area.left,
+                                          bbox->top - copy->src_area.top);
+        } else {
+            spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
+                                              src_image,
+                                              bbox->left - copy->src_area.left,
+                                              bbox->top - copy->src_area.top,
+                                              rop);
+        }
+    } else {
+        if (rop == SPICE_ROP_COPY) {
+            spice_canvas->ops->scale_image(spice_canvas, &dest_region,
+                                           src_image,
+                                           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 {
+            spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+                                               src_image,
+                                               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);
+        }
+    }
+
+    pixman_image_unref(src_image);
+    pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_image_t *src_image;
+    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);
+
+    if (pixman_region32_n_rects (&dest_region) == 0) {
+        canvas_touch_image(canvas, transparent->src_bitmap);
+        pixman_region32_fini(&dest_region);
+        return;
+    }
+
+    src_image = canvas_get_image(canvas, transparent->src_bitmap);
+
+    if (rect_is_same_size(bbox, &transparent->src_area)) {
+        spice_canvas->ops->colorkey_image(spice_canvas, &dest_region,
+                                          src_image,
+                                          bbox->left - transparent->src_area.left,
+                                          bbox->top - transparent->src_area.top,
+                                          transparent->true_color);
+    } else {
+        spice_canvas->ops->colorkey_scale_image(spice_canvas, &dest_region,
+                                                src_image,
+                                                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);
+    }
+
+    pixman_image_unref(src_image);
+    pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_region32_t dest_region;
+    pixman_image_t *src_image;
+
+    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, alpha_blend->src_bitmap);
+        pixman_region32_fini(&dest_region);
+        return;
+    }
+
+    src_image = canvas_get_image(canvas, alpha_blend->src_bitmap);
+
+    if (rect_is_same_size(bbox, &alpha_blend->src_area)) {
+        spice_canvas->ops->blend_image(spice_canvas, &dest_region,
+                                       src_image,
+                                       alpha_blend->src_area.left,
+                                       alpha_blend->src_area.top,
+                                       bbox->left,
+                                       bbox->top,
+                                       bbox->right - bbox->left,
+                                       bbox->bottom - bbox->top,
+                                       alpha_blend->alpha);
+    } else {
+        spice_canvas->ops->blend_scale_image(spice_canvas, &dest_region,
+                                             src_image,
+                                             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);
+    }
+
+    pixman_image_unref(src_image);
+    pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_image_t *src_image;
+    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);
+
+    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_mask_pixman(canvas, &dest_region, &opaque->mask,
+                       bbox->left, bbox->top);
+
+    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, opaque->src_bitmap);
+        touch_brush(canvas, &opaque->brush);
+        pixman_region32_fini(&dest_region);
+        return;
+    }
+
+    src_image = canvas_get_image(canvas, opaque->src_bitmap);
+
+    if (rect_is_same_size(bbox, &opaque->src_area)) {
+        spice_canvas->ops->blit_image(spice_canvas, &dest_region,
+                                      src_image,
+                                      bbox->left - opaque->src_area.left,
+                                      bbox->top - opaque->src_area.top);
+    } else {
+        spice_canvas->ops->scale_image(spice_canvas, &dest_region,
+                                       src_image,
+                                       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);
+    }
+
+    draw_brush(spice_canvas, &dest_region, &opaque->brush, rop);
+
+    pixman_image_unref(src_image);
+    pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_image_t *src_image;
+    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);
+
+    canvas_clip_pixman(canvas, &dest_region, clip);
+    canvas_mask_pixman(canvas, &dest_region, &blend->mask,
+                       bbox->left, bbox->top);
+
+    rop = ropd_descriptor_to_rop(blend->rop_decriptor,
+                                 ROP_INPUT_SRC,
+                                 ROP_INPUT_DEST);
+
+    if (rop == SPICE_ROP_NOOP || pixman_region32_n_rects(&dest_region) == 0) {
+        canvas_touch_image(canvas, blend->src_bitmap);
+        pixman_region32_fini(&dest_region);
+        return;
+    }
+
+    src_image = canvas_get_image(canvas, blend->src_bitmap);
+
+    if (rect_is_same_size(bbox, &blend->src_area)) {
+        if (rop == SPICE_ROP_COPY)
+            spice_canvas->ops->blit_image(spice_canvas, &dest_region,
+                                          src_image,
+                                          bbox->left - blend->src_area.left,
+                                          bbox->top - blend->src_area.top);
+        else
+            spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
+                                              src_image,
+                                              bbox->left - blend->src_area.left,
+                                              bbox->top - blend->src_area.top,
+                                              rop);
+    } else {
+        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) {
+            spice_canvas->ops->scale_image(spice_canvas, &dest_region,
+                                           src_image,
+                                           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 {
+            spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+                                               src_image,
+                                               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);
+        }
+    }
+
+    pixman_image_unref(src_image);
+    pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_region32_t dest_region;
+    pixman_box32_t *rects;
+    int n_rects;
+
+   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;
+    }
+
+    rects = pixman_region32_rectangles(&dest_region, &n_rects);
+
+    spice_canvas->ops->fill_solid_rects(spice_canvas, rects, n_rects, 0x000000);
+
+    pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_region32_t dest_region;
+    pixman_box32_t *rects;
+    int n_rects;
+
+    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;
+    }
+
+    rects = pixman_region32_rectangles(&dest_region, &n_rects);
+    spice_canvas->ops->fill_solid_rects(spice_canvas, rects, n_rects, 0xffffffff);
+
+    pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_region32_t dest_region;
+    pixman_box32_t *rects;
+    int n_rects;
+
+    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;
+    }
+
+    rects = pixman_region32_rectangles(&dest_region, &n_rects);
+    spice_canvas->ops->fill_solid_rects_rop(spice_canvas, rects, n_rects, 0x00000000,
+                                            SPICE_ROP_INVERT);
+
+    pixman_region32_fini(&dest_region);
+}
+
+static void canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_region32_t dest_region;
+    int dx, dy;
+
+    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);
+
+    dx = bbox->left - src_pos->x;
+    dy = bbox->top - src_pos->y;
+
+    if (dx != 0 || dy != 0) {
+        pixman_region32_t src_region;
+
+        /* Clip so we don't read outside canvas */
+        pixman_region32_init_rect(&src_region,
+                                  dx, dy,
+                                  canvas->width,
+                                  canvas->height);
+        pixman_region32_intersect(&dest_region, &dest_region, &src_region);
+        pixman_region32_fini(&src_region);
+
+        spice_canvas->ops->copy_region(spice_canvas, &dest_region, dx, dy);
+    }
+
+    pixman_region32_fini(&dest_region);
+}
+
+
+
 static void canvas_base_group_start(SpiceCanvas *spice_canvas, QRegion *region)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
@@ -1622,6 +2430,16 @@ inline static void canvas_base_init_ops(SpiceCanvasOps *ops)
         ops_cast[i] = (void *) unimplemented_op;
     }
 
+    ops->draw_fill = canvas_draw_fill;
+    ops->draw_copy = canvas_draw_copy;
+    ops->draw_opaque = canvas_draw_opaque;
+    ops->copy_bits = canvas_copy_bits;
+    ops->draw_blend = canvas_draw_blend;
+    ops->draw_blackness = canvas_draw_blackness;
+    ops->draw_whiteness = canvas_draw_whiteness;
+    ops->draw_invers = canvas_draw_invers;
+    ops->draw_transparent = canvas_draw_transparent;
+    ops->draw_alpha_blend = canvas_draw_alpha_blend;
     ops->group_start = canvas_base_group_start;
     ops->group_end = canvas_base_group_end;
 }
diff --git a/common/canvas_base.h b/common/canvas_base.h
index 48e921e..bed1e1b 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -106,6 +106,90 @@ typedef struct {
     void (*group_end)(SpiceCanvas *canvas);
     void (*set_access_params)(SpiceCanvas *canvas, unsigned long base, unsigned long max);
     void (*destroy)(SpiceCanvas *canvas);
+
+    /* Implementation vfuncs */
+    void (*fill_solid_spans)(SpiceCanvas *canvas,
+                             SpicePoint *points,
+                             int *widths,
+                             int n_spans,
+                             uint32_t color);
+    void (*fill_solid_rects)(SpiceCanvas *canvas,
+                             pixman_box32_t *rects,
+                             int n_rects,
+                             uint32_t color);
+    void (*fill_solid_rects_rop)(SpiceCanvas *canvas,
+                                 pixman_box32_t *rects,
+                                 int n_rects,
+                                 uint32_t color,
+                                 SpiceROP rop);
+    void (*fill_tiled_rects)(SpiceCanvas *canvas,
+                             pixman_box32_t *rects,
+                             int n_rects,
+                             pixman_image_t *tile,
+                             int offset_x, int offset_y);
+    void (*fill_tiled_rects_rop)(SpiceCanvas *canvas,
+                                 pixman_box32_t *rects,
+                                 int n_rects,
+                                 pixman_image_t *tile,
+                                 int offset_x, int offset_y,
+                                 SpiceROP rop);
+    void (*blit_image)(SpiceCanvas *canvas,
+                       pixman_region32_t *region,
+                       pixman_image_t *src_image,
+                       int offset_x, int offset_y);
+    void (*blit_image_rop)(SpiceCanvas *canvas,
+                           pixman_region32_t *region,
+                           pixman_image_t *src_image,
+                           int offset_x, int offset_y,
+                           SpiceROP rop);
+    void (*scale_image)(SpiceCanvas *canvas,
+                        pixman_region32_t *region,
+                        pixman_image_t *src_image,
+                        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);
+    void (*scale_image_rop)(SpiceCanvas *canvas,
+                            pixman_region32_t *region,
+                            pixman_image_t *src_image,
+                            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);
+    void (*blend_image)(SpiceCanvas *canvas,
+                        pixman_region32_t *region,
+                        pixman_image_t *src_image,
+                        int src_x, int src_y,
+                        int dest_x, int dest_y,
+                        int width, int height,
+                        int overall_alpha);
+    void (*blend_scale_image)(SpiceCanvas *canvas,
+                              pixman_region32_t *region,
+                              pixman_image_t *src_image,
+                              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);
+    void (*colorkey_image)(SpiceCanvas *canvas,
+                           pixman_region32_t *region,
+                           pixman_image_t *src_image,
+                           int offset_x, int offset_y,
+                           uint32_t transparent_color);
+    void (*colorkey_scale_image)(SpiceCanvas *canvas,
+                                 pixman_region32_t *region,
+                                 pixman_image_t *src_image,
+                                 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);
+    void (*copy_region)(SpiceCanvas *canvas,
+                        pixman_region32_t *dest_region,
+                        int dx, int dy);
 } SpiceCanvasOps;
 
 void spice_canvas_set_usr_data(SpiceCanvas *canvas, void *data, spice_destroy_fn_t destroy_fn);
commit 18606d99eaf11dbceb79dc24f0a41e85345db300
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Mar 3 16:43:36 2010 +0100

    Move canvas_region and group_start/end to canvas_base

diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp
index a623e64..9f38dfb 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -80,7 +80,7 @@ void GDICanvas::set_mode(int width, int height, int depth)
 {
     destroy();
     create_pixmap(width, height);
-    if (!(_canvas = gdi_canvas_create(_pixmap->get_dc(),
+    if (!(_canvas = gdi_canvas_create(width, height, _pixmap->get_dc(),
                                       &_pixmap->get_mutex(),
                                       depth, &pixmap_cache().base,
                                       &palette_cache().base,
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 426643e..2c5b0d7 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -34,7 +34,6 @@ struct CairoCanvas {
     uint32_t *private_data;
     int private_data_size;
     pixman_image_t *image;
-    pixman_region32_t canvas_region;
 };
 
 typedef enum {
@@ -211,7 +210,7 @@ static void canvas_clip_pixman(CairoCanvas *canvas,
                                pixman_region32_t *dest_region,
                                SpiceClip *clip)
 {
-    pixman_region32_intersect(dest_region, dest_region, &canvas->canvas_region);
+    pixman_region32_intersect(dest_region, dest_region, &canvas->base.canvas_region);
 
     switch (clip->type) {
     case SPICE_CLIP_TYPE_NONE:
@@ -2109,29 +2108,6 @@ static void canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest, int dest_
     }
 }
 
-static void canvas_group_start(SpiceCanvas *spice_canvas, QRegion *region)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    pixman_region32_fini(&canvas->canvas_region);
-    /* Make sure we always clip to canvas size */
-    pixman_region32_init_rect(&canvas->canvas_region,
-                              0, 0,
-                              pixman_image_get_width (canvas->image),
-                              pixman_image_get_height (canvas->image));
-
-    pixman_region32_intersect(&canvas->canvas_region, &canvas->canvas_region, region);
-}
-
-static void canvas_group_end(SpiceCanvas *spice_canvas)
-{
-    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
-    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));
-}
-
 static void canvas_clear(SpiceCanvas *spice_canvas)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
@@ -2187,7 +2163,10 @@ SpiceCanvas *canvas_create(pixman_image_t *image, int bits
         return NULL;
     }
     memset(canvas, 0, sizeof(CairoCanvas));
-    init_ok = canvas_base_init(&canvas->base, &cairo_canvas_ops, bits
+    init_ok = canvas_base_init(&canvas->base, &cairo_canvas_ops,
+                               pixman_image_get_width (image),
+                               pixman_image_get_height (image),
+                               bits
 #ifdef CAIRO_CANVAS_CACHE
                                , bits_cache
                                , palette_cache
@@ -2203,10 +2182,6 @@ SpiceCanvas *canvas_create(pixman_image_t *image, int bits
     canvas->private_data_size = 0;
 
     canvas->image = pixman_image_ref(image);
-    pixman_region32_init_rect(&canvas->canvas_region,
-                              0, 0,
-                              pixman_image_get_width (canvas->image),
-                              pixman_image_get_height (canvas->image));
 
     return (SpiceCanvas *)canvas;
 }
@@ -2235,8 +2210,6 @@ void cairo_canvas_init() //unsafe global function
     cairo_canvas_ops.put_image = canvas_put_image;
     cairo_canvas_ops.clear = canvas_clear;
     cairo_canvas_ops.read_bits = canvas_read_bits;
-    cairo_canvas_ops.group_start = canvas_group_start;
-    cairo_canvas_ops.group_end = canvas_group_end;
     cairo_canvas_ops.set_access_params = canvas_set_access_params;
     cairo_canvas_ops.destroy = canvas_destroy;
 
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 2a94607..4bf2be7 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -184,6 +184,10 @@ typedef struct CanvasBase {
     unsigned long max;
 #endif
 
+    int width;
+    int height;
+    pixman_region32_t canvas_region;
+
 #if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
     SpiceImageCache *bits_cache;
 #endif
@@ -1578,6 +1582,31 @@ void *spice_canvas_get_usr_data(SpiceCanvas *spice_canvas)
 }
 #endif
 
+static void canvas_base_group_start(SpiceCanvas *spice_canvas, QRegion *region)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_region32_fini(&canvas->canvas_region);
+
+    /* Make sure we always clip to canvas size */
+    pixman_region32_init_rect(&canvas->canvas_region,
+                              0, 0,
+                              canvas->width,
+                              canvas->height);
+
+    pixman_region32_intersect(&canvas->canvas_region, &canvas->canvas_region, region);
+}
+
+static void canvas_base_group_end(SpiceCanvas *spice_canvas)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    pixman_region32_fini(&canvas->canvas_region);
+    pixman_region32_init_rect(&canvas->canvas_region,
+                              0, 0,
+                              canvas->width,
+                              canvas->height);
+}
+
+
 static void unimplemented_op(SpiceCanvas *canvas)
 {
     PANIC("unimplemented canvas operation");
@@ -1592,9 +1621,13 @@ inline static void canvas_base_init_ops(SpiceCanvasOps *ops)
     for (i = 0; i < sizeof(SpiceCanvasOps) / sizeof(void *); i++) {
         ops_cast[i] = (void *) unimplemented_op;
     }
+
+    ops->group_start = canvas_base_group_start;
+    ops->group_end = canvas_base_group_end;
 }
 
-static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops, int depth
+static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
+                            int width, int height, int depth
 #ifdef CAIRO_CANVAS_CACHE
                             , SpiceImageCache *bits_cache
                             , SpicePaletteCache *palette_cache
@@ -1643,6 +1676,12 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops, int depth
         canvas->color_mask = 0xff;
     }
 
+    canvas->width = width;
+    canvas->height = height;
+    pixman_region32_init_rect(&canvas->canvas_region,
+                              0, 0,
+                              canvas->width,
+                              canvas->height);
 
 #if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
     canvas->bits_cache = bits_cache;
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index 4d5bc9d..54d6094 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -1716,7 +1716,8 @@ static void gdi_canvas_destroy(SpiceCanvas *spice_canvas)
 static int need_init = 1;
 static SpiceCanvasOps gdi_canvas_ops;
 
-SpiceCanvas *gdi_canvas_create(HDC dc, Mutex* lock, int bits
+SpiceCanvas *gdi_canvas_create(int width, int height,
+                               HDC dc, Mutex* lock, int bits
 #ifdef CAIRO_CANVAS_CACHE
                              , SpiceImageCache *bits_cache
                              , SpicePaletteCache *palette_cache
@@ -1733,7 +1734,8 @@ SpiceCanvas *gdi_canvas_create(HDC dc, Mutex* lock, int bits
         return NULL;
     }
     memset(canvas, 0, sizeof(GdiCanvas));
-    init_ok = canvas_base_init(&canvas->base, &gdi_canvas_ops, bits
+    init_ok = canvas_base_init(&canvas->base, &gdi_canvas_ops,
+                               width, height, bits
 #ifdef CAIRO_CANVAS_CACHE
                                ,bits_cache
                                ,palette_cache
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
index b3dc605..8b8f847 100644
--- a/common/gdi_canvas.h
+++ b/common/gdi_canvas.h
@@ -33,7 +33,8 @@ typedef struct {
     uint8_t *pixels;
 } GdiImage;
 
-SpiceCanvas *gdi_canvas_create(HDC dc, class Mutex *lock, int bits,
+SpiceCanvas *gdi_canvas_create(int width, int height,
+                               HDC dc, class Mutex *lock, int bits,
                                SpiceImageCache *bits_cache,
                                SpicePaletteCache *palette_cache,
                                SpiceGlzDecoder *glz_decoder);
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index 99f14f5..c3c9388 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -738,6 +738,8 @@ static void gl_canvas_group_start(SpiceCanvas *spice_canvas, QRegion *region)
     int num_rect;
     pixman_box32_t *rects;
 
+    canvas_base_group_start(spice_canvas, region);
+
     rects = pixman_region32_rectangles(region, &num_rect);
 
     glc_rects = (GLCRect *)malloc(num_rect * sizeof(GLCRect));
@@ -803,6 +805,8 @@ static void gl_canvas_put_image(SpiceCanvas *spice_canvas, const SpiceRect *dest
 static void gl_canvas_group_end(SpiceCanvas *spice_canvas)
 {
     GLCanvas *canvas = (GLCanvas *)spice_canvas;
+
+    canvas_base_group_end(spice_canvas);
     glc_clear_mask(canvas->glc, GLC_MASK_B);
 }
 
@@ -842,7 +846,8 @@ SpiceCanvas *gl_canvas_create(int width, int height, int depth
         goto error_1;
     }
     canvas->private_data = NULL;
-    init_ok = canvas_base_init(&canvas->base, &gl_canvas_ops, depth
+    init_ok = canvas_base_init(&canvas->base, &gl_canvas_ops,
+                               width, height, depth
 #ifdef CAIRO_CANVAS_CACHE
                                , bits_cache
                                , palette_cache
commit f8217d8b96f35fd297c74df1da6428b56b01149a
Author: Alexander Larsson <alexl at redhat.com>
Date:   Wed Mar 3 15:08:58 2010 +0100

    Move virtualization of canvas drawing into common/canvas_base
    
    Instead of having two virtualizations of the canvas we push the
    virtualization into the canvas code itself. This not only avoids
    the duplication of this code, it also makes the exposed API for the
    canvas much smaller (in terms of exported API).
    
    It also lets us use the virtualization to implement basic support
    for operations in canvas_base which is then overridden by each canvas
    implementation.

diff --git a/client/canvas.cpp b/client/canvas.cpp
index 516a874..ba446c9 100644
--- a/client/canvas.cpp
+++ b/client/canvas.cpp
@@ -23,7 +23,8 @@
 
 Canvas::Canvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
                GlzDecoderWindow &glz_decoder_window)
-    : _pixmap_cache (pixmap_cache)
+    : _canvas (NULL)
+    , _pixmap_cache (pixmap_cache)
     , _palette_cache (palette_cache)
     , _glz_decoder(glz_decoder_window, _glz_handler, _glz_debug)
 {
@@ -31,8 +32,17 @@ Canvas::Canvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
 
 Canvas::~Canvas()
 {
+    /* _canvas is both set and destroyed by derived class */
 }
 
+void Canvas::clear()
+{
+    if (_canvas) {
+        _canvas->ops->clear(_canvas);
+    }
+}
+
+
 inline void Canvas::access_test(void *ptr, size_t size)
 {
     if ((unsigned long)ptr < _base || (unsigned long)ptr + size > _max) {
@@ -107,7 +117,7 @@ void Canvas::begin_draw(SpiceMsgDisplayBase& base, int size, size_t min_size)
 {
     _base = (unsigned long)&base;
     _max = _base + size;
-    set_access_params(_base, _max);
+    _canvas->ops->set_access_params(_canvas, _base, _max);
     access_test(&base, min_size);
     localalize_ptr(&base.clip.data);
 }
@@ -117,7 +127,8 @@ void Canvas::draw_fill(SpiceMsgDisplayDrawFill& fill, int size)
     begin_draw(fill.base, size, sizeof(SpiceMsgDisplayDrawFill));
     localalize_brush(fill.data.brush);
     localalize_mask(fill.data.mask);
-    draw_fill(&fill.base.box, &fill.base.clip, &fill.data);
+    _canvas->ops->draw_fill(_canvas, &fill.base.box, &fill.base.clip, &fill.data);
+    touched_bbox(&fill.base.box);
 }
 
 void Canvas::draw_text(SpiceMsgDisplayDrawText& text, int size)
@@ -126,7 +137,8 @@ void Canvas::draw_text(SpiceMsgDisplayDrawText& text, int size)
     localalize_brush(text.data.fore_brush);
     localalize_brush(text.data.back_brush);
     localalize_ptr(&text.data.str);
-    draw_text(&text.base.box, &text.base.clip, &text.data);
+    _canvas->ops->draw_text(_canvas, &text.base.box, &text.base.clip, &text.data);
+    touched_bbox(&text.base.box);
 }
 
 void Canvas::draw_opaque(SpiceMsgDisplayDrawOpaque& opaque, int size)
@@ -135,7 +147,8 @@ void Canvas::draw_opaque(SpiceMsgDisplayDrawOpaque& opaque, int size)
     localalize_brush(opaque.data.brush);
     localalize_image(&opaque.data.src_bitmap);
     localalize_mask(opaque.data.mask);
-    draw_opaque(&opaque.base.box, &opaque.base.clip, &opaque.data);
+    _canvas->ops->draw_opaque(_canvas, &opaque.base.box, &opaque.base.clip, &opaque.data);
+    touched_bbox(&opaque.base.box);
 }
 
 void Canvas::draw_copy(SpiceMsgDisplayDrawCopy& copy, int size)
@@ -143,27 +156,31 @@ void Canvas::draw_copy(SpiceMsgDisplayDrawCopy& copy, int size)
     begin_draw(copy.base, size, sizeof(SpiceMsgDisplayDrawCopy));
     localalize_image(&copy.data.src_bitmap);
     localalize_mask(copy.data.mask);
-    draw_copy(&copy.base.box, &copy.base.clip, &copy.data);
+    _canvas->ops->draw_copy(_canvas, &copy.base.box, &copy.base.clip, &copy.data);
+    touched_bbox(&copy.base.box);
 }
 
 void Canvas::draw_transparent(SpiceMsgDisplayDrawTransparent& transparent, int size)
 {
     begin_draw(transparent.base, size, sizeof(SpiceMsgDisplayDrawTransparent));
     localalize_image(&transparent.data.src_bitmap);
-    draw_transparent(&transparent.base.box, &transparent.base.clip, &transparent.data);
+    _canvas->ops->draw_transparent(_canvas, &transparent.base.box, &transparent.base.clip, &transparent.data);
+    touched_bbox(&transparent.base.box);
 }
 
 void Canvas::draw_alpha_blend(SpiceMsgDisplayDrawAlphaBlend& alpha_blend, int size)
 {
     begin_draw(alpha_blend.base, size, sizeof(SpiceMsgDisplayDrawAlphaBlend));
     localalize_image(&alpha_blend.data.src_bitmap);
-    draw_alpha_blend(&alpha_blend.base.box, &alpha_blend.base.clip, &alpha_blend.data);
+    _canvas->ops->draw_alpha_blend(_canvas, &alpha_blend.base.box, &alpha_blend.base.clip, &alpha_blend.data);
+    touched_bbox(&alpha_blend.base.box);
 }
 
 void Canvas::copy_bits(SpiceMsgDisplayCopyBits& copy, int size)
 {
     begin_draw(copy.base, size, sizeof(SpiceMsgDisplayCopyBits));
-    copy_bits(&copy.base.box, &copy.base.clip, &copy.src_pos);
+    _canvas->ops->copy_bits(_canvas, &copy.base.box, &copy.base.clip, &copy.src_pos);
+    touched_bbox(&copy.base.box);
 }
 
 void Canvas::draw_blend(SpiceMsgDisplayDrawBlend& blend, int size)
@@ -171,28 +188,32 @@ void Canvas::draw_blend(SpiceMsgDisplayDrawBlend& blend, int size)
     begin_draw(blend.base, size, sizeof(SpiceMsgDisplayDrawBlend));
     localalize_image(&blend.data.src_bitmap);
     localalize_mask(blend.data.mask);
-    draw_blend(&blend.base.box, &blend.base.clip, &blend.data);
+    _canvas->ops->draw_blend(_canvas, &blend.base.box, &blend.base.clip, &blend.data);
+    touched_bbox(&blend.base.box);
 }
 
 void Canvas::draw_blackness(SpiceMsgDisplayDrawBlackness& blackness, int size)
 {
     begin_draw(blackness.base, size, sizeof(SpiceMsgDisplayDrawBlackness));
     localalize_mask(blackness.data.mask);
-    draw_blackness(&blackness.base.box, &blackness.base.clip, &blackness.data);
+    _canvas->ops->draw_blackness(_canvas, &blackness.base.box, &blackness.base.clip, &blackness.data);
+    touched_bbox(&blackness.base.box);
 }
 
 void Canvas::draw_whiteness(SpiceMsgDisplayDrawWhiteness& whiteness, int size)
 {
     begin_draw(whiteness.base, size, sizeof(SpiceMsgDisplayDrawWhiteness));
     localalize_mask(whiteness.data.mask);
-    draw_whiteness(&whiteness.base.box, &whiteness.base.clip, &whiteness.data);
+    _canvas->ops->draw_whiteness(_canvas, &whiteness.base.box, &whiteness.base.clip, &whiteness.data);
+    touched_bbox(&whiteness.base.box);
 }
 
 void Canvas::draw_invers(SpiceMsgDisplayDrawInvers& invers, int size)
 {
     begin_draw(invers.base, size, sizeof(SpiceMsgDisplayDrawInvers));
     localalize_mask(invers.data.mask);
-    draw_invers(&invers.base.box, &invers.base.clip, &invers.data);
+    _canvas->ops->draw_invers(_canvas, &invers.base.box, &invers.base.clip, &invers.data);
+    touched_bbox(&invers.base.box);
 }
 
 void Canvas::draw_rop3(SpiceMsgDisplayDrawRop3& rop3, int size)
@@ -201,7 +222,8 @@ void Canvas::draw_rop3(SpiceMsgDisplayDrawRop3& rop3, int size)
     localalize_brush(rop3.data.brush);
     localalize_image(&rop3.data.src_bitmap);
     localalize_mask(rop3.data.mask);
-    draw_rop3(&rop3.base.box, &rop3.base.clip, &rop3.data);
+    _canvas->ops->draw_rop3(_canvas, &rop3.base.box, &rop3.base.clip, &rop3.data);
+    touched_bbox(&rop3.base.box);
 }
 
 void Canvas::draw_stroke(SpiceMsgDisplayDrawStroke& stroke, int size)
@@ -210,5 +232,21 @@ void Canvas::draw_stroke(SpiceMsgDisplayDrawStroke& stroke, int size)
     localalize_brush(stroke.data.brush);
     localalize_ptr(&stroke.data.path);
     localalize_attr(stroke.data.attr);
-    draw_stroke(&stroke.base.box, &stroke.base.clip, &stroke.data);
+    _canvas->ops->draw_stroke(_canvas, &stroke.base.box, &stroke.base.clip, &stroke.data);
+    touched_bbox(&stroke.base.box);
+}
+
+void Canvas::put_image(
+#ifdef WIN32
+                        HDC dc,
+#endif
+                        const PixmapHeader& image, const SpiceRect& dest, const QRegion* clip)
+{
+    _canvas->ops->put_image(_canvas,
+#ifdef WIN32
+                            dc,
+#endif
+                            &dest, image.data, image.width, image.height, image.stride,
+                            clip);
+    touched_bbox(&dest);
 }
diff --git a/client/canvas.h b/client/canvas.h
index 2f12263..8e3a8f1 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -249,7 +249,7 @@ public:
     virtual void copy_pixels(const QRegion& region, RedDrawable& dc) = 0;
     virtual void thread_touch() = 0;
 
-    virtual void clear() = 0;
+    void clear();
 
     void draw_fill(SpiceMsgDisplayDrawFill& fill, int size);
     void draw_text(SpiceMsgDisplayDrawText& text, int size);
@@ -265,31 +265,17 @@ public:
     void draw_rop3(SpiceMsgDisplayDrawRop3& rop3, int size);
     void draw_stroke(SpiceMsgDisplayDrawStroke& stroke, int size);
 
+    void put_image(
 #ifdef WIN32
-    virtual void put_image(HDC dc, const PixmapHeader& image,
-                           const SpiceRect& dest, const QRegion* clip) = 0;
-#else
-    virtual void put_image(const PixmapHeader& image, const SpiceRect& dest,
-                           const QRegion* clip) = 0;
+                   HDC dc,
 #endif
+                   const PixmapHeader& image,
+                   const SpiceRect& dest, const QRegion* clip);
 
     virtual CanvasType get_pixmap_type() { return CANVAS_TYPE_INVALID; }
 
 protected:
-    virtual void set_access_params(unsigned long base, unsigned long max) = 0;
-    virtual void draw_fill(SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill) = 0;
-    virtual void draw_copy(SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy) = 0;
-    virtual void draw_opaque(SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque) = 0;
-    virtual void copy_bits(SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos) = 0;
-    virtual void draw_text(SpiceRect *bbox, SpiceClip *clip, SpiceText *text) = 0;
-    virtual void draw_stroke(SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke) = 0;
-    virtual void draw_rop3(SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3) = 0;
-    virtual void draw_blend(SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend) = 0;
-    virtual void draw_blackness(SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness) = 0;
-    virtual void draw_whiteness(SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness) = 0;
-    virtual void draw_invers(SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers) = 0;
-    virtual void draw_transparent(SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent) = 0;
-    virtual void draw_alpha_blend(SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend) = 0;
+    virtual void touched_bbox(const SpiceRect *bbox) {};
 
     PixmapCache& pixmap_cache() { return _pixmap_cache;}
     PaletteCache& palette_cache() { return _palette_cache;}
@@ -305,6 +291,9 @@ private:
     void localalize_mask(SpiceQMask& mask);
     void begin_draw(SpiceMsgDisplayBase& base, int size, size_t min_size);
 
+protected:
+    SpiceCanvas* _canvas;
+
 private:
     PixmapCache& _pixmap_cache;
     PaletteCache& _palette_cache;
diff --git a/client/red_cairo_canvas.cpp b/client/red_cairo_canvas.cpp
index 97277fb..02c105a 100644
--- a/client/red_cairo_canvas.cpp
+++ b/client/red_cairo_canvas.cpp
@@ -27,7 +27,6 @@
 CCanvas::CCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
                  GlzDecoderWindow &glz_decoder_window)
     : Canvas (pixmap_cache, palette_cache, glz_decoder_window)
-    , _canvas (NULL)
     , _pixmap (0)
 {
 }
@@ -40,19 +39,12 @@ CCanvas::~CCanvas()
 void CCanvas::destroy()
 {
     if (_canvas) {
-        canvas_destroy(_canvas);
+        _canvas->ops->destroy(_canvas);
         _canvas = NULL;
     }
     destroy_pixmap();
 }
 
-void CCanvas::clear()
-{
-    if (_canvas) {
-        canvas_clear(_canvas);
-    }
-}
-
 void CCanvas::destroy_pixmap()
 {
     delete _pixmap;
@@ -108,92 +100,6 @@ void CCanvas::set_mode(int width, int height, int depth, RedWindow *win)
     pixman_image_unref (surface);
 }
 
-void CCanvas::set_access_params(unsigned long base, unsigned long max)
-{
-    canvas_set_access_params(_canvas, base, max);
-}
-
-void CCanvas::draw_fill(SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
-{
-    canvas_draw_fill(_canvas, bbox, clip, fill);
-}
-
-void CCanvas::draw_text(SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
-{
-    canvas_draw_text(_canvas, bbox, clip, text);
-}
-
-void CCanvas::draw_opaque(SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
-{
-    canvas_draw_opaque(_canvas, bbox, clip, opaque);
-}
-
-void CCanvas::draw_copy(SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
-{
-    canvas_draw_copy(_canvas, bbox, clip, copy);
-}
-
-void CCanvas::draw_transparent(SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
-{
-    canvas_draw_transparent(_canvas, bbox, clip, transparent);
-}
-
-void CCanvas::draw_alpha_blend(SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
-{
-    canvas_draw_alpha_blend(_canvas, bbox, clip, alpha_blend);
-}
-
-void CCanvas::copy_bits(SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
-{
-    canvas_copy_bits(_canvas, bbox, clip, src_pos);
-}
-
-void CCanvas::draw_blend(SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
-{
-    canvas_draw_blend(_canvas, bbox, clip, blend);
-}
-
-void CCanvas::draw_blackness(SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
-{
-    canvas_draw_blackness(_canvas, bbox, clip, blackness);
-}
-
-void CCanvas::draw_whiteness(SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
-{
-    canvas_draw_whiteness(_canvas, bbox, clip, whiteness);
-}
-
-void CCanvas::draw_invers(SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
-{
-    canvas_draw_invers(_canvas, bbox, clip, invers);
-}
-
-void CCanvas::draw_rop3(SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
-{
-    canvas_draw_rop3(_canvas, bbox, clip, rop3);
-}
-
-void CCanvas::draw_stroke(SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
-{
-    canvas_draw_stroke(_canvas, bbox, clip, stroke);
-}
-
-#ifdef WIN32
-void CCanvas::put_image(HDC dc, const PixmapHeader& image, const SpiceRect& dest, const QRegion* clip)
-{
-    canvas_put_image(_canvas, dc, &dest, image.data, image.width, image.height, image.stride,
-                     clip);
-}
-
-#else
-void CCanvas::put_image(const PixmapHeader& image, const SpiceRect& dest, const QRegion* clip)
-{
-    canvas_put_image(_canvas, &dest, image.data, image.width, image.height, image.stride,
-                     clip);
-}
-
-#endif
-
 CanvasType CCanvas::get_pixmap_type()
 {
     return CANVAS_TYPE_CAIRO;
diff --git a/client/red_cairo_canvas.h b/client/red_cairo_canvas.h
index 2bcb3a8..51c6c5a 100644
--- a/client/red_cairo_canvas.h
+++ b/client/red_cairo_canvas.h
@@ -30,44 +30,19 @@ public:
     virtual ~CCanvas();
 
     virtual void set_mode(int x, int y, int bits, RedWindow *win);
-    virtual void clear();
     virtual void thread_touch() {}
     virtual void copy_pixels(const QRegion& region, RedDrawable* dc,
                              const PixmapHeader* pixmap);
     virtual void copy_pixels(const QRegion& region, RedDrawable& dc);
-#ifdef WIN32
-    virtual void put_image(HDC dc, const PixmapHeader& image,
-                           const SpiceRect& dest, const QRegion* clip);
-#else
-    virtual void put_image(const PixmapHeader& image, const SpiceRect& dest,
-                           const QRegion* clip);
-#endif
 
     virtual CanvasType get_pixmap_type();
 
-protected:
-    virtual void set_access_params(unsigned long base, unsigned long max);
-    virtual void draw_fill(SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
-    virtual void draw_copy(SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy);
-    virtual void draw_opaque(SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque);
-    virtual void copy_bits(SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos);
-    virtual void draw_text(SpiceRect *bbox, SpiceClip *clip, SpiceText *text);
-    virtual void draw_stroke(SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke);
-    virtual void draw_rop3(SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3);
-    virtual void draw_blend(SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend);
-    virtual void draw_blackness(SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness);
-    virtual void draw_whiteness(SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness);
-    virtual void draw_invers(SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers);
-    virtual void draw_transparent(SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent);
-    virtual void draw_alpha_blend(SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend);
-
 private:
     void create_pixmap(int width, int height, RedWindow *win);
     void destroy_pixmap();
     void destroy();
 
 private:
-    CairoCanvas* _canvas;
     RedPixmap *_pixmap;
     unsigned long _base;
     unsigned long _max;
diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp
index efdf5d4..a623e64 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -26,7 +26,6 @@
 GDICanvas::GDICanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
                      GlzDecoderWindow &glz_decoder_window)
     : Canvas (pixmap_cache, palette_cache, glz_decoder_window)
-    , _canvas (NULL)
     , _pixmap (0)
 {
 }
@@ -44,13 +43,6 @@ void GDICanvas::destroy()
     destroy_pixmap();
 }
 
-void GDICanvas::clear()
-{
-    if (_canvas) {
-        gdi_canvas_clear(_canvas);
-    }
-}
-
 void GDICanvas::destroy_pixmap()
 {
     delete _pixmap;
@@ -97,82 +89,6 @@ void GDICanvas::set_mode(int width, int height, int depth)
     }
 }
 
-void GDICanvas::set_access_params(unsigned long base, unsigned long max)
-{
-    gdi_canvas_set_access_params(_canvas, base, max);
-}
-
-void GDICanvas::draw_fill(SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
-{
-    gdi_canvas_draw_fill(_canvas, bbox, clip, fill);
-}
-
-void GDICanvas::draw_text(SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
-{
-    gdi_canvas_draw_text(_canvas, bbox, clip, text);
-}
-
-void GDICanvas::draw_opaque(SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
-{
-    gdi_canvas_draw_opaque(_canvas, bbox, clip, opaque);
-}
-
-void GDICanvas::draw_copy(SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
-{
-    gdi_canvas_draw_copy(_canvas, bbox, clip, copy);
-}
-
-void GDICanvas::draw_transparent(SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
-{
-    gdi_canvas_draw_transparent(_canvas, bbox, clip, transparent);
-}
-
-void GDICanvas::draw_alpha_blend(SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
-{
-    gdi_canvas_draw_alpha_blend(_canvas, bbox, clip, alpha_blend);
-}
-
-void GDICanvas::copy_bits(SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
-{
-    gdi_canvas_copy_bits(_canvas, bbox, clip, src_pos);
-}
-
-void GDICanvas::draw_blend(SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
-{
-    gdi_canvas_draw_blend(_canvas, bbox, clip, blend);
-}
-
-void GDICanvas::draw_blackness(SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
-{
-    gdi_canvas_draw_blackness(_canvas, bbox, clip, blackness);
-}
-
-void GDICanvas::draw_whiteness(SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
-{
-    gdi_canvas_draw_whiteness(_canvas, bbox, clip, whiteness);
-}
-
-void GDICanvas::draw_invers(SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
-{
-    gdi_canvas_draw_invers(_canvas, bbox, clip, invers);
-}
-
-void GDICanvas::draw_rop3(SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
-{
-    gdi_canvas_draw_rop3(_canvas, bbox, clip, rop3);
-}
-
-void GDICanvas::draw_stroke(SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
-{
-    gdi_canvas_draw_stroke(_canvas, bbox, clip, stroke);
-}
-
-void GDICanvas::put_image(HDC dc, const PixmapHeader& image, const SpiceRect& dest, const QRegion* clip)
-{
-    gdi_canvas_put_image(_canvas, dc, &dest, image.data, image.width, image.height, image.stride,
-                         clip);
-}
-
 CanvasType GDICanvas::get_pixmap_type()
 {
     return CANVAS_TYPE_GDI;
diff --git a/client/red_gdi_canvas.h b/client/red_gdi_canvas.h
index 6f49434..83fc120 100644
--- a/client/red_gdi_canvas.h
+++ b/client/red_gdi_canvas.h
@@ -32,39 +32,19 @@ public:
     virtual ~GDICanvas();
 
     virtual void set_mode(int x, int y, int bits);
-    virtual void clear();
     virtual void thread_touch() {}
     virtual void copy_pixels(const QRegion& region, RedDrawable* dc,
                              const PixmapHeader* pixmap);
     virtual void copy_pixels(const QRegion& region, RedDrawable& dc);
-    virtual void put_image(HDC dc, const PixmapHeader& image, const SpiceRect& dest,
-                           const QRegion* clip);
 
     virtual CanvasType get_pixmap_type();
 
-protected:
-    virtual void set_access_params(unsigned long base, unsigned long max);
-    virtual void draw_fill(SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
-    virtual void draw_copy(SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy);
-    virtual void draw_opaque(SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque);
-    virtual void copy_bits(SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos);
-    virtual void draw_text(SpiceRect *bbox, SpiceClip *clip, SpiceText *text);
-    virtual void draw_stroke(SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke);
-    virtual void draw_rop3(SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3);
-    virtual void draw_blend(SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend);
-    virtual void draw_blackness(SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness);
-    virtual void draw_whiteness(SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness);
-    virtual void draw_invers(SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers);
-    virtual void draw_transparent(SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent);
-    virtual void draw_alpha_blend(SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend);
-
 private:
     void create_pixmap(int width, int height);
     void destroy_pixmap();
     void destroy();
 
 private:
-    GdiCanvas* _canvas;
     RedPixmapGdi *_pixmap;
     RedPixmapGdi *_helper_pixmap;
     HDC _dc;
diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp
index 76dabc7..c6bb113 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -27,7 +27,6 @@
 GCanvas::GCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
                  GlzDecoderWindow &glz_decoder_window)
     : Canvas(pixmap_cache, palette_cache, glz_decoder_window)
-    , _canvas (NULL)
     , _pixmap (0)
     , _textures_lost (false)
 {
@@ -41,19 +40,13 @@ GCanvas::~GCanvas()
 void GCanvas::destroy()
 {
     if (_canvas) {
-        gl_canvas_destroy(_canvas, _textures_lost);
+        gl_canvas_set_textures_lost (_canvas, (int)_textures_lost);
+        _canvas->ops->destroy(_canvas);
         _canvas = NULL;
     }
     destroy_pixmap();
 }
 
-void GCanvas::clear()
-{
-    if (_canvas) {
-        gl_canvas_clear(_canvas);
-    }
-}
-
 void GCanvas::destroy_pixmap()
 {
     delete _pixmap;
@@ -96,7 +89,7 @@ void GCanvas::set_mode(int width, int height, int depth, RedWindow *win,
     destroy();
 
     create_pixmap(width, height, win, rendertype);
-    if (!(_canvas = gl_canvas_create(NULL, width, height, depth,
+    if (!(_canvas = gl_canvas_create(width, height, depth,
                                      &pixmap_cache().base,
                                      &palette_cache().base,
                                      &glz_decoder()))) {
@@ -104,97 +97,11 @@ void GCanvas::set_mode(int width, int height, int depth, RedWindow *win,
     }
 }
 
-void GCanvas::set_access_params(unsigned long base, unsigned long max)
-{
-    gl_canvas_set_access_params(_canvas, base, max);
-}
-
-void GCanvas::draw_fill(SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
-{
-    gl_canvas_draw_fill(_canvas, bbox, clip, fill);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::draw_text(SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
-{
-    gl_canvas_draw_text(_canvas, bbox, clip, text);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::draw_opaque(SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
-{
-    gl_canvas_draw_opaque(_canvas, bbox, clip, opaque);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::draw_copy(SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
+void GCanvas::touched_bbox(const SpiceRect *bbox)
 {
-    gl_canvas_draw_copy(_canvas, bbox, clip, copy);
     _pixmap->update_texture(bbox);
 }
 
-void GCanvas::draw_transparent(SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
-{
-    gl_canvas_draw_transparent(_canvas, bbox, clip, transparent);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::draw_alpha_blend(SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
-{
-    gl_canvas_draw_alpha_blend(_canvas, bbox, clip, alpha_blend);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::copy_bits(SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
-{
-    gl_canvas_copy_pixels(_canvas, bbox, clip, src_pos);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::draw_blend(SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
-{
-    gl_canvas_draw_blend(_canvas, bbox, clip, blend);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::draw_blackness(SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
-{
-    gl_canvas_draw_blackness(_canvas, bbox, clip, blackness);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::draw_whiteness(SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
-{
-    gl_canvas_draw_whiteness(_canvas, bbox, clip, whiteness);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::draw_invers(SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
-{
-    gl_canvas_draw_invers(_canvas, bbox, clip, invers);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::draw_rop3(SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
-{
-    gl_canvas_draw_rop3(_canvas, bbox, clip, rop3);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::draw_stroke(SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
-{
-    gl_canvas_draw_stroke(_canvas, bbox, clip, stroke);
-    _pixmap->update_texture(bbox);
-}
-
-void GCanvas::put_image(const PixmapHeader& image, const SpiceRect& dest,
-                        const QRegion* clip)
-{
-    gl_canvas_put_image(_canvas, &dest, image.data, image.width, image.height,
-                        image.stride, clip);
-    _pixmap->update_texture(&dest);
-}
-
 CanvasType GCanvas::get_pixmap_type()
 {
     return CANVAS_TYPE_GL;
diff --git a/client/red_gl_canvas.h b/client/red_gl_canvas.h
index 918aa6c..983f772 100644
--- a/client/red_gl_canvas.h
+++ b/client/red_gl_canvas.h
@@ -39,29 +39,12 @@ public:
     void copy_pixels(const QRegion& region, RedDrawable* dc,
                      const PixmapHeader* pixmap);
     void copy_pixels(const QRegion& region, RedDrawable& dc);
-    void put_image(const PixmapHeader& image, const SpiceRect& dest,
-                   const QRegion* clip);
-
-    void set_access_params(unsigned long base, unsigned long max);
-    void draw_fill(SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
-    void draw_copy(SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy);
-    void draw_opaque(SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque);
-    void copy_bits(SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos);
-    void draw_text(SpiceRect *bbox, SpiceClip *clip, SpiceText *text);
-    void draw_stroke(SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke);
-    void draw_rop3(SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3);
-    void draw_blend(SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend);
-    void draw_blackness(SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness);
-    void draw_whiteness(SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness);
-    void draw_invers(SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers);
-    void draw_transparent(SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent);
-    void draw_alpha_blend(SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend);
-
     virtual void textures_lost();
     virtual CanvasType get_pixmap_type();
     virtual void touch_context();
     virtual void pre_gl_copy();
     virtual void post_gl_copy();
+    void touched_bbox(const SpiceRect *bbox);
 
 private:
     void create_pixmap(int width, int height, RedWindow *win,
@@ -70,7 +53,6 @@ private:
     void destroy();
 
 private:
-    GLCanvas* _canvas;
     RedPixmapGL *_pixmap;
     bool _textures_lost;
 };
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 8dcb12d..426643e 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -19,6 +19,7 @@
 #include <math.h>
 #include "cairo_canvas.h"
 #define CANVAS_USE_PIXMAN
+#define CANVAS_SINGLE_INSTANCE
 #include "canvas_base.c"
 #include "rop3.h"
 #include "rect.h"
@@ -26,6 +27,8 @@
 #include "lines.h"
 #include "pixman_utils.h"
 
+typedef struct CairoCanvas CairoCanvas;
+
 struct CairoCanvas {
     CanvasBase base;
     uint32_t *private_data;
@@ -1007,8 +1010,9 @@ static void touch_brush(CairoCanvas *canvas, SpiceBrush *brush)
     }
 }
 
-void canvas_draw_fill(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
+static void canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
     SpiceROP rop;
 
@@ -1037,8 +1041,9 @@ void canvas_draw_fill(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     pixman_region32_fini(&dest_region);
 }
 
-void canvas_draw_copy(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
+static void canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
     SpiceROP rop;
 
@@ -1106,16 +1111,15 @@ void canvas_draw_copy(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     pixman_region32_fini(&dest_region);
 }
 
+static void canvas_put_image(SpiceCanvas *spice_canvas,
 #ifdef WIN32
-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,
-                      uint32_t src_width, uint32_t src_height, int src_stride,
-                      const QRegion *clip)
+                             HDC dc,
 #endif
+                             const SpiceRect *dest, const uint8_t *src_data,
+                             uint32_t src_width, uint32_t src_height, int src_stride,
+                             const QRegion *clip)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_image_t *src;
     int dest_width;
     int dest_height;
@@ -1165,8 +1169,9 @@ void canvas_put_image(CairoCanvas *canvas, const SpiceRect *dest, const uint8_t
     pixman_image_unref(src);
 }
 
-void canvas_draw_transparent(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
+static void canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
 
     pixman_region32_init_rect(&dest_region,
@@ -1205,8 +1210,9 @@ void canvas_draw_transparent(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *cl
     pixman_region32_fini(&dest_region);
 }
 
-void canvas_draw_alpha_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
+static void canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
 
     pixman_region32_init_rect(&dest_region,
@@ -1251,8 +1257,9 @@ void canvas_draw_alpha_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *cl
     pixman_region32_fini(&dest_region);
 }
 
-void canvas_draw_opaque(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
+static void canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
     SpiceROP rop;
 
@@ -1300,8 +1307,9 @@ 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)
+static void canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
     SpiceROP rop;
 
@@ -1372,8 +1380,9 @@ void canvas_draw_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Sp
     pixman_region32_fini(&dest_region);
 }
 
-void canvas_draw_blackness(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
+static void canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
 
     pixman_region32_init_rect(&dest_region,
@@ -1396,8 +1405,9 @@ void canvas_draw_blackness(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip
     pixman_region32_fini(&dest_region);
 }
 
-void canvas_draw_whiteness(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
+static void canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
 
     pixman_region32_init_rect(&dest_region,
@@ -1420,8 +1430,9 @@ void canvas_draw_whiteness(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip
     pixman_region32_fini(&dest_region);
 }
 
-void canvas_draw_invers(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
+static void canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
 
     pixman_region32_init_rect(&dest_region,
@@ -1445,8 +1456,9 @@ void canvas_draw_invers(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     pixman_region32_fini(&dest_region);
 }
 
-void canvas_draw_rop3(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
+static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
     pixman_image_t *d;
     pixman_image_t *s;
@@ -1509,8 +1521,9 @@ void canvas_draw_rop3(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     pixman_region32_fini(&dest_region);
 }
 
-void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
+static void canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
     int dx, dy;
 
@@ -1541,8 +1554,9 @@ void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     pixman_region32_fini(&dest_region);
 }
 
-void canvas_draw_text(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
+static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_t dest_region;
     pixman_image_t *str_mask, *brush;
     SpiceString *str;
@@ -1909,8 +1923,9 @@ static void stroke_lines_draw(StrokeLines *lines,
 }
 
 
-void canvas_draw_stroke(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
+static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     StrokeGC gc = { {0} };
     lineGCOps ops = {
         stroke_fill_spans,
@@ -2074,8 +2089,9 @@ void canvas_draw_stroke(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     pixman_region32_fini(&gc.dest_region);
 }
 
-void canvas_read_bits(CairoCanvas *canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
+static void canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_image_t* surface;
     uint8_t *src;
     int src_stride;
@@ -2093,8 +2109,9 @@ void canvas_read_bits(CairoCanvas *canvas, uint8_t *dest, int dest_stride, const
     }
 }
 
-void canvas_group_start(CairoCanvas *canvas, QRegion *region)
+static void canvas_group_start(SpiceCanvas *spice_canvas, QRegion *region)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_fini(&canvas->canvas_region);
     /* Make sure we always clip to canvas size */
     pixman_region32_init_rect(&canvas->canvas_region,
@@ -2105,8 +2122,9 @@ void canvas_group_start(CairoCanvas *canvas, QRegion *region)
     pixman_region32_intersect(&canvas->canvas_region, &canvas->canvas_region, region);
 }
 
-void canvas_group_end(CairoCanvas *canvas)
+static void canvas_group_end(SpiceCanvas *spice_canvas)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_region32_fini(&canvas->canvas_region);
     pixman_region32_init_rect(&canvas->canvas_region,
                               0, 0,
@@ -2114,8 +2132,9 @@ void canvas_group_end(CairoCanvas *canvas)
                               pixman_image_get_height(canvas->image));
 }
 
-void canvas_clear(CairoCanvas *canvas)
+static void canvas_clear(SpiceCanvas *spice_canvas)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     spice_pixman_fill_rect(canvas->image,
                            0, 0,
                            pixman_image_get_width(canvas->image),
@@ -2123,15 +2142,17 @@ void canvas_clear(CairoCanvas *canvas)
                            0);
 }
 
-#ifdef CAIRO_CANVAS_ACCESS_TEST
-void canvas_set_access_params(CairoCanvas *canvas, unsigned long base, unsigned long max)
+static void canvas_set_access_params(SpiceCanvas *spice_canvas, unsigned long base, unsigned long max)
 {
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     __canvas_set_access_params(&canvas->base, base, max);
-}
 #endif
+}
 
-void canvas_destroy(CairoCanvas *canvas)
+static void canvas_destroy(SpiceCanvas *spice_canvas)
 {
+    CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     if (!canvas) {
         return;
     }
@@ -2144,18 +2165,16 @@ void canvas_destroy(CairoCanvas *canvas)
 }
 
 static int need_init = 1;
+static SpiceCanvasOps cairo_canvas_ops;
 
+SpiceCanvas *canvas_create(pixman_image_t *image, int bits
 #ifdef CAIRO_CANVAS_CACHE
-CairoCanvas *canvas_create(pixman_image_t *image, int bits,
-                           SpiceImageCache *bits_cache,
-                           SpicePaletteCache *palette_cache
+                           , SpiceImageCache *bits_cache
+                           , SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
-CairoCanvas *canvas_create(pixman_image_t *image, int bits,
-                           SpiceImageCache *bits_cache
-#else
-CairoCanvas *canvas_create(pixman_image_t *image, int bits
+                           , SpiceImageCache *bits_cache
 #endif
-                            , SpiceGlzDecoder *glz_decoder
+                           , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
 #endif
@@ -2168,15 +2187,12 @@ CairoCanvas *canvas_create(pixman_image_t *image, int bits
         return NULL;
     }
     memset(canvas, 0, sizeof(CairoCanvas));
+    init_ok = canvas_base_init(&canvas->base, &cairo_canvas_ops, bits
 #ifdef CAIRO_CANVAS_CACHE
-    init_ok = canvas_base_init(&canvas->base, bits,
-                               bits_cache,
-                               palette_cache
+                               , bits_cache
+                               , palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
-    init_ok = canvas_base_init(&canvas->base, bits,
-                               bits_cache
-#else
-    init_ok = canvas_base_init(&canvas->base, bits
+                               , bits_cache
 #endif
                                , glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
@@ -2192,7 +2208,7 @@ CairoCanvas *canvas_create(pixman_image_t *image, int bits
                               pixman_image_get_width (canvas->image),
                               pixman_image_get_height (canvas->image));
 
-    return canvas;
+    return (SpiceCanvas *)canvas;
 }
 
 void cairo_canvas_init() //unsafe global function
@@ -2201,6 +2217,29 @@ void cairo_canvas_init() //unsafe global function
         return;
     }
     need_init = 0;
+
+    canvas_base_init_ops(&cairo_canvas_ops);
+    cairo_canvas_ops.draw_fill = canvas_draw_fill;
+    cairo_canvas_ops.draw_copy = canvas_draw_copy;
+    cairo_canvas_ops.draw_opaque = canvas_draw_opaque;
+    cairo_canvas_ops.copy_bits = canvas_copy_bits;
+    cairo_canvas_ops.draw_text = canvas_draw_text;
+    cairo_canvas_ops.draw_stroke = canvas_draw_stroke;
+    cairo_canvas_ops.draw_rop3 = canvas_draw_rop3;
+    cairo_canvas_ops.draw_blend = canvas_draw_blend;
+    cairo_canvas_ops.draw_blackness = canvas_draw_blackness;
+    cairo_canvas_ops.draw_whiteness = canvas_draw_whiteness;
+    cairo_canvas_ops.draw_invers = canvas_draw_invers;
+    cairo_canvas_ops.draw_transparent = canvas_draw_transparent;
+    cairo_canvas_ops.draw_alpha_blend = canvas_draw_alpha_blend;
+    cairo_canvas_ops.put_image = canvas_put_image;
+    cairo_canvas_ops.clear = canvas_clear;
+    cairo_canvas_ops.read_bits = canvas_read_bits;
+    cairo_canvas_ops.group_start = canvas_group_start;
+    cairo_canvas_ops.group_end = canvas_group_end;
+    cairo_canvas_ops.set_access_params = canvas_set_access_params;
+    cairo_canvas_ops.destroy = canvas_destroy;
+
     rop3_init();
 }
 
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
index c0102ae..3f6fbbc 100644
--- a/common/cairo_canvas.h
+++ b/common/cairo_canvas.h
@@ -26,55 +26,18 @@
 #include "canvas_base.h"
 #include "region.h"
 
-typedef struct CairoCanvas CairoCanvas;
-
-void canvas_draw_fill(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
-void canvas_draw_copy(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy);
-void canvas_draw_opaque(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque);
-void canvas_copy_bits(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos);
-void canvas_draw_text(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text);
-void canvas_draw_stroke(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke);
-void canvas_draw_rop3(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3);
-void canvas_draw_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend);
-void canvas_draw_blackness(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness);
-void canvas_draw_whiteness(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness);
-void canvas_draw_invers(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers);
-void canvas_draw_transparent(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent);
-void canvas_draw_alpha_blend(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend);
-#ifdef WIN32
-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,
-                      uint32_t src_width, uint32_t src_height, int src_stride,
-                      const QRegion *clip);
-#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, QRegion *region);
-void canvas_group_end(CairoCanvas *canvas);
-void canvas_set_addr_delta(CairoCanvas *canvas, SPICE_ADDRESS delta);
-#ifdef CAIRO_CANVAS_ACCESS_TEST
-void canvas_set_access_params(CairoCanvas *canvas, unsigned long base, unsigned long max);
-#endif
-
+SpiceCanvas *canvas_create(pixman_image_t *image, int bits
 #ifdef CAIRO_CANVAS_CACHE
-CairoCanvas *canvas_create(pixman_image_t *image, int bits,
-                           SpiceImageCache *bits_cache,
-                           SpicePaletteCache *palette_cache
+                           , SpiceImageCache *bits_cache
+                           , SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
-CairoCanvas *canvas_create(pixman_image_t *image, int bits,
-                           SpiceImageCache *bits_cache
-#else
-CairoCanvas *canvas_create(pixman_image_t *image, int bits
+                           , SpiceImageCache *bits_cache
 #endif
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
 #endif
                            );
-void canvas_destroy(CairoCanvas *canvas);
 
 void cairo_canvas_init();
 
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 01945ec..2a94607 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -60,6 +60,11 @@
 #define WARN(x) printf("warning: %s\n", x)
 #endif
 
+#define PANIC(str) {                                \
+    printf("%s: panic: %s", __FUNCTION__, str);     \
+    abort();                                        \
+}
+
 #ifndef DBG
 #define DBG(level, format, ...) printf("%s: debug: " format "\n", __FUNCTION__, ## __VA_ARGS__);
 #endif
@@ -170,6 +175,7 @@ typedef struct QuicData {
 } QuicData;
 
 typedef struct CanvasBase {
+    SpiceCanvas parent;
     uint32_t color_shift;
     uint32_t color_mask;
     QuicData quic_data;
@@ -190,6 +196,9 @@ typedef struct CanvasBase {
 
     LzData lz_data;
     GlzData glz_data;
+
+    void *usr_data;
+    spice_destroy_fn_t usr_data_destroy;
 } CanvasBase;
 
 
@@ -1538,17 +1547,59 @@ static void canvas_base_destroy(CanvasBase *canvas)
 #ifdef GDI_CANVAS
     DeleteDC(canvas->dc);
 #endif
+
+    if (canvas->usr_data && canvas->usr_data_destroy) {
+        canvas->usr_data_destroy (canvas->usr_data);
+        canvas->usr_data = NULL;
+    }
 }
 
+/* This is kind of lame, but it protects against muliple
+   instances of these functions. We really should stop including
+   canvas_base.c and build it separately instead */
+#ifdef  CANVAS_SINGLE_INSTANCE
+
+void spice_canvas_set_usr_data(SpiceCanvas *spice_canvas,
+                               void *data,
+                               spice_destroy_fn_t destroy_fn)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    if (canvas->usr_data && canvas->usr_data_destroy) {
+        canvas->usr_data_destroy (canvas->usr_data);
+    }
+    canvas->usr_data = data;
+    canvas->usr_data_destroy = destroy_fn;
+}
+
+void *spice_canvas_get_usr_data(SpiceCanvas *spice_canvas)
+{
+    CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    return  canvas->usr_data;
+}
+#endif
+
+static void unimplemented_op(SpiceCanvas *canvas)
+{
+    PANIC("unimplemented canvas operation");
+}
+
+inline static void canvas_base_init_ops(SpiceCanvasOps *ops)
+{
+    void **ops_cast;
+    int i;
+
+    ops_cast = (void **)ops;
+    for (i = 0; i < sizeof(SpiceCanvasOps) / sizeof(void *); i++) {
+        ops_cast[i] = (void *) unimplemented_op;
+    }
+}
+
+static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops, int depth
 #ifdef CAIRO_CANVAS_CACHE
-static int canvas_base_init(CanvasBase *canvas, int depth,
-                            SpiceImageCache *bits_cache,
-                            SpicePaletteCache *palette_cache
+                            , SpiceImageCache *bits_cache
+                            , SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
-static int canvas_base_init(CanvasBase *canvas, int depth,
-                            SpiceImageCache *bits_cache
-#else
-static int canvas_base_init(CanvasBase *canvas, int depth
+                            , SpiceImageCache *bits_cache
 #endif
                             , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
@@ -1556,6 +1607,7 @@ static int canvas_base_init(CanvasBase *canvas, int depth
 #endif
                             )
 {
+    canvas->parent.ops = ops;
     canvas->quic_data.usr.error = quic_usr_error;
     canvas->quic_data.usr.warn = quic_usr_warn;
     canvas->quic_data.usr.info = quic_usr_warn;
@@ -1612,4 +1664,3 @@ static int canvas_base_init(CanvasBase *canvas, int depth
 #endif
     return 1;
 }
-
diff --git a/common/canvas_base.h b/common/canvas_base.h
index 4c28654..48e921e 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -22,12 +22,16 @@
 
 #include "pixman_utils.h"
 #include "lz.h"
+#include "region.h"
 #include <spice/draw.h>
 
+typedef void (*spice_destroy_fn_t)(void *data);
+
 typedef struct _SpiceImageCache SpiceImageCache;
 typedef struct _SpicePaletteCache SpicePaletteCache;
 typedef struct _SpiceGlzDecoder SpiceGlzDecoder;
 typedef struct _SpiceVirtMapping SpiceVirtMapping;
+typedef struct _SpiceCanvas SpiceCanvas;
 
 typedef struct {
     void (*put)(SpiceImageCache *cache,
@@ -75,5 +79,40 @@ struct _SpiceVirtMapping {
   SpiceVirtMappingOps *ops;
 };
 
+typedef struct {
+    void (*draw_fill)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
+    void (*draw_copy)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy);
+    void (*draw_opaque)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque);
+    void (*copy_bits)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos);
+    void (*draw_text)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text);
+    void (*draw_stroke)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke);
+    void (*draw_rop3)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3);
+    void (*draw_blend)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend);
+    void (*draw_blackness)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness);
+    void (*draw_whiteness)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness);
+    void (*draw_invers)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers);
+    void (*draw_transparent)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent);
+    void (*draw_alpha_blend)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend);
+    void (*put_image)(SpiceCanvas *canvas,
+#ifdef WIN32
+                      HDC dc,
 #endif
+                      const SpiceRect *dest, const uint8_t *src_data,
+                      uint32_t src_width, uint32_t src_height, int src_stride,
+                      const QRegion *clip);
+    void (*clear)(SpiceCanvas *canvas);
+    void (*read_bits)(SpiceCanvas *canvas, uint8_t *dest, int dest_stride, const SpiceRect *area);
+    void (*group_start)(SpiceCanvas *canvas, QRegion *region);
+    void (*group_end)(SpiceCanvas *canvas);
+    void (*set_access_params)(SpiceCanvas *canvas, unsigned long base, unsigned long max);
+    void (*destroy)(SpiceCanvas *canvas);
+} SpiceCanvasOps;
+
+void spice_canvas_set_usr_data(SpiceCanvas *canvas, void *data, spice_destroy_fn_t destroy_fn);
+void *spice_canvas_get_usr_data(SpiceCanvas *canvas);
+
+struct _SpiceCanvas {
+  SpiceCanvasOps *ops;
+};
 
+#endif
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index 40305df..4d5bc9d 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -26,6 +26,8 @@
 #include "region.h"
 #include "threads.h"
 
+typedef struct GdiCanvas GdiCanvas;
+
 struct GdiCanvas {
     CanvasBase base;
     HDC dc;
@@ -976,8 +978,9 @@ static void gdi_draw_image_rop3(HDC dest_dc, const SpiceRect *src, const SpiceRe
     release_bitmap(dc, bitmap, prev_bitmap, 0);
 }
 
-void gdi_canvas_draw_fill(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
+static void gdi_canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     HBRUSH prev_hbrush;
     HBRUSH brush;
     struct BitmapData bitmapmask;
@@ -998,8 +1001,9 @@ void gdi_canvas_draw_fill(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     unset_brush(canvas->dc, prev_hbrush);
 }
 
-void gdi_canvas_draw_copy(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
+static void gdi_canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     pixman_image_t *surface;
     GdiImage image;
     struct BitmapData bitmapmask;
@@ -1036,10 +1040,11 @@ void gdi_canvas_draw_copy(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     pixman_image_unref(surface);
 }
 
-void gdi_canvas_put_image(GdiCanvas *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)
+static void gdi_canvas_put_image(SpiceCanvas *spice_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)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     SpiceRect src;
     src.top = 0;
     src.bottom = src_height;
@@ -1127,9 +1132,10 @@ static void gdi_draw_image_transparent(GdiCanvas *canvas, HDC dest_dc, const Spi
     release_bitmap(dc, bitmap, prev_bitmap, 0);
 }
 
-void gdi_canvas_draw_transparent(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
-                                 SpiceTransparent* transparent)
+static void gdi_canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip,
+                                        SpiceTransparent* transparent)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     pixman_image_t *surface;
     GdiImage image;
     PixmanData *pixman_data;
@@ -1199,8 +1205,9 @@ static void gdi_draw_image_alpha(HDC dest_dc, const SpiceRect *src, const SpiceR
     release_bitmap(dc, bitmap, prev_bitmap, 0);
 }
 
-void gdi_canvas_draw_alpha_blend(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
+static void gdi_canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     pixman_image_t *surface;
     GdiImage image;
     PixmanData *pixman_data;
@@ -1233,8 +1240,9 @@ void gdi_canvas_draw_alpha_blend(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *
     pixman_image_unref(surface);
 }
 
-void gdi_canvas_draw_opaque(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
+static void gdi_canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     pixman_image_t *surface;
     GdiImage image;
     struct BitmapData bitmapmask;
@@ -1278,8 +1286,9 @@ void gdi_canvas_draw_opaque(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
     pixman_image_unref(surface);
 }
 
-void gdi_canvas_draw_blend(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
+static void gdi_canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     pixman_image_t *surface;
     GdiImage image;
     struct BitmapData bitmapmask;
@@ -1315,8 +1324,9 @@ void gdi_canvas_draw_blend(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
     pixman_image_unref(surface);
 }
 
-void gdi_canvas_draw_blackness(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
+static void gdi_canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     struct BitmapData bitmapmask;
 
     bitmapmask = get_mask_bitmap(canvas, &blackness->mask);
@@ -1328,8 +1338,9 @@ void gdi_canvas_draw_blackness(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *cl
     free_mask(&bitmapmask);
 }
 
-void gdi_canvas_draw_invers(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
+static void gdi_canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     struct BitmapData bitmapmask;
 
     bitmapmask = get_mask_bitmap(canvas, &invers->mask);
@@ -1341,8 +1352,9 @@ void gdi_canvas_draw_invers(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
     free_mask(&bitmapmask);
 }
 
-void gdi_canvas_draw_whiteness(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
+static void gdi_canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     struct BitmapData bitmapmask;
 
     bitmapmask = get_mask_bitmap(canvas, &whiteness->mask);
@@ -1354,8 +1366,9 @@ void gdi_canvas_draw_whiteness(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *cl
     free_mask(&bitmapmask);
 }
 
-void gdi_canvas_draw_rop3(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
+static void gdi_canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     pixman_image_t *surface;
     GdiImage image;
     struct BitmapData bitmapmask;
@@ -1396,8 +1409,9 @@ void gdi_canvas_draw_rop3(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     pixman_image_unref(surface);
 }
 
-void gdi_canvas_copy_bits(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
+static void gdi_canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     Lock lock(*canvas->lock);
 
     set_clip(canvas, clip);
@@ -1406,8 +1420,9 @@ void gdi_canvas_copy_bits(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
            bbox->bottom - bbox->top, canvas->dc, src_pos->x, src_pos->y, SRCCOPY);
 }
 
-void gdi_canvas_draw_text(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
+static void gdi_canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     SpiceString *str;
 
     Lock lock(*canvas->lock);
@@ -1512,8 +1527,9 @@ static uint32_t *gdi_get_userstyle(GdiCanvas *canvas, uint8_t nseg, SPICE_ADDRES
     return local_style;
 }
 
-void gdi_canvas_draw_stroke(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
+static void gdi_canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     HPEN hpen;
     HPEN prev_hpen;
     LOGBRUSH logbrush;
@@ -1675,19 +1691,21 @@ void gdi_canvas_draw_stroke(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
     }
 }
 
-void gdi_canvas_clear(GdiCanvas *canvas)
+static void gdi_canvas_clear(SpiceCanvas *spice_canvas)
 {
 }
 
-#ifdef CAIRO_CANVAS_ACCESS_TEST
-void gdi_canvas_set_access_params(GdiCanvas *canvas, unsigned long base, unsigned long max)
+static void gdi_canvas_set_access_params(SpiceCanvas *spice_canvas, unsigned long base, unsigned long max)
 {
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     __canvas_set_access_params(&canvas->base, base, max);
-}
 #endif
+}
 
-void gdi_canvas_destroy(GdiCanvas *canvas)
+static void gdi_canvas_destroy(SpiceCanvas *spice_canvas)
 {
+    GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     if (!canvas) {
         return;
     }
@@ -1696,16 +1714,14 @@ void gdi_canvas_destroy(GdiCanvas *canvas)
 }
 
 static int need_init = 1;
+static SpiceCanvasOps gdi_canvas_ops;
 
+SpiceCanvas *gdi_canvas_create(HDC dc, Mutex* lock, int bits
 #ifdef CAIRO_CANVAS_CACHE
-GdiCanvas *gdi_canvas_create(HDC dc, Mutex* lock, int bits,
-                             SpiceImageCache *bits_cache,
-                             SpicePaletteCache *palette_cache
+                             , SpiceImageCache *bits_cache
+                             , SpicePaletteCache *palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
-GdiCanvas *gdi_canvas_create(HDC dc, int bits,
-                             SpiceImageCache *bits_cache
-#else
-GdiCanvas *gdi_canvas_create(HDC dc, int bits
+                             , SpiceImageCache *bits_cache
 #endif
                             , SpiceGlzDecoder *glz_decoder
                             )
@@ -1717,20 +1733,17 @@ GdiCanvas *gdi_canvas_create(HDC dc, int bits
         return NULL;
     }
     memset(canvas, 0, sizeof(GdiCanvas));
+    init_ok = canvas_base_init(&canvas->base, &gdi_canvas_ops, bits
 #ifdef CAIRO_CANVAS_CACHE
-    init_ok = canvas_base_init(&canvas->base, bits,
-                               bits_cache,
-                               palette_cache
+                               ,bits_cache
+                               ,palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
-    init_ok = gdi_canvas_base_init(&canvas->base, bits,
-                                   bits_cache
-#else
-    init_ok = gdi_canvas_base_init(&canvas->base, bits
+                               , bits_cache
 #endif
                                , glz_decoder);
     canvas->dc = dc;
     canvas->lock = lock;
-    return canvas;
+    return (SpiceCanvas *)canvas;
 }
 
 void gdi_canvas_init() //unsafe global function
@@ -1739,6 +1752,26 @@ void gdi_canvas_init() //unsafe global function
         return;
     }
     need_init = 0;
+
+    canvas_base_init_ops(&gdi_canvas_ops);
+    gdi_canvas_ops.draw_fill = gdi_canvas_draw_fill;
+    gdi_canvas_ops.draw_copy = gdi_canvas_draw_copy;
+    gdi_canvas_ops.draw_opaque = gdi_canvas_draw_opaque;
+    gdi_canvas_ops.copy_bits = gdi_canvas_copy_bits;
+    gdi_canvas_ops.draw_text = gdi_canvas_draw_text;
+    gdi_canvas_ops.draw_stroke = gdi_canvas_draw_stroke;
+    gdi_canvas_ops.draw_rop3 = gdi_canvas_draw_rop3;
+    gdi_canvas_ops.draw_blend = gdi_canvas_draw_blend;
+    gdi_canvas_ops.draw_blackness = gdi_canvas_draw_blackness;
+    gdi_canvas_ops.draw_whiteness = gdi_canvas_draw_whiteness;
+    gdi_canvas_ops.draw_invers = gdi_canvas_draw_invers;
+    gdi_canvas_ops.draw_transparent = gdi_canvas_draw_transparent;
+    gdi_canvas_ops.draw_alpha_blend = gdi_canvas_draw_alpha_blend;
+    gdi_canvas_ops.put_image = gdi_canvas_put_image;
+    gdi_canvas_ops.clear = gdi_canvas_clear;
+    gdi_canvas_ops.set_access_params = gdi_canvas_set_access_params;
+    gdi_canvas_ops.destroy = gdi_canvas_destroy;
+
     rop3_init();
 }
 
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
index 12082d8..b3dc605 100644
--- a/common/gdi_canvas.h
+++ b/common/gdi_canvas.h
@@ -26,8 +26,6 @@
 #include "canvas_base.h"
 #include "region.h"
 
-typedef struct GdiCanvas GdiCanvas;
-
 typedef struct {
     int width;
     int height;
@@ -35,36 +33,10 @@ typedef struct {
     uint8_t *pixels;
 } GdiImage;
 
-void gdi_canvas_draw_fill(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
-void gdi_canvas_draw_copy(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy);
-void gdi_canvas_draw_opaque(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque);
-void gdi_canvas_copy_bits(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos);
-void gdi_canvas_draw_text(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text);
-void gdi_canvas_draw_stroke(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke);
-void gdi_canvas_draw_rop3(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3);
-void gdi_canvas_draw_blend(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend);
-void gdi_canvas_draw_blackness(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness);
-void gdi_canvas_draw_whiteness(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness);
-void gdi_canvas_draw_invers(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers);
-void gdi_canvas_draw_transparent(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip,
-                                 SpiceTransparent* transparent);
-void gdi_canvas_draw_alpha_blend(GdiCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend);
-void gdi_canvas_put_image(GdiCanvas *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);
-void gdi_canvas_clear(GdiCanvas *canvas);
-
-#ifdef CAIRO_CANVAS_ACCESS_TEST
-void gdi_canvas_set_access_params(GdiCanvas *canvas, unsigned long base, unsigned long max);
-#endif
-
-
-GdiCanvas *gdi_canvas_create(HDC dc, class Mutex *lock, int bits,
-                             SpiceImageCache *bits_cache,
-                             SpicePaletteCache *palette_cache,
-                             SpiceGlzDecoder *glz_decoder);
-
-void gdi_canvas_destroy(GdiCanvas *canvas);
+SpiceCanvas *gdi_canvas_create(HDC dc, class Mutex *lock, int bits,
+                               SpiceImageCache *bits_cache,
+                               SpicePaletteCache *palette_cache,
+                               SpiceGlzDecoder *glz_decoder);
 
 void gdi_canvas_init();
 
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index e7a7bff..99f14f5 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -28,12 +28,14 @@
 #define GL_CANVAS
 #include "canvas_base.c"
 
+typedef struct GLCanvas GLCanvas;
+
 struct GLCanvas {
     CanvasBase base;
     GLCCtx glc;
-    void *usr_data;
     void *private_data;
     int private_data_size;
+    int textures_lost;
 };
 
 static inline uint8_t *copy_opposite_image(GLCanvas *canvas, void *data, int stride, int height)
@@ -350,8 +352,9 @@ static void set_op(GLCanvas *canvas, uint16_t rop_decriptor)
     glc_set_op(canvas->glc, op);
 }
 
-void gl_canvas_draw_fill(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
+static void gl_canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     GLCRect rect;
     set_clip(canvas, bbox, clip);
     set_mask(canvas, &fill->mask, bbox->left, bbox->top);
@@ -363,8 +366,9 @@ void gl_canvas_draw_fill(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     glc_flush(canvas->glc);
 }
 
-void gl_canvas_draw_copy(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
+static void gl_canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     pixman_image_t *surface;
     GLCRecti src;
     GLCRecti dest;
@@ -385,8 +389,9 @@ void gl_canvas_draw_copy(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     glc_flush(canvas->glc);
 }
 
-void gl_canvas_draw_opaque(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
+static void gl_canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     pixman_image_t *surface;
     GLCRecti src;
     GLCRecti dest;
@@ -413,8 +418,9 @@ void gl_canvas_draw_opaque(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     glc_flush(canvas->glc);
 }
 
-void gl_canvas_draw_alpha_blend(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd *alpha_blend)
+static void gl_canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd *alpha_blend)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     pixman_image_t *surface;
     GLCRecti src;
     GLCRecti dest;
@@ -434,8 +440,9 @@ void gl_canvas_draw_alpha_blend(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *cl
     glc_flush(canvas->glc);
 }
 
-void gl_canvas_draw_blend(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
+static void gl_canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     pixman_image_t *surface;
     GLCRecti src;
     GLCRecti dest;
@@ -455,8 +462,9 @@ void gl_canvas_draw_blend(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Sp
     glc_flush(canvas->glc);
 }
 
-void gl_canvas_draw_transparent(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent *transparent)
+static void gl_canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent *transparent)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     pixman_image_t *surface;
     pixman_image_t *trans_surf;
     GLCImage image;
@@ -493,23 +501,27 @@ static inline void fill_common(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *cli
     glc_fill_rect(canvas->glc, &rect);
 }
 
-void gl_canvas_draw_whiteness(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
+static void gl_canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     fill_common(canvas, bbox, clip, &whiteness->mask, GLC_OP_SET);
 }
 
-void gl_canvas_draw_blackness(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
+static void gl_canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     fill_common(canvas, bbox, clip, &blackness->mask, GLC_OP_CLEAR);
 }
 
-void gl_canvas_draw_invers(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
+static void gl_canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     fill_common(canvas, bbox, clip, &invers->mask, GLC_OP_INVERT);
 }
 
-void gl_canvas_draw_rop3(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
+static void gl_canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     pixman_image_t *d;
     pixman_image_t *s;
     GLCImage image;
@@ -608,8 +620,9 @@ void gl_canvas_draw_rop3(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     pixman_image_unref(d);
 }
 
-void gl_canvas_draw_stroke(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
+static void gl_canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     GLCPath path;
 
     set_clip(canvas, bbox, clip);
@@ -627,8 +640,9 @@ void gl_canvas_draw_stroke(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
     glc_path_destroy(path);
 }
 
-void gl_canvas_draw_text(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
+static void gl_canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     GLCRect rect;
     SpiceString *str;
 
@@ -685,14 +699,16 @@ void gl_canvas_draw_text(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, Spi
     glc_flush(canvas->glc);
 }
 
-void gl_canvas_clear(GLCanvas *canvas)
+static void gl_canvas_clear(SpiceCanvas *spice_canvas)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     glc_clear(canvas->glc);
     glc_flush(canvas->glc);
 }
 
-void gl_canvas_copy_pixels(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
+static void gl_canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     set_clip(canvas, bbox, clip);
     glc_clear_mask(canvas->glc, GLC_MASK_A);
     glc_set_op(canvas->glc, GLC_OP_COPY);
@@ -700,8 +716,9 @@ void gl_canvas_copy_pixels(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, S
                     bbox->right - bbox->left, bbox->bottom - bbox->top);
 }
 
-void gl_canvas_read_pixels(GLCanvas *canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
+static void gl_canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     GLCImage image;
 
     ASSERT(dest_stride > 0);
@@ -713,8 +730,9 @@ 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, QRegion *region)
+static void gl_canvas_group_start(SpiceCanvas *spice_canvas, QRegion *region)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     GLCRect *glc_rects;
     GLCRect *now, *end;
     int num_rect;
@@ -734,10 +752,11 @@ void gl_canvas_set_top_mask(GLCanvas *canvas, QRegion *region)
     free(glc_rects);
 }
 
-void gl_canvas_put_image(GLCanvas *canvas, const SpiceRect *dest, const uint8_t *src_data,
+static void gl_canvas_put_image(SpiceCanvas *spice_canvas, const SpiceRect *dest, const uint8_t *src_data,
                          uint32_t src_width, uint32_t src_height, int src_stride,
                          const QRegion *clip)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     GLCRecti src;
     GLCRecti gldest;
     GLCImage image;
@@ -781,39 +800,33 @@ void gl_canvas_put_image(GLCanvas *canvas, const SpiceRect *dest, const uint8_t
     glc_flush(canvas->glc);
 }
 
-void gl_canvas_clear_top_mask(GLCanvas *canvas)
+static void gl_canvas_group_end(SpiceCanvas *spice_canvas)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     glc_clear_mask(canvas->glc, GLC_MASK_B);
 }
 
-#ifdef CAIRO_CANVAS_ACCESS_TEST
-void gl_canvas_set_access_params(GLCanvas *canvas, unsigned long base, unsigned long max)
+static void gl_canvas_set_access_params(SpiceCanvas *spice_canvas, unsigned long base, unsigned long max)
 {
+#ifdef CAIRO_CANVAS_ACCESS_TEST
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
     __canvas_set_access_params(&canvas->base, base, max);
-}
-
 #endif
-
-void *gl_canvas_get_usr_data(GLCanvas *canvas)
-{
-    return canvas->usr_data;
 }
 
 static int need_init = 1;
+static SpiceCanvasOps gl_canvas_ops;
 
+SpiceCanvas *gl_canvas_create(int width, int height, int depth
 #ifdef CAIRO_CANVAS_CACHE
-GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
-                           SpiceImageCache *bits_cache,
-                           SpicePaletteCache *palette_cache
+                              , 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,
-                           SpiceImageCache *bits_cache
-#else
-GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
+                              , SpiceImageCache *bits_cache
 #endif
-                           , SpiceGlzDecoder *glz_decoder
+                              , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
-                           , SpiceVirtMapping *virt_mapping
+                              , SpiceVirtMapping *virt_mapping
 #endif
                            )
 {
@@ -828,17 +841,13 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
     if (!(canvas->glc = glc_create(width, height))) {
         goto error_1;
     }
-    canvas->usr_data = usr_data;
     canvas->private_data = NULL;
+    init_ok = canvas_base_init(&canvas->base, &gl_canvas_ops, depth
 #ifdef CAIRO_CANVAS_CACHE
-    init_ok = canvas_base_init(&canvas->base, depth,
-                               bits_cache,
-                               palette_cache
+                               , bits_cache
+                               , palette_cache
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
-    init_ok = canvas_base_init(&canvas->base, depth,
-                               bits_cache
-#else
-    init_ok = canvas_base_init(&canvas->base, depth
+                               , bits_cache
 #endif
                                , glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
@@ -849,7 +858,7 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
         goto error_2;
     }
 
-    return canvas;
+    return (SpiceCanvas *)canvas;
 
 error_2:
     glc_destroy(canvas->glc, 0);
@@ -859,13 +868,23 @@ error_1:
     return NULL;
 }
 
-void gl_canvas_destroy(GLCanvas *canvas, int textures_lost)
+void gl_canvas_set_textures_lost(SpiceCanvas *spice_canvas,
+                                 int textures_lost)
 {
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
+
+    canvas->textures_lost = textures_lost;
+}
+
+static void gl_canvas_destroy(SpiceCanvas *spice_canvas)
+{
+    GLCanvas *canvas = (GLCanvas *)spice_canvas;
+
     if (!canvas) {
         return;
     }
     canvas_base_destroy(&canvas->base);
-    glc_destroy(canvas->glc, textures_lost);
+    glc_destroy(canvas->glc, canvas->textures_lost);
     if (canvas->private_data) {
         free(canvas->private_data);
     }
@@ -878,5 +897,28 @@ void gl_canvas_init() //unsafe global function
         return;
     }
     need_init = 0;
+
+    canvas_base_init_ops(&gl_canvas_ops);
+    gl_canvas_ops.draw_fill = gl_canvas_draw_fill;
+    gl_canvas_ops.draw_copy = gl_canvas_draw_copy;
+    gl_canvas_ops.draw_opaque = gl_canvas_draw_opaque;
+    gl_canvas_ops.copy_bits = gl_canvas_copy_bits;
+    gl_canvas_ops.draw_text = gl_canvas_draw_text;
+    gl_canvas_ops.draw_stroke = gl_canvas_draw_stroke;
+    gl_canvas_ops.draw_rop3 = gl_canvas_draw_rop3;
+    gl_canvas_ops.draw_blend = gl_canvas_draw_blend;
+    gl_canvas_ops.draw_blackness = gl_canvas_draw_blackness;
+    gl_canvas_ops.draw_whiteness = gl_canvas_draw_whiteness;
+    gl_canvas_ops.draw_invers = gl_canvas_draw_invers;
+    gl_canvas_ops.draw_transparent = gl_canvas_draw_transparent;
+    gl_canvas_ops.draw_alpha_blend = gl_canvas_draw_alpha_blend;
+    gl_canvas_ops.put_image = gl_canvas_put_image;
+    gl_canvas_ops.clear = gl_canvas_clear;
+    gl_canvas_ops.read_bits = gl_canvas_read_bits;
+    gl_canvas_ops.group_start = gl_canvas_group_start;
+    gl_canvas_ops.group_end = gl_canvas_group_end;
+    gl_canvas_ops.set_access_params = gl_canvas_set_access_params;
+    gl_canvas_ops.destroy = gl_canvas_destroy;
+
     rop3_init();
 }
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index ee272a8..29737ec 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -21,55 +21,18 @@
 #include "canvas_base.h"
 #include "region.h"
 
-typedef struct GLCanvas GLCanvas;
-
-void gl_canvas_draw_fill(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
-void gl_canvas_draw_copy(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy);
-void gl_canvas_draw_opaque(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque);
-void gl_canvas_draw_blend(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend);
-void gl_canvas_draw_alpha_blend(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd *alpha_blend);
-void gl_canvas_draw_transparent(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent *transparent);
-void gl_canvas_draw_whiteness(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness);
-void gl_canvas_draw_blackness(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness);
-void gl_canvas_draw_invers(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers);
-void gl_canvas_draw_rop3(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3);
-void gl_canvas_draw_stroke(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke);
-void gl_canvas_draw_text(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text);
-
-void gl_canvas_copy_pixels(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos);
-void gl_canvas_read_pixels(GLCanvas *canvas, uint8_t *dest, int dest_stride, const SpiceRect *area);
-
-void gl_canvas_put_image(GLCanvas *canvas, const SpiceRect *dest, const uint8_t *src_data,
-                         uint32_t src_width, uint32_t src_height, int src_stride,
-                         const QRegion *clip);
-
-void gl_canvas_clear(GLCanvas *canvas);
-
-void gl_canvas_set_top_mask(GLCanvas *canvas, QRegion *region);
-void gl_canvas_clear_top_mask(GLCanvas *canvas);
-
-#ifdef CAIRO_CANVAS_ACCESS_TEST
-void gl_canvas_set_access_params(GLCanvas *canvas, unsigned long base, unsigned long max);
-#endif
-
-void *gl_canvas_get_usr_data(GLCanvas *canvas);
-
+SpiceCanvas *gl_canvas_create(int width, int height, int depth
 #ifdef CAIRO_CANVAS_CACHE
-GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
-                           SpiceImageCache *bits_cache,
-                           SpicePaletteCache *palette_cache
+                           , 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,
-                           SpiceImageCache *bits_cache
-#else
-GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
+                           , SpiceImageCache *bits_cache
 #endif
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                             , SpiceVirtMapping *virt_mapping
 #endif
                            );
-void gl_canvas_destroy(GLCanvas *, int);
-
+void gl_canvas_set_textures_lost(SpiceCanvas *canvas, int textures_lost);
 void gl_canvas_init();
 
diff --git a/server/red_worker.c b/server/red_worker.c
index 6eaf391..e8c986f 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -830,48 +830,9 @@ typedef struct UpgradeItem {
     uint32_t n_rects;
 } UpgradeItem;
 
-typedef void (*draw_fill_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
-typedef void (*draw_copy_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy);
-typedef void (*draw_opaque_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque);
-typedef void (*copy_bits_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos);
-typedef void (*draw_text_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text);
-typedef void (*draw_stroke_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke);
-typedef void (*draw_rop3_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3);
-typedef void (*draw_blend_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend);
-typedef void (*draw_blackness_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness);
-typedef void (*draw_whiteness_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness);
-typedef void (*draw_invers_t)(void *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers);
-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, 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);
-
-typedef struct DrawFuncs {
-    draw_fill_t draw_fill;
-    draw_copy_t draw_copy;
-    draw_opaque_t draw_opaque;
-    copy_bits_t copy_bits;
-    draw_text_t draw_text;
-    draw_stroke_t draw_stroke;
-    draw_rop3_t draw_rop3;
-    draw_blend_t draw_blend;
-    draw_blackness_t draw_blackness;
-    draw_whiteness_t draw_whiteness;
-    draw_invers_t draw_invers;
-    draw_transparent_t draw_transparent;
-    draw_alpha_blend_t draw_alpha_blend;
-    read_pixels_t read_pixels;
-    set_top_mask_t set_top_mask;
-    clear_top_mask_t clear_top_mask;
-    validate_area_t validate_area;
-    destroy_t destroy;
-} DrawFuncs;
-
 typedef struct DrawContext {
-    void *canvas;
+    SpiceCanvas *canvas;
+    int canvas_draws_on_surface;
     int top_down;
     uint32_t width;
     uint32_t height;
@@ -929,7 +890,6 @@ typedef struct RedWorker {
     uint32_t renderers[RED_MAX_RENDERERS];
     uint32_t renderer;
 
-    DrawFuncs draw_funcs;
     Surface surface;
 
     Ring current_list;
@@ -1546,8 +1506,10 @@ static inline void __red_destroy_surface(RedWorker *worker)
 #ifdef STREAM_TRACE
         red_reset_stream_trace(worker);
 #endif
-        worker->draw_funcs.destroy(surface->context.canvas);
-        surface->context.canvas = NULL;
+        if (surface->context.canvas) {
+            surface->context.canvas->ops->destroy(surface->context.canvas);
+            surface->context.canvas = NULL;
+        }
     }
 }
 
@@ -3574,11 +3536,12 @@ static inline int red_current_add_qxl(RedWorker *worker, Drawable *drawable,
 static void red_get_area(RedWorker *worker, const SpiceRect *area, uint8_t *dest, int dest_stride,
                          int update)
 {
+    SpiceCanvas *canvas = worker->surface.context.canvas;
     if (update) {
         red_update_area(worker, area);
     }
 
-    worker->draw_funcs.read_pixels(worker->surface.context.canvas, dest, dest_stride, area);
+    canvas->ops->read_bits(canvas, dest, dest_stride, area);
 }
 
 static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
@@ -4173,6 +4136,7 @@ static void unlocalize_attr(SpiceLineAttr *attr)
 static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
 {
     SpiceClip clip = drawable->qxl_drawable->clip;
+    SpiceCanvas *canvas = worker->surface.context.canvas;
 
     worker->local_images_pos = 0;
     image_cache_eaging(&worker->image_cache);
@@ -4185,8 +4149,8 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
         SpiceFill fill = drawable->qxl_drawable->u.fill;
         localize_brush(worker, &fill.brush, drawable->group_id);
         localize_mask(worker, &fill.mask, drawable->group_id);
-        worker->draw_funcs.draw_fill(worker->surface.context.canvas, &drawable->qxl_drawable->bbox,
-                                     &clip, &fill); unlocalize_mask(&fill.mask);
+        canvas->ops->draw_fill(canvas, &drawable->qxl_drawable->bbox,
+                               &clip, &fill); unlocalize_mask(&fill.mask);
         unlocalize_brush(&fill.brush);
         break;
     }
@@ -4195,8 +4159,7 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
         localize_brush(worker, &opaque.brush, drawable->group_id);
         localize_bitmap(worker, &opaque.src_bitmap, drawable->group_id);
         localize_mask(worker, &opaque.mask, drawable->group_id);
-        worker->draw_funcs.draw_opaque(worker->surface.context.canvas,
-                                       &drawable->qxl_drawable->bbox, &clip, &opaque);
+        canvas->ops->draw_opaque(canvas, &drawable->qxl_drawable->bbox, &clip, &opaque);
         unlocalize_mask(&opaque.mask);
         unlocalize_bitmap(&opaque.src_bitmap);
         unlocalize_brush(&opaque.brush);
@@ -4206,8 +4169,8 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
         SpiceCopy copy = drawable->qxl_drawable->u.copy;
         localize_bitmap(worker, &copy.src_bitmap, drawable->group_id);
         localize_mask(worker, &copy.mask, drawable->group_id);
-        worker->draw_funcs.draw_copy(worker->surface.context.canvas, &drawable->qxl_drawable->bbox,
-                                     &clip, &copy);
+        canvas->ops->draw_copy(canvas, &drawable->qxl_drawable->bbox,
+                               &clip, &copy);
         unlocalize_mask(&copy.mask);
         unlocalize_bitmap(&copy.src_bitmap);
         break;
@@ -4215,30 +4178,30 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
     case QXL_DRAW_TRANSPARENT: {
         SpiceTransparent transparent = drawable->qxl_drawable->u.transparent;
         localize_bitmap(worker, &transparent.src_bitmap, drawable->group_id);
-        worker->draw_funcs.draw_transparent(worker->surface.context.canvas,
-                                            &drawable->qxl_drawable->bbox, &clip, &transparent);
+        canvas->ops->draw_transparent(canvas,
+                                      &drawable->qxl_drawable->bbox, &clip, &transparent);
         unlocalize_bitmap(&transparent.src_bitmap);
         break;
     }
     case QXL_DRAW_ALPHA_BLEND: {
         SpiceAlphaBlnd alpha_blend = drawable->qxl_drawable->u.alpha_blend;
         localize_bitmap(worker, &alpha_blend.src_bitmap, drawable->group_id);
-        worker->draw_funcs.draw_alpha_blend(worker->surface.context.canvas,
-                                            &drawable->qxl_drawable->bbox, &clip, &alpha_blend);
+        canvas->ops->draw_alpha_blend(canvas,
+                                      &drawable->qxl_drawable->bbox, &clip, &alpha_blend);
         unlocalize_bitmap(&alpha_blend.src_bitmap);
         break;
     }
     case QXL_COPY_BITS: {
-        worker->draw_funcs.copy_bits(worker->surface.context.canvas, &drawable->qxl_drawable->bbox,
-                                     &clip, &drawable->qxl_drawable->u.copy_bits.src_pos);
+        canvas->ops->copy_bits(canvas, &drawable->qxl_drawable->bbox,
+                               &clip, &drawable->qxl_drawable->u.copy_bits.src_pos);
         break;
     }
     case QXL_DRAW_BLEND: {
         SpiceBlend blend = drawable->qxl_drawable->u.blend;
         localize_bitmap(worker, &blend.src_bitmap, drawable->group_id);
         localize_mask(worker, &blend.mask, drawable->group_id);
-        worker->draw_funcs.draw_blend(worker->surface.context.canvas, &drawable->qxl_drawable->bbox,
-                                      &clip, &blend);
+        canvas->ops->draw_blend(canvas, &drawable->qxl_drawable->bbox,
+                                &clip, &blend);
         unlocalize_mask(&blend.mask);
         unlocalize_bitmap(&blend.src_bitmap);
         break;
@@ -4246,24 +4209,24 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
     case QXL_DRAW_BLACKNESS: {
         SpiceBlackness blackness = drawable->qxl_drawable->u.blackness;
         localize_mask(worker, &blackness.mask, drawable->group_id);
-        worker->draw_funcs.draw_blackness(worker->surface.context.canvas,
-                                          &drawable->qxl_drawable->bbox, &clip, &blackness);
+        canvas->ops->draw_blackness(canvas,
+                                    &drawable->qxl_drawable->bbox, &clip, &blackness);
         unlocalize_mask(&blackness.mask);
         break;
     }
     case QXL_DRAW_WHITENESS: {
         SpiceWhiteness whiteness = drawable->qxl_drawable->u.whiteness;
         localize_mask(worker, &whiteness.mask, drawable->group_id);
-        worker->draw_funcs.draw_whiteness(worker->surface.context.canvas,
-                                          &drawable->qxl_drawable->bbox, &clip, &whiteness);
+        canvas->ops->draw_whiteness(canvas,
+                                    &drawable->qxl_drawable->bbox, &clip, &whiteness);
         unlocalize_mask(&whiteness.mask);
         break;
     }
     case QXL_DRAW_INVERS: {
         SpiceInvers invers = drawable->qxl_drawable->u.invers;
         localize_mask(worker, &invers.mask, drawable->group_id);
-        worker->draw_funcs.draw_invers(worker->surface.context.canvas,
-                                       &drawable->qxl_drawable->bbox, &clip, &invers);
+        canvas->ops->draw_invers(canvas,
+                                 &drawable->qxl_drawable->bbox, &clip, &invers);
         unlocalize_mask(&invers.mask);
         break;
     }
@@ -4272,8 +4235,8 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
         localize_brush(worker, &rop3.brush, drawable->group_id);
         localize_bitmap(worker, &rop3.src_bitmap, drawable->group_id);
         localize_mask(worker, &rop3.mask, drawable->group_id);
-        worker->draw_funcs.draw_rop3(worker->surface.context.canvas, &drawable->qxl_drawable->bbox,
-                                     &clip, &rop3); unlocalize_mask(&rop3.mask);
+        canvas->ops->draw_rop3(canvas, &drawable->qxl_drawable->bbox,
+                               &clip, &rop3); unlocalize_mask(&rop3.mask);
         unlocalize_bitmap(&rop3.src_bitmap);
         unlocalize_brush(&rop3.brush);
         break;
@@ -4283,8 +4246,8 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
         localize_brush(worker, &stroke.brush, drawable->group_id);
         localize_path(worker, &stroke.path, drawable->group_id);
         localize_attr(worker, &stroke.attr, drawable->group_id);
-        worker->draw_funcs.draw_stroke(worker->surface.context.canvas,
-                                       &drawable->qxl_drawable->bbox, &clip, &stroke);
+        canvas->ops->draw_stroke(canvas,
+                                 &drawable->qxl_drawable->bbox, &clip, &stroke);
         unlocalize_attr(&stroke.attr);
         unlocalize_path(&stroke.path);
         unlocalize_brush(&stroke.brush);
@@ -4295,8 +4258,8 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
         localize_brush(worker, &text.fore_brush, drawable->group_id);
         localize_brush(worker, &text.back_brush, drawable->group_id);
         localize_str(worker, &text.str, drawable->group_id);
-        worker->draw_funcs.draw_text(worker->surface.context.canvas, &drawable->qxl_drawable->bbox,
-                                     &clip, &text);
+        canvas->ops->draw_text(canvas, &drawable->qxl_drawable->bbox,
+                               &clip, &text);
         unlocalize_str(&text.str);
         unlocalize_brush(&text.back_brush);
         unlocalize_brush(&text.fore_brush);
@@ -4313,16 +4276,36 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
 static void red_draw_drawable(RedWorker *worker, Drawable *drawable)
 {
 #ifdef UPDATE_AREA_BY_TREE
+    SpiceCanvas *canvas = worker->surface.context.canvas;
     //todo: add need top mask flag
-    worker->draw_funcs.set_top_mask(worker->surface.context.canvas,
-                                    &drawable->tree_item.base.rgn);
+    canvas->ops->group_start(canvas,
+                             &drawable->tree_item.base.rgn);
 #endif
     red_draw_qxl_drawable(worker, drawable);
 #ifdef UPDATE_AREA_BY_TREE
-    worker->draw_funcs.clear_top_mask(worker->surface.context.canvas);
+    canvas->ops->group_end(canvas);
 #endif
 }
 
+static void validate_area(RedWorker *worker, const SpiceRect *area)
+{
+    if (!worker->surface.context.canvas_draws_on_surface) {
+        SpiceCanvas *canvas = worker->surface.context.canvas;
+        int h;
+        int stride = worker->surface.context.stride;
+        uint8_t *line_0 = worker->surface.context.line_0;
+
+        if (!(h = area->bottom - area->top)) {
+            return;
+        }
+
+        ASSERT(stride < 0);
+        uint8_t *dest = line_0 + (area->top * stride) + area->left * sizeof(uint32_t);
+        dest += (h - 1) * stride;
+        canvas->ops->read_bits(canvas, dest, -stride, area);
+    }
+}
+
 #ifdef UPDATE_AREA_BY_TREE
 
 static inline void __red_collect_for_update(RedWorker *worker, Ring *ring, RingItem *ring_item,
@@ -4401,8 +4384,7 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area)
         current_remove_drawable(worker, drawable);
         container_cleanup(worker, container);
     }
-    worker->draw_funcs.validate_area(worker->surface.context.canvas, worker->surface.context.stride,
-                                     worker->surface.context.line_0, area);
+    validate_area(worker, area);
 }
 
 #else
@@ -4427,9 +4409,7 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area)
     region_destroy(&rgn);
 
     if (!last) {
-        worker->draw_funcs.validate_area(worker->surface.context.canvas,
-                                         worker->surface.context.stride,
-                                         worker->surface.context.line_0, area);
+        validate_area(worker, area);
         return;
     }
 
@@ -4443,8 +4423,7 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area)
         current_remove_drawable(worker, now);
         container_cleanup(worker, container);
     } while (now != last);
-    worker->draw_funcs.validate_area(worker->surface.context.canvas, worker->surface.context.stride,
-                                     worker->surface.context.line_0, area);
+    validate_area(worker, area);
 }
 
 #endif
@@ -4702,8 +4681,9 @@ static void red_add_screen_image(RedWorker *worker)
     ImageItem *item;
     int stride;
     SpiceRect area;
+    SpiceCanvas *canvas = worker->surface.context.canvas;
 
-    if (!worker->display_channel || !worker->surface.context.canvas) {
+    if (!worker->display_channel || !canvas) {
         return;
     }
 
@@ -4726,7 +4706,7 @@ static void red_add_screen_image(RedWorker *worker)
     area.top = area.left = 0;
     area.right = worker->surface.context.width;
     area.bottom = worker->surface.context.height;
-    worker->draw_funcs.read_pixels(worker->surface.context.canvas, item->data, stride, &area);
+    canvas->ops->read_bits(canvas, item->data, stride, &area);
     red_pipe_add_image_item(worker, item);
     release_image_item(item);
     display_channel_push(worker);
@@ -7445,46 +7425,10 @@ static void red_migrate_display(RedWorker *worker)
     }
 }
 
-static void destroy_cairo_canvas(CairoCanvas *canvas)
-{
-    if (!canvas) {
-        return;
-    }
-
-    canvas_destroy(canvas);
-}
-
-static void validate_area_nop(void *canvas, int32_t stride, uint8_t *line_0, const SpiceRect *area)
-{
-}
-
-static void init_cairo_draw_funcs(RedWorker *worker)
-{
-    worker->draw_funcs.draw_fill = (draw_fill_t)canvas_draw_fill;
-    worker->draw_funcs.draw_copy = (draw_copy_t)canvas_draw_copy;
-    worker->draw_funcs.draw_opaque = (draw_opaque_t)canvas_draw_opaque;
-    worker->draw_funcs.copy_bits = (copy_bits_t)canvas_copy_bits;
-    worker->draw_funcs.draw_text = (draw_text_t)canvas_draw_text;
-    worker->draw_funcs.draw_stroke = (draw_stroke_t)canvas_draw_stroke;
-    worker->draw_funcs.draw_rop3 = (draw_rop3_t)canvas_draw_rop3;
-    worker->draw_funcs.draw_blend = (draw_blend_t)canvas_draw_blend;
-    worker->draw_funcs.draw_blackness = (draw_blackness_t)canvas_draw_blackness;
-    worker->draw_funcs.draw_whiteness = (draw_whiteness_t)canvas_draw_whiteness;
-    worker->draw_funcs.draw_invers = (draw_invers_t)canvas_draw_invers;
-    worker->draw_funcs.draw_transparent = (draw_transparent_t)canvas_draw_transparent;
-    worker->draw_funcs.draw_alpha_blend = (draw_alpha_blend_t)canvas_draw_alpha_blend;
-    worker->draw_funcs.read_pixels = (read_pixels_t)canvas_read_bits;
-
-    worker->draw_funcs.set_top_mask = (set_top_mask_t)canvas_group_start;
-    worker->draw_funcs.clear_top_mask = (clear_top_mask_t)canvas_group_end;
-    worker->draw_funcs.validate_area = validate_area_nop;
-    worker->draw_funcs.destroy = (destroy_t)destroy_cairo_canvas;
-}
-
-static CairoCanvas *create_cairo_context(RedWorker *worker, uint32_t width, uint32_t height,
+static SpiceCanvas *create_cairo_context(RedWorker *worker, uint32_t width, uint32_t height,
                                          int32_t stride, uint8_t depth, void *line_0)
 {
-    CairoCanvas *canvas;
+    SpiceCanvas *canvas;
     pixman_image_t *surface;
 
     surface = pixman_image_create_bits(PIXMAN_x8r8g8b8, width, height,
@@ -7499,78 +7443,29 @@ static CairoCanvas *create_cairo_context(RedWorker *worker, uint32_t width, uint
     return canvas;
 }
 
-static void destroy_gl_canvas(GLCanvas *canvas)
+static SpiceCanvas *create_ogl_context_common(RedWorker *worker, OGLCtx *ctx, uint32_t width,
+                                              uint32_t height, int32_t stride, uint8_t depth)
 {
-    OGLCtx *ctx;
-
-    if (!canvas) {
-        return;
-    }
-
-    ctx = gl_canvas_get_usr_data(canvas);
-    ASSERT(ctx);
-    gl_canvas_destroy(canvas, 0);
-    oglctx_destroy(ctx);
-}
-
-static void gl_validate_area(GLCanvas *canvas, int32_t stride, uint8_t *line_0, const SpiceRect *area)
-{
-    int h;
-
-    if (!(h = area->bottom - area->top)) {
-        return;
-    }
-
-    ASSERT(stride < 0);
-    uint8_t *dest = line_0 + (area->top * stride) + area->left * sizeof(uint32_t);
-    dest += (h - 1) * stride;
-    gl_canvas_read_pixels(canvas, dest, -stride, area);
-}
-
-static void init_ogl_draw_funcs(RedWorker *worker)
-{
-    worker->draw_funcs.draw_fill = (draw_fill_t)gl_canvas_draw_fill;
-    worker->draw_funcs.draw_copy = (draw_copy_t)gl_canvas_draw_copy;
-    worker->draw_funcs.draw_opaque = (draw_opaque_t)gl_canvas_draw_opaque;
-    worker->draw_funcs.copy_bits = (copy_bits_t)gl_canvas_copy_pixels;
-    worker->draw_funcs.draw_text = (draw_text_t)gl_canvas_draw_text;
-    worker->draw_funcs.draw_stroke = (draw_stroke_t)gl_canvas_draw_stroke;
-    worker->draw_funcs.draw_rop3 = (draw_rop3_t)gl_canvas_draw_rop3;
-    worker->draw_funcs.draw_blend = (draw_blend_t)gl_canvas_draw_blend;
-    worker->draw_funcs.draw_blackness = (draw_blackness_t)gl_canvas_draw_blackness;
-    worker->draw_funcs.draw_whiteness = (draw_whiteness_t)gl_canvas_draw_whiteness;
-    worker->draw_funcs.draw_invers = (draw_invers_t)gl_canvas_draw_invers;
-    worker->draw_funcs.draw_transparent = (draw_transparent_t)gl_canvas_draw_transparent;
-    worker->draw_funcs.draw_alpha_blend = (draw_alpha_blend_t)gl_canvas_draw_alpha_blend;
-    worker->draw_funcs.read_pixels = (read_pixels_t)gl_canvas_read_pixels;
-
-    worker->draw_funcs.set_top_mask = (set_top_mask_t)gl_canvas_set_top_mask;
-    worker->draw_funcs.clear_top_mask = (clear_top_mask_t)gl_canvas_clear_top_mask;
-    worker->draw_funcs.validate_area = (validate_area_t)gl_validate_area;
-    worker->draw_funcs.destroy = (destroy_t)destroy_gl_canvas;
-}
-
-static GLCanvas *create_ogl_context_common(RedWorker *worker, OGLCtx *ctx, uint32_t width,
-                                           uint32_t height, int32_t stride, uint8_t depth)
-{
-    GLCanvas *canvas;
+    SpiceCanvas *canvas;
 
     oglctx_make_current(ctx);
-    if (!(canvas = gl_canvas_create(ctx, width, height, depth, &worker->image_cache.base, NULL,
+    if (!(canvas = gl_canvas_create(width, height, depth, &worker->image_cache.base, NULL,
                                     &worker->preload_group_virt_mapping))) {
         return NULL;
     }
 
-    gl_canvas_clear(canvas);
+    spice_canvas_set_usr_data(canvas, ctx, (spice_destroy_fn_t)oglctx_destroy);
+
+    canvas->ops->clear(canvas);
 
     return canvas;
 }
 
-static GLCanvas *create_ogl_pbuf_context(RedWorker *worker, uint32_t width, uint32_t height,
+static SpiceCanvas *create_ogl_pbuf_context(RedWorker *worker, uint32_t width, uint32_t height,
                                          int32_t stride, uint8_t depth)
 {
     OGLCtx *ctx;
-    GLCanvas *canvas;
+    SpiceCanvas *canvas;
 
     if (!(ctx = pbuf_create(width, height))) {
         return NULL;
@@ -7584,10 +7479,10 @@ static GLCanvas *create_ogl_pbuf_context(RedWorker *worker, uint32_t width, uint
     return canvas;
 }
 
-static GLCanvas *create_ogl_pixmap_context(RedWorker *worker, uint32_t width, uint32_t height,
-                                           int32_t stride, uint8_t depth) {
+static SpiceCanvas *create_ogl_pixmap_context(RedWorker *worker, uint32_t width, uint32_t height,
+                                              int32_t stride, uint8_t depth) {
     OGLCtx *ctx;
-    GLCanvas *canvas;
+    SpiceCanvas *canvas;
 
     if (!(ctx = pixmap_create(width, height))) {
         return NULL;
@@ -7601,36 +7496,17 @@ static GLCanvas *create_ogl_pixmap_context(RedWorker *worker, uint32_t width, ui
     return canvas;
 }
 
-static inline void surface_init_draw_funcs(RedWorker *worker, uint32_t renderer)
-{
-    switch (renderer) {
-    case RED_RENDERER_CAIRO:
-        init_cairo_draw_funcs(worker);
-        red_printf("using cairo canvas");
-        return;
-    case RED_RENDERER_OGL_PBUF:
-        init_ogl_draw_funcs(worker);
-        red_printf("using opengl pbuff canvas");
-        return;
-    case RED_RENDERER_OGL_PIXMAP:
-        init_ogl_draw_funcs(worker);
-        red_printf("using opengl pixmap canvas");
-        return;
-    default:
-        red_error("invalid renderer type");
-    };
-}
-
 static inline void *create_canvas_for_surface(RedWorker *worker, Surface *surface,
                                               uint32_t renderer, uint32_t width, uint32_t height,
                                               int32_t stride, uint8_t depth, void *line_0)
 {
-    void *canvas;
+    SpiceCanvas *canvas;
 
     switch (renderer) {
     case RED_RENDERER_CAIRO:
         canvas = create_cairo_context(worker, width, height, stride, depth, line_0);
         surface->context.top_down = TRUE;
+        surface->context.canvas_draws_on_surface = TRUE;
         return canvas;
     case RED_RENDERER_OGL_PBUF:
         canvas = create_ogl_pbuf_context(worker, width, height, stride, depth);
@@ -7656,6 +7532,7 @@ static inline void red_create_surface(RedWorker *worker, uint32_t width, uint32_
     }
     PANIC_ON(surface->context.canvas);
 
+    surface->context.canvas_draws_on_surface = FALSE;
     surface->context.width = width;
     surface->context.height = height;
     surface->context.depth = depth;
@@ -7679,7 +7556,6 @@ static inline void red_create_surface(RedWorker *worker, uint32_t width, uint32_
                                                             surface->context.depth, line_0);
         if (surface->context.canvas) {
             worker->renderer = worker->renderers[i];
-            surface_init_draw_funcs(worker, worker->renderers[i]);
             return;
         }
     }
commit 05697e502d709974f9b94feca5d9bdde20b993be
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Mar 2 16:25:22 2010 +0100

    Make virt mapping an interface

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index a3211de..8dcb12d 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -2157,8 +2157,7 @@ CairoCanvas *canvas_create(pixman_image_t *image, int bits
 #endif
                             , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
-                           , void *get_virt_opaque, get_virt_fn_t get_virt,
-                           void *validate_virt_opaque, validate_virt_fn_t validate_virt
+                           , SpiceVirtMapping *virt_mapping
 #endif
                            )
 {
@@ -2181,11 +2180,7 @@ CairoCanvas *canvas_create(pixman_image_t *image, int bits
 #endif
                                , glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
-                               ,
-                               get_virt_opaque,
-                               get_virt,
-                               validate_virt_opaque,
-                               validate_virt
+                               , virt_mapping
 #endif
                                );
     canvas->private_data = NULL;
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
index 3800903..c0102ae 100644
--- a/common/cairo_canvas.h
+++ b/common/cairo_canvas.h
@@ -71,8 +71,7 @@ CairoCanvas *canvas_create(pixman_image_t *image, int bits
 #endif
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
-                           , void *get_virt_opaque, get_virt_fn_t get_virt,
-                           void *validate_virt_opaque, validate_virt_fn_t validate_virt
+                           , SpiceVirtMapping *virt_mapping
 #endif
                            );
 void canvas_destroy(CairoCanvas *canvas);
diff --git a/common/canvas_base.c b/common/canvas_base.c
index aeb1cb5..01945ec 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -164,10 +164,7 @@ typedef struct QuicData {
     jmp_buf jmp_env;
 #ifndef CAIRO_CANVAS_NO_CHUNKS
     SPICE_ADDRESS next;
-    get_virt_fn_t get_virt;
-    void *get_virt_opaque;
-    validate_virt_fn_t validate_virt;
-    void *validate_virt_opaque;
+    SpiceVirtMapping *virt_mapping;
 #endif
     char message_buf[512];
 } QuicData;
@@ -1506,11 +1503,11 @@ static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_
     if (!quic_data->next) {
         return 0;
     }
-    chunk = (DataChunk *)quic_data->get_virt(quic_data->get_virt_opaque, quic_data->next,
-                                             sizeof(DataChunk));
+    chunk = (DataChunk *)quic_data->virt_mapping->ops->get_virt(quic_data->virt_mapping, quic_data->next,
+                                                                sizeof(DataChunk));
     size = chunk->size;
-    quic_data->validate_virt(quic_data->validate_virt_opaque, (unsigned long)chunk->data,
-                             quic_data->next, size);
+    quic_data->virt_mapping->ops->validate_virt(quic_data->virt_mapping, (unsigned long)chunk->data,
+                                                quic_data->next, size);
 
     quic_data->next = chunk->next;
     *io_ptr = (uint32_t *)chunk->data;
@@ -1555,8 +1552,7 @@ static int canvas_base_init(CanvasBase *canvas, int depth
 #endif
                             , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
-                           , void *get_virt_opaque, get_virt_fn_t get_virt,
-                           void *validate_virt_opaque, validate_virt_fn_t validate_virt
+                            , SpiceVirtMapping *virt_mapping
 #endif
                             )
 {
@@ -1568,10 +1564,7 @@ static int canvas_base_init(CanvasBase *canvas, int depth
     canvas->quic_data.usr.more_space = quic_usr_more_space;
     canvas->quic_data.usr.more_lines = quic_usr_more_lines;
 #ifndef CAIRO_CANVAS_NO_CHUNKS
-    canvas->quic_data.get_virt_opaque = get_virt_opaque;
-    canvas->quic_data.get_virt = get_virt;
-    canvas->quic_data.validate_virt_opaque = validate_virt_opaque;
-    canvas->quic_data.validate_virt = validate_virt;
+    canvas->quic_data.virt_mapping = virt_mapping;
 #endif
     if (!(canvas->quic_data.quic = quic_create(&canvas->quic_data.usr))) {
             return 0;
diff --git a/common/canvas_base.h b/common/canvas_base.h
index 7796245..4c28654 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -27,6 +27,7 @@
 typedef struct _SpiceImageCache SpiceImageCache;
 typedef struct _SpicePaletteCache SpicePaletteCache;
 typedef struct _SpiceGlzDecoder SpiceGlzDecoder;
+typedef struct _SpiceVirtMapping SpiceVirtMapping;
 
 typedef struct {
     void (*put)(SpiceImageCache *cache,
@@ -64,11 +65,15 @@ struct _SpiceGlzDecoder {
   SpiceGlzDecoderOps *ops;
 };
 
-#ifndef CAIRO_CANVAS_NO_CHUNKS
-typedef void *(*get_virt_fn_t)(void *get_virt_opaque, unsigned long addr, uint32_t add_size);
-typedef void (*validate_virt_fn_t)(void *validate_virt_opaque, unsigned long virt,
-                                   unsigned long from_addr, uint32_t add_size);
-#endif
+typedef struct {
+    void *(*get_virt)(SpiceVirtMapping *mapping, unsigned long addr, uint32_t add_size);
+    void (*validate_virt)(SpiceVirtMapping *mapping, unsigned long virt,
+                          unsigned long from_addr, uint32_t add_size);
+} SpiceVirtMappingOps;
+
+struct _SpiceVirtMapping {
+  SpiceVirtMappingOps *ops;
+};
 
 #endif
 
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index f173194..e7a7bff 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -813,8 +813,7 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #endif
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
-                           , void *get_virt_opaque, get_virt_fn_t get_virt,
-                           void *validate_virt_opaque, validate_virt_fn_t validate_virt
+                           , SpiceVirtMapping *virt_mapping
 #endif
                            )
 {
@@ -843,11 +842,7 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #endif
                                , glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
-                               ,
-                               get_virt_opaque,
-                               get_virt,
-                               validate_virt_opaque,
-                               validate_virt
+                               , virt_mapping
 #endif
                                );
     if (!init_ok) {
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index ec0cbdb..ee272a8 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -66,8 +66,7 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #endif
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
-                           , void *get_virt_opaque, get_virt_fn_t get_virt,
-                           void *validate_virt_opaque, validate_virt_fn_t validate_virt
+                            , SpiceVirtMapping *virt_mapping
 #endif
                            );
 void gl_canvas_destroy(GLCanvas *, int);
diff --git a/server/red_worker.c b/server/red_worker.c
index 52d8451..6eaf391 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -1007,6 +1007,7 @@ typedef struct RedWorker {
     uint64_t *wakeup_counter;
     uint64_t *command_counter;
 #endif
+    SpiceVirtMapping preload_group_virt_mapping;
 } RedWorker;
 
 pthread_mutex_t avcodec_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -1217,18 +1218,20 @@ static void cb_validate_virt(void *opaque, unsigned long virt, unsigned long fro
     validate_virt((RedWorker *)opaque, virt, slot_id, add_size, group_id);
 }
 
-static void *cb_get_virt_preload_group(void *opaque, unsigned long addr, uint32_t add_size)
+static void *op_get_virt_preload_group(SpiceVirtMapping *mapping, unsigned long addr, uint32_t add_size)
 {
-    return (void *)get_virt((RedWorker *)opaque, addr, add_size,
-                            ((RedWorker *)opaque)->preload_group_id);
+    RedWorker *worker = CONTAINEROF(mapping, RedWorker, preload_group_virt_mapping);
+    return (void *)get_virt(worker, addr, add_size,
+                            worker->preload_group_id);
 }
 
-static void cb_validate_virt_preload_group(void *opaque, unsigned long virt,
+static void op_validate_virt_preload_group(SpiceVirtMapping *mapping, unsigned long virt,
                                            unsigned long from_addr, uint32_t add_size)
 {
-    int slot_id = get_memslot_id((RedWorker *)opaque, from_addr);
-    validate_virt((RedWorker *)opaque, virt, slot_id, add_size,
-                  ((RedWorker *)opaque)->preload_group_id);
+    RedWorker *worker = CONTAINEROF(mapping, RedWorker, preload_group_virt_mapping);
+    int slot_id = get_memslot_id(worker, from_addr);
+    validate_virt(worker, virt, slot_id, add_size,
+                  worker->preload_group_id);
 }
 
 char *draw_type_to_str(uint8_t type)
@@ -7491,8 +7494,7 @@ static CairoCanvas *create_cairo_context(RedWorker *worker, uint32_t width, uint
         red_error("create cairo surface failed");
     }
     canvas = canvas_create(surface, depth, &worker->image_cache.base, NULL,
-                           worker, cb_get_virt_preload_group, worker,
-                           cb_validate_virt_preload_group);
+                           &worker->preload_group_virt_mapping);
     pixman_image_unref (surface);
     return canvas;
 }
@@ -7555,8 +7557,7 @@ static GLCanvas *create_ogl_context_common(RedWorker *worker, OGLCtx *ctx, uint3
 
     oglctx_make_current(ctx);
     if (!(canvas = gl_canvas_create(ctx, width, height, depth, &worker->image_cache.base, NULL,
-                                    worker, cb_get_virt_preload_group,
-                                    worker, cb_validate_virt_preload_group))) {
+                                    &worker->preload_group_virt_mapping))) {
         return NULL;
     }
 
@@ -9028,6 +9029,10 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     struct epoll_event event;
     RedWorkeMessage message;
     int epoll;
+    static SpiceVirtMappingOps preload_group_virt_mapping_ops = {
+        op_get_virt_preload_group,
+        op_validate_virt_preload_group
+    };
 
     ASSERT(sizeof(CursorItem) <= QXL_CURSUR_DEVICE_DATA_SIZE);
 
@@ -9081,6 +9086,8 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     worker->internal_groupslot_id = init_data->internal_groupslot_id;
     red_create_mem_slots(worker);
 
+    worker->preload_group_virt_mapping.ops = &preload_group_virt_mapping_ops;
+
     message = RED_WORKER_MESSAGE_READY;
     write_message(worker->channel, &message);
 }
commit f7a77f9881d5cd1d360b043d6b619216603941dd
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Mar 2 15:51:12 2010 +0100

    Make glz_decoder non-optional canvas_base in canvas constructors
    
    It can still be NULL, but we simplify the headers by always including it.
    There is no practical performance difference here.

diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index ed815d1..a3211de 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -2155,9 +2155,7 @@ CairoCanvas *canvas_create(pixman_image_t *image, int bits,
 #else
 CairoCanvas *canvas_create(pixman_image_t *image, int bits
 #endif
-#ifdef USE_GLZ
                             , SpiceGlzDecoder *glz_decoder
-#endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , void *get_virt_opaque, get_virt_fn_t get_virt,
                            void *validate_virt_opaque, validate_virt_fn_t validate_virt
@@ -2181,10 +2179,7 @@ CairoCanvas *canvas_create(pixman_image_t *image, int bits
 #else
     init_ok = canvas_base_init(&canvas->base, bits
 #endif
-#ifdef USE_GLZ
-                               ,
-                               glz_decoder
-#endif
+                               , glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                                ,
                                get_virt_opaque,
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
index 390c61c..3800903 100644
--- a/common/cairo_canvas.h
+++ b/common/cairo_canvas.h
@@ -69,9 +69,7 @@ CairoCanvas *canvas_create(pixman_image_t *image, int bits,
 #else
 CairoCanvas *canvas_create(pixman_image_t *image, int bits
 #endif
-#ifdef USE_GLZ
                            , SpiceGlzDecoder *glz_decoder
-#endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , void *get_virt_opaque, get_virt_fn_t get_virt,
                            void *validate_virt_opaque, validate_virt_fn_t validate_virt
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 8bac592..aeb1cb5 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -622,6 +622,11 @@ static pixman_image_t *canvas_get_glz(CanvasBase *canvas, LZImage *image)
 #ifdef WIN32
     canvas->glz_data.decode_data.dc = canvas->dc;
 #endif
+
+    if (canvas->glz_data.decoder == NULL) {
+        CANVAS_ERROR("glz not supported");
+    }
+
     canvas->glz_data.decoder->ops->decode(canvas->glz_data.decoder,
                                           image->lz_rgb.data, NULL,
                                           &canvas->glz_data.decode_data);
@@ -816,7 +821,8 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
         break;
     }
 #endif
-#ifdef USE_GLZ
+
+#if defined(CAIRO_CANVAS_CACHE)
     case SPICE_IMAGE_TYPE_GLZ_RGB: {
         access_test(canvas, descriptor, sizeof(SpiceLZRGBImage));
         LZImage *image = (LZImage *)descriptor;
@@ -824,6 +830,7 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
         break;
     }
 #endif
+
     case SPICE_IMAGE_TYPE_FROM_CACHE:
         return canvas->bits_cache->ops->get(canvas->bits_cache, descriptor->id);
     case SPICE_IMAGE_TYPE_BITMAP: {
@@ -1546,9 +1553,7 @@ static int canvas_base_init(CanvasBase *canvas, int depth,
 #else
 static int canvas_base_init(CanvasBase *canvas, int depth
 #endif
-#ifdef USE_GLZ
                             , SpiceGlzDecoder *glz_decoder
-#endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , void *get_virt_opaque, get_virt_fn_t get_virt,
                            void *validate_virt_opaque, validate_virt_fn_t validate_virt
@@ -1583,9 +1588,7 @@ static int canvas_base_init(CanvasBase *canvas, int depth
             return 0;
     }
 #endif
-#ifdef USE_GLZ
     canvas->glz_data.decoder = glz_decoder;
-#endif
 
     if (depth == 16) {
         canvas->color_shift = 5;
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index c4750a8..40305df 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -1707,9 +1707,7 @@ GdiCanvas *gdi_canvas_create(HDC dc, int bits,
 #else
 GdiCanvas *gdi_canvas_create(HDC dc, int bits
 #endif
-#ifdef USE_GLZ
                             , SpiceGlzDecoder *glz_decoder
-#endif
                             )
 {
     GdiCanvas *canvas;
@@ -1729,11 +1727,7 @@ GdiCanvas *gdi_canvas_create(HDC dc, int bits
 #else
     init_ok = gdi_canvas_base_init(&canvas->base, bits
 #endif
-#ifdef USE_GLZ
-                               ,
-                               glz_decoder
-#endif
-                               );
+                               , glz_decoder);
     canvas->dc = dc;
     canvas->lock = lock;
     return canvas;
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index 6b5eda9..f173194 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -811,9 +811,7 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
 #else
 GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #endif
-#ifdef USE_GLZ
                            , SpiceGlzDecoder *glz_decoder
-#endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , void *get_virt_opaque, get_virt_fn_t get_virt,
                            void *validate_virt_opaque, validate_virt_fn_t validate_virt
@@ -843,10 +841,7 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #else
     init_ok = canvas_base_init(&canvas->base, depth
 #endif
-#ifdef USE_GLZ
-                               ,
-                               glz_decoder
-#endif
+                               , glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                                ,
                                get_virt_opaque,
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index 1c57340..ec0cbdb 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -64,9 +64,7 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
 #else
 GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #endif
-#ifdef USE_GLZ
                            , SpiceGlzDecoder *glz_decoder
-#endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , void *get_virt_opaque, get_virt_fn_t get_virt,
                            void *validate_virt_opaque, validate_virt_fn_t validate_virt
diff --git a/server/red_worker.c b/server/red_worker.c
index 100f309..52d8451 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -7490,7 +7490,7 @@ static CairoCanvas *create_cairo_context(RedWorker *worker, uint32_t width, uint
     if (surface == NULL) {
         red_error("create cairo surface failed");
     }
-    canvas = canvas_create(surface, depth, &worker->image_cache.base,
+    canvas = canvas_create(surface, depth, &worker->image_cache.base, NULL,
                            worker, cb_get_virt_preload_group, worker,
                            cb_validate_virt_preload_group);
     pixman_image_unref (surface);
@@ -7554,7 +7554,7 @@ 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.base,
+    if (!(canvas = gl_canvas_create(ctx, width, height, depth, &worker->image_cache.base, NULL,
                                     worker, cb_get_virt_preload_group,
                                     worker, cb_validate_virt_preload_group))) {
         return NULL;
commit c0fdcd1a10d0eae92fec9e00382b445d669c7428
Author: Alexander Larsson <alexl at redhat.com>
Date:   Tue Mar 2 15:41:08 2010 +0100

    Make canvas Glz decoder integration nicer
    
    We use a dynamic interface similar to e.g. SpiceImageCache instead
    of passing both function and opaque

diff --git a/client/canvas.cpp b/client/canvas.cpp
index 24ebd01..516a874 100644
--- a/client/canvas.cpp
+++ b/client/canvas.cpp
@@ -212,10 +212,3 @@ void Canvas::draw_stroke(SpiceMsgDisplayDrawStroke& stroke, int size)
     localalize_attr(stroke.data.attr);
     draw_stroke(&stroke.base.box, &stroke.base.clip, &stroke.data);
 }
-
-void Canvas::glz_decode(void *opaque, uint8_t *data, SpicePalette *plt, void *usr_data)
-{
-    GlzDecoder* decoder = static_cast<GlzDecoder*>(opaque);
-    decoder->decode(data, plt, usr_data);
-}
-
diff --git a/client/canvas.h b/client/canvas.h
index 79400cc..2f12263 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -295,7 +295,6 @@ protected:
     PaletteCache& palette_cache() { return _palette_cache;}
 
     GlzDecoder& glz_decoder() {return _glz_decoder;}
-    static void glz_decode(void *opaque, uint8_t *data, SpicePalette *plt, void *usr_data);
 
 private:
     void access_test(void* ptr, size_t size);
diff --git a/client/glz_decoder.cpp b/client/glz_decoder.cpp
index 0453aaa..30eecc1 100644
--- a/client/glz_decoder.cpp
+++ b/client/glz_decoder.cpp
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
@@ -19,12 +20,25 @@
 #include "glz_decoder_config.h"
 #include "glz_decoder.h"
 
+static void op_decode (SpiceGlzDecoder *decoder,
+                       uint8_t *data,
+                       SpicePalette *plt,
+                       void *usr_data)
+{
+    GlzDecoder* _decoder = static_cast<GlzDecoder*>(decoder);
+    _decoder->decode(data, plt, usr_data);
+}
+
 GlzDecoder::GlzDecoder(GlzDecoderWindow &images_window,
                        GlzDecodeHandler &usr_handler, GlzDecoderDebug &debug_calls)
     : _images_window (images_window)
     , _usr_handler (usr_handler)
     , _debug_calls (debug_calls)
 {
+    static SpiceGlzDecoderOps decoder_ops = {
+        op_decode,
+    };
+    ops = &decoder_ops;
 }
 
 GlzDecoder::~GlzDecoder()
diff --git a/client/glz_decoder.h b/client/glz_decoder.h
index c2afa54..2b797d8 100644
--- a/client/glz_decoder.h
+++ b/client/glz_decoder.h
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
    Copyright (C) 2009 Red Hat, Inc.
 
@@ -21,6 +22,7 @@
 #include "lz_common.h"
 #include "glz_decoder_config.h"
 #include "glz_decoder_window.h"
+#include "canvas_base.h"
 #include <spice/draw.h>
 
 
@@ -47,9 +49,9 @@ public:
     This class implements the lz decoding algorithm
 */
 
-class GlzDecoder {
+class GlzDecoder : public SpiceGlzDecoder
+{
 public:
-
     GlzDecoder(GlzDecoderWindow &images_window, GlzDecodeHandler &usr_handler,
                GlzDecoderDebug &debug_calls);
     virtual ~GlzDecoder();
diff --git a/client/red_cairo_canvas.cpp b/client/red_cairo_canvas.cpp
index 87023e1..97277fb 100644
--- a/client/red_cairo_canvas.cpp
+++ b/client/red_cairo_canvas.cpp
@@ -102,8 +102,7 @@ void CCanvas::set_mode(int width, int height, int depth, RedWindow *win)
     if (!(_canvas = canvas_create(surface, depth,
                                   &pixmap_cache().base,
                                   &palette_cache().base,
-                                  &glz_decoder(),
-                                  glz_decode))) {
+                                  &glz_decoder()))) {
         THROW("create canvas failed");
     }
     pixman_image_unref (surface);
diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp
index 671fe82..efdf5d4 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -92,8 +92,7 @@ void GDICanvas::set_mode(int width, int height, int depth)
                                       &_pixmap->get_mutex(),
                                       depth, &pixmap_cache().base,
                                       &palette_cache().base,
-                                      &glz_decoder(),
-                                      glz_decode))) {
+                                      &glz_decoder()))) {
         THROW("create canvas failed");
     }
 }
diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp
index 6598425..76dabc7 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -99,8 +99,7 @@ void GCanvas::set_mode(int width, int height, int depth, RedWindow *win,
     if (!(_canvas = gl_canvas_create(NULL, width, height, depth,
                                      &pixmap_cache().base,
                                      &palette_cache().base,
-                                     &glz_decoder(),
-                                     glz_decode))) {
+                                     &glz_decoder()))) {
         THROW("create canvas failed");
     }
 }
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index addb3d4..ed815d1 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -2156,7 +2156,7 @@ CairoCanvas *canvas_create(pixman_image_t *image, 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
+                            , SpiceGlzDecoder *glz_decoder
 #endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , void *get_virt_opaque, get_virt_fn_t get_virt,
@@ -2183,8 +2183,7 @@ CairoCanvas *canvas_create(pixman_image_t *image, int bits
 #endif
 #ifdef USE_GLZ
                                ,
-                               glz_decoder_opaque,
-                               glz_decode
+                               glz_decoder
 #endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                                ,
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
index 205c62c..390c61c 100644
--- a/common/cairo_canvas.h
+++ b/common/cairo_canvas.h
@@ -70,7 +70,7 @@ CairoCanvas *canvas_create(pixman_image_t *image, 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
+                           , SpiceGlzDecoder *glz_decoder
 #endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , void *get_virt_opaque, get_virt_fn_t get_virt,
diff --git a/common/canvas_base.c b/common/canvas_base.c
index 23039eb..8bac592 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -154,8 +154,7 @@ typedef struct LzData {
 } LzData;
 
 typedef struct GlzData {
-    void                  *decoder_opaque;
-    glz_decode_fn_t decode;
+    SpiceGlzDecoder *decoder;
     LzDecodeUsrData decode_data;
 } GlzData;
 
@@ -623,8 +622,9 @@ static pixman_image_t *canvas_get_glz(CanvasBase *canvas, LZImage *image)
 #ifdef WIN32
     canvas->glz_data.decode_data.dc = canvas->dc;
 #endif
-    canvas->glz_data.decode(canvas->glz_data.decoder_opaque, image->lz_rgb.data, NULL,
-                            &canvas->glz_data.decode_data);
+    canvas->glz_data.decoder->ops->decode(canvas->glz_data.decoder,
+                                          image->lz_rgb.data, NULL,
+                                          &canvas->glz_data.decode_data);
     /* global_decode calls alloc_lz_image, which sets canvas->glz_data.surface */
     return (canvas->glz_data.decode_data.out_surface);
 }
@@ -1547,7 +1547,7 @@ static int canvas_base_init(CanvasBase *canvas, int depth,
 static int canvas_base_init(CanvasBase *canvas, int depth
 #endif
 #ifdef USE_GLZ
-                            , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
+                            , SpiceGlzDecoder *glz_decoder
 #endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , void *get_virt_opaque, get_virt_fn_t get_virt,
@@ -1584,8 +1584,7 @@ static int canvas_base_init(CanvasBase *canvas, int depth
     }
 #endif
 #ifdef USE_GLZ
-    canvas->glz_data.decoder_opaque = glz_decoder_opaque;
-    canvas->glz_data.decode = glz_decode;
+    canvas->glz_data.decoder = glz_decoder;
 #endif
 
     if (depth == 16) {
diff --git a/common/canvas_base.h b/common/canvas_base.h
index 78ece62..7796245 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -26,6 +26,7 @@
 
 typedef struct _SpiceImageCache SpiceImageCache;
 typedef struct _SpicePaletteCache SpicePaletteCache;
+typedef struct _SpiceGlzDecoder SpiceGlzDecoder;
 
 typedef struct {
     void (*put)(SpiceImageCache *cache,
@@ -52,8 +53,17 @@ struct _SpicePaletteCache {
   SpicePaletteCacheOps *ops;
 };
 
-typedef void (*glz_decode_fn_t)(void *glz_decoder_opaque, uint8_t *data,
-                                SpicePalette *plt, void *usr_data);
+typedef struct {
+    void (*decode)(SpiceGlzDecoder *decoder,
+                   uint8_t *data,
+                   SpicePalette *plt,
+                   void *usr_data);
+} SpiceGlzDecoderOps;
+
+struct _SpiceGlzDecoder {
+  SpiceGlzDecoderOps *ops;
+};
+
 #ifndef CAIRO_CANVAS_NO_CHUNKS
 typedef void *(*get_virt_fn_t)(void *get_virt_opaque, unsigned long addr, uint32_t add_size);
 typedef void (*validate_virt_fn_t)(void *validate_virt_opaque, unsigned long virt,
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index ae477db..c4750a8 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -1708,7 +1708,7 @@ GdiCanvas *gdi_canvas_create(HDC dc, int bits,
 GdiCanvas *gdi_canvas_create(HDC dc, int bits
 #endif
 #ifdef USE_GLZ
-                            , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
+                            , SpiceGlzDecoder *glz_decoder
 #endif
                             )
 {
@@ -1731,8 +1731,7 @@ GdiCanvas *gdi_canvas_create(HDC dc, int bits
 #endif
 #ifdef USE_GLZ
                                ,
-                               glz_decoder_opaque,
-                               glz_decode
+                               glz_decoder
 #endif
                                );
     canvas->dc = dc;
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
index d9b8d21..12082d8 100644
--- a/common/gdi_canvas.h
+++ b/common/gdi_canvas.h
@@ -62,8 +62,7 @@ void gdi_canvas_set_access_params(GdiCanvas *canvas, unsigned long base, unsigne
 GdiCanvas *gdi_canvas_create(HDC dc, class Mutex *lock, int bits,
                              SpiceImageCache *bits_cache,
                              SpicePaletteCache *palette_cache,
-                             void *glz_decoder_opaque,
-                             glz_decode_fn_t glz_decode);
+                             SpiceGlzDecoder *glz_decoder);
 
 void gdi_canvas_destroy(GdiCanvas *canvas);
 
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index 076090e..6b5eda9 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -812,7 +812,7 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
 GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #endif
 #ifdef USE_GLZ
-                           , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
+                           , SpiceGlzDecoder *glz_decoder
 #endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , void *get_virt_opaque, get_virt_fn_t get_virt,
@@ -845,8 +845,7 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #endif
 #ifdef USE_GLZ
                                ,
-                               glz_decoder_opaque,
-                               glz_decode
+                               glz_decoder
 #endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                                ,
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index 39b2792..1c57340 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -65,7 +65,7 @@ GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth,
 GLCanvas *gl_canvas_create(void *usr_data, int width, int height, int depth
 #endif
 #ifdef USE_GLZ
-                           , void *glz_decoder_opaque, glz_decode_fn_t glz_decode
+                           , SpiceGlzDecoder *glz_decoder
 #endif
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , void *get_virt_opaque, get_virt_fn_t get_virt,


More information about the Spice-commits mailing list