diff --git a/libs/canvas/canvas/wave_view.h b/libs/canvas/canvas/wave_view.h index 9a4cbafa7c..9f87855e54 100644 --- a/libs/canvas/canvas/wave_view.h +++ b/libs/canvas/canvas/wave_view.h @@ -143,7 +143,8 @@ class LIBCANVAS_API WaveViewCache Coord height, float amplitude, Color fill_color, - double samples_per_pixel); + double samples_per_pixel, + bool& full_image); private: /* an unsorted, unindexd collection of cache entries associated with @@ -250,6 +251,8 @@ public: double gradient_depth() const { return _gradient_depth; } void set_shape (Shape); + void set_always_get_image_in_thread (bool yn); + /* currently missing because we don't need them (yet): set_shape_independent(); set_logscaled_independent() @@ -330,6 +333,12 @@ public: */ mutable bool get_image_in_thread; + /** If true, calls to get_image() will render a missing wave image + in the calling thread. Set true for waveviews we expect to + keep updating (e.g. while recording) + */ + bool always_get_image_in_thread; + /** Set to true by render(). Used so that we know if the wave view * has actually been displayed on screen. ::set_height() when this * is true does not use get_image_in_thread, because it implies @@ -351,8 +360,8 @@ public: void handle_visual_property_change (); void handle_clip_level_change (); - boost::shared_ptr get_image (framepos_t start, framepos_t end) const; - boost::shared_ptr get_image_from_cache (framepos_t start, framepos_t end) const; + boost::shared_ptr get_image (framepos_t start, framepos_t end, bool& full_image) const; + boost::shared_ptr get_image_from_cache (framepos_t start, framepos_t end, bool& full_image) const; ArdourCanvas::Coord y_extent (double, bool) const; void draw_image (Cairo::RefPtr&, ARDOUR::PeakData*, int n_peaks, boost::shared_ptr) const; diff --git a/libs/canvas/wave_view.cc b/libs/canvas/wave_view.cc index a756cfe430..fc7a7e868f 100644 --- a/libs/canvas/wave_view.cc +++ b/libs/canvas/wave_view.cc @@ -87,6 +87,7 @@ WaveView::WaveView (Canvas* c, boost::shared_ptr region) , _start_shift (0.0) , _region_start (region->start()) , get_image_in_thread (false) + , always_get_image_in_thread (false) , rendered (false) { VisualPropertiesChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_visual_property_change, this)); @@ -114,6 +115,7 @@ WaveView::WaveView (Item* parent, boost::shared_ptr region) , _region_amplitude (region->scale_amplitude ()) , _region_start (region->start()) , get_image_in_thread (false) + , always_get_image_in_thread (false) , rendered (false) { VisualPropertiesChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_visual_property_change, this)); @@ -139,6 +141,12 @@ WaveView::image_ready () redraw (); } +void +WaveView::set_always_get_image_in_thread (bool yn) +{ + always_get_image_in_thread = yn; +} + void WaveView::handle_visual_property_change () { @@ -714,9 +722,11 @@ WaveView::cache_request_result (boost::shared_ptr req) co } boost::shared_ptr -WaveView::get_image (framepos_t start, framepos_t end) const +WaveView::get_image (framepos_t start, framepos_t end, bool& full_image) const { boost::shared_ptr ret; + + full_image = true; /* this is called from a ::render() call, when we need an image to draw with. @@ -759,13 +769,13 @@ WaveView::get_image (framepos_t start, framepos_t end) const /* no current image draw request, so look in the cache */ - ret = get_image_from_cache (start, end); + ret = get_image_from_cache (start, end, full_image); } - if (!ret) { + if (!ret || !full_image) { - if (get_image_in_thread) { + if (get_image_in_thread || always_get_image_in_thread) { boost::shared_ptr req (new WaveViewThreadRequest); @@ -799,19 +809,18 @@ WaveView::get_image (framepos_t start, framepos_t end) const } } - return ret; } boost::shared_ptr -WaveView::get_image_from_cache (framepos_t start, framepos_t end) const +WaveView::get_image_from_cache (framepos_t start, framepos_t end, bool& full) const { if (!images) { return boost::shared_ptr(); } return images->lookup_image (_region->audio_source (_channel), start, end, _channel, - _height, _region_amplitude, _fill_color, _samples_per_pixel); + _height, _region_amplitude, _fill_color, _samples_per_pixel, full); } void @@ -996,7 +1005,8 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons // cerr << debug_name() << " will need image spanning " << sample_start << " .. " << sample_end << " region spans " << _region_start << " .. " << region_end() << endl; double image_offset; - + boost::shared_ptr image_to_draw; + if (_current_image) { /* check it covers the right sample range */ @@ -1007,26 +1017,37 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons } else { /* timestamp our continuing use of this image/cache entry */ images->use (_region->audio_source (_channel), _current_image); + image_to_draw = _current_image; } } - if (!_current_image) { + if (!image_to_draw) { /* look it up */ - - _current_image = get_image (sample_start, sample_end); - if (!_current_image) { + bool full_image; + image_to_draw = get_image (sample_start, sample_end, full_image); + + if (!image_to_draw) { /* image not currently available. A redraw will be scheduled when it is ready. */ return; } + + if (full_image) { + /* found an image that covers our entire sample range, + * so keep a reference to it. + */ + _current_image = image_to_draw; + } } - /* fix up offset: returned value is the first sample of the returned image */ + /* compute the first pixel of the image that should be used when we + * render the specified range. + */ - image_offset = (_current_image->start - _region_start) / _samples_per_pixel; + image_offset = (image_to_draw->start - _region_start) / _samples_per_pixel; // cerr << "Offset into image to place at zero: " << image_offset << endl; @@ -1040,7 +1061,20 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons //image_offset += _start_shift; } - context->rectangle (draw_start, draw.y0, draw_end - draw_start, draw.height()); + /* the image may only be a best-effort ... it may not span the entire + * range requested, though it is guaranteed to cover the start. So + * determine how many pixels we can actually draw. + */ + + double draw_width; + + if (image_to_draw != _current_image) { + draw_width = min ((double) image_to_draw->image->get_width() - image_offset, (draw_end - draw_start)); + } else { + draw_width = draw_end - draw_start; + } + + context->rectangle (draw_start, draw.y0, draw_width, draw.height()); /* round image origin position to an exact pixel in device space to * avoid blurring @@ -1053,7 +1087,13 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons y = round (y); context->device_to_user (x, y); - context->set_source (_current_image->image, x, y); + /* the coordinates specify where in "user coordinates" (i.e. what we + * generally call "canvas coordinates" in this code) the image origin + * will appear. So specifying (10,10) will put the upper left corner of + * the image at (10,10) in user space. + */ + + context->set_source (image_to_draw->image, x, y); context->fill (); } @@ -1419,7 +1459,8 @@ WaveViewCache::lookup_image (boost::shared_ptr src, Coord height, float amplitude, Color fill_color, - double samples_per_pixel) + double samples_per_pixel, + bool& full_coverage) { ImageCache::iterator x; @@ -1429,7 +1470,9 @@ WaveViewCache::lookup_image (boost::shared_ptr src, } CacheLine& caches = x->second; - + boost::shared_ptr best_partial; + framecnt_t max_coverage = 0; + /* Find a suitable ImageSurface, if it exists. */ @@ -1448,8 +1491,27 @@ WaveViewCache::lookup_image (boost::shared_ptr src, if (end <= e->end && start >= e->start) { /* found an image that covers the range we need */ use (src, e); + full_coverage = true; return e; } + + if (start >= e->start) { + /* found an image that covers the start, but not the + * end. See if it is longer than any other similar + * partial image that we've found so far. + */ + + if ((e->end - e->start) > max_coverage) { + best_partial = e; + max_coverage = e->end - e->start; + } + } + } + + if (best_partial) { + use (src, best_partial); + full_coverage = false; + return best_partial; } return boost::shared_ptr ();