summaryrefslogtreecommitdiff
path: root/libs/canvas/canvas
diff options
context:
space:
mode:
authorTim Mayberry <mojofunk@gmail.com>2017-03-16 14:12:05 +1000
committerTim Mayberry <mojofunk@gmail.com>2017-06-26 08:40:47 +1000
commit6e91ee071c23440827541adb4c057f8c55d4a2f2 (patch)
tree1a4370e8db9eb8af7081e1af179f014cd6719961 /libs/canvas/canvas
parent5f30d87fd0127bb475894825c6e4acc01fcedb29 (diff)
Reimplementation of large parts of the WaveView class
The drawing itself should be unchanged but much of the rest of the implementation has changed. The WaveViewThreads and WaveViewDrawingThread classes were added and allow multiple drawing threads. The Item::prepare_for_render interface is implemented by WaveView to enable queuing draw requests for the drawing threads to process as soon as the state change occurs during Editor::visual_changer, which often means the images will be finished by the time they are needed in WaveView::render. This can significantly reduce total render time and also flickering caused by images not being ready for display. If the drawing thread/s cannot finish the request by the time it is required in WaveView::render then cancel it and draw the WaveViewImage in the GUI thread if it is likely it can be completed in the current render pass/frame. This change also helps reduce the flickering caused by images not being ready with threaded rendering, but with several drawing threads, drawing in the GUI thread may not often occur (unless explicitly requested). Allow unfinished images to be returned from the cache in WaveView::prepare_for_render so that new draw requests aren't queued for duplicate images. This reduces the amount of drawing for instance in compositions where there are many instances of the same sample/waveform displayed on the canvas as only a single image should be drawn. Use a random width within a certain range for WaveView::optimal_image_width_samples so that image drawing is less likely to occur at the same time (which will cause a spike in render/draw time and increase the chance of flickering waveforms). Move implementations of the private WaveView classes into wave_view_private.h and wave_view_private.cc source files. Incorporate a fix for limiting the waveview image size to the cairo image size limit. Should hopefully Resolve: #6478
Diffstat (limited to 'libs/canvas/canvas')
-rw-r--r--libs/canvas/canvas/wave_view.h393
-rw-r--r--libs/canvas/canvas/wave_view_private.h365
2 files changed, 489 insertions, 269 deletions
diff --git a/libs/canvas/canvas/wave_view.h b/libs/canvas/canvas/wave_view.h
index abb5200244..de6b38d13e 100644
--- a/libs/canvas/canvas/wave_view.h
+++ b/libs/canvas/canvas/wave_view.h
@@ -1,5 +1,6 @@
/*
Copyright (C) 2011-2013 Paul Davis
+ Copyright (C) 2017 Tim Mayberry
Author: Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
@@ -22,10 +23,7 @@
#define __CANVAS_WAVE_VIEW_H__
#include <boost/shared_ptr.hpp>
-#include <boost/shared_array.hpp>
-#include <boost/scoped_array.hpp>
-
-#include "pbd/properties.h"
+#include <boost/scoped_ptr.hpp>
#include "ardour/types.h"
@@ -33,8 +31,6 @@
#include "canvas/visibility.h"
#include "canvas/item.h"
-#include "canvas/fill.h"
-#include "canvas/outline.h"
namespace ARDOUR {
class AudioRegion;
@@ -48,157 +44,19 @@ class WaveViewTest;
namespace ArdourCanvas {
-struct LIBCANVAS_API WaveViewThreadRequest
-{
- public:
- enum RequestType {
- Quit,
- Cancel,
- Draw
- };
-
- WaveViewThreadRequest () : stop (0) {}
-
- bool should_stop () const { return (bool) g_atomic_int_get (const_cast<gint*>(&stop)); }
- void cancel() { g_atomic_int_set (&stop, 1); }
-
- RequestType type;
- framepos_t start;
- framepos_t end;
- double width;
- double height;
- double samples_per_pixel;
- uint16_t channel;
- double amplitude;
- Color fill_color;
- boost::weak_ptr<const ARDOUR::Region> region;
-
- /* resulting image, after request has been satisfied */
-
- Cairo::RefPtr<Cairo::ImageSurface> image;
-
- private:
- gint stop; /* intended for atomic access */
-};
-
-class LIBCANVAS_API WaveView;
-
-class LIBCANVAS_API WaveViewCache
-{
- public:
- WaveViewCache();
- ~WaveViewCache();
-
- struct Entry {
-
- /* these properties define the cache entry as unique.
-
- If an image is in use by a WaveView and any of these
- properties are modified on the WaveView, the image can no
- longer be used (or may no longer be usable for start/end
- parameters). It will remain in the cache until flushed for
- some reason (typically the cache is full).
- */
-
- int channel;
- Coord height;
- float amplitude;
- Color fill_color;
- double samples_per_pixel;
- framepos_t start;
- framepos_t end;
-
- /* the actual image referred to by the cache entry */
-
- Cairo::RefPtr<Cairo::ImageSurface> image;
-
- /* last time the cache entry was used */
- uint64_t timestamp;
-
- Entry (int chan, Coord hght, float amp, Color fcl, double spp, framepos_t strt, framepos_t ed,
- Cairo::RefPtr<Cairo::ImageSurface> img)
- : channel (chan)
- , height (hght)
- , amplitude (amp)
- , fill_color (fcl)
- , samples_per_pixel (spp)
- , start (strt)
- , end (ed)
- , image (img) {}
- };
-
- uint64_t image_cache_threshold () const { return _image_cache_threshold; }
- void set_image_cache_threshold (uint64_t);
- void clear_cache ();
-
- void add (boost::shared_ptr<ARDOUR::AudioSource>, boost::shared_ptr<Entry>);
- void use (boost::shared_ptr<ARDOUR::AudioSource>, boost::shared_ptr<Entry>);
-
- void consolidate_image_cache (boost::shared_ptr<ARDOUR::AudioSource>,
- int channel,
- Coord height,
- float amplitude,
- Color fill_color,
- double samples_per_pixel);
-
- boost::shared_ptr<Entry> lookup_image (boost::shared_ptr<ARDOUR::AudioSource>,
- framepos_t start, framepos_t end,
- int _channel,
- Coord height,
- float amplitude,
- Color fill_color,
- double samples_per_pixel,
- bool& full_image);
-
- private:
- /* an unsorted, unindexd collection of cache entries associated with
- a particular AudioSource. All cache Entries in the collection
- share the AudioSource in common, but represent different parameter
- settings (e.g. height, color, samples per pixel etc.)
- */
- typedef std::vector<boost::shared_ptr<Entry> > CacheLine;
- /* Indexed, non-sortable structure used to lookup images associated
- * with a particular AudioSource
- */
- typedef std::map <boost::shared_ptr<ARDOUR::AudioSource>,CacheLine> ImageCache;
- ImageCache cache_map;
-
- /* Linear, sortable structure used when we need to do a timestamp-based
- * flush of entries from the cache.
- */
- typedef std::pair<boost::shared_ptr<ARDOUR::AudioSource>,boost::shared_ptr<Entry> > ListEntry;
- typedef std::vector<ListEntry> CacheList;
-
- struct SortByTimestamp {
- bool operator() (const WaveViewCache::ListEntry& a, const WaveViewCache::ListEntry& b) {
- return a.second->timestamp < b.second->timestamp;
- }
- };
- friend struct SortByTimestamp;
-
- uint64_t image_cache_size;
- uint64_t _image_cache_threshold;
-
- uint64_t compute_image_cache_size ();
- void cache_flush ();
- bool cache_full ();
-};
+class WaveViewCacheGroup;
+class WaveViewDrawRequest;
+class WaveViewDrawRequestQueue;
+class WaveViewImage;
+class WaveViewProperties;
+class WaveViewDrawingThread;
class LIBCANVAS_API WaveView : public Item, public sigc::trackable
{
public:
+ enum Shape { Normal, Rectified };
- enum Shape {
- Normal,
- Rectified
- };
-
- std::string debug_name() const;
-
- /* final ImageSurface rendered with colours */
-
- Cairo::RefPtr<Cairo::ImageSurface> _image;
- PBD::Signal0<void> ImageReady;
+ std::string debug_name () const;
/* Displays a single channel of waveform data for the given Region.
@@ -218,11 +76,14 @@ public:
other view parameters).
*/
- WaveView (Canvas *, boost::shared_ptr<ARDOUR::AudioRegion>);
+ WaveView (Canvas*, boost::shared_ptr<ARDOUR::AudioRegion>);
WaveView (Item*, boost::shared_ptr<ARDOUR::AudioRegion>);
- ~WaveView ();
+ ~WaveView ();
+
+ virtual void prepare_for_render (Rect const& window_area) const;
+
+ virtual void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
- void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
void compute_bounding_box () const;
void set_samples_per_pixel (double);
@@ -239,39 +100,45 @@ public:
*/
void set_start_shift (double pixels);
- void set_fill_color (Color);
- void set_outline_color (Color);
+ void set_fill_color (Color);
+ void set_outline_color (Color);
void region_resized ();
- void gain_changed ();
+ void gain_changed ();
+
+ void set_show_zero_line (bool);
+ bool show_zero_line () const;
+
+ void set_zero_color (Color);
+ void set_clip_color (Color);
+ void set_logscaled (bool);
+
+ void set_gradient_depth (double);
+ double gradient_depth () const;
+
+ void set_shape (Shape);
+
+ void set_always_get_image_in_thread (bool yn);
- void set_show_zero_line (bool);
- bool show_zero_line() const { return _show_zero; }
- void set_zero_color (Color);
- void set_clip_color (Color);
- void set_logscaled (bool);
- void set_gradient_depth (double);
- double gradient_depth() const { return _gradient_depth; }
- void set_shape (Shape);
+ /* currently missing because we don't need them (yet):
+ * set_shape_independent();
+ * set_logscaled_independent();
+ */
+
+ static void set_global_gradient_depth (double);
+ static void set_global_logscaled (bool);
+ static void set_global_shape (Shape);
+ static void set_global_show_waveform_clipping (bool);
- void set_always_get_image_in_thread (bool yn);
+ static double global_gradient_depth () { return _global_gradient_depth; }
- /* currently missing because we don't need them (yet):
- set_shape_independent();
- set_logscaled_independent()
- */
+ static bool global_logscaled () { return _global_logscaled; }
- static void set_global_gradient_depth (double);
- static void set_global_logscaled (bool);
- static void set_global_shape (Shape);
- static void set_global_show_waveform_clipping (bool);
+ static Shape global_shape () { return _global_shape; }
- static double global_gradient_depth() { return _global_gradient_depth; }
- static bool global_logscaled() { return _global_logscaled; }
- static Shape global_shape() { return _global_shape; }
+ void set_amplitude_above_axis (double v);
- void set_amplitude_above_axis (double v);
- double amplitude_above_axis () const { return _amplitude_above_axis; }
+ double amplitude_above_axis () const;
static void set_clip_level (double dB);
static PBD::Signal0<void> ClipLevelChanged;
@@ -288,127 +155,115 @@ public:
void*& property_gain_function () {
return _foo_void;
}
- private:
- void* _foo_void;
+private:
+ void* _foo_void;
#endif
- private:
- friend class ::WaveViewTest;
- friend class WaveViewThreadClient;
-
- void invalidate_image_cache ();
+private:
+ friend class ::WaveViewTest;
+ friend class WaveViewThreadClient;
+ friend class WaveViewDrawingThread;
boost::shared_ptr<ARDOUR::AudioRegion> _region;
- int _channel;
- double _samples_per_pixel;
- Coord _height;
- bool _show_zero;
- Color _zero_color;
- Color _clip_color;
- bool _logscaled;
- Shape _shape;
- double _gradient_depth;
- bool _shape_independent;
- bool _logscaled_independent;
- bool _gradient_depth_independent;
- double _amplitude_above_axis;
- float _region_amplitude;
- double _start_shift;
-
- /** The `start' value to use for the region; we can't use the region's
- * value as the crossfade editor needs to alter it.
- */
- ARDOUR::frameoffset_t _region_start;
+
+ boost::scoped_ptr<WaveViewProperties> _props;
+
+ mutable boost::shared_ptr<WaveViewImage> _image;
+
+ mutable boost::shared_ptr<WaveViewCacheGroup> _cache_group;
+
+ bool _shape_independent;
+ bool _logscaled_independent;
+ bool _gradient_depth_independent;
/** Under almost conditions, this is going to return _region->length(),
- * but if _region_start has been reset, then we need
- * to use this modified computation.
+ * but if region_start has been reset, then we need to use this modified
+ * computation.
*/
- ARDOUR::framecnt_t region_length() const;
+ ARDOUR::framecnt_t region_length () const;
+
/** Under almost conditions, this is going to return _region->start() +
- * _region->length(), but if _region_start has been reset, then we need
- * to use this modified computation.
+ * _region->length(), but if region_start has been reset, then we need to use
+ * this modified computation.
*/
- ARDOUR::framepos_t region_end() const;
+ ARDOUR::framepos_t region_end () const;
- /** If true, calls to get_image() will render a missing wave image
- in the calling thread. Generally set to false, but true after a
- call to set_height().
- */
- mutable bool get_image_in_thread;
+ /**
+ * _image stays non-null after the first time it is set
+ */
+ bool rendered () const { return _image.get(); }
- /** 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;
+ bool draw_image_in_gui_thread () const;
- /** 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
- * that the height is being set BEFORE the waveview is drawn.
+ /** If true, calls to render() will render a missing wave image in the GUI
+ * thread. Generally set to false, but true after a call to set_height().
*/
- mutable bool rendered;
+ mutable bool _draw_image_in_gui_thread;
- PBD::ScopedConnectionList invalidation_connection;
- PBD::ScopedConnection image_ready_connection;
+ /** If true, calls to render() will render a missing wave image in the GUI
+ * thread. Set true for waveviews we expect to keep updating (e.g. while
+ * recording)
+ */
+ bool _always_draw_image_in_gui_thread;
- static double _global_gradient_depth;
- static bool _global_logscaled;
- static Shape _global_shape;
- static bool _global_show_waveform_clipping;
- static double _clip_level;
+ void init();
- static PBD::Signal0<void> VisualPropertiesChanged;
+ mutable boost::shared_ptr<WaveViewDrawRequest> current_request;
- void handle_visual_property_change ();
- void handle_clip_level_change ();
+ PBD::ScopedConnectionList invalidation_connection;
- boost::shared_ptr<WaveViewCache::Entry> get_image (framepos_t start, framepos_t end, bool& full_image) const;
- boost::shared_ptr<WaveViewCache::Entry> get_image_from_cache (framepos_t start, framepos_t end, bool& full_image) const;
+ static double _global_gradient_depth;
+ static bool _global_logscaled;
+ static Shape _global_shape;
+ static bool _global_show_waveform_clipping;
+ static double _global_clip_level;
- struct LineTips {
- double top;
- double bot;
- double spread;
- bool clip_max;
- bool clip_min;
+ static PBD::Signal0<void> VisualPropertiesChanged;
- LineTips() : top (0.0), bot (0.0), clip_max (false), clip_min (false) {}
- };
+ void handle_visual_property_change ();
+ void handle_clip_level_change ();
- ArdourCanvas::Coord y_extent (double) const;
- void compute_tips (ARDOUR::PeakData const & peak, LineTips& tips) const;
+ struct LineTips {
+ double top;
+ double bot;
+ double spread;
+ bool clip_max;
+ bool clip_min;
- ARDOUR::framecnt_t desired_image_width () const;
+ LineTips () : top (0.0), bot (0.0), clip_max (false), clip_min (false) {}
+ };
+
+ static ArdourCanvas::Coord y_extent (double, Shape const, double const height);
- void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int n_peaks, boost::shared_ptr<WaveViewThreadRequest>) const;
- void draw_absent_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int) const;
+ static void compute_tips (ARDOUR::PeakData const& peak, LineTips& tips, double const effective_height);
- void cancel_my_render_request () const;
+ static void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int n_peaks,
+ boost::shared_ptr<WaveViewDrawRequest>);
+ static void draw_absent_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int);
- void queue_get_image (boost::shared_ptr<const ARDOUR::Region> region, framepos_t start, framepos_t end) const;
- void generate_image (boost::shared_ptr<WaveViewThreadRequest>, bool in_render_thread) const;
- boost::shared_ptr<WaveViewCache::Entry> cache_request_result (boost::shared_ptr<WaveViewThreadRequest> req) const;
+ ARDOUR::framecnt_t optimal_image_width_samples () const;
- void image_ready ();
+ void set_image (boost::shared_ptr<WaveViewImage> img) const;
- mutable boost::shared_ptr<WaveViewCache::Entry> _current_image;
+ // @return true if item area intersects with draw area
+ bool get_item_and_draw_rect_in_window_coords (Rect const& canvas_rect, Rect& item_area,
+ Rect& draw_rect) const;
- mutable boost::shared_ptr<WaveViewThreadRequest> current_request;
+ boost::shared_ptr<WaveViewDrawRequest> create_draw_request (WaveViewProperties const&) const;
- static WaveViewCache* images;
+ void queue_draw_request (boost::shared_ptr<WaveViewDrawRequest> const&) const;
- static void drawing_thread ();
+ static void process_draw_request (boost::shared_ptr<WaveViewDrawRequest>);
- static gint drawing_thread_should_quit;
- static Glib::Threads::Mutex request_queue_lock;
- static Glib::Threads::Mutex current_image_lock;
- static Glib::Threads::Cond request_cond;
- static Glib::Threads::Thread* _drawing_thread;
- typedef std::set<WaveView const *> DrawingRequestQueue;
- static DrawingRequestQueue request_queue;
+ boost::shared_ptr<WaveViewCacheGroup> get_cache_group () const;
+
+ /**
+ * Notify the Cache that we are dropping our reference to the
+ * CacheGroup so it can also do so if it is the only reference holder
+ * of the cache group.
+ */
+ void reset_cache_group ();
};
} // namespace ArdourCanvas
diff --git a/libs/canvas/canvas/wave_view_private.h b/libs/canvas/canvas/wave_view_private.h
new file mode 100644
index 0000000000..1b5e0be706
--- /dev/null
+++ b/libs/canvas/canvas/wave_view_private.h
@@ -0,0 +1,365 @@
+/*
+ Copyright (C) 2017 Tim Mayberry
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __CANVAS_WAVE_VIEW_PRIVATE_H__
+#define __CANVAS_WAVE_VIEW_PRIVATE_H__
+
+#include <deque>
+
+#include "canvas/wave_view.h"
+
+namespace ARDOUR {
+ class AudioRegion;
+}
+
+namespace ArdourCanvas {
+
+struct WaveViewProperties
+{
+public: // ctors
+ WaveViewProperties (boost::shared_ptr<ARDOUR::AudioRegion> region);
+
+ // WaveViewProperties (WaveViewProperties const& other) = default;
+
+ // WaveViewProperties& operator=(WaveViewProperties const& other) = default;
+
+public: // member variables
+
+ framepos_t region_start;
+ framepos_t region_end;
+ uint16_t channel;
+ double height;
+ double samples_per_pixel;
+ double amplitude;
+ double amplitude_above_axis;
+ Color fill_color;
+ Color outline_color;
+ Color zero_color;
+ Color clip_color;
+ bool show_zero;
+ bool logscaled;
+ WaveView::Shape shape;
+ double gradient_depth;
+ double start_shift;
+
+private: // member variables
+
+ framepos_t sample_start;
+ framepos_t sample_end;
+
+public: // methods
+
+ bool is_valid () const
+ {
+ return (sample_end != 0 && samples_per_pixel != 0);
+ }
+
+ void set_width_samples (ARDOUR::framecnt_t const width_samples)
+ {
+ assert (is_valid());
+ assert (width_samples != 0);
+ ARDOUR::framecnt_t half_width = width_samples / 2;
+ framepos_t new_sample_start = std::max (region_start, get_center_sample () - half_width);
+ framepos_t new_sample_end = std::min (get_center_sample () + half_width, region_end);
+ assert (new_sample_start <= new_sample_end);
+ sample_start = new_sample_start;
+ sample_end = new_sample_end;
+ }
+
+ uint64_t get_width_pixels () const
+ {
+ return (uint64_t)std::max (1LL, llrint (ceil (get_length_samples () / samples_per_pixel)));
+ }
+
+
+ void set_sample_offsets (framepos_t const start, framepos_t const end)
+ {
+ assert (start <= end);
+
+ // sample_start and sample_end are bounded by the region limits.
+ if (start < region_start) {
+ sample_start = region_start;
+ } else if (start > region_end) {
+ sample_start = region_end;
+ } else {
+ sample_start = start;
+ }
+
+ if (end > region_end) {
+ sample_end = region_end;
+ } else if (end < region_start) {
+ sample_end = region_start;
+ } else {
+ sample_end = end;
+ }
+
+ assert (sample_start <= sample_end);
+ }
+
+ framepos_t get_sample_start () const
+ {
+ return sample_start;
+ }
+
+ framepos_t get_sample_end () const
+ {
+ return sample_end;
+ }
+
+ void set_sample_positions_from_pixel_offsets (double start_pixel, double end_pixel)
+ {
+ assert (start_pixel <= end_pixel);
+ /**
+ * It is possible for the new sample positions to be past the region_end,
+ * so we have to do bounds checking/adjustment for this in set_sample_offsets.
+ */
+ framepos_t new_sample_start = region_start + (start_pixel * samples_per_pixel);
+ framepos_t new_sample_end = region_start + (end_pixel * samples_per_pixel);
+ set_sample_offsets (new_sample_start, new_sample_end);
+ }
+
+ ARDOUR::framecnt_t get_length_samples () const
+ {
+ assert (sample_start <= sample_end);
+ return sample_end - sample_start;
+ }
+
+ framepos_t get_center_sample ()
+ {
+ return sample_start + (get_length_samples() / 2);
+ }
+
+ bool is_equivalent (WaveViewProperties const& other)
+ {
+ return (samples_per_pixel == other.samples_per_pixel &&
+ contains (other.sample_start, other.sample_end) && channel == other.channel &&
+ height == other.height && amplitude == other.amplitude &&
+ amplitude_above_axis == other.amplitude_above_axis && fill_color == other.fill_color &&
+ outline_color == other.outline_color && zero_color == other.zero_color &&
+ clip_color == other.clip_color && show_zero == other.show_zero &&
+ logscaled == other.logscaled && shape == other.shape &&
+ gradient_depth == other.gradient_depth);
+ // region_start && start_shift??
+ }
+
+ bool contains (framepos_t start, framepos_t end)
+ {
+ return (sample_start <= start && end <= sample_end);
+ }
+};
+
+struct WaveViewImage {
+public: // ctors
+ WaveViewImage (boost::shared_ptr<const ARDOUR::AudioRegion> const& region_ptr,
+ WaveViewProperties const& properties);
+
+ ~WaveViewImage ();
+
+public: // member variables
+ boost::weak_ptr<const ARDOUR::AudioRegion> region;
+ WaveViewProperties props;
+ Cairo::RefPtr<Cairo::ImageSurface> cairo_image;
+ uint64_t timestamp;
+
+public: // methods
+ bool finished() { return static_cast<bool>(cairo_image); }
+
+ bool
+ contains_image_with_properties (WaveViewProperties const& other_props)
+ {
+ return cairo_image && props.is_equivalent (other_props);
+ }
+
+ bool is_valid () {
+ return props.is_valid ();
+ }
+
+ size_t size_in_bytes ()
+ {
+ // 4 = bytes per FORMAT_ARGB32 pixel
+ return props.height * props.get_width_pixels() * 4;
+ }
+};
+
+struct WaveViewDrawRequest
+{
+public:
+ WaveViewDrawRequest ();
+ ~WaveViewDrawRequest ();
+
+ bool stopped() const { return (bool) g_atomic_int_get (const_cast<gint*>(&stop)); }
+ void cancel() { g_atomic_int_set (&stop, 1); }
+ bool finished() { return image->finished(); }
+
+ boost::shared_ptr<WaveViewImage> image;
+
+ bool is_valid () {
+ return (image && image->is_valid());
+ }
+
+private:
+ gint stop; /* intended for atomic access */
+};
+
+class WaveViewCache;
+
+class WaveViewCacheGroup
+{
+public:
+ WaveViewCacheGroup (WaveViewCache& parent_cache);
+
+ ~WaveViewCacheGroup ();
+
+public:
+
+ // @return image with matching properties or null
+ boost::shared_ptr<WaveViewImage> lookup_image (WaveViewProperties const&);
+
+ void add_image (boost::shared_ptr<WaveViewImage>);
+
+ bool full () const { return _cached_images.size() > max_size(); }
+
+ static uint32_t max_size () { return 16; }
+
+ void clear_cache ();
+
+private:
+
+ /**
+ * At time of writing we don't strictly need a reference to the parent cache
+ * as there is only a single global cache but if the image cache ever becomes
+ * a per canvas cache then a using a reference is handy.
+ */
+ WaveViewCache& _parent_cache;
+
+ typedef std::list<boost::shared_ptr<WaveViewImage> > ImageCache;
+ ImageCache _cached_images;
+};
+
+class WaveViewCache
+{
+public:
+ static WaveViewCache* get_instance ();
+
+ uint64_t image_cache_threshold () const { return _image_cache_threshold; }
+ void set_image_cache_threshold (uint64_t);
+
+ void clear_cache ();
+
+ boost::shared_ptr<WaveViewCacheGroup> get_cache_group (boost::shared_ptr<ARDOUR::AudioSource>);
+
+ void reset_cache_group (boost::shared_ptr<WaveViewCacheGroup>&);
+
+private:
+ WaveViewCache();
+ ~WaveViewCache();
+
+private:
+ typedef std::map<boost::shared_ptr<ARDOUR::AudioSource>, boost::shared_ptr<WaveViewCacheGroup> >
+ CacheGroups;
+
+ CacheGroups cache_group_map;
+
+ uint64_t image_cache_size;
+ uint64_t _image_cache_threshold;
+
+private:
+ friend WaveViewCacheGroup;
+
+ void increase_size (uint64_t bytes);
+ void decrease_size (uint64_t bytes);
+
+ bool full () { return image_cache_size > _image_cache_threshold; }
+};
+
+class WaveViewDrawRequestQueue
+{
+public:
+
+ void enqueue (boost::shared_ptr<WaveViewDrawRequest>&);
+
+ // @return valid request or null if non-blocking or no request is available
+ boost::shared_ptr<WaveViewDrawRequest> dequeue (bool block);
+
+ void wake_up ();
+
+private:
+
+ mutable Glib::Threads::Mutex _queue_mutex;
+ Glib::Threads::Cond _cond;
+
+ typedef std::deque<boost::shared_ptr<WaveViewDrawRequest> > DrawRequestQueueType;
+ DrawRequestQueueType _queue;
+};
+
+class WaveViewDrawingThread
+{
+public:
+ WaveViewDrawingThread ();
+ ~WaveViewDrawingThread ();
+
+private:
+ void start ();
+ void quit ();
+ void run ();
+
+private:
+ Glib::Threads::Thread* _thread;
+ gint _quit;
+};
+
+class WaveViewThreads {
+private:
+ WaveViewThreads ();
+ ~WaveViewThreads ();
+
+public:
+ static void initialize ();
+ static void deinitialize ();
+
+ static bool enabled () { return (instance); }
+
+ static void enqueue_draw_request (boost::shared_ptr<WaveViewDrawRequest>&);
+
+private:
+ friend WaveViewDrawingThread;
+
+ static void wake_up ();
+
+ // will block until a request is available
+ static boost::shared_ptr<WaveViewDrawRequest> dequeue_draw_request ();
+
+ void start_threads ();
+ void stop_threads ();
+
+private:
+ static uint32_t init_count;
+ static WaveViewThreads* instance;
+
+ // TODO use std::unique_ptr when possible
+ typedef std::vector<boost::shared_ptr<WaveViewDrawingThread> > WaveViewThreadList;
+
+ WaveViewThreadList _threads;
+ WaveViewDrawRequestQueue _request_queue;
+};
+
+
+} // namespace ArdourCanvas
+
+#endif // __CANVAS_WAVE_VIEW_PRIVATE_H__