allow to use cairo-image/software surface for canvas & cairowidgets
authorRobin Gareus <robin@gareus.org>
Tue, 28 Oct 2014 01:15:10 +0000 (02:15 +0100)
committerRobin Gareus <robin@gareus.org>
Tue, 28 Oct 2014 01:15:10 +0000 (02:15 +0100)
libs/canvas/canvas.cc
libs/canvas/canvas/canvas.h
libs/gtkmm2ext/cairo_widget.cc
libs/gtkmm2ext/gtkmm2ext/cairo_widget.h
wscript

index 02127da36aeb0058127451d11b5ee582783ffbba..9d1d9f3ad9df7c5965b85c827ae4f6fdbe25b79d 100644 (file)
@@ -720,6 +720,18 @@ GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
        
 }
 
+void
+GtkCanvas::on_size_allocate (Gtk::Allocation& a)
+{
+       EventBox::on_size_allocate (a);
+#ifdef USE_CAIRO_IMAGE_SURFACE
+       /* allocate an image surface as large as the canvas itself */
+
+       canvas_image.clear ();
+       canvas_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, a.get_width(), a.get_height());
+#endif
+}
+
 /** Handler for GDK expose events.
  *  @param ev Event.
  *  @return true if the event was handled.
@@ -727,11 +739,49 @@ GtkCanvas::item_going_away (Item* item, boost::optional<Rect> bounding_box)
 bool
 GtkCanvas::on_expose_event (GdkEventExpose* ev)
 {
+#ifdef USE_CAIRO_IMAGE_SURFACE
+       if (!canvas_image) {
+               canvas_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height());
+       }
+
+       {
+               /* scope for image_context */
+               Cairo::RefPtr<Cairo::Context> image_context = Cairo::Context::create (canvas_image);
+
+               /* clear expose area to transparent */
+
+               image_context->save ();
+               image_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+               image_context->clip ();
+               image_context->set_operator (Cairo::OPERATOR_CLEAR);
+               image_context->paint ();
+               image_context->restore ();
+
+               /* render into image surface */
+
+               render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), image_context);
+
+               /* image surface is flushed when image_context goes out of scope */
+       }
+
+       /* now blit our private surface back to the GDK one */
+
+       Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
+
+       cairo_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+       cairo_context->clip ();
+       cairo_context->set_source (canvas_image, 0, 0);
+       cairo_context->set_operator (Cairo::OPERATOR_SOURCE);
+       cairo_context->paint ();
+
+#else
+
        Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
        render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), cairo_context);
-       return true;
 
+#endif
 
+       return true;
 }
 
 /** Handler for GDK scroll events.
index c45908f0b30afde3b25b82ecab1e15488a7c027b..107ea0d7b243551e3d19ba0bc96665a70286dc19 100644 (file)
@@ -190,6 +190,7 @@ public:
        void stop_tooltip_timeout ();
 
 protected:
+       void on_size_allocate (Gtk::Allocation&);
        bool on_scroll_event (GdkEventScroll *);
        bool on_expose_event (GdkEventExpose *);
        bool on_button_press_event (GdkEventButton *);
@@ -210,6 +211,8 @@ private:
        void item_going_away (Item *, boost::optional<Rect>);
        bool send_leave_event (Item const *, double, double) const;
 
+       Cairo::RefPtr<Cairo::Surface> canvas_image;
+
         /** Item currently chosen for event delivery based on pointer position */
         Item * _current_item;
         /** Item pending as _current_item */
index 03c23000a267c0cd0c8b7dfa8272e2af01ef2972..5180c2e8d0f37f592c4f14478983d9aabde22875 100644 (file)
@@ -64,17 +64,27 @@ CairoWidget::on_button_press_event (GdkEventButton*)
 bool
 CairoWidget::on_expose_event (GdkEventExpose *ev)
 {
-       cairo_t* cr = gdk_cairo_create (get_window ()->gobj());
-       cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
-       cairo_clip_preserve (cr);
+#ifdef USE_CAIRO_IMAGE_SURFACE
+
+       if (!image_surface) {
+               image_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height());
+       }
+
+       Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create (image_surface);
+#else
+       Cairo::RefPtr<Cairo::Context> cr = get_window()->create_cairo_context ();
+#endif
+
+       cr->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+       cr->clip_preserve ();
 
        /* paint expose area the color of the parent window bg
        */
        
        Gdk::Color bg (get_parent_bg());
        
-       cairo_set_source_rgb (cr, bg.get_red_p(), bg.get_green_p(), bg.get_blue_p());
-       cairo_fill (cr);
+       cr->set_source_rgb (bg.get_red_p(), bg.get_green_p(), bg.get_blue_p());
+       cr->fill ();
 
        cairo_rectangle_t expose_area;
        expose_area.x = ev->area.x;
@@ -82,9 +92,20 @@ CairoWidget::on_expose_event (GdkEventExpose *ev)
        expose_area.width = ev->area.width;
        expose_area.height = ev->area.height;
 
-       render (cr, &expose_area);
+       render (cr->cobj(), &expose_area);
 
-       cairo_destroy (cr);
+#ifdef USE_CAIRO_IMAGE_SURFACE
+       image_surface->flush();
+       /* now blit our private surface back to the GDK one */
+
+       Cairo::RefPtr<Cairo::Context> cairo_context = get_window()->create_cairo_context ();
+
+       cairo_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+       cairo_context->clip ();
+       cairo_context->set_source (image_surface, 0, 0);
+       cairo_context->set_operator (Cairo::OPERATOR_SOURCE);
+       cairo_context->paint ();
+#endif
 
        return true;
 }
@@ -108,6 +129,10 @@ CairoWidget::on_size_allocate (Gtk::Allocation& alloc)
 {
        Gtk::EventBox::on_size_allocate (alloc);
 
+#ifdef USE_CAIRO_IMAGE_SURFACE
+       image_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, alloc.get_width(), alloc.get_height());
+#endif
+
        set_dirty ();
 }
 
index 87002101bd7df99a14aadea0b030435e29aaa7b5..038198ab573b5ab52536bfa6f5a22dbcc72ca51f 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef __gtk2_ardour_cairo_widget_h__
 #define __gtk2_ardour_cairo_widget_h__
 
+#include <cairomm/surface.h>
 #include <gtkmm/eventbox.h>
 
 #include "gtkmm2ext/visibility.h"
@@ -116,6 +117,7 @@ protected:
        static sigc::slot<void> focus_handler;
 
   private:
+       Cairo::RefPtr<Cairo::Surface> image_surface;
        Glib::SignalProxyProperty _name_proxy;
        sigc::connection _parent_style_change;
        Widget * _current_parent;
diff --git a/wscript b/wscript
index f482bf1e1af915f97dde405fa5f52d3c5ff7a166..9f0c06b991e07567ee0386746afa2ab553334040 100644 (file)
--- a/wscript
+++ b/wscript
@@ -716,6 +716,11 @@ def configure(conf):
         # TODO put this only where it is needed
         conf.env.append_value('LIB', 'regex')
 
+        # work around GdkDrawable BitBlt performance issue on windows
+        # see http://gareus.org/wiki/ardour_windows_gdk_and_cairo
+        conf.env.append_value('CFLAGS', '-DUSE_CAIRO_IMAGE_SURFACE')
+        conf.env.append_value('CXXFLAGS', '-DUSE_CAIRO_IMAGE_SURFACE')
+
     # Tell everyone that this is a waf build
 
     conf.env.append_value('CFLAGS', '-DWAF_BUILD')