diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2013-04-24 15:42:14 -0400 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2013-04-24 15:42:14 -0400 |
commit | 6f664c1f67f9a62d44b3ee8c3cae8c341e0731d3 (patch) | |
tree | 88133cd525135ef198b7bf9c4a7f1081826a5ac5 /libs | |
parent | b36e085001cabc13f20f6b6779d08d9816d76b00 (diff) |
many pervasive changes primarily related to waveform drawing, particular content-dragging, colors, and more
Diffstat (limited to 'libs')
-rw-r--r-- | libs/ardour/ardour/region.h | 2 | ||||
-rw-r--r-- | libs/ardour/region.cc | 18 | ||||
-rw-r--r-- | libs/canvas/canvas.cc | 85 | ||||
-rw-r--r-- | libs/canvas/canvas/wave_view.h | 32 | ||||
-rw-r--r-- | libs/canvas/group.cc | 8 | ||||
-rw-r--r-- | libs/canvas/outline.cc | 36 | ||||
-rw-r--r-- | libs/canvas/wave_view.cc | 229 |
7 files changed, 258 insertions, 152 deletions
diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 3ee829ed12..593832343f 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -220,7 +220,7 @@ class Region bool at_natural_position () const; void move_to_natural_position (); - void trim_start (framepos_t new_position); + void move_start (frameoffset_t distance); void trim_front (framepos_t new_position); void trim_end (framepos_t new_position); void trim_to (framepos_t position, framecnt_t length); diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 706dda4a0a..847a72b23f 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -698,33 +698,32 @@ Region::set_start (framepos_t pos) } void -Region::trim_start (framepos_t new_position) +Region::move_start (frameoffset_t distance) { if (locked() || position_locked() || video_locked()) { return; } framepos_t new_start; - frameoffset_t const start_shift = new_position - _position; - if (start_shift > 0) { + if (distance > 0) { - if (_start > max_framepos - start_shift) { - new_start = max_framepos; + if (_start > max_framepos - distance) { + new_start = max_framepos; // makes no sense } else { - new_start = _start + start_shift; + new_start = _start + distance; } if (!verify_start (new_start)) { return; } - } else if (start_shift < 0) { + } else if (distance < 0) { - if (_start < -start_shift) { + if (_start < -distance) { new_start = 0; } else { - new_start = _start + start_shift; + new_start = _start + distance; } } else { @@ -736,6 +735,7 @@ Region::trim_start (framepos_t new_position) } set_start_internal (new_start); + _whole_file = false; first_edit (); diff --git a/libs/canvas/canvas.cc b/libs/canvas/canvas.cc index 9707f2304d..48bfbaaf4d 100644 --- a/libs/canvas/canvas.cc +++ b/libs/canvas/canvas.cc @@ -61,9 +61,10 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context { #ifdef CANVAS_DEBUG if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) { - cerr << "CANVAS @ " << this << endl; - dump (cerr); - cerr << "-------------------------\n"; + cerr << "RENDER: " << area << endl; + //cerr << "CANVAS @ " << this << endl; + //dump (cerr); + //cerr << "-------------------------\n"; } #endif @@ -78,9 +79,9 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context context->set_source_rgba (random()%255 / 255.0, random()%255 / 255.0, random()%255 / 255.0, - 255); + 0.3); context->rectangle (area.x0, area.y0, area.width(), area.height()); - context->stroke (); + context->fill (); } #endif @@ -119,7 +120,7 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context context->set_source_rgba (random()%255 / 255.0, random()%255 / 255.0, random()%255 / 255.0, - 255); + 0.4); context->rectangle (area.x0, area.y0, area.width(), area.height()); context->fill (); } @@ -327,6 +328,11 @@ GtkCanvas::enter_leave_items () { int x; int y; + + /* this version of ::enter_leave_items() is called after an item is + * added or removed, so we have no coordinates to work from as is the + * case with a motion event. Find out where the mouse is and use that. + */ Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y); @@ -340,31 +346,60 @@ GtkCanvas::enter_leave_items () void GtkCanvas::enter_leave_items (Duple const & point) { - /* find the items at the new mouse position */ + /* find the items at the given position */ + vector<Item const *> items; _root.add_items_at_point (point, items); - Item const * new_item = items.empty() ? 0 : items.back (); - - if (_current_item && _current_item != new_item) { - /* leave event */ - GdkEventCrossing leave_event; - leave_event.type = GDK_LEAVE_NOTIFY; - leave_event.x = point.x; - leave_event.y = point.y; - _current_item->Event (reinterpret_cast<GdkEvent*> (&leave_event)); + if (items.empty()) { + if (_current_item) { + /* leave event */ + GdkEventCrossing leave_event; + leave_event.type = GDK_LEAVE_NOTIFY; + leave_event.x = point.x; + leave_event.y = point.y; + cerr << "Leaving (without entering)" << _current_item->name << endl; + _current_item->Event (reinterpret_cast<GdkEvent*> (&leave_event)); + _current_item = 0; + } + return; } - if (new_item && _current_item != new_item) { - /* enter event */ - GdkEventCrossing enter_event; - enter_event.type = GDK_ENTER_NOTIFY; - enter_event.x = point.x; - enter_event.y = point.y; - new_item->Event (reinterpret_cast<GdkEvent*> (&enter_event)); - } + /* items is sorted from bottom to top, so reverse through it from top + * to bottom to find the first event-sensitive item and notify that + * we have entered it + */ - _current_item = new_item; + for (vector<Item const*>::const_reverse_iterator i = items.rbegin(); i != items.rend(); ++i) { + + Item const * new_item = *i; + + if (new_item->ignore_events()) { + continue; + } + if (_current_item && _current_item != new_item) { + /* leave event */ + GdkEventCrossing leave_event; + leave_event.type = GDK_LEAVE_NOTIFY; + leave_event.x = point.x; + leave_event.y = point.y; + cerr << "Leaving " << _current_item->name << endl; + _current_item->Event (reinterpret_cast<GdkEvent*> (&leave_event)); + } + + if (new_item && _current_item != new_item) { + /* enter event */ + GdkEventCrossing enter_event; + enter_event.type = GDK_ENTER_NOTIFY; + enter_event.x = point.x; + enter_event.y = point.y; + cerr << "Entering (" << new_item->name << ") " << new_item->whatami() << endl; + new_item->Event (reinterpret_cast<GdkEvent*> (&enter_event)); + } + + _current_item = new_item; + break; + } } /** Deliver an event to the appropriate item; either the grabbed item, or diff --git a/libs/canvas/canvas/wave_view.h b/libs/canvas/canvas/wave_view.h index f88ca41ba4..b75b8c5312 100644 --- a/libs/canvas/canvas/wave_view.h +++ b/libs/canvas/canvas/wave_view.h @@ -105,17 +105,31 @@ private: #endif + /** A cached, pre-rendered image of some section of a waveform. + + It spans a range given relative to the start of the source + of the waveform data, so a range from N..M corresponds + to the sample range N..M within the source. + + Invalidated by a changes to: + + samples_per_pixel + colors + height + + */ + class CacheEntry { - public: - CacheEntry (WaveView const *, int, int); + public: + CacheEntry (WaveView const *, double, double, int); ~CacheEntry (); - int start () const { + double start () const { return _start; } - int end () const { + double end () const { return _end; } @@ -130,9 +144,11 @@ private: Coord position (Coord) const; WaveView const * _wave_view; - int _start; - int _end; - int _n_peaks; + + double _start; + double _end; + int _n_peaks; + boost::shared_array<ARDOUR::PeakData> _peaks; Cairo::RefPtr<Cairo::ImageSurface> _image; }; @@ -163,7 +179,7 @@ private: * value as the crossfade editor needs to alter it. */ ARDOUR::frameoffset_t _region_start; - + mutable std::list<CacheEntry*> _cache; PBD::ScopedConnection invalidation_connection; diff --git a/libs/canvas/group.cc b/libs/canvas/group.cc index fe783ad8b5..1272d0e438 100644 --- a/libs/canvas/group.cc +++ b/libs/canvas/group.cc @@ -88,7 +88,7 @@ Group::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const if (!(*i)->visible ()) { #ifdef CANVAS_DEBUG if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) { - cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n"; + // cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n"; } #endif continue; @@ -99,7 +99,7 @@ Group::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const if (!item_bbox) { #ifdef CANVAS_DEBUG if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) { - cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n"; + // cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n"; } #endif continue; @@ -127,8 +127,8 @@ Group::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const } else { #ifdef CANVAS_DEBUG if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) { - cerr << string_compose ("%1skip render of %2 %3, no intersection\n", _canvas->render_indent(), (*i)->whatami(), - (*i)->name); + //cerr << string_compose ("%1skip render of %2 %3, no intersection\n", _canvas->render_indent(), (*i)->whatami(), + // (*i)->name); } #endif } diff --git a/libs/canvas/outline.cc b/libs/canvas/outline.cc index 15ea4776b6..3e57887070 100644 --- a/libs/canvas/outline.cc +++ b/libs/canvas/outline.cc @@ -41,35 +41,33 @@ Outline::Outline (Group* parent) void Outline::set_outline_color (Color color) { - begin_visual_change (); - - _outline_color = color; - - end_visual_change (); + if (color != _outline_color) { + begin_visual_change (); + _outline_color = color; + end_visual_change (); + } } void Outline::set_outline_width (Distance width) { - begin_change (); - - _outline_width = width; - - _bounding_box_dirty = true; - end_change (); - - DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: outline width change\n"); + if (width != _outline_width) { + begin_change (); + _outline_width = width; + _bounding_box_dirty = true; + end_change (); + } } void Outline::set_outline (bool outline) { - begin_change (); - - _outline = outline; - - _bounding_box_dirty = true; - end_change (); + if (outline != _outline) { + begin_change (); + _outline = outline; + _bounding_box_dirty = true; + end_change (); + } } void diff --git a/libs/canvas/wave_view.cc b/libs/canvas/wave_view.cc index c6ba816288..f39dca7594 100644 --- a/libs/canvas/wave_view.cc +++ b/libs/canvas/wave_view.cc @@ -25,6 +25,7 @@ #include "pbd/compose.h" #include "pbd/signals.h" +#include "pbd/stacktrace.h" #include "ardour/types.h" #include "ardour/dB.h" @@ -65,7 +66,7 @@ WaveView::WaveView (Group* parent, boost::shared_ptr<ARDOUR::AudioRegion> region , _logscaled_independent (false) , _gradient_depth_independent (false) , _amplitude_above_axis (1.0) - , _region_start (0) + , _region_start (region->start()) { VisualPropertiesChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_visual_property_change, this)); } @@ -98,28 +99,47 @@ WaveView::handle_visual_property_change () void WaveView::set_fill_color (Color c) { - invalidate_image_cache (); - Fill::set_fill_color (c); + if (c != _fill_color) { + invalidate_image_cache (); + Fill::set_fill_color (c); + } } void WaveView::set_outline_color (Color c) { - invalidate_image_cache (); - Outline::set_outline_color (c); + if (c != _outline_color) { + invalidate_image_cache (); + Outline::set_outline_color (c); + } } void WaveView::set_samples_per_pixel (double samples_per_pixel) { - begin_change (); + if (samples_per_pixel != _samples_per_pixel) { + begin_change (); - _samples_per_pixel = samples_per_pixel; + _samples_per_pixel = samples_per_pixel; + + _bounding_box_dirty = true; + + end_change (); + + invalidate_whole_cache (); + } +} - _bounding_box_dirty = true; - end_change (); +static inline double +to_src_sample_offset (frameoffset_t src_sample_start, double pixel_offset, double spp) +{ + return src_sample_start + (pixel_offset * spp); +} - invalidate_whole_cache (); +static inline double +to_pixel_offset (frameoffset_t src_sample_start, double sample_offset, double spp) +{ + return (sample_offset - src_sample_start) / spp; } void @@ -131,53 +151,91 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons return; } - /* p, start and end are offsets from the start of the source. - area is relative to the position of the region. + /* These are all pixel (integer) coordinates from the left hand edge of + * the waveview. */ - int const start = rint (area.x0 + _region_start / _samples_per_pixel); - int const end = rint (area.x1 + _region_start / _samples_per_pixel); + double start = area.x0; + double const end = area.x1; + double const rend = _region->length() / _samples_per_pixel; - int p = start; list<CacheEntry*>::iterator cache = _cache.begin (); - while (p < end) { + while ((end - start) > 1.0) { + + 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, p */ - while (cache != _cache.end() && (*cache)->end() <= p) { + /* Step through cache entries that end at or before our current position */ + + while (cache != _cache.end() && (*cache)->end() <= start_sample_offset) { ++cache; } /* Now either: 1. we have run out of cache entries - 2. the one we are looking at finishes after p but also starts after p. - 3. the one we are looking at finishes after p and starts before p. + 2. the one we are looking at finishes after start(_sample_offset) but also starts after start(_sample_offset). + 3. the one we are looking at finishes after start(_sample_offset) and starts before start(_sample_offset). Set up a pointer to the cache entry that we will use on this iteration. */ - CacheEntry* render = 0; + CacheEntry* image = 0; if (cache == _cache.end ()) { /* 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. + + We would like to avoid lots of little images in the + cache, so when we create a new one, make it as wide + as possible, within a sensible limit (here, the + visible width of the canvas we're on). + + However, we don't want to try to make it larger than + the region actually is, so clamp with that too. */ - - CacheEntry* c = new CacheEntry (this, p, end); + + double const endpoint = min (rend, max (end, start + _canvas->visible_area().width())); + CacheEntry* c = new CacheEntry (this, + start_sample_offset, + to_src_sample_offset (_region_start, endpoint, _samples_per_pixel), + endpoint - start); _cache.push_back (c); - render = c; + image = c; - } else if ((*cache)->start() > p) { + } else if ((*cache)->start() > start_sample_offset) { - /* Case 2: we have a cache entry, but it starts after p, so we - need another one for the missing bit. - */ + /* Case 2: we have a cache entry, but it starts after + * start(_sample_offset), so we need another one for + * the missing bit. + * + * Create a new cached image that extends as far as the + * next cached image's start, or the end of the region, + * 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); + int npeaks; + + if (end_sample_offset < (*cache)->start()) { + npeaks = end_pixel - start; + assert (npeaks > 0); + } 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); + } + + CacheEntry* c = new CacheEntry (this, + start_sample_offset, + end_sample_offset, + npeaks); - CacheEntry* c = new CacheEntry (this, p, min (end, (*cache)->start())); cache = _cache.insert (cache, c); ++cache; - render = c; + image = c; } else { @@ -185,29 +243,19 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons we have left, so render it. */ - render = *cache; + image = *cache; ++cache; } - int const this_end = min (end, render->end ()); - - Coord const left = p - _region_start / _samples_per_pixel; - Coord const right = this_end - _region_start / _samples_per_pixel; - - context->save (); - - context->rectangle (left, area.y0, right, area.height()); - context->clip (); - - context->translate (left, 0); + 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); - context->set_source (render->image(), render->start() - p, 0); - context->paint (); + context->rectangle (start, area.y0, this_end - start, area.height()); + context->set_source (image->image(), image_origin, 0); + context->fill (); - context->restore (); - - p = min (end, render->end ()); + start = this_end; } } @@ -215,7 +263,7 @@ void WaveView::compute_bounding_box () const { if (_region) { - _bounding_box = Rect (0, 0, _region->length() / _samples_per_pixel, _height); + _bounding_box = Rect (0.0, 0.0, _region->length() / _samples_per_pixel, _height); } else { _bounding_box = boost::optional<Rect> (); } @@ -226,33 +274,38 @@ WaveView::compute_bounding_box () const void WaveView::set_height (Distance height) { - begin_change (); - - _height = height; - - _bounding_box_dirty = true; - end_change (); - - invalidate_image_cache (); + if (height != _height) { + begin_change (); + + _height = height; + + _bounding_box_dirty = true; + end_change (); + + invalidate_image_cache (); + } } void WaveView::set_channel (int channel) { - begin_change (); - - _channel = channel; - - _bounding_box_dirty = true; - end_change (); - - invalidate_whole_cache (); + if (channel != _channel) { + begin_change (); + + _channel = channel; + + _bounding_box_dirty = true; + end_change (); + + invalidate_whole_cache (); + } } void WaveView::invalidate_whole_cache () { begin_visual_change (); + for (list<CacheEntry*>::iterator i = _cache.begin(); i != _cache.end(); ++i) { delete *i; } @@ -266,7 +319,7 @@ void WaveView::invalidate_image_cache () { begin_visual_change (); - + for (list<CacheEntry*>::iterator i = _cache.begin(); i != _cache.end(); ++i) { (*i)->clear_image (); } @@ -275,12 +328,6 @@ WaveView::invalidate_image_cache () } void -WaveView::region_resized () -{ - _bounding_box_dirty = true; -} - -void WaveView::set_logscaled (bool yn) { if (_logscaled != yn) { @@ -360,31 +407,41 @@ WaveView::set_global_logscaled (bool yn) } void -WaveView::set_region_start (frameoffset_t start) +WaveView::region_resized () { - _region_start = start; + if (!_region) { + return; + } + + /* special: do not use _region->length() here to compute + bounding box because it will already have changed. + + if we have a bounding box, use it. + */ + + _pre_change_bounding_box = _bounding_box; + + _region_start = _region->start(); + invalidate_whole_cache (); + _bounding_box_dirty = true; + compute_bounding_box (); + + end_change (); } -/** Construct a new CacheEntry with peak data between two offsets - * in the source. - */ -WaveView::CacheEntry::CacheEntry (WaveView const * wave_view, int start, int end) +WaveView::CacheEntry::CacheEntry (WaveView const * wave_view, double start, double end, int npeaks) : _wave_view (wave_view) , _start (start) , _end (end) + , _n_peaks (npeaks) { - _n_peaks = _end - _start; _peaks.reset (new PeakData[_n_peaks]); - _wave_view->_region->read_peaks ( - _peaks.get(), - _n_peaks, - _start * _wave_view->_samples_per_pixel, - (_end - _start) * _wave_view->_samples_per_pixel, - _wave_view->_channel, - _wave_view->_samples_per_pixel - ); + _wave_view->_region->read_peaks (_peaks.get(), _n_peaks, + (framecnt_t) floor (_start), + (framecnt_t) ceil (_end), + _wave_view->_channel, _wave_view->_samples_per_pixel); } WaveView::CacheEntry::~CacheEntry () |