Add an optional ArdourCanvas::Item::prepare_for_render interface
authorTim Mayberry <mojofunk@gmail.com>
Sat, 1 Apr 2017 13:02:49 +0000 (23:02 +1000)
committerTim Mayberry <mojofunk@gmail.com>
Sun, 25 Jun 2017 22:40:47 +0000 (08:40 +1000)
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
libs/canvas/canvas/canvas.h
libs/canvas/canvas/container.h
libs/canvas/canvas/item.h
libs/canvas/container.cc
libs/canvas/item.cc

index be61b6cb84864aa9fed9db63ba16cd2a07dc0f65..baa10f9cf7740f4e6f901b1c7d7bda693afeb887 100644 (file)
@@ -144,6 +144,22 @@ 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);
+       }
+}
+
 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.
index 2f506fc3c0f625bdc506869423102e4b4f54ecc8..cb4f6dd70ce4ce02a5cafa840eb756a45ccb37a6 100644 (file)
@@ -87,6 +87,8 @@ public:
 
        void render (Rect const &, Cairo::RefPtr<Cairo::Context> 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:
index f95f2f9e2bd5aa4f07dad149b48ac38fd509cb10..4f2fad8f60209d30bb78461a8884e32779906ef5 100644 (file)
@@ -53,6 +53,12 @@ public:
         *  (just call Item::render_children()). It can be overridden as necessary.
         */
        void render (Rect const & area, Cairo::RefPtr<Cairo::Context> 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;
 };
 
 }
index 02f84a62ee355bad7b5d2dac9e7d92ffca6ab378..4088011a01c44648bafd95c85ed365e3fbc8718a 100644 (file)
@@ -75,6 +75,13 @@ public:
         */
        virtual void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) 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<Cairo::Context> context) const;
+       void prepare_for_render_children (Rect const & area) const;
 
        Duple scroll_offset() const;
        Duple position_offset() const;
index 78598d51187dc81b229a9f1c132c8def9f3f9869..71085a8257828d1ae0e11bfd8997700242bddadf 100644 (file)
@@ -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<Cairo::Context> context) const
 {
index 589f1214b7d4f2b96b01129942a958548b08bdb9..98fe6362dba6298711206ee0bbb7a930f83ea12f 100644 (file)
@@ -830,6 +830,43 @@ Item::render_children (Rect const & area, Cairo::RefPtr<Cairo::Context> context)
        --render_depth;
 }
 
+void
+Item::prepare_for_render_children (Rect const & area) const
+{
+       if (_items.empty()) {
+               return;
+       }
+
+       ensure_lut ();
+       std::vector<Item*> items = _lut->get (area);
+
+       for (std::vector<Item*>::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
 {