an awful lot of tweaks to drawing details
authorPaul Davis <paul@linuxaudiosystems.com>
Mon, 24 Jun 2013 20:28:53 +0000 (16:28 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Mon, 24 Jun 2013 20:28:53 +0000 (16:28 -0400)
12 files changed:
gtk2_ardour/audio_region_view.cc
gtk2_ardour/editor_cursors.cc
gtk2_ardour/tempo_lines.cc
gtk2_ardour/theme_manager.cc
gtk2_ardour/time_axis_view_item.cc
gtk2_ardour/time_axis_view_item.h
libs/canvas/canvas.cc
libs/canvas/canvas/types.h
libs/canvas/line.cc
libs/canvas/rectangle.cc
libs/canvas/types.cc
libs/canvas/wave_view.cc

index 47aebb765f333b712f751818ea9df70f3c5f7ef1..077755ff83a3bba949f544342deac35dd450eab0 100644 (file)
@@ -1208,14 +1208,13 @@ AudioRegionView::create_one_wave (uint32_t which, bool /*direct*/)
                v = min (1.0, v * 3.0);
                
                c = ArdourCanvas::hsv_to_color (h, s, v, _region->muted() ? MUTED_ALPHA : 1.0);
-
+               
                wave->set_fill_color (c);
        }
 
        wave->set_clip_color (ARDOUR_UI::config()->get_canvasvar_WaveFormClip());
        wave->set_zero_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
-       // CAIROCANVAS
-       // wave->property_zero_line() = true;
+       wave->set_show_zero_line (true);
 
        switch (Config->get_waveform_shape()) {
        case Rectified:
index 9d8c255d2ffb26526b8910b425582279f0b46b60..557120c1db783b54fcd799816ab6d2fe8549c56b 100644 (file)
@@ -45,11 +45,11 @@ EditorCursor::EditorCursor (Editor& ed, bool (Editor::*callbck)(GdkEvent*,Ardour
        _time_bars_canvas_item.set_head_width (0, 16);
        _time_bars_canvas_item.set_head_outward (0, false);
        _time_bars_canvas_item.set_show_head (1, false); // head only
-       _time_bars_canvas_item.set_outline_width (1.5);
+       _time_bars_canvas_item.set_outline_width (0.5);
 
        _time_bars_canvas_item.set_data ("cursor", this);
        _track_canvas_item.set_data ("cursor", this);
-       _track_canvas_item.set_outline_width (1.5);
+       _track_canvas_item.set_outline_width (0.5);
 
        _time_bars_canvas_item.Event.connect (sigc::bind (sigc::mem_fun (ed, callbck), &_time_bars_canvas_item));
        _track_canvas_item.Event.connect (sigc::bind (sigc::mem_fun (ed, callbck), &_track_canvas_item));
@@ -70,7 +70,11 @@ EditorCursor::set_position (framepos_t frame)
 {
        PositionChanged (frame);
 
-       double const new_pos = _editor.sample_to_pixel (frame);
+       /* See Cairo FAQ question on single pixel lines to understand
+          why we add 0.5
+       */
+
+       double const new_pos = _editor.sample_to_pixel (frame) + 0.5;
 
        if (new_pos != _time_bars_canvas_item.x ()) {
                _time_bars_canvas_item.set_x (new_pos);
index d73fb54b81309b04b1f330ed1d572f3c959948de..6bf7dfbde9081e472f441309545cf9ab95978550 100644 (file)
@@ -111,8 +111,12 @@ TempoLines::draw (const ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
                        line->set_ignore_events (true);
                }
 
-               line->set_x0 (xpos);
-               line->set_x1 (xpos);
+               /* move to 0.5 offset to ensure single pixel lines (see Cairo
+                * FAQ for info on why we do this).
+                */
+
+               line->set_x0 (xpos + 0.5);
+               line->set_x1 (xpos + 0.5);
                line->set_y0 (0.0);
                line->set_y1 (_height);
                line->set_outline_color (color);
index 1fabfacb7eba7219a1ca3593def7b9b4bc36dfb8..947d92bddfd0e4c4d1b1a047618854e145fd53ec 100644 (file)
@@ -59,9 +59,9 @@ ThemeManager::ThemeManager()
        , light_button (_("Light Theme"))
        , reset_button (_("Restore Defaults"))
        , flat_buttons (_("Draw \"flat\" buttons"))
-       , waveform_gradient_depth (0, 1.0, 0.1)
+       , waveform_gradient_depth (0, 1.0, 0.05)
        , waveform_gradient_depth_label (_("Waveforms color gradient depth"))
-       , timeline_item_gradient_depth (0, 2.0, 0.1)
+       , timeline_item_gradient_depth (0, 1.0, 0.05)
        , timeline_item_gradient_depth_label (_("Timeline item gradient depth"))
        , all_dialogs (_("All floating windows are dialogs"))
 {
index ebda7312d46ada29786bed2eafc3af33d4cfbdc0..d8d3cee8078e387486dd50bdd6ed8836e4317c2e 100644 (file)
@@ -731,30 +731,15 @@ TimeAxisViewItem::set_colors()
        set_trim_handle_colors();
 }
 
-/**
- * Sets the frame color depending on whether this item is selected
- */
-void
-TimeAxisViewItem::set_frame_color()
+uint32_t
+TimeAxisViewItem::get_fill_color () const
 {
         uint32_t f = 0;
 
-       if (!frame) {
-               return;
-       }
-
        if (_selected) {
 
                 f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase();
 
-               if (fill_opacity) {
-                        f = UINT_RGBA_CHANGE_A (f, fill_opacity);
-               }
-
-                if (!rect_visible) {
-                        f = UINT_RGBA_CHANGE_A (f, 0);
-                }
-
        } else {
 
                if (_recregion) {
@@ -766,15 +751,32 @@ TimeAxisViewItem::set_frame_color()
                        } else {
                                f = fill_color;
                        }
+               }
+       }
 
-                       if (fill_opacity) {
-                               f = UINT_RGBA_CHANGE_A (f, fill_opacity);
-                       }
+       return f;
+}
 
-                        if (!rect_visible) {
-                                f = UINT_RGBA_CHANGE_A (f, 0);
-                        }
-                }
+/**
+ * Sets the frame color depending on whether this item is selected
+ */
+void
+TimeAxisViewItem::set_frame_color()
+{
+        uint32_t f = 0;
+
+       if (!frame) {
+               return;
+       }
+
+       f = get_fill_color ();
+
+       if (fill_opacity) {
+               f = UINT_RGBA_CHANGE_A (f, fill_opacity);
+       }
+       
+       if (!rect_visible) {
+               f = UINT_RGBA_CHANGE_A (f, 0);
        }
 
         frame->set_fill_color (f);
@@ -806,7 +808,7 @@ TimeAxisViewItem::set_frame_gradient ()
        ArdourCanvas::Fill::StopList stops;
        double r, g, b, a;
        double h, s, v;
-       ArdourCanvas::Color f (frame->fill_color());
+       ArdourCanvas::Color f (get_fill_color());
 
        /* need to get alpha value */
        ArdourCanvas::color_to_rgba (f, r, g, b, a);
@@ -816,10 +818,8 @@ TimeAxisViewItem::set_frame_gradient ()
        /* now a darker version */
        
        ArdourCanvas::color_to_hsv (f, h, s, v);
-       s *= ARDOUR_UI::config()->get_timeline_item_gradient_depth();
-       if (s > 1.0) {
-               s = 1.0;
-       }
+
+       v = min (1.0, v * (1.0 - ARDOUR_UI::config()->get_timeline_item_gradient_depth()));
        
        ArdourCanvas::Color darker = ArdourCanvas::hsv_to_color (h, s, v, a);
        stops.push_back (std::make_pair (1.0, darker));
index fc9ab4106e6b0604c2234fd20d26c7d0eed7a00b..054d496013d097455f6c43ab2aad0d9c8ad95938 100644 (file)
@@ -74,6 +74,8 @@ class TimeAxisViewItem : public Selectable, public PBD::ScopedConnectionList
        void set_y (double);
        void set_color (Gdk::Color const &);
 
+        uint32_t get_fill_color () const;
+
        ArdourCanvas::Item* get_canvas_frame();
        ArdourCanvas::Group* get_canvas_group();
        ArdourCanvas::Item* get_name_highlight();
index 04929ca1af3c60b098b07cc79502e8124c8a9c15..dab8ce6f687bcc336591a627ab4708275accb046 100644 (file)
@@ -80,6 +80,7 @@ Canvas::render (Rect const & area, Cairo::RefPtr<Cairo::Context> const & context
                /* there's a common area between the root and the requested
                   area, so render it.
                */
+
                _root.render (*draw, context);
        }
 }
index 37c0c213ea3b48669dc85e20872057e69908a901..de98ddb18f4d3c96fcfa8c6fb6d74fc7b76fc7de 100644 (file)
 #include <stdint.h>
 #include <boost/optional.hpp>
 
+#include <cairomm/refptr.h>
+
+namespace Cairo {
+       struct Context;
+}
+
 namespace ArdourCanvas
 {
 
@@ -89,6 +95,9 @@ struct Rect
        bool contains (Duple) const;
        Rect fix () const;
 
+        Rect convert_to_device (Cairo::RefPtr<Cairo::Context>) const;
+        Rect convert_to_user (Cairo::RefPtr<Cairo::Context>) const;
+
        Distance width () const {
                return x1 - x0;
        }
index b6a802b8df7ce4fce9977ecdca36f8f87107563b..af2a0e47db19ea230acf2469ae044e6e71e8739f 100644 (file)
@@ -56,10 +56,15 @@ void
 Line::render (Rect const & /*area*/, Cairo::RefPtr<Cairo::Context> context) const
 {
        setup_outline_context (context);
+
        Duple p0 = item_to_window (Duple (_points[0].x, _points[0].y));
        Duple p1 = item_to_window (Duple (_points[1].x, _points[1].y));
-       context->move_to (p0.x, p0.y);
-       context->line_to (p1.x, p1.y);
+
+       /* See Cairo FAQ on single pixel lines to understand why we add 0.5
+        */
+
+       context->move_to (p0.x + 0.5, p0.y + 0.5);
+       context->line_to (p1.x + 0.5, p1.y + 0.5);
        context->stroke ();
 }
 
index 6ce62b0144490766ea07a592210e6981619f3b0f..9512b69417ffd4bfa7ce5b13ec824ee7cd872758 100644 (file)
@@ -68,69 +68,40 @@ Rectangle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) con
        draw.y0 = max (self.y0, max (0.0, draw.y0 - boundary));
        draw.y1 = min (self.y1, min (2000.0, draw.y1 + boundary));
 
+       Rect fill_rect = draw;
+       Rect stroke_rect = fill_rect.expand (0.5);
+
        if (_fill) {
                setup_fill_context (context);
-
-               context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
-               
-               if (!_outline) {
-                       context->fill ();
-               } else {
-                       
-                       /* special/common case: outline the entire rectangle is
-                        * requested, so just use the same path for the fill
-                        * and stroke.
-                        */
-
-                       if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) {
-                               context->fill_preserve();
-                               setup_outline_context (context);
-                               context->stroke ();
-                       } else {
-                               context->fill ();
-                       }
-               }
-       } 
+               context->rectangle (fill_rect.x0, fill_rect.y0, fill_rect.width(), fill_rect.height());
+               context->fill ();
+       }
        
        if (_outline) {
-               
+
                setup_outline_context (context);
 
-               if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) {
-
-                       /* if we filled and use full outline, we are already
-                        * done. otherwise, draw the frame here.
-                        */
-
-                       if (!_fill) { 
-                               context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
-                               context->stroke ();
-                       }
-                       
-               } else {
-                       
-                       if (_outline_what & LEFT) {
-                               context->move_to (draw.x0, draw.y0);
-                               context->line_to (draw.x0, draw.y1);
-                       }
-                       
-                       if (_outline_what & BOTTOM) {
-                               context->move_to (draw.x0, draw.y1);
-                               context->line_to (draw.x1, draw.y1);
-                       }
-                       
-                       if (_outline_what & RIGHT) {
-                               context->move_to (draw.x1, draw.y0);
-                               context->line_to (draw.x1, draw.y1);
-                       }
-                       
-                       if (_outline_what & TOP) {
-                               context->move_to (draw.x0, draw.y0);
-                               context->line_to (draw.x1, draw.y0);
-                       }
-                       
-                       context->stroke ();
+               if (_outline_what & LEFT) {
+                       context->move_to (stroke_rect.x0, stroke_rect.y0);
+                       context->line_to (stroke_rect.x0, stroke_rect.y1);
                }
+               
+               if (_outline_what & BOTTOM) {
+                       context->move_to (stroke_rect.x0, stroke_rect.y1);
+                       context->line_to (stroke_rect.x1, stroke_rect.y1);
+               }
+               
+               if (_outline_what & RIGHT) {
+                       context->move_to (stroke_rect.x1, stroke_rect.y0);
+                       context->line_to (stroke_rect.x1, stroke_rect.y1);
+               }
+               
+               if (_outline_what & TOP) {
+                       context->move_to (stroke_rect.x0, stroke_rect.y0);
+                       context->line_to (stroke_rect.x1, stroke_rect.y0);
+               }
+               
+               context->stroke ();
        }
 }
 
index dfd934b126f7bbfd47d13bd11dbb9edfd31f3868..a8c690bbbe85e2a9bea8cbc7af143f82a3cb0807 100644 (file)
@@ -20,6 +20,9 @@
 #include <algorithm>
 #include <cfloat>
 #include <cassert>
+
+#include <cairomm/context.h>
+
 #include "canvas/types.h"
 
 using namespace std;
@@ -118,6 +121,39 @@ Rect::fix () const
        return r;
 }
 
+Rect
+Rect::convert_to_device (Cairo::RefPtr<Cairo::Context> c) const
+{
+       Coord xa, ya, xb, yb;
+
+       xa = x0;
+       xb = x1;
+       ya = y0;
+       yb = y1;
+
+       c->user_to_device (xa, ya);
+       c->user_to_device (xb, yb);
+
+       return Rect (xa, ya, xb, yb);
+}
+
+
+Rect
+Rect::convert_to_user (Cairo::RefPtr<Cairo::Context> c) const
+{
+       Coord xa, ya, xb, yb;
+
+       xa = x0;
+       xb = x1;
+       ya = y0;
+       yb = y1;
+
+       c->device_to_user (xa, ya);
+       c->device_to_user (xb, yb);
+
+       return Rect (xa, ya, xb, yb);
+}
+
 Duple
 ArdourCanvas::operator- (Duple const & o)
 {
index 8a8b528b6f2bd7a576137f59169de0f958881c1c..2118838a039295502a45866a95614ede719b112d 100644 (file)
@@ -178,6 +178,7 @@ WaveView::ensure_cache (framecnt_t start, framecnt_t end,
        sample_start = max ((framepos_t) 0, (center - canvas_samples));
        sample_end = min (center + canvas_samples, _region->source_length (0));
 
+#if 0
        if (sample_end <= sample_start) {
                cerr << "sample start = " << sample_start << endl;
                cerr << "center+ = " << center<< endl;
@@ -188,13 +189,15 @@ WaveView::ensure_cache (framecnt_t start, framecnt_t end,
                cerr << "END: " << sample_end << endl;
                assert (false);
        }
+#endif
        
        start = floor (sample_start / (double) _samples_per_pixel);
        end = ceil (sample_end / (double) _samples_per_pixel);
        
        assert (end > start);
 
-       cerr << name << " cache miss - new CE, span " << start << " .. " << end << " (" << sample_start << " .. " << sample_end << ")\n";
+       // cerr << name << " cache miss - new CE, span " << start << " .. " << end << " (" << sample_start << " .. " << sample_end << ")\n";
+
        _cache = new CacheEntry (this, start, end, sample_start, sample_end);
 }
 
@@ -254,7 +257,19 @@ WaveView::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) cons
        // cerr << "Offset into image to place at zero: " << image_offset << endl;
 
        context->rectangle (draw_start, draw.y0, draw_end - draw_start, draw.height());
-       context->set_source (_cache->image(), self.x0 + image_offset, self.y0);
+
+       /* round image origin position to an exact pixel in device space to
+        * avoid blurring
+        */
+
+       double x  = self.x0 + image_offset;
+       double y  = self.y0;
+       context->user_to_device (x, y);
+       x = round (x);
+       y = round (y);
+       context->device_to_user (x, y);
+
+       context->set_source (_cache->image(), x, y);
        context->fill ();
 }
 
@@ -408,18 +423,6 @@ WaveView::region_resized ()
 
        _pre_change_bounding_box = _bounding_box;
 
-       frameoffset_t s = _region->start();
-
-       if (s != _region_start) {
-               /* if the region start changes, the information we have 
-                  in the image cache is out of date and not useful
-                  since it will fragmented into little pieces. invalidate
-                  the cache.
-               */
-               _region_start = _region->start();
-               invalidate_whole_cache ();
-       }
-
        _bounding_box_dirty = true;
        compute_bounding_box ();
 
@@ -602,9 +605,6 @@ WaveView::CacheEntry::image ()
                context->stroke ();
 
 #else
-               cerr << "draw, logscaled = " << _wave_view->_logscaled << " global " << WaveView::_global_logscaled << endl;
-               cerr << "gradient depth: " << _wave_view->gradient_depth() << endl;
-
                boost::scoped_array<LineTips> tips (new LineTips[_n_peaks]);
 
                if (_wave_view->_shape == WaveView::Rectified) {
@@ -656,8 +656,8 @@ WaveView::CacheEntry::image ()
 
                        } else {
                                for (int i = 0; i < _n_peaks; ++i) {
-                                       tips[i].top = floor (position (_peaks[i].min));
-                                       tips[i].bot = ceil (position (_peaks[i].max));
+                                       tips[i].top = position (_peaks[i].min);
+                                       tips[i].bot = position (_peaks[i].max);
                                }
                        }
                }
@@ -687,7 +687,7 @@ WaveView::CacheEntry::image ()
                        /* 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 */
+                       /* change v towards white */
                        v *= 1.0 - _wave_view->gradient_depth();
                        Color center = hsv_to_color (h, s, v, a);
                        color_to_rgba (center, r, g, b, a);
@@ -695,24 +695,26 @@ WaveView::CacheEntry::image ()
                        
                        context->set_source (gradient);
                } else {
-                       cerr << "\tno gradient\n";
                        set_source_rgba (context, _wave_view->_fill_color);
                }
 
+               /* ensure single-pixel lines */
+               
                context->set_line_width (0.5);
+               context->translate (0.5, 0.0);
 
                /* draw the lines */
-               
+
                if (_wave_view->_shape == WaveView::Rectified) {
                        for (int i = 0; i < _n_peaks; ++i) {
-                               context->move_to (i + 0.5, tips[i].top + 0.5); /* down 1 pixel */
-                               context->line_to (i + 0.5, tips[i].bot);
+                               context->move_to (i, tips[i].top); /* down 1 pixel */
+                               context->line_to (i, tips[i].bot);
                                context->stroke ();
                        }
                } else {
                        for (int i = 0; i < _n_peaks; ++i) {
-                               context->move_to (i + 0.5, tips[i].top + 0.5); /* down 1 pixel */
-                               context->line_to (i + 0.5, tips[i].bot - 0.5); /* up 1 pixel */
+                               context->move_to (i, tips[i].top);
+                               context->line_to (i, tips[i].bot);
                                context->stroke ();
                        }
                }
@@ -721,14 +723,16 @@ WaveView::CacheEntry::image ()
                 * modelled on pyramix, except that we add clipping indicators.
                 */
 
-               context->set_source_rgb (0, 0, 0);
+               context->set_source_rgba (0, 0, 0, 1.0);
 
                for (int i = 0; i < _n_peaks; ++i) {
-                       context->rectangle (i + 0.5, tips[i].top, 0.5, 0.5);
-                       context->fill ();
+                       context->move_to (i, tips[i].top);
+                       context->rel_line_to (0, 1.0);
+                       context->stroke ();
                        if (_wave_view->_shape != WaveView::Rectified) {
-                               context->rectangle (i + 0.5, tips[i].bot, 0.5, 0.5);
-                               context->fill ();
+                               context->move_to (i, tips[i].bot);
+                               context->rel_line_to (0, -1.0);
+                               context->stroke ();
                        }
                }
 #endif
@@ -749,13 +753,17 @@ WaveView::CacheEntry::image ()
 Coord
 WaveView::CacheEntry::position (double s) const
 {
+       /* it is important that this returns an integral value, so that we 
+          can ensure correct single pixel behaviour.
+        */
+
        switch (_wave_view->_shape) {
        case Rectified:
-               return _wave_view->_height - (s * _wave_view->_height);
+               return floor (_wave_view->_height - (s * _wave_view->_height));
        default:
                break;
        }
-       return (1.0-s) * (_wave_view->_height / 2.0);
+       return floor ((1.0-s) * (_wave_view->_height / 2.0));
 }
 
 void