[Spice-devel] [PATCH 4/8] Move virtualization of canvas drawing into common/canvas_base

alexl at redhat.com alexl at redhat.com
Mon Mar 8 09:33:55 PST 2010


From: Alexander Larsson <alexl at redhat.com>

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.
---
 client/canvas.cpp           |   68 ++++++++---
 client/canvas.h             |   29 ++---
 client/red_cairo_canvas.cpp |   96 +--------------
 client/red_cairo_canvas.h   |   25 ----
 client/red_gdi_canvas.cpp   |   84 -------------
 client/red_gdi_canvas.h     |   20 ---
 client/red_gl_canvas.cpp    |  101 +---------------
 client/red_gl_canvas.h      |   20 +---
 common/cairo_canvas.c       |  127 +++++++++++++-------
 common/cairo_canvas.h       |   45 +------
 common/canvas_base.c        |   67 +++++++++--
 common/canvas_base.h        |   39 ++++++
 common/gdi_canvas.c         |  107 +++++++++++------
 common/gdi_canvas.h         |   36 +-----
 common/gl_canvas.c          |  136 ++++++++++++++-------
 common/gl_canvas.h          |   47 +-------
 server/red_worker.c         |  284 ++++++++++++-------------------------------
 17 files changed, 501 insertions(+), 830 deletions(-)

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;
         }
     }
-- 
1.6.6



More information about the Spice-devel mailing list