less prominent meter shade
[ardour.git] / libs / gtkmm2ext / fastmeter.cc
index 5b5cd160d865e6d638606543b473dad6746e6285..57d53c8b623e4af0ee76470162c7d57eb7024bd1 100644 (file)
@@ -35,15 +35,19 @@ using namespace Glib;
 using namespace Gtkmm2ext;
 using namespace std;
 
-int FastMeter::min_pattern_metric_size = 10;
+int FastMeter::min_pattern_metric_size = 16;
 int FastMeter::max_pattern_metric_size = 1024;
 
-FastMeter::PatternMap FastMeter::v_pattern_cache;
-FastMeter::PatternMap FastMeter::h_pattern_cache;
+FastMeter::Pattern10Map FastMeter::vm_pattern_cache;
+FastMeter::PatternBgMap FastMeter::vb_pattern_cache;
 
 FastMeter::FastMeter (long hold, unsigned long dimen, Orientation o, int len,
                int clr0, int clr1, int clr2, int clr3,
-               int bgc0, int bgc1, int bgc2, int bgc3)
+               int clr4, int clr5, int clr6, int clr7,
+               int clr8, int clr9, int bgc0, int bgc1,
+               float stp0, float stp1,
+               float stp2, float stp3
+               )
 {
        orientation = o;
        hold_cnt = hold;
@@ -53,103 +57,160 @@ FastMeter::FastMeter (long hold, unsigned long dimen, Orientation o, int len,
        current_level = 0;
        last_peak_rect.width = 0;
        last_peak_rect.height = 0;
-       _clr0 = clr0;
-       _clr1 = clr1;
-       _clr2 = clr2;
-       _clr3 = clr3;
 
-       _bgc0 = bgc0;
-       _bgc1 = bgc1;
-       _bgc2 = bgc2;
-       _bgc3 = bgc3;
+       _clr[0] = clr0;
+       _clr[1] = clr1;
+       _clr[2] = clr2;
+       _clr[3] = clr3;
+       _clr[4] = clr4;
+       _clr[5] = clr5;
+       _clr[6] = clr6;
+       _clr[7] = clr7;
+       _clr[8] = clr8;
+       _clr[9] = clr9;
+
+       _bgc[0] = bgc0;
+       _bgc[1] = bgc1;
+
+       _stp[0] = stp0;
+       _stp[1] = stp1;
+       _stp[2] = stp2;
+       _stp[3] = stp3;
 
        set_events (BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK);
 
        pixrect.x = 1;
        pixrect.y = 1;
 
-       if (orientation == Vertical) {
-               if (!len) {
-                       len = 250;
-               }
-               fgpattern = request_vertical_meter(dimen, len, clr0, clr1, clr2, clr3);
-               bgpattern = request_vertical_meter(dimen, len, _bgc0, _bgc1, _bgc2, _bgc3);
-               pixheight = len;
-               pixwidth = dimen;
-       } else {
-               if (!len) {
-                       len = 186; // interesting size, eh?
-               }
-               fgpattern = request_horizontal_meter(len, dimen, clr0, clr1, clr2, clr3);
-               bgpattern = request_horizontal_meter(len, dimen, _bgc0, _bgc1, _bgc2, _bgc3);
-               pixheight = dimen;
-               pixwidth = len;
+       if (!len) {
+               len = 250;
        }
+       fgpattern = request_vertical_meter(dimen, len, _clr, _stp);
+       bgpattern = request_vertical_background (dimen, len, _bgc);
+       pixheight = len;
+       pixwidth = dimen;
 
-       if (orientation == Vertical) {
-               pixrect.width = pixwidth;
-               pixrect.height = pixheight;
-       } else {
-               pixrect.width = pixwidth;
-               pixrect.height = min (pixheight, (gint) dimen);
-       }
+       pixrect.width = pixwidth;
+       pixrect.height = pixheight;
 
        request_width = pixrect.width + 2;
        request_height= pixrect.height + 2;
 }
 
+FastMeter::~FastMeter ()
+{
+}
+
 Cairo::RefPtr<Cairo::Pattern>
 FastMeter::generate_meter_pattern (
-               int width, int height, int clr0, int clr1, int clr2, int clr3)
+               int width, int height, int *clr, float *stp)
 {
-       guint8 r0,g0,b0,r1,g1,b1,r2,g2,b2,r3,g3,b3,a;
-
-       /*
-         The knee is the hard transition point (e.g. at 0dB where the colors
-         change dramatically to make clipping apparent). Thus there are two
-         gradients in the pattern, the "normal range" and the "clip range", which
-         are separated at the knee point.
-
-         clr0: color at bottom of normal range gradient
-         clr1: color at top of normal range gradient
-         clr2: color at bottom of clip range gradient
-         clr3: color at top of clip range gradient
-       */
+       guint8 r,g,b,a;
+       double knee;
+       double soft = 1.5 / (double) height;
 
-       UINT_TO_RGBA (clr0, &r0, &g0, &b0, &a);
-       UINT_TO_RGBA (clr1, &r1, &g1, &b1, &a);
-       UINT_TO_RGBA (clr2, &r2, &g2, &b2, &a);
-       UINT_TO_RGBA (clr3, &r3, &g3, &b3, &a);
-
-       // fake log calculation copied from log_meter.h
-       // actual calculation:
-       // log_meter(0.0f) =
-       //  def = (0.0f + 20.0f) * 2.5f + 50f
-       //  return def / 115.0f
-
-       const int knee = (int)floor((float)height * 100.0f / 115.0f);
-       cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, width, height);
+       cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
 
        /*
          Cairo coordinate space goes downwards as y value goes up, so invert
          knee-based positions by using (1.0 - y)
        */
 
-       // Clip range top
+       UINT_TO_RGBA (clr[9], &r, &g, &b, &a); // top/clip
        cairo_pattern_add_color_stop_rgb (pat, 0.0,
-                                         r3/255.0, g3/255.0, b3/255.0);
+                                         r/255.0, g/255.0, b/255.0);
+
+       knee = ((float)height * stp[3] / 115.0f); // -0dB
+
+       UINT_TO_RGBA (clr[8], &r, &g, &b, &a);
+       cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height) - soft,
+                                         r/255.0, g/255.0, b/255.0);
+
+       UINT_TO_RGBA (clr[7], &r, &g, &b, &a);
+       cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height) + soft,
+                                         r/255.0, g/255.0, b/255.0);
+
+       knee = ((float)height * stp[2]/ 115.0f); // -3dB || -2dB
+
+       UINT_TO_RGBA (clr[6], &r, &g, &b, &a);
+       cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height) - soft,
+                                         r/255.0, g/255.0, b/255.0);
+
+       UINT_TO_RGBA (clr[5], &r, &g, &b, &a);
+       cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height) + soft,
+                                         r/255.0, g/255.0, b/255.0);
+
+       knee = ((float)height * stp[1] / 115.0f); // -9dB
+
+       UINT_TO_RGBA (clr[4], &r, &g, &b, &a);
+       cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height) - soft,
+                                         r/255.0, g/255.0, b/255.0);
+
+       UINT_TO_RGBA (clr[3], &r, &g, &b, &a);
+       cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height) + soft,
+                                         r/255.0, g/255.0, b/255.0);
+
+       knee = ((float)height * stp[0] / 115.0f); // -18dB
+
+       UINT_TO_RGBA (clr[2], &r, &g, &b, &a);
+       cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height) - soft,
+                                         r/255.0, g/255.0, b/255.0);
+
+       UINT_TO_RGBA (clr[1], &r, &g, &b, &a);
+       cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height) + soft,
+                                         r/255.0, g/255.0, b/255.0);
+
+       UINT_TO_RGBA (clr[0], &r, &g, &b, &a); // bottom
+       cairo_pattern_add_color_stop_rgb (pat, 1.0,
+                                         r/255.0, g/255.0, b/255.0);
+
+       if (1) { // TODO Config->get_meter_shade()
+               cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
+               cairo_pattern_add_color_stop_rgba (shade_pattern, 0, 1.0, 1.0, 1.0, 0.2);
+               cairo_pattern_add_color_stop_rgba (shade_pattern, 1, 0.0, 0.0, 0.0, 0.3);
+
+               cairo_surface_t* surface;
+               cairo_t* tc = 0;
+               surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+               tc = cairo_create (surface);
+               cairo_set_source (tc, pat);
+               cairo_rectangle (tc, 0, 0, width, height);
+               cairo_fill (tc);
+               cairo_set_source (tc, shade_pattern);
+               cairo_rectangle (tc, 0, 0, width, height);
+               cairo_fill (tc);
+
+               cairo_pattern_destroy (pat);
+               cairo_pattern_destroy (shade_pattern);
+
+               pat = cairo_pattern_create_for_surface (surface);
+
+               cairo_destroy (tc);
+               cairo_surface_destroy (surface);
+       }
 
-       // Clip range bottom
-       cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height),
-                                         r2/255.0, g2/255.0, b2/255.0);
+       Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
+
+       return p;
+}
+
+
+Cairo::RefPtr<Cairo::Pattern>
+FastMeter::generate_meter_background (
+               int width, int height, int *clr)
+{
+       guint8 r0,g0,b0,r1,g1,b1,a;
 
-       // Normal range top (double-stop at knee)
-       cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height),
+       cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, width, height);
+
+       UINT_TO_RGBA (clr[0], &r0, &g0, &b0, &a);
+       UINT_TO_RGBA (clr[1], &r1, &g1, &b1, &a);
+
+       cairo_pattern_add_color_stop_rgb (pat, 0.0,
                                          r1/255.0, g1/255.0, b1/255.0);
 
-       // Normal range bottom
        cairo_pattern_add_color_stop_rgb (pat, 1.0,
-                                         r0/255.0, g0/255.0, b0/255.0); // top
+                                         r0/255.0, g0/255.0, b0/255.0);
 
        Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
 
@@ -158,60 +219,55 @@ FastMeter::generate_meter_pattern (
 
 Cairo::RefPtr<Cairo::Pattern>
 FastMeter::request_vertical_meter(
-               int width, int height, int clr0, int clr1, int clr2, int clr3)
+               int width, int height, int *clr, float *stp)
 {
        if (height < min_pattern_metric_size)
                height = min_pattern_metric_size;
        if (height > max_pattern_metric_size)
                height = max_pattern_metric_size;
 
-       const PatternMapKey key (width, height, clr0, clr1, clr2, clr3);
-       PatternMap::iterator i;
-       if ((i = v_pattern_cache.find (key)) != v_pattern_cache.end()) {
+       const Pattern10MapKey key (width, height,
+                       stp[0], stp[1], stp[2], stp[3],
+                       clr[0], clr[1], clr[2], clr[3],
+                       clr[4], clr[5], clr[6], clr[7],
+                       clr[8], clr[9]);
+
+       Pattern10Map::iterator i;
+       if ((i = vm_pattern_cache.find (key)) != vm_pattern_cache.end()) {
                return i->second;
        }
+       // TODO flush pattern cache if it gets too large
 
        Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
-               width, height, clr0, clr1, clr2, clr3);
-       v_pattern_cache[key] = p;
+               width, height, clr, stp);
+       vm_pattern_cache[key] = p;
 
        return p;
 }
 
 Cairo::RefPtr<Cairo::Pattern>
-FastMeter::request_horizontal_meter(
-               int width, int height, int clr0, int clr1, int clr2, int clr3)
+FastMeter::request_vertical_background(
+               int width, int height, int *bgc)
 {
-       if (width < min_pattern_metric_size)
-               width = min_pattern_metric_size;
-       if (width > max_pattern_metric_size)
-               width = max_pattern_metric_size;
-
-       const PatternMapKey key (width, height, clr0, clr1, clr2, clr3);
-       PatternMap::iterator i;
-       if ((i = h_pattern_cache.find (key)) != h_pattern_cache.end()) {
+       if (height < min_pattern_metric_size)
+               height = min_pattern_metric_size;
+       if (height > max_pattern_metric_size)
+               height = max_pattern_metric_size;
+
+       const PatternBgMapKey key (width, height, bgc[0], bgc[1]);
+       PatternBgMap::iterator i;
+       if ((i = vb_pattern_cache.find (key)) != vb_pattern_cache.end()) {
                return i->second;
        }
+       // TODO flush pattern cache if it gets too large
 
-       /* flip height/width so that we get the right pattern */
-
-       Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
-               height, width, clr0, clr1, clr2, clr3);
-
-       /* rotate to make it horizontal */
-
-       cairo_matrix_t m;
-       cairo_matrix_init_rotate (&m, -M_PI/2.0);
-       cairo_pattern_set_matrix (p->cobj(), &m);
-
-       h_pattern_cache[key] = p;
+       Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (
+               width, height, bgc);
+       vb_pattern_cache[key] = p;
 
        return p;
 }
 
-FastMeter::~FastMeter ()
-{
-}
 
 void
 FastMeter::set_hold_count (long val)
@@ -230,74 +286,34 @@ FastMeter::set_hold_count (long val)
 void
 FastMeter::on_size_request (GtkRequisition* req)
 {
-       if (orientation == Vertical) {
-
-               req->height = request_height;
-               req->height = max(req->height, min_pattern_metric_size);
-               req->height = min(req->height, max_pattern_metric_size);
-               req->height += 2;
-
-               req->width  = request_width;
-
-       } else {
-
-               req->width  = request_width;
-               req->width  = max(req->width,  min_pattern_metric_size);
-               req->width  = min(req->width,  max_pattern_metric_size);
-
-               req->height = request_height;
-       }
+       req->height = request_height;
+       req->height = max(req->height, min_pattern_metric_size);
+       req->height = min(req->height, max_pattern_metric_size);
+       req->height += 2;
 
+       req->width  = request_width;
 }
 
 void
 FastMeter::on_size_allocate (Gtk::Allocation &alloc)
 {
-       if (orientation == Vertical) {
-
-               if (alloc.get_width() != request_width) {
-                       alloc.set_width (request_width);
-               }
-
-               int h = alloc.get_height();
-               h = max (h, min_pattern_metric_size + 2);
-               h = min (h, max_pattern_metric_size + 2);
-
-               if (h != alloc.get_height()) {
-                       alloc.set_height (h);
-               }
-
-               if (pixheight != h) {
-                       fgpattern = request_vertical_meter (
-                               request_width, h, _clr0, _clr1, _clr2, _clr3);
-                       bgpattern = request_vertical_meter (
-                               request_width, h, _bgc0, _bgc1, _bgc2, _bgc3);
-                       pixheight = h - 2;
-                       pixwidth  = request_width - 2;
-               }
-
-       } else {
-
-               if (alloc.get_height() != request_height) {
-                       alloc.set_height(request_height);
-               }
+       if (alloc.get_width() != request_width) {
+               alloc.set_width (request_width);
+       }
 
-               int w = alloc.get_width();
-               w = max (w, min_pattern_metric_size);
-               w = min (w, max_pattern_metric_size);
+       int h = alloc.get_height();
+       h = max (h, min_pattern_metric_size + 2);
+       h = min (h, max_pattern_metric_size + 2);
 
-               if (w != alloc.get_width()) {
-                       alloc.set_width (w);
-               }
+       if (h != alloc.get_height()) {
+               alloc.set_height (h);
+       }
 
-               if (pixwidth != w) {
-                       fgpattern = request_horizontal_meter (
-                               w, request_height, _clr0, _clr1, _clr2, _clr3);
-                       bgpattern = request_horizontal_meter (
-                               w, request_height, _bgc0, _bgc1, _bgc2, _bgc3);
-                       pixheight = request_height;
-                       pixwidth  = w;
-               }
+       if (pixheight != h) {
+               fgpattern = request_vertical_meter (request_width, h, _clr, _stp);
+               bgpattern = request_vertical_background (request_width, h, _bgc);
+               pixheight = h - 2;
+               pixwidth  = request_width - 2;
        }
 
        DrawingArea::on_size_allocate (alloc);
@@ -307,11 +323,7 @@ FastMeter::on_size_allocate (Gtk::Allocation &alloc)
 bool
 FastMeter::on_expose_event (GdkEventExpose* ev)
 {
-       if (orientation == Vertical) {
-               return vertical_expose (ev);
-       } else {
-               return horizontal_expose (ev);
-       }
+       return vertical_expose (ev);
 }
 
 bool
@@ -365,7 +377,7 @@ FastMeter::vertical_expose (GdkEventExpose* ev)
                last_peak_rect.x = 1;
                last_peak_rect.width = pixwidth;
                last_peak_rect.y = 1 + pixheight - (gint) floor (pixheight * current_peak);
-               last_peak_rect.height = min(3, pixheight - last_peak_rect.y);
+               last_peak_rect.height = min(2, pixheight - last_peak_rect.y);
 
                cairo_set_source (cr, fgpattern->cobj());
                cairo_rectangle (cr, 1, last_peak_rect.y, pixwidth, last_peak_rect.height);
@@ -381,61 +393,6 @@ FastMeter::vertical_expose (GdkEventExpose* ev)
        return TRUE;
 }
 
-bool
-FastMeter::horizontal_expose (GdkEventExpose* ev)
-{
-       Glib::RefPtr<Gdk::Window> win = get_window ();
-       gint right_of_meter;
-       GdkRectangle intersection;
-       GdkRectangle background;
-
-       cairo_t* cr = gdk_cairo_create (get_window ()->gobj());
-       cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
-       cairo_clip (cr);
-
-       right_of_meter = (gint) floor (pixwidth * current_level);
-       pixrect.width = right_of_meter;
-
-       background.x = 0;
-       background.y = 0;
-       background.width  = pixwidth - right_of_meter;
-       background.height = pixrect.height;
-
-       if (gdk_rectangle_intersect (&background, &ev->area, &intersection)) {
-               cairo_set_source_rgb (cr, 0, 0, 0); // black
-               cairo_rectangle (cr, intersection.x + right_of_meter, intersection.y, intersection.width, intersection.height);
-               cairo_fill (cr);
-       }
-
-       if (gdk_rectangle_intersect (&pixrect, &ev->area, &intersection)) {
-               // draw the part of the meter image that we need. the area we draw is bounded "in reverse" (top->bottom)
-               cairo_matrix_t m;
-               cairo_matrix_init_translate (&m, -intersection.x, -intersection.y);
-               cairo_pattern_set_matrix (fgpattern->cobj(), &m);
-               cairo_set_source (cr, fgpattern->cobj());
-               cairo_rectangle (cr, intersection.x, intersection.y, pixrect.width, intersection.height);
-               cairo_fill (cr);
-       }
-
-       // draw peak bar
-       // XXX: peaks don't work properly
-       /*
-       if (hold_state && intersection.height > 0) {
-               gint x = (gint) floor(pixwidth * current_peak);
-
-               get_window()->draw_pixbuf (get_style()->get_fg_gc(get_state()), pixbuf,
-                                          x, intersection.y,
-                                          x, intersection.y,
-                                          3, intersection.height,
-                                          Gdk::RGB_DITHER_NONE, 0, 0);
-       }
-       */
-
-       cairo_destroy (cr);
-
-       return true;
-}
-
 void
 FastMeter::set (float lvl)
 {
@@ -467,11 +424,7 @@ FastMeter::set (float lvl)
                return;
        }
 
-       if (orientation == Vertical) {
-               queue_vertical_redraw (win, old_level);
-       } else {
-               queue_horizontal_redraw (win, old_level);
-       }
+       queue_vertical_redraw (win, old_level);
 }
 
 void
@@ -536,13 +489,6 @@ FastMeter::queue_vertical_redraw (const Glib::RefPtr<Gdk::Window>& win, float ol
        }
 }
 
-void
-FastMeter::queue_horizontal_redraw (const Glib::RefPtr<Gdk::Window>& /*win*/, float /*old_level*/)
-{
-       /* XXX OPTIMIZE (when we have some horizontal meters) */
-       queue_draw ();
-}
-
 void
 FastMeter::clear ()
 {