13
0

basically operational switch to canvas drawing coordinates, although text and waves don't work, and redraw areas are too small

This commit is contained in:
Paul Davis 2013-06-18 08:23:06 -04:00
parent a0c5de281a
commit 77f5f4c4bf
14 changed files with 160 additions and 149 deletions

View File

@ -65,7 +65,10 @@ Arc::render (Rect const & /*area*/, Cairo::RefPtr<Cairo::Context> 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);

View File

@ -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<Cairo::Context> 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<Rect> 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<Rect> draw = root_bbox.get().intersection (area);
boost::optional<Rect> 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<Rect> bounding_box)
bool
GtkCanvas::on_expose_event (GdkEventExpose* ev)
{
Cairo::RefPtr<Cairo::Context> 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;
}

View File

@ -79,14 +79,6 @@ public:
virtual Cairo::RefPtr<Cairo::Context> context () = 0;
std::list<Rect> 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<Rect> _renders;
bool _log_renders;
Coord _scroll_offset_x;
Coord _scroll_offset_y;

View File

@ -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<Cairo::Context>) 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 ();

View File

@ -78,8 +78,8 @@ Group::render (Rect const & area, Cairo::RefPtr<Cairo::Context> 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<Cairo::Context> 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<Rect> 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<Rect> 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)) {

View File

@ -43,10 +43,13 @@ Image::render (Rect const& area, Cairo::RefPtr<Cairo::Context> context) const
_pending->stride);
_current = _pending;
}
Rect self = item_to_window (Rect (0, 0, _width, _height));
boost::optional<Rect> 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 ();
}
}

View File

@ -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)

View File

@ -56,8 +56,10 @@ void
Line::render (Rect const & /*area*/, Cairo::RefPtr<Cairo::Context> 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 ();
}

View File

@ -73,8 +73,10 @@ LineSet::render (Rect const & area, Cairo::RefPtr<Cairo::Context> 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 ();
}
}

View File

@ -70,9 +70,11 @@ PolyItem::render_path (Rect const & /*area*/, Cairo::RefPtr<Cairo::Context> 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<Cairo::Context> 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;
}

View File

@ -37,7 +37,9 @@ Polygon::render (Rect const & area, Cairo::RefPtr<Cairo::Context> 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 ();

View File

@ -50,7 +50,7 @@ Rectangle::Rectangle (Group* parent, Rect const & rect)
}
void
Rectangle::render (Rect const & /*area*/, Cairo::RefPtr<Cairo::Context> context) const
Rectangle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> 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<Cairo::Context> 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<Rect> 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<Cairo::Context> 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 ();

View File

@ -96,6 +96,8 @@ Text::redraw (Cairo::RefPtr<Cairo::Context> 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<Cairo::Context> context) const
Text::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
{
if (_text.empty()) {
return;
@ -146,8 +148,12 @@ Text::render (Rect const & /*area*/, Cairo::RefPtr<Cairo::Context> 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 ();
}

View File

@ -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<Cairo::Context> 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<Rect> 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<CacheEntry*>::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<Cairo::Context> 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<Cairo::Context> 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<Cairo::Context> 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<Cairo::Context> 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<Cairo::Context> 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;