From c4e31fc32273a84c0436f32fce742ae21718b03c Mon Sep 17 00:00:00 2001 From: Tim Mayberry Date: Sat, 1 Apr 2017 23:02:49 +1000 Subject: [PATCH] Add an optional ArdourCanvas::Item::prepare_for_render interface Called when an item has requested a redraw and intersects with visible canvas area. Also add Canvas::prepare_for_render that will call Item::prepare_for_render for items visible on the canvas. --- libs/canvas/canvas.cc | 33 +++++++++++++++++++++++++++++- libs/canvas/canvas/canvas.h | 4 ++++ libs/canvas/canvas/container.h | 6 ++++++ libs/canvas/canvas/item.h | 8 ++++++++ libs/canvas/container.cc | 6 ++++++ libs/canvas/item.cc | 37 ++++++++++++++++++++++++++++++++++ 6 files changed, 93 insertions(+), 1 deletion(-) diff --git a/libs/canvas/canvas.cc b/libs/canvas/canvas.cc index be61b6cb84..baa10f9cf7 100644 --- a/libs/canvas/canvas.cc +++ b/libs/canvas/canvas.cc @@ -144,6 +144,22 @@ Canvas::render (Rect const & area, Cairo::RefPtr 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); + } +} + ostream& operator<< (ostream& o, Canvas& c) { @@ -231,9 +247,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 } } } @@ -937,6 +961,13 @@ GtkCanvas::on_expose_event (GdkEventExpose* ev) 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. diff --git a/libs/canvas/canvas/canvas.h b/libs/canvas/canvas/canvas.h index 2f506fc3c0..cb4f6dd70c 100644 --- a/libs/canvas/canvas/canvas.h +++ b/libs/canvas/canvas/canvas.h @@ -87,6 +87,8 @@ public: void render (Rect const &, Cairo::RefPtr const &) const; + void prepare_for_render (Rect const &) const; + /** @return root group */ Item* root () { return &_root; @@ -214,6 +216,8 @@ public: Canvas::render (rect, ctx); } + void prepare_for_render () const; + uint32_t background_color() { return Canvas::background_color (); } protected: diff --git a/libs/canvas/canvas/container.h b/libs/canvas/canvas/container.h index f95f2f9e2b..4f2fad8f60 100644 --- a/libs/canvas/canvas/container.h +++ b/libs/canvas/canvas/container.h @@ -53,6 +53,12 @@ public: * (just call Item::render_children()). It can be overridden as necessary. */ void render (Rect const & area, Cairo::RefPtr context) const; + + /** The prepare_for_render() method is likely to be identical in all + * containers (just call Item::prepare_for_render_children()). It can be + * overridden as necessary. + */ + void prepare_for_render (Rect const & area) const; }; } diff --git a/libs/canvas/canvas/item.h b/libs/canvas/canvas/item.h index 02f84a62ee..4088011a01 100644 --- a/libs/canvas/canvas/item.h +++ b/libs/canvas/canvas/item.h @@ -75,6 +75,13 @@ public: */ virtual void render (Rect const & area, Cairo::RefPtr) const = 0; + /** Item has changed will be rendered in next render pass so give item a + * chance to perhaps schedule work in another thread etc. + * + * @param area Area to draw, in **window** coordinates + */ + virtual void prepare_for_render (Rect const & area) const { } + /** Adds one or more items to the vector @param items based on their * covering @param point which is in **window** coordinates * @@ -309,6 +316,7 @@ protected: void add_child_bounding_boxes (bool include_hidden = false) const; void render_children (Rect const & area, Cairo::RefPtr context) const; + void prepare_for_render_children (Rect const & area) const; Duple scroll_offset() const; Duple position_offset() const; diff --git a/libs/canvas/container.cc b/libs/canvas/container.cc index 78598d5118..71085a8257 100644 --- a/libs/canvas/container.cc +++ b/libs/canvas/container.cc @@ -37,6 +37,12 @@ Container::Container (Item* parent, Duple const & p) { } +void +Container::prepare_for_render (Rect const & area) const +{ + Item::prepare_for_render_children (area); +} + void Container::render (Rect const & area, Cairo::RefPtr context) const { diff --git a/libs/canvas/item.cc b/libs/canvas/item.cc index 589f1214b7..98fe6362db 100644 --- a/libs/canvas/item.cc +++ b/libs/canvas/item.cc @@ -830,6 +830,43 @@ Item::render_children (Rect const & area, Cairo::RefPtr context) --render_depth; } +void +Item::prepare_for_render_children (Rect const & area) const +{ + if (_items.empty()) { + return; + } + + ensure_lut (); + std::vector items = _lut->get (area); + + for (std::vector::const_iterator i = items.begin(); i != items.end(); ++i) { + + if (!(*i)->visible ()) { + continue; + } + + Rect item_bbox = (*i)->bounding_box (); + + if (!item_bbox) { + continue; + } + + Rect item = (*i)->item_to_window (item_bbox, false); + Rect d = item.intersection (area); + + if (d) { + Rect draw = d; + if (draw.width() && draw.height()) { + (*i)->prepare_for_render (area); + } + + } else { + // Item does not intersect with visible canvas area + } + } +} + void Item::add_child_bounding_boxes (bool include_hidden) const {