#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;
/** 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 ();
}
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;
}
+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)
{
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
}
}
}
}
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
, 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 |
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)
{
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.
}
+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)
{
#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.
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;
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.
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.
*/
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.
}
/** @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 ());