change rendering technique for waveforms, add back optional gradient, add back amplit...
authorPaul Davis <paul@linuxaudiosystems.com>
Tue, 16 Apr 2013 18:04:59 +0000 (14:04 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 16 Apr 2013 18:04:59 +0000 (14:04 -0400)
gtk2_ardour/theme_manager.cc
libs/canvas/canvas/utils.h
libs/canvas/canvas/wave_view.h
libs/canvas/utils.cc
libs/canvas/wave_view.cc

index a2624c562f71f5a1445c8592aa3838eee0cd7c39..f1538c98e974834bf759348ac7f78c9fb964117a 100644 (file)
@@ -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
index 2a4f681e3751a1211d877b344bc24c4e962b1ab3..9db677b61b6ce7d8c00257f1d42ce38831988abb 100644 (file)
 
 namespace ArdourCanvas {
 
-extern void set_source_rgba (Cairo::RefPtr<Cairo::Context>, 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<Cairo::Context>, Color);
 }
 
index 33fec55e72b420f27d3d0ed899b256a86c7be24f..63be77cfe47a30ff6696cd44970f1cfeffb99cc6 100644 (file)
@@ -55,6 +55,8 @@ public:
 
        void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) 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<CacheEntry*> _cache;
+       
+        PBD::ScopedConnection invalidation_connection;
+
+        static bool _gradient_waveforms;
+        static PBD::Signal0<void> InvalidateAllImages;
 };
 
 }
index cc291d83be054afe4a090a654efaa97078bebd2a..b431042c3573344b068e49a206a3b24fb620264a 100644 (file)
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#include <algorithm>
+#include <cmath>
 #include <stdint.h>
 #include <cairomm/context.h>
 #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<Cairo::Context> context, Color color)
 {
index 4a4a78d47e06990230291e17c174ae4cb7a2cca2..6b5c5b0b3bfe8ef0992bc9fdb8051868174b6b85 100644 (file)
@@ -38,6 +38,9 @@ using namespace std;
 using namespace ARDOUR;
 using namespace ArdourCanvas;
 
+bool WaveView::_gradient_waveforms = true;
+PBD::Signal0<void> WaveView::InvalidateAllImages;
+
 WaveView::WaveView (Group* parent, boost::shared_ptr<ARDOUR::AudioRegion> region)
        : Item (parent)
        , Outline (parent)
@@ -55,7 +58,14 @@ WaveView::WaveView (Group* parent, boost::shared_ptr<ARDOUR::AudioRegion> 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<Cairo::Context> 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<Cairo::LinearGradient> 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 */
+       }
+}