ardour
fastmeter.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003-2006 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18  $Id$
19 */
20 
21 #include <iostream>
22 #include <cmath>
23 #include <algorithm>
24 #include <cstring>
25 
26 #include <stdlib.h>
27 
28 #include <glibmm.h>
29 #include <gdkmm.h>
30 #include <gdkmm/rectangle.h>
31 #include <gtkmm2ext/fastmeter.h>
32 #include <gtkmm2ext/utils.h>
33 
34 #define UINT_TO_RGB(u,r,g,b) { (*(r)) = ((u)>>16)&0xff; (*(g)) = ((u)>>8)&0xff; (*(b)) = (u)&0xff; }
35 #define UINT_TO_RGBA(u,r,g,b,a) { UINT_TO_RGB(((u)>>8),r,g,b); (*(a)) = (u)&0xff; }
36 
37 using namespace Gtk;
38 using namespace Glib;
39 using namespace Gtkmm2ext;
40 using namespace std;
41 
42 int FastMeter::min_pattern_metric_size = 16;
44 bool FastMeter::no_rgba_overlay = false;
45 
46 FastMeter::Pattern10Map FastMeter::vm_pattern_cache;
47 FastMeter::PatternBgMap FastMeter::vb_pattern_cache;
48 
49 FastMeter::Pattern10Map FastMeter::hm_pattern_cache;
50 FastMeter::PatternBgMap FastMeter::hb_pattern_cache;
51 
52 FastMeter::FastMeter (long hold, unsigned long dimen, Orientation o, int len,
53  int clr0, int clr1, int clr2, int clr3,
54  int clr4, int clr5, int clr6, int clr7,
55  int clr8, int clr9,
56  int bgc0, int bgc1,
57  int bgh0, int bgh1,
58  float stp0, float stp1,
59  float stp2, float stp3,
60  int styleflags
61  )
62  : pixheight(0)
63  , pixwidth(0)
64  , _styleflags(styleflags)
65  , orientation(o)
66  , hold_cnt(hold)
67  , hold_state(0)
68  , bright_hold(false)
69  , current_level(0)
70  , current_peak(0)
71  , highlight(false)
72 {
73  last_peak_rect.width = 0;
74  last_peak_rect.height = 0;
75  last_peak_rect.x = 0;
76  last_peak_rect.y = 0;
77 
78  no_rgba_overlay = ! Glib::getenv("NO_METER_SHADE").empty();
79 
80  _clr[0] = clr0;
81  _clr[1] = clr1;
82  _clr[2] = clr2;
83  _clr[3] = clr3;
84  _clr[4] = clr4;
85  _clr[5] = clr5;
86  _clr[6] = clr6;
87  _clr[7] = clr7;
88  _clr[8] = clr8;
89  _clr[9] = clr9;
90 
91  _bgc[0] = bgc0;
92  _bgc[1] = bgc1;
93 
94  _bgh[0] = bgh0;
95  _bgh[1] = bgh1;
96 
97  _stp[0] = stp0;
98  _stp[1] = stp1;
99  _stp[2] = stp2;
100  _stp[3] = stp3;
101 
102  set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
103 
104  pixrect.x = 1;
105  pixrect.y = 1;
106 
107  if (!len) {
108  len = 250;
109  }
110  if (orientation == Vertical) {
111  pixheight = len;
112  pixwidth = dimen;
115 
116  } else {
117  pixheight = dimen;
118  pixwidth = len;
121  }
122 
123  pixrect.width = pixwidth;
124  pixrect.height = pixheight;
125 
126  request_width = pixrect.width + 2;
127  request_height= pixrect.height + 2;
128 
129  clear ();
130 }
131 
133 {
134 }
135 
136 void
138  Pattern10Map::iterator i1;
139  PatternBgMap::iterator ib;
140  for (ib = hb_pattern_cache.begin(); ib != hb_pattern_cache.end(); ++ib) {
141  hb_pattern_cache.erase(ib);
142  }
143  for (i1 = hm_pattern_cache.begin(); i1 != hm_pattern_cache.end(); ++i1) {
144  hm_pattern_cache.erase(i1);
145  }
146  for (ib = vb_pattern_cache.begin(); ib != vb_pattern_cache.end(); ++ib) {
147  vb_pattern_cache.erase(ib);
148  }
149  for (i1 = vm_pattern_cache.begin(); i1 != vm_pattern_cache.end(); ++i1) {
150  vm_pattern_cache.erase(i1);
151  }
152 }
153 
154 Cairo::RefPtr<Cairo::Pattern>
156  int width, int height, int *clr, float *stp, int styleflags, bool horiz)
157 {
158  guint8 r,g,b,a;
159  double knee;
160  const double soft = 3.0 / (double) height;
161  const double offs = -1.0 / (double) height;
162 
163  cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
164 
165  /*
166  Cairo coordinate space goes downwards as y value goes up, so invert
167  knee-based positions by using (1.0 - y)
168  */
169 
170  UINT_TO_RGBA (clr[9], &r, &g, &b, &a); // top/clip
171  cairo_pattern_add_color_stop_rgb (pat, 0.0,
172  r/255.0, g/255.0, b/255.0);
173 
174  knee = offs + stp[3] / 115.0f; // -0dB
175 
176  UINT_TO_RGBA (clr[8], &r, &g, &b, &a);
177  cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
178  r/255.0, g/255.0, b/255.0);
179 
180  UINT_TO_RGBA (clr[7], &r, &g, &b, &a);
181  cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
182  r/255.0, g/255.0, b/255.0);
183 
184  knee = offs + stp[2]/ 115.0f; // -3dB || -2dB
185 
186  UINT_TO_RGBA (clr[6], &r, &g, &b, &a);
187  cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
188  r/255.0, g/255.0, b/255.0);
189 
190  UINT_TO_RGBA (clr[5], &r, &g, &b, &a);
191  cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
192  r/255.0, g/255.0, b/255.0);
193 
194  knee = offs + stp[1] / 115.0f; // -9dB
195 
196  UINT_TO_RGBA (clr[4], &r, &g, &b, &a);
197  cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
198  r/255.0, g/255.0, b/255.0);
199 
200  UINT_TO_RGBA (clr[3], &r, &g, &b, &a);
201  cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
202  r/255.0, g/255.0, b/255.0);
203 
204  knee = offs + stp[0] / 115.0f; // -18dB
205 
206  UINT_TO_RGBA (clr[2], &r, &g, &b, &a);
207  cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
208  r/255.0, g/255.0, b/255.0);
209 
210  UINT_TO_RGBA (clr[1], &r, &g, &b, &a);
211  cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
212  r/255.0, g/255.0, b/255.0);
213 
214  UINT_TO_RGBA (clr[0], &r, &g, &b, &a); // bottom
215  cairo_pattern_add_color_stop_rgb (pat, 1.0,
216  r/255.0, g/255.0, b/255.0);
217 
218  if ((styleflags & 1) && !no_rgba_overlay) {
219  cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
220  cairo_pattern_add_color_stop_rgba (shade_pattern, 0, 0.0, 0.0, 0.0, 0.15);
221  cairo_pattern_add_color_stop_rgba (shade_pattern, 0.4, 1.0, 1.0, 1.0, 0.05);
222  cairo_pattern_add_color_stop_rgba (shade_pattern, 1, 0.0, 0.0, 0.0, 0.25);
223 
224  cairo_surface_t* surface;
225  cairo_t* tc = 0;
226  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
227  tc = cairo_create (surface);
228  cairo_set_source (tc, pat);
229  cairo_rectangle (tc, 0, 0, width, height);
230  cairo_fill (tc);
231  cairo_pattern_destroy (pat);
232 
233  cairo_set_source (tc, shade_pattern);
234  cairo_rectangle (tc, 0, 0, width, height);
235  cairo_fill (tc);
236  cairo_pattern_destroy (shade_pattern);
237 
238  if (styleflags & 2) { // LED stripes
239  cairo_save (tc);
240  cairo_set_line_width(tc, 1.0);
241  cairo_set_source_rgba(tc, .0, .0, .0, 0.4);
242  //cairo_set_operator (tc, CAIRO_OPERATOR_SOURCE);
243  for (float y=0.5; y < height; y+= 2.0) {
244  cairo_move_to(tc, 0, y);
245  cairo_line_to(tc, width, y);
246  cairo_stroke (tc);
247  }
248  cairo_restore (tc);
249  }
250 
251  pat = cairo_pattern_create_for_surface (surface);
252  cairo_destroy (tc);
253  cairo_surface_destroy (surface);
254  }
255 
256  if (horiz) {
257  cairo_surface_t* surface;
258  cairo_t* tc = 0;
259  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width);
260  tc = cairo_create (surface);
261 
262  cairo_matrix_t m;
263  cairo_matrix_init_rotate (&m, -M_PI/2.0);
264  cairo_matrix_translate (&m, -height, 0);
265  cairo_pattern_set_matrix (pat, &m);
266  cairo_set_source (tc, pat);
267  cairo_rectangle (tc, 0, 0, height, width);
268  cairo_fill (tc);
269  cairo_pattern_destroy (pat);
270  pat = cairo_pattern_create_for_surface (surface);
271  cairo_destroy (tc);
272  cairo_surface_destroy (surface);
273  }
274  Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
275 
276  return p;
277 }
278 
279 
280 Cairo::RefPtr<Cairo::Pattern>
282  int width, int height, int *clr, bool shade, bool horiz)
283 {
284  guint8 r0,g0,b0,r1,g1,b1,a;
285 
286  cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
287 
288  UINT_TO_RGBA (clr[0], &r0, &g0, &b0, &a);
289  UINT_TO_RGBA (clr[1], &r1, &g1, &b1, &a);
290 
291  cairo_pattern_add_color_stop_rgb (pat, 0.0,
292  r1/255.0, g1/255.0, b1/255.0);
293 
294  cairo_pattern_add_color_stop_rgb (pat, 1.0,
295  r0/255.0, g0/255.0, b0/255.0);
296 
297  if (shade && !no_rgba_overlay) {
298  cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
299  cairo_pattern_add_color_stop_rgba (shade_pattern, 0.0, 1.0, 1.0, 1.0, 0.15);
300  cairo_pattern_add_color_stop_rgba (shade_pattern, 0.6, 0.0, 0.0, 0.0, 0.10);
301  cairo_pattern_add_color_stop_rgba (shade_pattern, 1.0, 1.0, 1.0, 1.0, 0.20);
302 
303  cairo_surface_t* surface;
304  cairo_t* tc = 0;
305  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
306  tc = cairo_create (surface);
307  cairo_set_source (tc, pat);
308  cairo_rectangle (tc, 0, 0, width, height);
309  cairo_fill (tc);
310  cairo_set_source (tc, shade_pattern);
311  cairo_rectangle (tc, 0, 0, width, height);
312  cairo_fill (tc);
313 
314  cairo_pattern_destroy (pat);
315  cairo_pattern_destroy (shade_pattern);
316 
317  pat = cairo_pattern_create_for_surface (surface);
318 
319  cairo_destroy (tc);
320  cairo_surface_destroy (surface);
321  }
322 
323  if (horiz) {
324  cairo_surface_t* surface;
325  cairo_t* tc = 0;
326  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width);
327  tc = cairo_create (surface);
328 
329  cairo_matrix_t m;
330  cairo_matrix_init_rotate (&m, -M_PI/2.0);
331  cairo_matrix_translate (&m, -height, 0);
332  cairo_pattern_set_matrix (pat, &m);
333  cairo_set_source (tc, pat);
334  cairo_rectangle (tc, 0, 0, height, width);
335  cairo_fill (tc);
336  cairo_pattern_destroy (pat);
337  pat = cairo_pattern_create_for_surface (surface);
338  cairo_destroy (tc);
339  cairo_surface_destroy (surface);
340  }
341 
342  Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
343 
344  return p;
345 }
346 
347 Cairo::RefPtr<Cairo::Pattern>
349  int width, int height, int *clr, float *stp, int styleflags)
350 {
351  height = max(height, min_pattern_metric_size);
352  height = min(height, max_pattern_metric_size);
353 
354  const Pattern10MapKey key (width, height,
355  stp[0], stp[1], stp[2], stp[3],
356  clr[0], clr[1], clr[2], clr[3],
357  clr[4], clr[5], clr[6], clr[7],
358  clr[8], clr[9], styleflags);
359 
360  Pattern10Map::iterator i;
361  if ((i = vm_pattern_cache.find (key)) != vm_pattern_cache.end()) {
362  return i->second;
363  }
364  // TODO flush pattern cache if it gets too large
365 
366  Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
367  width, height, clr, stp, styleflags, false);
368  vm_pattern_cache[key] = p;
369 
370  return p;
371 }
372 
373 Cairo::RefPtr<Cairo::Pattern>
375  int width, int height, int *bgc, bool shade)
376 {
377  height = max(height, min_pattern_metric_size);
378  height = min(height, max_pattern_metric_size);
379  height += 2;
380 
381  const PatternBgMapKey key (width, height, bgc[0], bgc[1], shade);
382  PatternBgMap::iterator i;
383  if ((i = vb_pattern_cache.find (key)) != vb_pattern_cache.end()) {
384  return i->second;
385  }
386  // TODO flush pattern cache if it gets too large
387 
388  Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (
389  width, height, bgc, shade, false);
390  vb_pattern_cache[key] = p;
391 
392  return p;
393 }
394 
395 Cairo::RefPtr<Cairo::Pattern>
397  int width, int height, int *clr, float *stp, int styleflags)
398 {
399  width = max(width, min_pattern_metric_size);
400  width = min(width, max_pattern_metric_size);
401 
402  const Pattern10MapKey key (width, height,
403  stp[0], stp[1], stp[2], stp[3],
404  clr[0], clr[1], clr[2], clr[3],
405  clr[4], clr[5], clr[6], clr[7],
406  clr[8], clr[9], styleflags);
407 
408  Pattern10Map::iterator i;
409  if ((i = hm_pattern_cache.find (key)) != hm_pattern_cache.end()) {
410  return i->second;
411  }
412  // TODO flush pattern cache if it gets too large
413 
414  Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
415  height, width, clr, stp, styleflags, true);
416 
417  hm_pattern_cache[key] = p;
418  return p;
419 }
420 
421 Cairo::RefPtr<Cairo::Pattern>
423  int width, int height, int *bgc, bool shade)
424 {
425  width = max(width, min_pattern_metric_size);
426  width = min(width, max_pattern_metric_size);
427  width += 2;
428 
429  const PatternBgMapKey key (width, height, bgc[0], bgc[1], shade);
430  PatternBgMap::iterator i;
431  if ((i = hb_pattern_cache.find (key)) != hb_pattern_cache.end()) {
432  return i->second;
433  }
434  // TODO flush pattern cache if it gets too large
435 
436  Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (
437  height, width, bgc, shade, true);
438 
439  hb_pattern_cache[key] = p;
440 
441  return p;
442 }
443 
444 
445 
446 void
448 {
449  if (val < 1) {
450  val = 1;
451  }
452 
453  hold_cnt = val;
454  hold_state = 0;
455  current_peak = 0;
456 
457  queue_draw ();
458 }
459 
460 void
461 FastMeter::on_size_request (GtkRequisition* req)
462 {
463  if (orientation == Vertical) {
464  vertical_size_request (req);
465  } else {
467  }
468 }
469 
470 void
471 FastMeter::vertical_size_request (GtkRequisition* req)
472 {
473  req->height = request_height;
474  req->height = max(req->height, min_pattern_metric_size);
475  req->height = min(req->height, max_pattern_metric_size);
476  req->height += 2;
477 
478  req->width = request_width;
479 }
480 
481 void
483 {
484  req->width = request_width;
485  req->width = max(req->width, min_pattern_metric_size);
486  req->width = min(req->width, max_pattern_metric_size);
487  req->width += 2;
488 
489  req->height = request_height;
490 }
491 
492 void
493 FastMeter::on_size_allocate (Gtk::Allocation &alloc)
494 {
495  if (orientation == Vertical) {
496  vertical_size_allocate (alloc);
497  } else {
498  horizontal_size_allocate (alloc);
499  }
500  queue_draw ();
501 }
502 
503 void
504 FastMeter::vertical_size_allocate (Gtk::Allocation &alloc)
505 {
506  if (alloc.get_width() != request_width) {
507  alloc.set_width (request_width);
508  }
509 
510  int h = alloc.get_height();
511  h = max (h, min_pattern_metric_size + 2);
512  h = min (h, max_pattern_metric_size + 2);
513 
514  if (h != alloc.get_height()) {
515  alloc.set_height (h);
516  }
517 
518  if (pixheight != h) {
521  pixheight = h - 2;
522  pixwidth = request_width - 2;
523  }
524 
526 }
527 
528 void
529 FastMeter::horizontal_size_allocate (Gtk::Allocation &alloc)
530 {
531  if (alloc.get_height() != request_height) {
532  alloc.set_height (request_height);
533  }
534 
535  int w = alloc.get_width();
536  w = max (w, min_pattern_metric_size + 2);
537  w = min (w, max_pattern_metric_size + 2);
538 
539  if (w != alloc.get_width()) {
540  alloc.set_width (w);
541  }
542 
543  if (pixwidth != w) {
546  pixwidth = w - 2;
548  }
549 
551 }
552 
553 void
554 FastMeter::render (cairo_t* cr, cairo_rectangle_t* area)
555 {
556  if (orientation == Vertical) {
557  return vertical_expose (cr, area);
558  } else {
559  return horizontal_expose (cr, area);
560  }
561 }
562 
563 void
564 FastMeter::vertical_expose (cairo_t* cr, cairo_rectangle_t* area)
565 {
566  gint top_of_meter;
567  GdkRectangle intersection;
568  GdkRectangle background;
569  GdkRectangle eventarea;
570 
571  cairo_set_source_rgb (cr, 0, 0, 0); // black
572  rounded_rectangle (cr, 0, 0, pixwidth + 2, pixheight + 2, 2);
573  cairo_stroke (cr);
574 
575  top_of_meter = (gint) floor (pixheight * current_level);
576 
577  /* reset the height & origin of the rect that needs to show the pixbuf
578  */
579 
580  pixrect.height = top_of_meter;
581  pixrect.y = 1 + pixheight - top_of_meter;
582 
583  background.x = 1;
584  background.y = 1;
585  background.width = pixrect.width;
586  background.height = pixheight - top_of_meter;
587 
588  eventarea.x = area->x;
589  eventarea.y = area->y;
590  eventarea.width = area->width;
591  eventarea.height = area->height;
592 
593  if (gdk_rectangle_intersect (&background, &eventarea, &intersection)) {
594  cairo_set_source (cr, bgpattern->cobj());
595  cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
596  cairo_fill (cr);
597  }
598 
599  if (gdk_rectangle_intersect (&pixrect, &eventarea, &intersection)) {
600  // draw the part of the meter image that we need. the area we draw is bounded "in reverse" (top->bottom)
601  cairo_set_source (cr, fgpattern->cobj());
602  cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
603  cairo_fill (cr);
604  }
605 
606  // draw peak bar
607 
608  if (hold_state) {
609  last_peak_rect.x = 1;
610  last_peak_rect.width = pixwidth;
611  last_peak_rect.y = max(1, 1 + pixheight - (int) floor (pixheight * current_peak));
612  if (_styleflags & 2) { // LED stripes
613  last_peak_rect.y = max(0, (last_peak_rect.y & (~1)));
614  }
615  if (bright_hold || (_styleflags & 2)) {
616  last_peak_rect.height = max(0, min(3, pixheight - last_peak_rect.y - 1 ));
617  } else {
618  last_peak_rect.height = max(0, min(2, pixheight - last_peak_rect.y - 1 ));
619  }
620 
621  cairo_set_source (cr, fgpattern->cobj());
622  cairo_rectangle (cr, last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height);
623 
624  if (bright_hold && !no_rgba_overlay) {
625  cairo_fill_preserve (cr);
626  cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.3);
627  }
628  cairo_fill (cr);
629 
630  } else {
631  last_peak_rect.width = 0;
632  last_peak_rect.height = 0;
633  }
634 }
635 
636 void
637 FastMeter::horizontal_expose (cairo_t* cr, cairo_rectangle_t* area)
638 {
639  gint right_of_meter;
640  GdkRectangle intersection;
641  GdkRectangle background;
642  GdkRectangle eventarea;
643 
644  cairo_set_source_rgb (cr, 0, 0, 0); // black
645  rounded_rectangle (cr, 0, 0, pixwidth + 2, pixheight + 2, 2);
646  cairo_stroke (cr);
647 
648  right_of_meter = (gint) floor (pixwidth * current_level);
649 
650  /* reset the height & origin of the rect that needs to show the pixbuf
651  */
652 
653  pixrect.width = right_of_meter;
654 
655  background.x = 1 + right_of_meter;
656  background.y = 1;
657  background.width = pixwidth - right_of_meter;
658  background.height = pixheight;
659 
660  eventarea.x = area->x;
661  eventarea.y = area->y;
662  eventarea.width = area->width;
663  eventarea.height = area->height;
664 
665  if (gdk_rectangle_intersect (&background, &eventarea, &intersection)) {
666  cairo_set_source (cr, bgpattern->cobj());
667  cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
668  cairo_fill (cr);
669  }
670 
671  if (gdk_rectangle_intersect (&pixrect, &eventarea, &intersection)) {
672  cairo_set_source (cr, fgpattern->cobj());
673  cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
674  cairo_fill (cr);
675  }
676 
677  // draw peak bar
678 
679  if (hold_state) {
680  last_peak_rect.y = 1;
681  last_peak_rect.height = pixheight;
682  const int xpos = floor (pixwidth * current_peak);
683  if (bright_hold || (_styleflags & 2)) {
684  last_peak_rect.width = min(3, xpos );
685  } else {
686  last_peak_rect.width = min(2, xpos );
687  }
688  last_peak_rect.x = 1 + max(0, xpos - last_peak_rect.width);
689 
690  cairo_set_source (cr, fgpattern->cobj());
691  cairo_rectangle (cr, last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height);
692 
693  if (bright_hold && !no_rgba_overlay) {
694  cairo_fill_preserve (cr);
695  cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.3);
696  }
697  cairo_fill (cr);
698 
699  } else {
700  last_peak_rect.width = 0;
701  last_peak_rect.height = 0;
702  }
703 }
704 
705 void
706 FastMeter::set (float lvl, float peak)
707 {
708  float old_level = current_level;
709  float old_peak = current_peak;
710 
711  if (pixwidth <= 0 || pixheight <=0) return;
712 
713  if (peak == -1) {
714  if (lvl >= current_peak) {
715  current_peak = lvl;
717  }
718 
719  if (hold_state > 0) {
720  if (--hold_state == 0) {
721  current_peak = lvl;
722  }
723  }
724  bright_hold = false;
725  } else {
726  current_peak = peak;
727  hold_state = 1;
728  bright_hold = true;
729  }
730 
731  current_level = lvl;
732 
733  const float pixscale = (orientation == Vertical) ? pixheight : pixwidth;
734 #define PIX(X) floor(pixscale * (X))
735  if (PIX(current_level) == PIX(old_level) && PIX(current_peak) == PIX(old_peak) && (hold_state == 0 || peak != -1)) {
736  return;
737  }
738 
739  Glib::RefPtr<Gdk::Window> win;
740 
741  if ((win = get_window()) == 0) {
742  queue_draw ();
743  return;
744  }
745 
746  if (orientation == Vertical) {
747  queue_vertical_redraw (win, old_level);
748  } else {
749  queue_horizontal_redraw (win, old_level);
750  }
751 }
752 
753 void
754 FastMeter::queue_vertical_redraw (const Glib::RefPtr<Gdk::Window>& win, float old_level)
755 {
756  GdkRectangle rect;
757 
758  gint new_top = (gint) floor (pixheight * current_level);
759 
760  rect.x = 1;
761  rect.width = pixwidth;
762  rect.height = new_top;
763  rect.y = 1 + pixheight - new_top;
764 
765  if (current_level > old_level) {
766  /* colored/pixbuf got larger, just draw the new section */
767  /* rect.y stays where it is because of X coordinates */
768  /* height of invalidated area is between new.y (smaller) and old.y
769  (larger).
770  X coordinates just make my brain hurt.
771  */
772  rect.height = pixrect.y - rect.y;
773  } else {
774  /* it got smaller, compute the difference */
775  /* rect.y becomes old.y (the smaller value) */
776  rect.y = pixrect.y;
777  /* rect.height is the old.y (smaller) minus the new.y (larger)
778  */
779  rect.height = pixrect.height - rect.height;
780  }
781 
782  GdkRegion* region = 0;
783  bool queue = false;
784 
785  if (rect.height != 0) {
786 
787  /* ok, first region to draw ... */
788 
789  region = gdk_region_rectangle (&rect);
790  queue = true;
791  }
792 
793  /* redraw the last place where the last peak hold bar was;
794  the next expose will draw the new one whether its part of
795  expose region or not.
796  */
797 
798  if (last_peak_rect.width * last_peak_rect.height != 0) {
799  if (!queue) {
800  region = gdk_region_new ();
801  queue = true;
802  }
803  gdk_region_union_with_rect (region, &last_peak_rect);
804  }
805 
806  if (hold_state && current_peak > 0) {
807  if (!queue) {
808  region = gdk_region_new ();
809  queue = true;
810  }
811  rect.x = 1;
812  rect.y = max(1, 1 + pixheight - (int) floor (pixheight * current_peak));
813  if (_styleflags & 2) { // LED stripes
814  rect.y = max(0, (rect.y & (~1)));
815  }
816  if (bright_hold || (_styleflags & 2)) {
817  rect.height = max(0, min(3, pixheight - last_peak_rect.y -1 ));
818  } else {
819  rect.height = max(0, min(2, pixheight - last_peak_rect.y -1 ));
820  }
821  rect.width = pixwidth;
822  gdk_region_union_with_rect (region, &rect);
823  }
824 
825  if (queue) {
826  gdk_window_invalidate_region (win->gobj(), region, true);
827  }
828  if (region) {
829  gdk_region_destroy(region);
830  region = 0;
831  }
832 }
833 
834 void
835 FastMeter::queue_horizontal_redraw (const Glib::RefPtr<Gdk::Window>& win, float old_level)
836 {
837  GdkRectangle rect;
838 
839  gint new_right = (gint) floor (pixwidth * current_level);
840 
841  rect.height = pixheight;
842  rect.y = 1;
843 
844  if (current_level > old_level) {
845  rect.x = 1 + pixrect.width;
846  /* colored/pixbuf got larger, just draw the new section */
847  rect.width = new_right - pixrect.width;
848  } else {
849  /* it got smaller, compute the difference */
850  rect.x = 1 + new_right;
851  /* rect.height is the old.x (smaller) minus the new.x (larger) */
852  rect.width = pixrect.width - new_right;
853  }
854 
855  GdkRegion* region = 0;
856  bool queue = false;
857 
858  if (rect.height != 0) {
859 
860  /* ok, first region to draw ... */
861 
862  region = gdk_region_rectangle (&rect);
863  queue = true;
864  }
865 
866  /* redraw the last place where the last peak hold bar was;
867  the next expose will draw the new one whether its part of
868  expose region or not.
869  */
870 
871  if (last_peak_rect.width * last_peak_rect.height != 0) {
872  if (!queue) {
873  region = gdk_region_new ();
874  queue = true;
875  }
876  gdk_region_union_with_rect (region, &last_peak_rect);
877  }
878 
879  if (hold_state && current_peak > 0) {
880  if (!queue) {
881  region = gdk_region_new ();
882  queue = true;
883  }
884  rect.y = 1;
885  rect.height = pixheight;
886  const int xpos = floor (pixwidth * current_peak);
887  if (bright_hold || (_styleflags & 2)) {
888  rect.width = min(3, xpos);
889  } else {
890  rect.width = min(2, xpos);
891  }
892  rect.x = 1 + max(0, xpos - rect.width);
893  gdk_region_union_with_rect (region, &rect);
894  }
895 
896  if (queue) {
897  gdk_window_invalidate_region (win->gobj(), region, true);
898  }
899  if (region) {
900  gdk_region_destroy(region);
901  region = 0;
902  }
903 }
904 
905 void
907 {
908  if (highlight == onoff) {
909  return;
910  }
911  highlight = onoff;
912  if (orientation == Vertical) {
914  } else {
916  }
917  queue_draw ();
918 }
919 
920 void
922 {
923  current_level = 0;
924  current_peak = 0;
925  hold_state = 0;
926  queue_draw ();
927 }
void render(cairo_t *, cairo_rectangle_t *)
Definition: fastmeter.cc:554
std::map< Pattern10MapKey, Cairo::RefPtr< Cairo::Pattern > > Pattern10Map
Definition: fastmeter.h:149
virtual ~FastMeter()
Definition: fastmeter.cc:132
std::map< PatternBgMapKey, Cairo::RefPtr< Cairo::Pattern > > PatternBgMap
Definition: fastmeter.h:164
void horizontal_expose(cairo_t *, cairo_rectangle_t *)
Definition: fastmeter.cc:637
static void flush_pattern_cache()
Definition: fastmeter.cc:137
Definition: ardour_ui.h:130
static Pattern10Map hm_pattern_cache
Definition: fastmeter.h:168
Cairo::RefPtr< Cairo::Pattern > bgpattern
Definition: fastmeter.h:76
static bool no_rgba_overlay
Definition: fastmeter.h:109
Definition: Beats.hpp:239
Orientation orientation
Definition: fastmeter.h:86
unsigned long hold_state
Definition: fastmeter.h:92
void horizontal_size_request(GtkRequisition *)
Definition: fastmeter.cc:482
#define UINT_TO_RGBA(u, r, g, b, a)
Definition: fastmeter.cc:35
static const int max_pattern_metric_size
static PatternBgMap hb_pattern_cache
Definition: fastmeter.h:169
static Cairo::RefPtr< Cairo::Pattern > generate_meter_background(int, int, int *, bool, bool)
Definition: fastmeter.cc:281
static Cairo::RefPtr< Cairo::Pattern > request_vertical_background(int, int, int *, bool)
Definition: fastmeter.cc:374
void set(float level, float peak=-1)
Definition: fastmeter.cc:706
Cairo::RefPtr< Cairo::Pattern > fgpattern
Definition: fastmeter.h:75
static Cairo::RefPtr< Cairo::Pattern > request_horizontal_meter(int, int, int *, float *, int)
Definition: fastmeter.cc:396
static Pattern10Map vm_pattern_cache
Definition: fastmeter.h:166
void vertical_size_request(GtkRequisition *)
Definition: fastmeter.cc:471
void vertical_size_allocate(Gtk::Allocation &)
Definition: fastmeter.cc:504
void queue_vertical_redraw(const Glib::RefPtr< Gdk::Window > &, float)
Definition: fastmeter.cc:754
unsigned long hold_cnt
Definition: fastmeter.h:91
GdkRectangle pixrect
Definition: fastmeter.h:87
static int min_pattern_metric_size
Definition: fastmeter.h:170
LIBGTKMM2EXT_API void rounded_rectangle(Cairo::RefPtr< Cairo::Context > context, double x, double y, double w, double h, double r=10)
Definition: utils.cc:520
void set_highlight(bool)
Definition: fastmeter.cc:906
static Cairo::RefPtr< Cairo::Pattern > request_vertical_meter(int, int, int *, float *, int)
Definition: fastmeter.cc:348
void on_size_request(GtkRequisition *)
Definition: fastmeter.cc:461
void vertical_expose(cairo_t *, cairo_rectangle_t *)
Definition: fastmeter.cc:564
void queue_horizontal_redraw(const Glib::RefPtr< Gdk::Window > &, float)
Definition: fastmeter.cc:835
void on_size_allocate(Gtk::Allocation &)
Definition: fastmeter.cc:493
#define PIX(X)
GdkRectangle last_peak_rect
Definition: fastmeter.h:88
static int max_pattern_metric_size
Definition: fastmeter.h:171
static PatternBgMap vb_pattern_cache
Definition: fastmeter.h:167
void set_hold_count(long)
Definition: fastmeter.cc:447
static Cairo::RefPtr< Cairo::Pattern > generate_meter_pattern(int, int, int *, float *, int, bool)
Definition: fastmeter.cc:155
void horizontal_size_allocate(Gtk::Allocation &)
Definition: fastmeter.cc:529
static Cairo::RefPtr< Cairo::Pattern > request_horizontal_background(int, int, int *, bool)
Definition: fastmeter.cc:422
void on_size_allocate(Gtk::Allocation &)