Only show user-presets in favorite sidebar
[ardour.git] / libs / canvas / canvas.cc
index 9f76f45dc6ce5b231d1194595d10faab7b66ed23..8456d44d828181462eacaec1735d5346425ac901 100644 (file)
 #include "pbd/stacktrace.h"
 
 #include "canvas/canvas.h"
-#include "canvas/colors.h"
+#include "gtkmm2ext/colors.h"
 #include "canvas/debug.h"
 #include "canvas/line.h"
 #include "canvas/scroll_group.h"
-#include "canvas/utils.h"
+
+#ifdef __APPLE__
+#include <gdk/gdk.h>
+#include "gtkmm2ext/nsglview.h"
+#endif
 
 using namespace std;
 using namespace ArdourCanvas;
@@ -52,7 +56,8 @@ uint32_t Canvas::tooltip_timeout_msecs = 750;
 /** Construct a new Canvas */
 Canvas::Canvas ()
        : _root (this)
-        , _bg_color (rgba_to_color (0, 1.0, 0.0, 1.0))
+       , _bg_color (Gtkmm2ext::rgba_to_color (0, 1.0, 0.0, 1.0))
+       , _last_render_start_timestamp(0)
 {
        set_epoch ();
 }
@@ -96,6 +101,10 @@ Canvas::zoomed ()
 void
 Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context) const
 {
+       PreRender (); // emit signal
+
+       _last_render_start_timestamp = g_get_monotonic_time();
+
 #ifdef CANVAS_DEBUG
        if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
                cerr << this << " RENDER: " << area << endl;
@@ -137,6 +146,34 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context
 
 }
 
+void
+Canvas::prepare_for_render (Rect const & area) const
+{
+       Rect root_bbox = _root.bounding_box();
+       if (!root_bbox) {
+               /* the root has no bounding box, so there's nothing to render */
+               return;
+       }
+
+       Rect draw = root_bbox.intersection (area);
+
+       if (draw) {
+               _root.prepare_for_render (draw);
+       }
+}
+
+gint64
+Canvas::get_microseconds_since_render_start () const
+{
+       gint64 timestamp = g_get_monotonic_time();
+
+       if (_last_render_start_timestamp == 0 || timestamp <= _last_render_start_timestamp) {
+               return 0;
+       }
+
+       return timestamp - _last_render_start_timestamp;
+}
+
 ostream&
 operator<< (ostream& o, Canvas& c)
 {
@@ -224,9 +261,17 @@ Canvas::item_changed (Item* item, Rect pre_change_bounding_box)
        Rect post_change_bounding_box = item->bounding_box ();
 
        if (post_change_bounding_box) {
-               if (item->item_to_window (post_change_bounding_box).intersection (window_bbox)) {
+               Rect const window_intersection =
+                   item->item_to_window (post_change_bounding_box).intersection (window_bbox);
+
+               if (window_intersection) {
                        /* request a redraw of the item's new bounding box */
                        queue_draw_item_area (item, post_change_bounding_box);
+
+                       // Allow item to do any work necessary to prepare for being rendered.
+                       item->prepare_for_render (window_intersection);
+               } else {
+                       // No intersection with visible window area
                }
        }
 }
@@ -361,15 +406,15 @@ Canvas::set_tooltip_timeout (uint32_t msecs)
 }
 
 void
-Canvas::set_background_color (Color c)
+Canvas::set_background_color (Gtkmm2ext::Color c)
 {
-        _bg_color = c;
+       _bg_color = c;
 
-        Rect r = _root.bounding_box();
+       Rect r = _root.bounding_box();
 
-        if (r) {
-                request_redraw (_root.item_to_window (r));
-        }
+       if (r) {
+               request_redraw (_root.item_to_window (r));
+       }
 }
 
 void
@@ -390,6 +435,7 @@ GtkCanvas::GtkCanvas ()
        , current_tooltip_item (0)
        , tooltip_window (0)
        , _in_dtor (false)
+       , _nsglview (0)
 {
        /* these are the events we want to know about */
        add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
@@ -397,6 +443,16 @@ GtkCanvas::GtkCanvas ()
                    Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
 }
 
+void
+GtkCanvas::use_nsglview ()
+{
+       assert (!_nsglview);
+       assert (!is_realized());
+#ifdef ARDOUR_CANVAS_NSVIEW_TAG // patched gdkquartz.h
+       _nsglview = Gtkmm2ext::nsglview_create (this);
+#endif
+}
+
 void
 GtkCanvas::pick_current_item (int state)
 {
@@ -713,6 +769,15 @@ GtkCanvas::deliver_event (GdkEvent* event)
        return false;
 }
 
+void
+GtkCanvas::item_shown_or_hidden (Item* item)
+{
+       if (item == current_tooltip_item) {
+               stop_tooltip_timeout ();
+       }
+       Canvas::item_shown_or_hidden (item);
+}
+
 /** Called when an item is being destroyed.
  *  @param item Item being destroyed.
  *  @param bounding_box Last known bounding box of the item.
@@ -755,6 +820,17 @@ GtkCanvas::item_going_away (Item* item, Rect bounding_box)
 
 }
 
+void
+GtkCanvas::on_realize ()
+{
+       Gtk::EventBox::on_realize();
+#ifdef __APPLE__
+       if (_nsglview) {
+               Gtkmm2ext::nsglview_overlay (_nsglview, get_window()->gobj());
+       }
+#endif
+}
+
 void
 GtkCanvas::on_size_allocate (Gtk::Allocation& a)
 {
@@ -771,6 +847,18 @@ GtkCanvas::on_size_allocate (Gtk::Allocation& a)
 #ifdef OPTIONAL_CAIRO_IMAGE_SURFACE
        }
 #endif
+
+#ifdef __APPLE__
+       if (_nsglview) {
+               gint xx, yy;
+               gtk_widget_translate_coordinates(
+                               GTK_WIDGET(gobj()),
+                               GTK_WIDGET(get_toplevel()->gobj()),
+                               0, 0, &xx, &yy);
+               Gtkmm2ext::nsglview_resize (_nsglview, xx, yy, a.get_width(), a.get_height());
+       }
+#endif
+
 }
 
 /** Handler for GDK expose events.
@@ -783,6 +871,15 @@ GtkCanvas::on_expose_event (GdkEventExpose* ev)
        if (_in_dtor) {
                return true;
        }
+#ifdef __APPLE__
+       if (_nsglview) {
+               return true;
+       }
+#endif
+
+#ifdef CANVAS_PROFILE
+       const int64_t start = g_get_monotonic_time ();
+#endif
 
 #ifdef OPTIONAL_CAIRO_IMAGE_SURFACE
        Cairo::RefPtr<Cairo::Context> draw_context;
@@ -800,55 +897,90 @@ GtkCanvas::on_expose_event (GdkEventExpose* ev)
        if (!canvas_image) {
                canvas_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, get_width(), get_height());
        }
-        Cairo::RefPtr<Cairo::Context> draw_context = Cairo::Context::create (canvas_image);
+       Cairo::RefPtr<Cairo::Context> draw_context = Cairo::Context::create (canvas_image);
        Cairo::RefPtr<Cairo::Context> window_context = get_window()->create_cairo_context ();
 #else
        Cairo::RefPtr<Cairo::Context> draw_context = get_window()->create_cairo_context ();
 #endif
 
-        /* draw background color */
+       draw_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+       draw_context->clip();
 
-        draw_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
-        draw_context->clip_preserve ();
-        set_source_rgba (draw_context, _bg_color);
-        draw_context->fill ();
+#ifdef __APPLE__
+       /* group calls cairo_quartz_surface_create() which
+        * effectively uses a CGBitmapContext + image-surface
+        *
+        * This avoids expensive argb32_image_mark_image() during drawing.
+        * Although the final paint() operation still takes the slow path
+        * through image_mark_image instead of ColorMaskCopyARGB888_sse :(
+        *
+        * profiling indicates a speed up of factor 2. (~ 5-10ms render time,
+        * instead of 10-20ms, which is still slow compared to XCB and win32 surfaces (~0.2 ms)
+        *
+        * Fixing this for good likely involves changes to GdkQuartzWindow, GdkQuartzView
+        */
+       draw_context->push_group ();
+#endif
 
-        /* render canvas */
-               if ( _single_exposure ) {
+       /* draw background color */
+       draw_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+       Gtkmm2ext::set_source_rgba (draw_context, _bg_color);
+       draw_context->fill ();
 
-                       render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), draw_context);
+       /* render canvas */
+       if ( _single_exposure ) {
 
-               } else {
-                       GdkRectangle* rects;
-                       gint nrects;
+               Canvas::render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), draw_context);
 
-                       gdk_region_get_rectangles (ev->region, &rects, &nrects);
-                       for (gint n = 0; n < nrects; ++n) {
-                               draw_context->set_identity_matrix();  //reset the cairo matrix, just in case someone left it transformed after drawing ( cough )
-                               render (Rect (rects[n].x, rects[n].y, rects[n].x + rects[n].width, rects[n].y + rects[n].height), draw_context);
-                       }
-                       g_free (rects);
+       } else {
+               GdkRectangle* rects;
+               gint nrects;
+
+               gdk_region_get_rectangles (ev->region, &rects, &nrects);
+               for (gint n = 0; n < nrects; ++n) {
+                       draw_context->set_identity_matrix();  //reset the cairo matrix, just in case someone left it transformed after drawing ( cough )
+                       Canvas::render (Rect (rects[n].x, rects[n].y, rects[n].x + rects[n].width, rects[n].y + rects[n].height), draw_context);
                }
+               g_free (rects);
+       }
+
+#ifdef __APPLE__
+       draw_context->pop_group_to_source ();
+       draw_context->paint ();
+#endif
 
 #ifdef OPTIONAL_CAIRO_IMAGE_SURFACE
        if (getenv("ARDOUR_IMAGE_SURFACE")) {
 #endif
 #if defined USE_CAIRO_IMAGE_SURFACE || defined OPTIONAL_CAIRO_IMAGE_SURFACE
-       /* now blit our private surface back to the GDK one */
+               /* now blit our private surface back to the GDK one */
 
-       window_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
-       window_context->clip ();
-       window_context->set_source (canvas_image, 0, 0);
-       window_context->set_operator (Cairo::OPERATOR_SOURCE);
-       window_context->paint ();
+               window_context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+               window_context->clip ();
+               window_context->set_source (canvas_image, 0, 0);
+               window_context->set_operator (Cairo::OPERATOR_SOURCE);
+               window_context->paint ();
 #endif
 #ifdef OPTIONAL_CAIRO_IMAGE_SURFACE
        }
 #endif
 
+#ifdef CANVAS_PROFILE
+       const int64_t end = g_get_monotonic_time ();
+       const int64_t elapsed = end - start;
+       printf ("GtkCanvas::on_expose_event %f ms\n", elapsed / 1000.f);
+#endif
+
        return true;
 }
 
+void
+GtkCanvas::prepare_for_render () const
+{
+       Rect window_bbox = visible_area ();
+       Canvas::prepare_for_render (window_bbox);
+}
+
 /** Handler for GDK scroll events.
  *  @param ev Event.
  *  @return true if the event was handled.
@@ -1038,6 +1170,60 @@ GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
        return true;
 }
 
+void
+GtkCanvas::on_map ()
+{
+       Gtk::EventBox::on_map();
+#ifdef __APPLE__
+       if (_nsglview) {
+               Gtkmm2ext::nsglview_set_visible (_nsglview, true);
+               Gtk::Allocation a = get_allocation();
+               gint xx, yy;
+               gtk_widget_translate_coordinates(
+                               GTK_WIDGET(gobj()),
+                               GTK_WIDGET(get_toplevel()->gobj()),
+                               0, 0, &xx, &yy);
+               Gtkmm2ext::nsglview_resize (_nsglview, xx, yy, a.get_width(), a.get_height());
+       }
+#endif
+}
+
+void
+GtkCanvas::on_unmap ()
+{
+       stop_tooltip_timeout ();
+       Gtk::EventBox::on_unmap();
+#ifdef __APPLE__
+       if (_nsglview) {
+               Gtkmm2ext::nsglview_set_visible (_nsglview, false);
+       }
+#endif
+}
+
+void
+GtkCanvas::queue_draw()
+{
+#ifdef __APPLE__
+       if (_nsglview) {
+               Gtkmm2ext::nsglview_queue_draw (_nsglview, 0, 0, get_width (), get_height ());
+               return;
+       }
+#endif
+       Gtk::Widget::queue_draw ();
+}
+
+void
+GtkCanvas::queue_draw_area (int x, int y, int width, int height)
+{
+#ifdef __APPLE__
+       if (_nsglview) {
+               Gtkmm2ext::nsglview_queue_draw (_nsglview, x, y, width, height);
+               return;
+       }
+#endif
+       Gtk::Widget::queue_draw_area (x, y, width, height);
+}
+
 /** Called to request a redraw of our canvas.
  *  @param area Area to redraw, in window coordinates.
  */
@@ -1048,19 +1234,19 @@ GtkCanvas::request_redraw (Rect const & request)
                return;
        }
 
-       Rect real_area;
-
-       Coord const w = width ();
-       Coord const h = height ();
-
        /* clamp area requested to actual visible window */
 
-       real_area.x0 = max (0.0, min (w, request.x0));
-       real_area.x1 = max (0.0, min (w, request.x1));
-       real_area.y0 = max (0.0, min (h, request.y0));
-       real_area.y1 = max (0.0, min (h, request.y1));
+       Rect real_area = request.intersection (visible_area());
+
+       if (real_area) {
+               if (real_area.width () && real_area.height ()) {
+                       // Item intersects with visible canvas area
+                       queue_draw_area (real_area.x0, real_area.y0, real_area.width(), real_area.height());
+               }
 
-       queue_draw_area (real_area.x0, real_area.y0, real_area.width(), real_area.height());
+       } else {
+               // Item does not intersect with visible canvas area
+       }
 }
 
 /** Called to request that we try to get a particular size for ourselves.
@@ -1122,7 +1308,7 @@ GtkCanvas::unfocus (Item* item)
 }
 
 /** @return The visible area of the canvas, in window coordinates */
-Rect
+ArdourCanvas::Rect
 GtkCanvas::visible_area () const
 {
        return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());