From ccc9b6adee762bc1e899bf5bcba7941d2e7d6db4 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 16 Apr 2013 14:04:59 -0400 Subject: [PATCH] change rendering technique for waveforms, add back optional gradient, add back amplitude scaling, add rgb/hsv conversion utilities to canvas --- gtk2_ardour/theme_manager.cc | 11 +-- libs/canvas/canvas/utils.h | 9 ++- libs/canvas/canvas/wave_view.h | 10 +++ libs/canvas/utils.cc | 122 +++++++++++++++++++++++++++++++++ libs/canvas/wave_view.cc | 81 ++++++++++++++++++---- 5 files changed, 209 insertions(+), 24 deletions(-) diff --git a/gtk2_ardour/theme_manager.cc b/gtk2_ardour/theme_manager.cc index a2624c562f..f1538c98e9 100644 --- a/gtk2_ardour/theme_manager.cc +++ b/gtk2_ardour/theme_manager.cc @@ -34,6 +34,8 @@ #include "ardour/filesystem_paths.h" +#include "canvas/wave_view.h" + #include "ardour_button.h" #include "theme_manager.h" #include "rgb_macros.h" @@ -247,16 +249,9 @@ ThemeManager::on_flat_buttons_toggled () void ThemeManager::on_gradient_waveforms_toggled () { - // CAIROCANVAS -#if 0 ARDOUR_UI::config()->gradient_waveforms.set (gradient_waveforms.get_active()); ARDOUR_UI::config()->set_dirty (); - - gnome_canvas_waveview_set_gradient_waveforms (gradient_waveforms.get_active()); - - /* force a redraw */ - gtk_rc_reset_styles (gtk_settings_get_default()); -#endif + ArdourCanvas::WaveView::set_gradient_waveforms (gradient_waveforms.get_active()); } void diff --git a/libs/canvas/canvas/utils.h b/libs/canvas/canvas/utils.h index 2a4f681e37..9db677b61b 100644 --- a/libs/canvas/canvas/utils.h +++ b/libs/canvas/canvas/utils.h @@ -21,7 +21,12 @@ namespace ArdourCanvas { -extern void set_source_rgba (Cairo::RefPtr, Color); - + extern void color_to_hsv (Color color, double& h, double& s, double& v); + extern Color hsv_to_color (double h, double s, double v, double a); + + extern void color_to_rgba (Color, double& r, double& g, double& b, double& a); + extern Color rgba_to_color (double r, double g, double b, double a); + + extern void set_source_rgba (Cairo::RefPtr, Color); } diff --git a/libs/canvas/canvas/wave_view.h b/libs/canvas/canvas/wave_view.h index 33fec55e72..63be77cfe4 100644 --- a/libs/canvas/canvas/wave_view.h +++ b/libs/canvas/canvas/wave_view.h @@ -55,6 +55,8 @@ public: void render (Rect const & area, Cairo::RefPtr) const; void compute_bounding_box () const; + + void rebuild (); void set_samples_per_pixel (double); void set_height (Distance); @@ -75,6 +77,9 @@ public: void set_shape (Shape); Shape shape() const; + static void set_gradient_waveforms (bool); + static bool gradient_waveforms() { return _gradient_waveforms; } + #ifdef CANVAS_COMPATIBILITY void*& property_gain_src () { return _foo_void; @@ -149,6 +154,11 @@ private: ARDOUR::frameoffset_t _region_start; mutable std::list _cache; + + PBD::ScopedConnection invalidation_connection; + + static bool _gradient_waveforms; + static PBD::Signal0 InvalidateAllImages; }; } diff --git a/libs/canvas/utils.cc b/libs/canvas/utils.cc index cc291d83be..b431042c35 100644 --- a/libs/canvas/utils.cc +++ b/libs/canvas/utils.cc @@ -17,10 +17,132 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include +#include #include #include #include "canvas/utils.h" +using std::max; +using std::min; + +void +ArdourCanvas::color_to_hsv (Color color, double& h, double& s, double& v) +{ + double r, g, b, a; + double cmax; + double cmin; + double delta; + + color_to_rgba (color, r, g, b, a); + + if (r > g) { + cmax = max (r, b); + } else { + cmax = max (g, b); + } + + if (r < g) { + cmin = min (r, b); + } else { + cmin = min (g, b); + } + + v = cmax; + + delta = cmax - cmin; + + if (cmax == 0) { + // r = g = b == 0 ... v is undefined, s = 0 + s = 0.0; + h = -1.0; + } + + if (delta != 0.0) { + if (cmax == r) { + h = fmod ((g - b)/delta, 6.0); + } else if (cmax == g) { + h = ((b - r)/delta) + 2; + } else { + h = ((r - g)/delta) + 4; + } + + h *= 60.0; + } + + if (delta == 0 || cmax == 0) { + s = 0; + } else { + s = delta / cmax; + } + +} + +ArdourCanvas::Color +ArdourCanvas::hsv_to_color (double h, double s, double v, double a) +{ + s = min (1.0, max (0.0, s)); + v = min (1.0, max (0.0, v)); + + if (s == 0) { + // achromatic (grey) + return rgba_to_color (v, v, v, a); + } + + h = min (360.0, max (0.0, h)); + + double c = v * s; + double x = c * (1.0 - fabs(fmod(h / 60.0, 2) - 1.0)); + double m = v - c; + + if (h >= 0.0 && h < 60.0) { + return rgba_to_color (c + m, x + m, m, a); + } else if (h >= 60.0 && h < 120.0) { + return rgba_to_color (x + m, c + m, m, a); + } else if (h >= 120.0 && h < 180.0) { + return rgba_to_color (m, c + m, x + m, a); + } else if (h >= 180.0 && h < 240.0) { + return rgba_to_color (m, x + m, c + m, a); + } else if (h >= 240.0 && h < 300.0) { + return rgba_to_color (x + m, m, c + m, a); + } else if (h >= 300.0 && h < 360.0) { + return rgba_to_color (c + m, m, x + m, a); + } + return rgba_to_color (m, m, m, a); +} + +void +ArdourCanvas::color_to_rgba (Color color, double& r, double& g, double& b, double& a) +{ + r = ((color >> 24) & 0xff) / 255.0; + g = ((color >> 16) & 0xff) / 255.0; + b = ((color >> 8) & 0xff) / 255.0; + a = ((color >> 0) & 0xff) / 255.0; +} + +ArdourCanvas::Color +ArdourCanvas::rgba_to_color (double r, double g, double b, double a) +{ + /* clamp to [0 .. 1] range */ + + r = min (1.0, max (0.0, r)); + g = min (1.0, max (0.0, g)); + b = min (1.0, max (0.0, b)); + a = min (1.0, max (0.0, a)); + + /* convert to [0..255] range */ + + unsigned int rc, gc, bc, ac; + rc = rint (r * 255.0); + gc = rint (g * 255.0); + bc = rint (b * 255.0); + ac = rint (a * 255.0); + + /* build-an-integer */ + + return (rc << 24) | (gc << 16) | (bc << 8) | ac; +} + void ArdourCanvas::set_source_rgba (Cairo::RefPtr context, Color color) { diff --git a/libs/canvas/wave_view.cc b/libs/canvas/wave_view.cc index 4a4a78d47e..6b5c5b0b3b 100644 --- a/libs/canvas/wave_view.cc +++ b/libs/canvas/wave_view.cc @@ -38,6 +38,9 @@ using namespace std; using namespace ARDOUR; using namespace ArdourCanvas; +bool WaveView::_gradient_waveforms = true; +PBD::Signal0 WaveView::InvalidateAllImages; + WaveView::WaveView (Group* parent, boost::shared_ptr region) : Item (parent) , Outline (parent) @@ -55,7 +58,14 @@ WaveView::WaveView (Group* parent, boost::shared_ptr region , _amplitude (1.0) , _region_start (0) { - + InvalidateAllImages.connect_same_thread (invalidation_connection, boost::bind (&WaveView::rebuild, this)); +} + +void +WaveView::rebuild () +{ + invalidate_image_cache (); + _canvas->item_visual_property_changed (this); } void @@ -324,27 +334,64 @@ WaveView::CacheEntry::image () _image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _n_peaks, _wave_view->_height); Cairo::RefPtr context = Cairo::Context::create (_image); + PeakData::PeakDatum pmax = 0.0; + PeakData::PeakDatum pmin = DBL_MAX; - _wave_view->setup_outline_context (context); - context->move_to (0.5, position (_peaks[0].min)); + /* Draw the edge of the waveform, top half first, the loop back + * for the bottom half to create a clockwise path + */ + + context->move_to (0.5, position (_peaks[0].max)); for (int i = 1; i < _n_peaks; ++i) { context->line_to (i + 0.5, position (_wave_view->amplitude() * _peaks[i].max)); + pmax = max (pmax, _peaks[i].max); + pmin = min (pmin, _peaks[i].min); } - context->stroke (); + + /* from final top point, move out of the clip zone */ + + context->line_to (_n_peaks + 10, position (0.0)); - context->move_to (0.5, position (_peaks[0].min)); - for (int i = 1; i < _n_peaks; ++i) { + /* bottom half, in reverse */ + + for (int i = _n_peaks-1; i >= 0; --i) { context->line_to (i + 0.5, position (_wave_view->amplitude() * _peaks[i].min)); } - context->stroke (); + + /* from final bottom point, move out of the clip zone */ - set_source_rgba (context, _wave_view->_fill_color); - for (int i = 0; i < _n_peaks; ++i) { - context->move_to (i + 0.5, position (_wave_view->amplitude() * (_peaks[i].max)) - 1); - context->line_to (i + 0.5, position (_wave_view->amplitude() * (_peaks[i].min)) + 1); - context->stroke (); + context->line_to (-10.0, position (0.0)); + + context->close_path (); + + if (WaveView::gradient_waveforms()) { + + Cairo::RefPtr gradient (Cairo::LinearGradient::create (0, 0, 0, _wave_view->_height)); + + double r, g, b, a; + + color_to_rgba (_wave_view->_fill_color, r, g, b, a); + gradient->add_color_stop_rgba (0.1, r, g, b, a); + gradient->add_color_stop_rgba (0.9, r, g, b, a); + + /* generate a new color for the middle of the gradient */ + double h, s, v; + color_to_hsv (_wave_view->_fill_color, h, s, v); + /* tone down the saturation */ + s *= 0.75; + Color center = hsv_to_color (h, s, v, a); + color_to_rgba (center, r, g, b, a); + gradient->add_color_stop_rgba (0.5, r, g, b, a); + + context->set_source (gradient); + } else { + set_source_rgba (context, _wave_view->_fill_color); } + context->fill_preserve (); + _wave_view->setup_outline_context (context); + context->stroke (); + if (_wave_view->show_zero_line()) { set_source_rgba (context, _wave_view->_zero_color); context->move_to (0, position (0.0)); @@ -368,6 +415,12 @@ WaveView::CacheEntry::clear_image () { _image.clear (); } - - +void +WaveView::set_gradient_waveforms (bool yn) +{ + if (_gradient_waveforms != yn) { + _gradient_waveforms = yn; + InvalidateAllImages (); /* EMIT SIGNAL */ + } +}