diff --git a/libs/canvas/arc.cc b/libs/canvas/arc.cc index c8eaf030e7..eeecd3204a 100644 --- a/libs/canvas/arc.cc +++ b/libs/canvas/arc.cc @@ -65,7 +65,10 @@ Arc::render (Rect const & /*area*/, Cairo::RefPtr context) const if (_radius <= 0.0 || _arc_degrees <= 0.0) { return; } - context->arc (_center.x, _center.y, _radius, _start_degrees * (M_PI/180.0), _arc_degrees * (M_PI/180.0)); + + Duple c = item_to_window (Duple (_center.x, _center.y)); + + context->arc (c.x, c.y, _radius, _start_degrees * (M_PI/180.0), _arc_degrees * (M_PI/180.0)); setup_fill_context (context); context->fill_preserve (); setup_outline_context (context); diff --git a/libs/canvas/canvas.cc b/libs/canvas/canvas.cc index 5987a136db..9c49eb4a87 100644 --- a/libs/canvas/canvas.cc +++ b/libs/canvas/canvas.cc @@ -38,7 +38,6 @@ using namespace ArdourCanvas; /** Construct a new Canvas */ Canvas::Canvas () : _root (this) - , _log_renders (true) , _scroll_offset_x (0) , _scroll_offset_y (0) { @@ -68,64 +67,21 @@ Canvas::render (Rect const & area, Cairo::RefPtr const & context } #endif - // checkpoint ("render", "-> render"); render_count = 0; - context->save (); - -#ifdef CANVAS_DEBUG - if (getenv ("ARDOUR_REDRAW_CANVAS")) { - /* light up the canvas to show redraws */ - context->set_source_rgba (random()%255 / 255.0, - random()%255 / 255.0, - random()%255 / 255.0, - 0.3); - context->rectangle (area.x0, area.y0, area.width(), area.height()); - context->fill (); - } -#endif - - /* clip to the requested area */ - context->rectangle (area.x0, area.y0, area.width(), area.height()); - context->clip (); - boost::optional root_bbox = _root.bounding_box(); if (!root_bbox) { /* the root has no bounding box, so there's nothing to render */ - // checkpoint ("render", "no root bbox"); - context->restore (); return; } - boost::optional draw = root_bbox.get().intersection (area); + boost::optional draw = root_bbox->intersection (area); if (draw) { /* there's a common area between the root and the requested area, so render it. */ - // checkpoint ("render", "... root"); - context->stroke (); - _root.render (*draw, context); } - - if (_log_renders) { - _renders.push_back (area); - } - - context->restore (); - -#ifdef CANVAS_DEBUG - if (getenv ("ARDOUR_HARLEQUIN_CANVAS")) { - /* light up the canvas to show redraws */ - context->set_source_rgba (random()%255 / 255.0, - random()%255 / 255.0, - random()%255 / 255.0, - 0.4); - context->rectangle (area.x0, area.y0, area.width(), area.height()); - context->fill (); - } -#endif - // checkpoint ("render", "<- render"); } ostream& @@ -502,29 +458,9 @@ GtkCanvas::item_going_away (Item* item, boost::optional bounding_box) bool GtkCanvas::on_expose_event (GdkEventExpose* ev) { - Cairo::RefPtr c = get_window()->create_cairo_context (); - - /* WINDOW CANVAS - * 0,0 _scroll_offset_x, _scroll_offset_y - */ - /* render using canvas coordinates */ - - Rect canvas_area (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height); - canvas_area = canvas_area.translate (Duple (_scroll_offset_x, _scroll_offset_y)); - - /* things are going to render to the cairo surface with canvas - * coordinates: - * - * an item at window/cairo 0,0 will have canvas_coords _scroll_offset_x,_scroll_offset_y - * - * let them render at their natural coordinates by using cairo_translate() - */ - - c->translate (-_scroll_offset_x, -_scroll_offset_y); - - render (canvas_area, c); + render (Rect (ev->area.x, ev->area.y, ev->area.x + ev->area.width, ev->area.y + ev->area.height), c); return true; } diff --git a/libs/canvas/canvas/canvas.h b/libs/canvas/canvas/canvas.h index 4967e79702..e65abf6b27 100644 --- a/libs/canvas/canvas/canvas.h +++ b/libs/canvas/canvas/canvas.h @@ -79,14 +79,6 @@ public: virtual Cairo::RefPtr context () = 0; - std::list const & renders () const { - return _renders; - } - - void set_log_renders (bool log) { - _log_renders = log; - } - Rect canvas_to_window (Rect const&) const; Rect window_to_canvas (Rect const&) const; Duple canvas_to_window (Duple const&) const; @@ -117,9 +109,6 @@ protected: /** our root group */ RootGroup _root; - mutable std::list _renders; - bool _log_renders; - Coord _scroll_offset_x; Coord _scroll_offset_y; diff --git a/libs/canvas/canvas/item.h b/libs/canvas/canvas/item.h index b4ea5671c7..e856c7e149 100644 --- a/libs/canvas/canvas/item.h +++ b/libs/canvas/canvas/item.h @@ -57,7 +57,15 @@ public: virtual ~Item (); /** Render this item to a Cairo context. - * @param area Area to draw in this item's coordinates. + * @param area Area to draw, in **window** coordinates + * + * Items must convert their own coordinates into window coordinates + * because Cairo is limited to a fixed point coordinate space that + * does not extend as far as the Ardour timeline. All rendering must + * be done using coordinates that do not exceed the (rough) limits + * of the canvas' window, to avoid odd errors within Cairo as it + * converts doubles into its fixed point format and then tesselates + * the results. */ virtual void render (Rect const & area, Cairo::RefPtr) const = 0; @@ -105,8 +113,13 @@ public: Duple canvas_to_item (Duple const &) const; void item_to_canvas (Coord &, Coord &) const; Rect item_to_canvas (Rect const &) const; + Rect canvas_to_item (Rect const &) const; Duple item_to_canvas (Duple const &) const; + Duple item_to_window (Duple const&) const; + Duple window_to_item (Duple const&) const; + Rect item_to_window (Rect const&) const; + void raise_to_top (); void raise (int); void lower_to_bottom (); diff --git a/libs/canvas/group.cc b/libs/canvas/group.cc index 028599148b..364d9fe583 100644 --- a/libs/canvas/group.cc +++ b/libs/canvas/group.cc @@ -78,8 +78,8 @@ Group::render (Rect const & area, Cairo::RefPtr context) const #ifdef CANVAS_DEBUG if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) { - cerr << string_compose ("%1GROUP %2 render %3 items out of %4\n", - _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size()); + cerr << string_compose ("%1GROUP %2 render %5 %3 items out of %4\n", + _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size(), area); } #endif @@ -105,25 +105,18 @@ Group::render (Rect const & area, Cairo::RefPtr context) const continue; } - /* convert the render area to our child's coordinates */ - Rect const item_area = (*i)->parent_to_item (area); - - /* intersect the child's render area with the child's bounding box */ - boost::optional r = item_bbox.get().intersection (item_area); - - if (r) { - /* render the intersection */ - context->save (); - context->translate ((*i)->position().x, (*i)->position().y); + Rect item = (*i)->item_to_window (item_bbox.get()); + boost::optional draw = item.intersection (area); + + if (draw) { #ifdef CANVAS_DEBUG if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) { cerr << string_compose ("%1render %2 %3\n", _canvas->render_indent(), (*i)->whatami(), (*i)->name); } #endif - (*i)->render (r.get(), context); + (*i)->render (draw.get(), context); ++render_count; - context->restore (); } else { #ifdef CANVAS_DEBUG if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) { diff --git a/libs/canvas/image.cc b/libs/canvas/image.cc index b13859aeda..4a9bcdf8f1 100644 --- a/libs/canvas/image.cc +++ b/libs/canvas/image.cc @@ -43,10 +43,13 @@ Image::render (Rect const& area, Cairo::RefPtr context) const _pending->stride); _current = _pending; } + + Rect self = item_to_window (Rect (0, 0, _width, _height)); + boost::optional draw = self.intersection (area); - if (_surface) { + if (_surface && draw) { context->set_source (_surface, 0, 0); - context->rectangle (area.x0, area.y0, area.width(), area.height()); + context->rectangle (draw->x0, draw->y0, draw->width(), draw->height()); context->fill (); } } diff --git a/libs/canvas/item.cc b/libs/canvas/item.cc index 569d03f81f..dca3064bce 100644 --- a/libs/canvas/item.cc +++ b/libs/canvas/item.cc @@ -127,6 +127,20 @@ Item::canvas_to_item (ArdourCanvas::Duple const & d) const return d.translate (offset); } +ArdourCanvas::Rect +Item::canvas_to_item (ArdourCanvas::Rect const & d) const +{ + Item const * i = this; + Duple offset; + + while (i) { + offset = offset.translate (-(i->position())); + i = i->parent(); + } + + return d.translate (offset); +} + void Item::item_to_canvas (Coord& x, Coord& y) const { @@ -145,6 +159,24 @@ Item::canvas_to_item (Coord& x, Coord& y) const y = d.y; } +Duple +Item::item_to_window (ArdourCanvas::Duple const & d) const +{ + return _canvas->canvas_to_window (item_to_canvas (d)); +} + +Duple +Item::window_to_item (ArdourCanvas::Duple const & d) const +{ + return _canvas->window_to_canvas (canvas_to_item (d)); +} + +Rect +Item::item_to_window (ArdourCanvas::Rect const & r) const +{ + return _canvas->canvas_to_window (item_to_canvas (r)); +} + /** Set the position of this item in the parent's coordinates */ void Item::set_position (Duple p) diff --git a/libs/canvas/line.cc b/libs/canvas/line.cc index 7c5852f4b2..b6a802b8df 100644 --- a/libs/canvas/line.cc +++ b/libs/canvas/line.cc @@ -56,8 +56,10 @@ void Line::render (Rect const & /*area*/, Cairo::RefPtr context) const { setup_outline_context (context); - context->move_to (_points[0].x, _points[0].y); - context->line_to (_points[1].x, _points[1].y); + Duple p0 = item_to_window (Duple (_points[0].x, _points[0].y)); + Duple p1 = item_to_window (Duple (_points[1].x, _points[1].y)); + context->move_to (p0.x, p0.y); + context->line_to (p1.x, p1.y); context->stroke (); } diff --git a/libs/canvas/line_set.cc b/libs/canvas/line_set.cc index c3ee5d44b9..5aefe1bf47 100644 --- a/libs/canvas/line_set.cc +++ b/libs/canvas/line_set.cc @@ -73,8 +73,10 @@ LineSet::render (Rect const & area, Cairo::RefPtr context) const } else if (i->y > area.y0) { set_source_rgba (context, i->color); context->set_line_width (i->width); - context->move_to (area.x0, i->y); - context->line_to (area.x1, i->y); + Duple p0 = item_to_window (Duple (area.x0, i->y)); + Duple p1 = item_to_window (Duple (area.x1, i->y)); + context->move_to (p0.x, p0.y); + context->line_to (p1.x, p1.y); context->stroke (); } } diff --git a/libs/canvas/poly_item.cc b/libs/canvas/poly_item.cc index 6c9f0c98ab..55ebb46bd3 100644 --- a/libs/canvas/poly_item.cc +++ b/libs/canvas/poly_item.cc @@ -70,9 +70,11 @@ PolyItem::render_path (Rect const & /*area*/, Cairo::RefPtr cont bool done_first = false; for (Points::const_iterator i = _points.begin(); i != _points.end(); ++i) { if (done_first) { - context->line_to (i->x, i->y); + Duple c = item_to_window (Duple (i->x, i->y)); + context->line_to (c.x, c.y); } else { - context->move_to (i->x, i->y); + Duple c = item_to_window (Duple (i->x, i->y)); + context->move_to (c.x, c.y); done_first = true; } } @@ -95,15 +97,18 @@ PolyItem::render_curve (Rect const & area, Cairo::RefPtr context if (done_first) { - context->curve_to (cp1->x, cp1->y, - cp2->x, cp2->y, - i->x, i->y); + Duple c1 = item_to_window (Duple (cp1->x, cp1->y)); + Duple c2 = item_to_window (Duple (cp2->x, cp2->y)); + Duple c3 = item_to_window (Duple (i->x, i->y)); + + context->curve_to (c1.x, c1.y, c2.x, c2.y, c3.x, c3.y); cp1++; cp2++; } else { + Duple c = item_to_window (Duple (i->x, i->y)); context->move_to (i->x, i->y); done_first = true; } diff --git a/libs/canvas/polygon.cc b/libs/canvas/polygon.cc index 43d7c60cf4..1103cedbee 100644 --- a/libs/canvas/polygon.cc +++ b/libs/canvas/polygon.cc @@ -37,7 +37,9 @@ Polygon::render (Rect const & area, Cairo::RefPtr context) const render_path (area, context); if (!_points.empty ()) { - context->move_to (_points.front().x, _points.front().y); + /* close path */ + Duple p = item_to_window (Duple (_points.front().x, _points.front().y)); + context->move_to (p.x, p.y); } context->stroke_preserve (); diff --git a/libs/canvas/rectangle.cc b/libs/canvas/rectangle.cc index bb1198c1bb..7abf13216b 100644 --- a/libs/canvas/rectangle.cc +++ b/libs/canvas/rectangle.cc @@ -50,7 +50,7 @@ Rectangle::Rectangle (Group* parent, Rect const & rect) } void -Rectangle::render (Rect const & /*area*/, Cairo::RefPtr context) const +Rectangle::render (Rect const & area, Cairo::RefPtr context) const { /* Cairo goes a little (!) wrong when asked to fill/stroke rectangles that * extend way beyond the surface boundaries. To avoid this issue, @@ -58,17 +58,13 @@ Rectangle::render (Rect const & /*area*/, Cairo::RefPtr context) * canvas, converting to item-space coordinates, of course. */ - Rect plot = _rect; - Rect visible = _canvas->visible_area(); - Duple visible_end = canvas_to_item (Duple (visible.x1, visible.y1)); + Rect self = item_to_window (_rect); + boost::optional draw = self.intersection (area); - plot.x1 = min (plot.x1, visible_end.x); - plot.y1 = min (plot.y1, visible_end.y); - - if (_fill) { + if (_fill && draw) { setup_fill_context (context); - cerr << "Fill rect: " << plot << endl; - context->rectangle (plot.x0, plot.y0, plot.width(), plot.height()); + + context->rectangle (draw->x0, draw->y0, draw->width(), draw->height()); if (!_outline) { context->fill (); @@ -100,30 +96,30 @@ Rectangle::render (Rect const & /*area*/, Cairo::RefPtr context) */ if (!_fill) { - context->rectangle (plot.x0, plot.y0, plot.width(), plot.height()); + context->rectangle (draw->x0, draw->y0, draw->width(), draw->height()); context->stroke (); } } else { if (_outline_what & LEFT) { - context->move_to (plot.x0, plot.y0); - context->line_to (plot.x0, plot.y1); + context->move_to (draw->x0, draw->y0); + context->line_to (draw->x0, draw->y1); } if (_outline_what & BOTTOM) { - context->move_to (plot.x0, plot.y1); - context->line_to (plot.x1, plot.y1); + context->move_to (draw->x0, draw->y1); + context->line_to (draw->x1, draw->y1); } if (_outline_what & RIGHT) { - context->move_to (plot.x1, plot.y0); - context->line_to (plot.x1, plot.y1); + context->move_to (draw->x1, draw->y0); + context->line_to (draw->x1, draw->y1); } if (_outline_what & TOP) { - context->move_to (plot.x0, plot.y0); - context->line_to (plot.x1, plot.y0); + context->move_to (draw->x0, draw->y0); + context->line_to (draw->x1, draw->y0); } context->stroke (); diff --git a/libs/canvas/text.cc b/libs/canvas/text.cc index c0bac2f7ee..e4575de3b5 100644 --- a/libs/canvas/text.cc +++ b/libs/canvas/text.cc @@ -96,6 +96,8 @@ Text::redraw (Cairo::RefPtr context) const * ::render */ + cerr << "rendered \"" << layout->get_text() << "\" into image\n"; + _need_redraw = false; } @@ -136,7 +138,7 @@ Text::compute_bounding_box () const } void -Text::render (Rect const & /*area*/, Cairo::RefPtr context) const +Text::render (Rect const & area, Cairo::RefPtr context) const { if (_text.empty()) { return; @@ -146,8 +148,12 @@ Text::render (Rect const & /*area*/, Cairo::RefPtr context) cons redraw (context); } + + Rect self = item_to_window (Rect (0, 0, min (_clamped_width, _width), _height)); + cerr << "Draw \"" << _text << "\" @ " << self.x0 << ", " << self.y0 << ' ' << self.width() << " x " << self.height() << endl; + context->rectangle (self.x0, self.y0, self.width(), self.height()); context->set_source (_image, 0, 0); - context->rectangle (0, 0, min (_clamped_width, _width), _height); + //context->set_source_rgb (0.3, 0.4, 0.02); context->fill (); } diff --git a/libs/canvas/wave_view.cc b/libs/canvas/wave_view.cc index 4535b11983..1edbf89c87 100644 --- a/libs/canvas/wave_view.cc +++ b/libs/canvas/wave_view.cc @@ -133,13 +133,13 @@ WaveView::set_samples_per_pixel (double samples_per_pixel) static inline double to_src_sample_offset (frameoffset_t src_sample_start, double pixel_offset, double spp) { - return src_sample_start + (pixel_offset * spp); + return llrintf (src_sample_start + (pixel_offset * spp)); } static inline double to_pixel_offset (frameoffset_t src_sample_start, double sample_offset, double spp) { - return (sample_offset - src_sample_start) / spp; + return llrintf ((sample_offset - src_sample_start) / spp); } void @@ -151,32 +151,43 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons return; } - /* These are all pixel (integer) coordinates from the left hand edge of - * the waveview. - */ - - double start = area.x0; - double const end = area.x1; - double const rend = _region->length() / _samples_per_pixel; + Rect self = item_to_window (Rect (0.0, 0.0, _region->length() / _samples_per_pixel, _height)); + boost::optional draw = self.intersection (area); + + context->rectangle (self.x0, self.y0, self.width(), self.height()); + context->set_source_rgb (1.0, 0.0, 0.0); + context->stroke (); + + if (!draw) { + return; + } + + /* pixel coordinates */ + + double start = draw->x0; + double const end = draw->x1; list::iterator cache = _cache.begin (); -#if 0 - cerr << name << " cache contains " << _cache.size() << endl; + + cerr << name << " draw " << area << "self = " << self << "\n\twill use " << draw.get() << endl; +#if 1 + cerr << " Cache contains " << _cache.size() << endl; while (cache != _cache.end()) { - cerr << "\tsamples " << (*cache)->start() << " .. " << (*cache)->end() - << " pixels " + cerr << "\tsample span " << (*cache)->start() << " .. " << (*cache)->end() + << " pixel span " << to_pixel_offset (_region_start, (*cache)->start(), _samples_per_pixel) << " .. " << to_pixel_offset (_region_start, (*cache)->end(), _samples_per_pixel) << endl; ++cache; } - cache = _cache.begin(); #endif while ((end - start) > 1.0) { + cerr << "***** RANGE = " << start << " .. " << end << " = " << end - start << endl; + frameoffset_t start_sample_offset = to_src_sample_offset (_region_start, start, _samples_per_pixel); /* Step through cache entries that end at or before our current position */ @@ -197,6 +208,8 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons if (cache == _cache.end ()) { + cerr << "Nothing in cache spans\n"; + /* Case 1: we have run out of cache entries, so make a new one for the whole required area and put it in the list. @@ -209,7 +222,12 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons the region actually is, so clamp with that too. */ + double const rend = _region->length() / _samples_per_pixel; double const endpoint = min (rend, max (end, start + _canvas->visible_area().width())); + + cerr << "New cache entry for " << start_sample_offset << " .. " << to_src_sample_offset (_region_start, endpoint, _samples_per_pixel) + << endl; + CacheEntry* c = new CacheEntry (this, start_sample_offset, to_src_sample_offset (_region_start, endpoint, _samples_per_pixel), @@ -228,20 +246,24 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons * or the end of the render area, whichever comes first. */ - double end_pixel = min (rend, end); - double end_sample_offset = to_src_sample_offset (_region_start, end_pixel, _samples_per_pixel); + double end_pixel; + double end_sample_offset; int npeaks; if (end_sample_offset < (*cache)->start()) { - npeaks = end_pixel - start; - assert (npeaks > 0); + double const rend = _region->length() / _samples_per_pixel; + end_sample_offset = to_src_sample_offset (_region_start, end_pixel, _samples_per_pixel); + end_pixel = min (rend, end); } else { end_sample_offset = (*cache)->start(); end_pixel = to_pixel_offset (_region_start, end_sample_offset, _samples_per_pixel); - npeaks = end_pixel - npeaks; - assert (npeaks > 0); } + npeaks = end_pixel - start; + assert (npeaks > 0); + + cerr << "New fill-in cache entry for " << start_sample_offset << " .. " << end_sample_offset << endl; + CacheEntry* c = new CacheEntry (this, start_sample_offset, end_sample_offset, @@ -257,14 +279,18 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons we have left, so render it. */ + cerr << "found suitable cache entry\n"; + image = *cache; ++cache; } + + double this_end = min (end, to_pixel_offset (_region_start, image->end (), _samples_per_pixel)); double const image_origin = to_pixel_offset (_region_start, image->start(), _samples_per_pixel); -#if 0 +#if 1 cerr << "\t\tDraw image between " << start << " .. " @@ -280,8 +306,11 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons << endl; #endif - context->rectangle (start, area.y0, this_end - start, area.height()); - context->set_source (image->image(), image_origin, 0); + cerr << "Fill rect " << draw->x0 << ", " << self.y0 << ' ' << draw->width() << " x " << draw->height() << endl; + + context->rectangle (start, draw->y0, this_end - start, _height); + // context->set_source (image->image(), image_origin, self.y0 - draw->y0); + context->set_source_rgb (0.0, 0.0, 1.0); context->fill (); start = this_end;