summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2013-04-24 15:42:14 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2013-04-24 15:42:14 -0400
commit6f664c1f67f9a62d44b3ee8c3cae8c341e0731d3 (patch)
tree88133cd525135ef198b7bf9c4a7f1081826a5ac5 /libs
parentb36e085001cabc13f20f6b6779d08d9816d76b00 (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.h2
-rw-r--r--libs/ardour/region.cc18
-rw-r--r--libs/canvas/canvas.cc85
-rw-r--r--libs/canvas/canvas/wave_view.h32
-rw-r--r--libs/canvas/group.cc8
-rw-r--r--libs/canvas/outline.cc36
-rw-r--r--libs/canvas/wave_view.cc229
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 ()