+
+ if (!ret || !full_image) {
+
+#ifndef ENABLE_THREADED_WAVEFORM_RENDERING
+ if (1)
+#else
+ if ((rendered && get_image_in_thread) || always_get_image_in_thread)
+#endif
+ {
+
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1: generating image in caller thread\n", name));
+
+ boost::shared_ptr<WaveViewThreadRequest> req (new WaveViewThreadRequest);
+
+ req->type = WaveViewThreadRequest::Draw;
+ req->start = start;
+ req->end = end;
+ req->samples_per_pixel = _samples_per_pixel;
+ req->region = _region; /* weak ptr, to avoid storing a reference in the request queue */
+ req->channel = _channel;
+ req->height = _height;
+ req->fill_color = _fill_color;
+ req->amplitude = _region_amplitude * _amplitude_above_axis;
+ req->width = desired_image_width ();
+
+ /* draw image in this (the GUI thread) */
+
+ generate_image (req, false);
+
+ /* cache the result */
+
+ ret = cache_request_result (req);
+
+ /* reset this so that future missing images are
+ * generated in a a worker thread.
+ */
+
+ get_image_in_thread = false;
+
+ } else {
+ queue_get_image (_region, start, end);
+ }
+ }
+
+ if (ret) {
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 got an image from %2 .. %3 (full ? %4)\n", name, ret->start, ret->end, full_image));
+ } else {
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 no useful image available\n", name));
+ }
+
+ return ret;
+}
+
+boost::shared_ptr<WaveViewCache::Entry>
+WaveView::get_image_from_cache (framepos_t start, framepos_t end, bool& full) const
+{
+ if (!images) {
+ return boost::shared_ptr<WaveViewCache::Entry>();
+ }
+
+ return images->lookup_image (_region->audio_source (_channel), start, end, _channel,
+ _height, _region_amplitude * _amplitude_above_axis, _fill_color, _samples_per_pixel, full);
+}
+
+framecnt_t
+WaveView::desired_image_width () const
+{
+ /* compute how wide the image should be, in samples.
+ *
+ * We want at least 1 canvas width's worth, but if that
+ * represents less than 1/10th of a second, use 1/10th of
+ * a second instead.
+ */
+
+ framecnt_t canvas_width_samples = _canvas->visible_area().width() * _samples_per_pixel;
+ const framecnt_t one_tenth_of_second = _region->session().frame_rate() / 10;
+
+ if (canvas_width_samples > one_tenth_of_second) {
+ return canvas_width_samples;
+ }
+
+ return one_tenth_of_second;
+}
+
+void
+WaveView::queue_get_image (boost::shared_ptr<const ARDOUR::Region> region, framepos_t start, framepos_t end) const
+{
+ boost::shared_ptr<WaveViewThreadRequest> req (new WaveViewThreadRequest);
+
+ req->type = WaveViewThreadRequest::Draw;
+ req->start = start;
+ req->end = end;
+ req->samples_per_pixel = _samples_per_pixel;
+ req->region = _region; /* weak ptr, to avoid storing a reference in the request queue */
+ req->channel = _channel;
+ req->height = _height;
+ req->fill_color = _fill_color;
+ req->amplitude = _region_amplitude * _amplitude_above_axis;
+ req->width = desired_image_width ();
+
+ if (current_request) {
+ /* this will stop rendering in progress (which might otherwise
+ be long lived) for any current request.
+ */
+ Glib::Threads::Mutex::Lock lm (request_queue_lock);
+ if (current_request) {
+ current_request->cancel ();
+ }
+ }
+
+ start_drawing_thread ();
+
+ /* swap requests (protected by lock) */
+
+ {
+ Glib::Threads::Mutex::Lock lm (request_queue_lock);
+ current_request = req;
+
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("%1 now has current request %2\n", this, req));
+
+ if (request_queue.insert (this).second) {
+ /* this waveview was not already in the request queue, make sure we wake
+ the rendering thread in case it is asleep.
+ */
+ request_cond.signal ();
+ }
+ }
+}
+
+void
+WaveView::generate_image (boost::shared_ptr<WaveViewThreadRequest> req, bool in_render_thread) const
+{
+ if (!req->should_stop()) {
+
+ /* sample position is canonical here, and we want to generate
+ * an image that spans about 3x the canvas width. We get to that
+ * width by using an image sample count of the screen width added
+ * on each side of the desired image center.
+ */
+
+ const framepos_t center = req->start + ((req->end - req->start) / 2);
+ const framecnt_t image_samples = req->width;
+
+ /* we can request data from anywhere in the Source, between 0 and its length
+ */
+
+ framepos_t sample_start = max (_region_start, (center - image_samples));
+ framepos_t sample_end = min (center + image_samples, region_end());
+ const int n_peaks = std::max (1LL, llrint (ceil ((sample_end - sample_start) / (req->samples_per_pixel))));
+
+ assert (n_peaks > 0 && n_peaks < 32767);
+
+ boost::scoped_array<ARDOUR::PeakData> peaks (new PeakData[n_peaks]);
+
+ /* Note that Region::read_peaks() takes a start position based on an
+ offset into the Region's **SOURCE**, rather than an offset into
+ the Region itself.
+ */
+
+ framecnt_t peaks_read = _region->read_peaks (peaks.get(), n_peaks,
+ sample_start, sample_end - sample_start,
+ req->channel,
+ req->samples_per_pixel);
+
+ if (req->should_stop()) {
+ // cerr << "Request stopped after reading peaks\n";
+ return;
+ }
+
+ req->image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, n_peaks, req->height);
+
+ // http://cairographics.org/manual/cairo-Image-Surfaces.html#cairo-image-surface-create
+ // This function always returns a valid pointer, but it will return a pointer to a "nil" surface..
+ // but there's some evidence that req->image can be NULL.
+ // http://tracker.ardour.org/view.php?id=6478
+ assert (req->image);
+
+ /* make sure we record the sample positions that were actually used */
+ req->start = sample_start;
+ req->end = sample_end;
+
+ if (peaks_read > 0) {
+
+ /* region amplitude will have been used to generate the
+ * peak values already, but not the visual-only
+ * amplitude_above_axis. So apply that here before
+ * rendering.
+ */
+
+ if (_amplitude_above_axis != 1.0) {
+ for (framecnt_t i = 0; i < n_peaks; ++i) {
+ peaks[i].max *= _amplitude_above_axis;
+ peaks[i].min *= _amplitude_above_axis;
+ }
+ }
+
+ draw_image (req->image, peaks.get(), n_peaks, req);
+ } else {
+ draw_absent_image (req->image, peaks.get(), n_peaks);
+ }
+ } else {
+ // cerr << "Request stopped before image generation\n";
+ }
+
+ if (in_render_thread && !req->should_stop()) {
+ DEBUG_TRACE (DEBUG::WaveView, string_compose ("done with request for %1 at %2 CR %3 req %4 range %5 .. %6\n", this, g_get_monotonic_time(), current_request, req, req->start, req->end));
+ const_cast<WaveView*>(this)->ImageReady (); /* emit signal */
+ }
+
+ return;
+}
+
+/** Given a waveform that starts at window x-coordinate @param wave_origin
+ * and the first pixel that we will actually draw @param draw_start, return
+ * the offset into an image of the entire waveform that we will need to use.
+ *
+ * Note: most of our cached images are NOT of the entire waveform, this is just
+ * computationally useful when determining which the sample range span for
+ * the image we need.
+ */
+static inline double
+window_to_image (double wave_origin, double image_start)
+{
+ return image_start - wave_origin;