2 Copyright (C) 2003-2016 Paul Davis
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.
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.
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.
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/rgb_macros.h>
33 #include "canvas/canvas.h"
34 #include "canvas/colors.h"
35 #include "canvas/meter.h"
36 #include "canvas/utils.h"
40 using namespace ArdourCanvas;
41 using namespace Gtkmm2ext;
43 int Meter::min_pattern_metric_size = 16;
44 int Meter::max_pattern_metric_size = 1024;
45 bool Meter::no_rgba_overlay = false;
47 Meter::Pattern10Map Meter::vm_pattern_cache;
48 Meter::PatternBgMap Meter::vb_pattern_cache;
50 Meter::Pattern10Map Meter::hm_pattern_cache;
51 Meter::PatternBgMap Meter::hb_pattern_cache;
53 Meter::Meter (Item* parent, long hold, unsigned long dimen, Orientation o, int len,
54 int clr0, int clr1, int clr2, int clr3,
55 int clr4, int clr5, int clr6, int clr7,
59 float stp0, float stp1,
60 float stp2, float stp3,
66 , _styleflags(styleflags)
75 init (clr0, clr1, clr2, clr3, clr4, clr5, clr6, clr7, clr8, clr9, bgc0, bgc1, bgh0, bgh1, stp0, stp1, stp2, stp3, dimen, len);
78 Meter::Meter (Canvas* canvas, long hold, unsigned long dimen, Orientation o, int len,
79 int clr0, int clr1, int clr2, int clr3,
80 int clr4, int clr5, int clr6, int clr7,
84 float stp0, float stp1,
85 float stp2, float stp3,
91 , _styleflags(styleflags)
100 init (clr0, clr1, clr2, clr3, clr4, clr5, clr6, clr7, clr8, clr9, bgc0, bgc1, bgh0, bgh1, stp0, stp1, stp2, stp3, dimen, len);
104 Meter::init (int clr0, int clr1, int clr2, int clr3,
105 int clr4, int clr5, int clr6, int clr7,
109 float stp0, float stp1,
110 float stp2, float stp3,
114 last_peak_rect.width = 0;
115 last_peak_rect.height = 0;
116 last_peak_rect.x = 0;
117 last_peak_rect.y = 0;
119 no_rgba_overlay = ! Glib::getenv("NO_METER_SHADE").empty();
147 if (orientation == Vertical) {
150 fgpattern = vertical_meter_pattern (pixwidth + 2, pixheight + 2, _clr, _stp, _styleflags);
151 bgpattern = vertical_background (pixwidth + 2, pixheight + 2, _bgc, false);
155 fgpattern = horizontal_meter_pattern (pixwidth + 2, pixheight + 2, _clr, _stp, _styleflags);
156 bgpattern = horizontal_background (pixwidth + 2, pixheight + 2, _bgc, false);
159 pixrect.height = pixheight;
162 if (orientation == Vertical) {
163 pixrect.width = pixwidth;
164 pixrect.y = pixheight; /* bottom of meter, so essentially "show zero" */
166 pixrect.width = 0; /* right of meter, so essentially "show zero" */
172 Meter::compute_bounding_box () const
175 _bounding_box = boost::optional<Rect> ();
176 _bounding_box_dirty = false;
180 Rect r (0, 0, pixwidth + 2, pixheight + 2);
182 _bounding_box_dirty = false;
191 Meter::flush_pattern_cache () {
192 hb_pattern_cache.clear();
193 hm_pattern_cache.clear();
194 vb_pattern_cache.clear();
195 vm_pattern_cache.clear();
198 Cairo::RefPtr<Cairo::Pattern>
199 Meter::generate_meter_pattern (int width, int height, int *clr, float *stp, int styleflags, bool horiz)
203 const double soft = 3.0 / (double) height;
204 const double offs = -1.0 / (double) height;
206 cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
209 Cairo coordinate space goes downwards as y value goes up, so invert
210 knee-based positions by using (1.0 - y)
213 UINT_TO_RGBA (clr[9], &r, &g, &b, &a); // top/clip
214 cairo_pattern_add_color_stop_rgb (pat, 0.0,
215 r/255.0, g/255.0, b/255.0);
217 knee = offs + stp[3] / 115.0f; // -0dB
219 UINT_TO_RGBA (clr[8], &r, &g, &b, &a);
220 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
221 r/255.0, g/255.0, b/255.0);
223 UINT_TO_RGBA (clr[7], &r, &g, &b, &a);
224 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
225 r/255.0, g/255.0, b/255.0);
227 knee = offs + stp[2]/ 115.0f; // -3dB || -2dB
229 UINT_TO_RGBA (clr[6], &r, &g, &b, &a);
230 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
231 r/255.0, g/255.0, b/255.0);
233 UINT_TO_RGBA (clr[5], &r, &g, &b, &a);
234 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
235 r/255.0, g/255.0, b/255.0);
237 knee = offs + stp[1] / 115.0f; // -9dB
239 UINT_TO_RGBA (clr[4], &r, &g, &b, &a);
240 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
241 r/255.0, g/255.0, b/255.0);
243 UINT_TO_RGBA (clr[3], &r, &g, &b, &a);
244 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
245 r/255.0, g/255.0, b/255.0);
247 knee = offs + stp[0] / 115.0f; // -18dB
249 UINT_TO_RGBA (clr[2], &r, &g, &b, &a);
250 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
251 r/255.0, g/255.0, b/255.0);
253 UINT_TO_RGBA (clr[1], &r, &g, &b, &a);
254 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
255 r/255.0, g/255.0, b/255.0);
257 UINT_TO_RGBA (clr[0], &r, &g, &b, &a); // bottom
258 cairo_pattern_add_color_stop_rgb (pat, 1.0,
259 r/255.0, g/255.0, b/255.0);
261 if ((styleflags & 1) && !no_rgba_overlay) {
262 cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
263 cairo_pattern_add_color_stop_rgba (shade_pattern, 0, 0.0, 0.0, 0.0, 0.15);
264 cairo_pattern_add_color_stop_rgba (shade_pattern, 0.4, 1.0, 1.0, 1.0, 0.05);
265 cairo_pattern_add_color_stop_rgba (shade_pattern, 1, 0.0, 0.0, 0.0, 0.25);
267 cairo_surface_t* surface;
269 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
270 tc = cairo_create (surface);
271 cairo_set_source (tc, pat);
272 cairo_rectangle (tc, 0, 0, width, height);
274 cairo_pattern_destroy (pat);
276 cairo_set_source (tc, shade_pattern);
277 cairo_rectangle (tc, 0, 0, width, height);
279 cairo_pattern_destroy (shade_pattern);
281 if (styleflags & 2) { // LED stripes
283 cairo_set_line_width(tc, 1.0);
284 cairo_set_source_rgba(tc, .0, .0, .0, 0.4);
285 //cairo_set_operator (tc, CAIRO_OPERATOR_SOURCE);
286 for (int i = 0; float y = 0.5 + i * 2.0; ++i) {
290 cairo_move_to(tc, 0, y);
291 cairo_line_to(tc, width, y);
297 pat = cairo_pattern_create_for_surface (surface);
299 cairo_surface_destroy (surface);
303 cairo_surface_t* surface;
305 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width);
306 tc = cairo_create (surface);
309 cairo_matrix_init_rotate (&m, -M_PI/2.0);
310 cairo_matrix_translate (&m, -height, 0);
311 cairo_pattern_set_matrix (pat, &m);
312 cairo_set_source (tc, pat);
313 cairo_rectangle (tc, 0, 0, height, width);
315 cairo_pattern_destroy (pat);
316 pat = cairo_pattern_create_for_surface (surface);
318 cairo_surface_destroy (surface);
320 Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
326 Cairo::RefPtr<Cairo::Pattern>
327 Meter::generate_meter_background (int width, int height, int *clr, bool shade, bool horiz)
329 guint8 r0,g0,b0,r1,g1,b1,a;
331 cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
333 UINT_TO_RGBA (clr[0], &r0, &g0, &b0, &a);
334 UINT_TO_RGBA (clr[1], &r1, &g1, &b1, &a);
336 cairo_pattern_add_color_stop_rgb (pat, 0.0,
337 r1/255.0, g1/255.0, b1/255.0);
339 cairo_pattern_add_color_stop_rgb (pat, 1.0,
340 r0/255.0, g0/255.0, b0/255.0);
342 if (shade && !no_rgba_overlay) {
343 cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
344 cairo_pattern_add_color_stop_rgba (shade_pattern, 0.0, 1.0, 1.0, 1.0, 0.15);
345 cairo_pattern_add_color_stop_rgba (shade_pattern, 0.6, 0.0, 0.0, 0.0, 0.10);
346 cairo_pattern_add_color_stop_rgba (shade_pattern, 1.0, 1.0, 1.0, 1.0, 0.20);
348 cairo_surface_t* surface;
350 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
351 tc = cairo_create (surface);
352 cairo_set_source (tc, pat);
353 cairo_rectangle (tc, 0, 0, width, height);
355 cairo_set_source (tc, shade_pattern);
356 cairo_rectangle (tc, 0, 0, width, height);
359 cairo_pattern_destroy (pat);
360 cairo_pattern_destroy (shade_pattern);
362 pat = cairo_pattern_create_for_surface (surface);
365 cairo_surface_destroy (surface);
369 cairo_surface_t* surface;
371 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width);
372 tc = cairo_create (surface);
375 cairo_matrix_init_rotate (&m, -M_PI/2.0);
376 cairo_matrix_translate (&m, -height, 0);
377 cairo_pattern_set_matrix (pat, &m);
378 cairo_set_source (tc, pat);
379 cairo_rectangle (tc, 0, 0, height, width);
381 cairo_pattern_destroy (pat);
382 pat = cairo_pattern_create_for_surface (surface);
384 cairo_surface_destroy (surface);
387 Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
392 Cairo::RefPtr<Cairo::Pattern>
393 Meter::vertical_meter_pattern (int width, int height, int *clr, float *stp, int styleflags)
395 height = max(height, min_pattern_metric_size);
396 height = min(height, max_pattern_metric_size);
398 const Pattern10MapKey key (width, height,
399 stp[0], stp[1], stp[2], stp[3],
400 clr[0], clr[1], clr[2], clr[3],
401 clr[4], clr[5], clr[6], clr[7],
402 clr[8], clr[9], styleflags);
404 Pattern10Map::iterator i;
405 if ((i = vm_pattern_cache.find (key)) != vm_pattern_cache.end()) {
408 // TODO flush pattern cache if it gets too large
410 Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (width, height, clr, stp, styleflags, false);
411 vm_pattern_cache[key] = p;
416 Cairo::RefPtr<Cairo::Pattern>
417 Meter::vertical_background (int width, int height, int *bgc, bool shade)
419 height = max(height, min_pattern_metric_size);
420 height = min(height, max_pattern_metric_size);
423 const PatternBgMapKey key (width, height, bgc[0], bgc[1], shade);
424 PatternBgMap::iterator i;
426 if ((i = vb_pattern_cache.find (key)) != vb_pattern_cache.end()) {
429 // TODO flush pattern cache if it gets too large
431 Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (width, height, bgc, shade, false);
432 vb_pattern_cache[key] = p;
437 Cairo::RefPtr<Cairo::Pattern>
438 Meter::horizontal_meter_pattern (int width, int height, int *clr, float *stp, int styleflags)
440 width = max(width, min_pattern_metric_size);
441 width = min(width, max_pattern_metric_size);
443 const Pattern10MapKey key (width, height,
444 stp[0], stp[1], stp[2], stp[3],
445 clr[0], clr[1], clr[2], clr[3],
446 clr[4], clr[5], clr[6], clr[7],
447 clr[8], clr[9], styleflags);
449 Pattern10Map::iterator i;
450 if ((i = hm_pattern_cache.find (key)) != hm_pattern_cache.end()) {
453 // TODO flush pattern cache if it gets too large
455 Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (height, width, clr, stp, styleflags, true);
457 hm_pattern_cache[key] = p;
461 Cairo::RefPtr<Cairo::Pattern>
462 Meter::horizontal_background (int width, int height, int *bgc, bool shade)
464 width = max(width, min_pattern_metric_size);
465 width = min(width, max_pattern_metric_size);
468 const PatternBgMapKey key (width, height, bgc[0], bgc[1], shade);
469 PatternBgMap::iterator i;
470 if ((i = hb_pattern_cache.find (key)) != hb_pattern_cache.end()) {
473 // TODO flush pattern cache if it gets too large
475 Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (height, width, bgc, shade, true);
477 hb_pattern_cache[key] = p;
483 Meter::set_hold_count (long val)
497 Meter::render (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
499 if (orientation == Vertical) {
500 return vertical_expose (area, context);
502 return horizontal_expose (area, context);
507 Meter::vertical_expose (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
510 Cairo::RectangleInt background;
511 Cairo::RectangleInt area_r;
513 /* convert expose area back to item coordinate space */
514 Rect area2 = window_to_item (area);
518 area_r.width = area2.width();
519 area_r.height = area2.height();
521 context->set_source_rgb (0, 0, 0); // black
522 rounded_rectangle (context, 0, 0, pixwidth + 2, pixheight + 2, 2);
525 top_of_meter = (gint) floor (pixheight * current_level);
527 /* reset the height & origin of the rect that needs to show the meter pattern
529 pixrect.height = top_of_meter;
531 /* X/cairo coordinates; y grows down so y origin of pixrect (pattern
532 fill area) is the TOP of the pattern area, which we compute like this:
535 - go to bottom of meter (pixheight)
536 - back up by current meter height (top_of_meter)
538 pixrect.y = 1 + pixheight - top_of_meter;
542 background.width = pixrect.width;
543 background.height = pixheight - top_of_meter;
545 /* translate so that item coordinates match window coordinates */
547 origin = item_to_window (origin);
548 context->translate (origin.x, origin.y);
550 Cairo::RefPtr<Cairo::Region> r1 = Cairo::Region::create (area_r);
551 r1->intersect (background);
554 Cairo::RectangleInt i (r1->get_extents ());
555 context->set_source (bgpattern);
556 context->rectangle (i.x, i.y, i.width, i.height);
560 Cairo::RefPtr<Cairo::Region> r2 = Cairo::Region::create (area_r);
561 r2->intersect (pixrect);
564 // draw the part of the meter image that we need. the area we draw is bounded "in reverse" (top->bottom)
565 Cairo::RectangleInt i (r2->get_extents ());
566 context->set_source (fgpattern);
567 context->rectangle (i.x, i.y, i.width, i.height);
574 last_peak_rect.x = 1;
575 last_peak_rect.width = pixwidth;
576 last_peak_rect.y = max(1, 1 + pixheight - (int) floor (pixheight * current_peak));
577 if (_styleflags & 2) { // LED stripes
578 last_peak_rect.y = max(0, (last_peak_rect.y & (~1)));
580 if (bright_hold || (_styleflags & 2)) {
581 last_peak_rect.height = max(0, min(3, pixheight - last_peak_rect.y - 1 ));
583 last_peak_rect.height = max(0, min(2, pixheight - last_peak_rect.y - 1 ));
586 context->set_source (fgpattern);
587 context->rectangle (last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height);
589 if (bright_hold && !no_rgba_overlay) {
590 context->fill_preserve ();
591 context->set_source_rgba (1.0, 1.0, 1.0, 0.3);
596 last_peak_rect.width = 0;
597 last_peak_rect.height = 0;
600 context->translate (-origin.x, -origin.y);
604 Meter::horizontal_expose (ArdourCanvas::Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
607 Cairo::RectangleInt background;
608 Cairo::RectangleInt area_r;
610 /* convert expose area back to item coordinate space */
611 Rect area2 = window_to_item (area);
613 /* create a Cairo object so that we can use intersect and Region */
616 area_r.width = area2.width();
617 area_r.height = area2.height();
619 /* draw the edge (rounded corners) */
620 context->set_source_rgb (0, 0, 0); // black
621 rounded_rectangle (context, 0, 0, pixwidth + 2, pixheight + 2, 2);
624 /* horizontal meter extends from left to right. Compute the right edge */
625 right_of_meter = (gint) floor (pixwidth * current_level);
627 /* reset the width the rect that needs to show the pattern of the meter */
628 pixrect.width = right_of_meter;
630 /* compute a rect for the part of the meter that is all background */
631 background.x = 1 + right_of_meter;
633 background.width = pixwidth - right_of_meter;
634 background.height = pixheight;
636 /* translate so that item coordinates match window coordinates */
638 origin = item_to_window (origin);
639 context->translate (origin.x, origin.y);
641 Cairo::RefPtr<Cairo::Region> r;
643 r = Cairo::Region::create (area_r);
644 r->intersect (background);
647 /* draw the background part */
648 Cairo::RectangleInt i (r->get_extents ());
649 context->set_source (bgpattern);
650 context->rectangle (i.x, i.y, i.width, i.height);
655 r = Cairo::Region::create (area_r);
656 r->intersect (pixrect);
659 // draw the part of the meter image that we need.
660 Cairo::RectangleInt i (r->get_extents ());
662 context->set_source (fgpattern);
663 context->rectangle (i.x, i.y, i.width, i.height);
670 last_peak_rect.y = 1;
671 last_peak_rect.height = pixheight;
672 const int xpos = floor (pixwidth * current_peak);
673 if (bright_hold || (_styleflags & 2)) {
674 last_peak_rect.width = min(3, xpos );
676 last_peak_rect.width = min(2, xpos );
678 last_peak_rect.x = 1 + max(0, xpos - last_peak_rect.width);
680 context->set_source (fgpattern);
681 context->rectangle (last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height);
683 if (bright_hold && !no_rgba_overlay) {
684 context->fill_preserve ();
685 context->set_source_rgba (1.0, 1.0, 1.0, 0.3);
690 last_peak_rect.width = 0;
691 last_peak_rect.height = 0;
694 context->translate (-origin.x, -origin.y);
698 Meter::set (float lvl, float peak)
700 float old_level = current_level;
701 float old_peak = current_peak;
703 if (pixwidth <= 0 || pixheight <=0) return;
706 if (lvl >= current_peak) {
708 hold_state = hold_cnt;
711 if (hold_state > 0) {
712 if (--hold_state == 0) {
725 const float pixscale = (orientation == Vertical) ? pixheight : pixwidth;
726 #define PIX(X) floor(pixscale * (X))
727 if (PIX(current_level) == PIX(old_level) && PIX(current_peak) == PIX(old_peak) && (hold_state == 0 || peak != -1)) {
731 if (orientation == Vertical) {
732 queue_vertical_redraw (old_level);
734 queue_horizontal_redraw (old_level);
739 Meter::queue_vertical_redraw (float old_level)
741 Cairo::RectangleInt rect;
743 gint new_height = (gint) floor (pixheight * current_level);
745 /* this is the nominal area that needs to be filled by the meter
750 rect.width = pixwidth;
752 /* compute new top of meter (rect.y) by starting at one (border
753 * offset), go down the full height of the meter (X/Cairo coordinates
754 * grow down) to get to the bottom coordinate, then back up by the
755 * height of the patterned area.
757 rect.y = 1 + pixheight - new_height;
758 /* remember: height extends DOWN thanks to X/Cairo */
759 rect.height = new_height;
761 /* now lets optimize redrawing by figuring out which part needs to be
762 actually redrawn (i.e. re-use the last drawn state).
765 if (current_level > old_level) {
766 /* filled area got taller, just draw the new section */
768 /* rect.y (new y origin) is smaller or equal to pixrect.y (old
769 * y origin) because the top of the meter is higher (X/Cairo:
770 * coordinates grow down).
772 * Leave rect.y alone, and recompute the height to be just the
773 * difference between the new bottom and the top of the previous
776 * The old pattern area extended DOWN from pixrect.y to
777 * pixrect.y + pixrect.height.
779 * The new pattern area extends DOWN from rect.y to
780 * rect.y + rect.height
782 * The area needing to be drawn is the difference between the
783 * old top (pixrect.y) and the new top (rect.y)
785 rect.height = pixrect.y - rect.y;
787 /* it got smaller, compute the difference */
788 /* rect.y becomes old.y (the smaller value) */
790 /* rect.height is the old.y (smaller) minus the new.y (larger)
792 rect.height = pixrect.height - rect.height;
795 Cairo::RefPtr<Cairo::Region> region;
798 if (rect.height != 0) {
800 /* ok, first region to draw ... */
802 region = Cairo::Region::create (rect);
806 /* redraw the last place where the last peak hold bar was;
807 the next expose will draw the new one whether its part of
808 expose region or not.
811 if (last_peak_rect.width * last_peak_rect.height != 0) {
813 region = Cairo::Region::create ();
816 region->do_union (last_peak_rect);
819 if (hold_state && current_peak > 0) {
821 region = Cairo::Region::create ();
825 rect.y = max(1, 1 + pixheight - (int) floor (pixheight * current_peak));
826 if (_styleflags & 2) { // LED stripes
827 rect.y = max(0, (rect.y & (~1)));
829 if (bright_hold || (_styleflags & 2)) {
830 rect.height = max(0, min(3, pixheight - last_peak_rect.y -1 ));
832 rect.height = max(0, min(2, pixheight - last_peak_rect.y -1 ));
834 rect.width = pixwidth;
835 region->do_union (rect);
839 if (visible() && _bounding_box && _canvas) {
840 Cairo::RectangleInt iri = region->get_extents();
841 Rect ir (iri.x, iri.y, iri.x + iri.width, iri.y + iri.height);
842 _canvas->request_redraw (item_to_window (ir));
848 Meter::queue_horizontal_redraw (float old_level)
850 Cairo::RectangleInt rect;
852 gint new_right = (gint) floor (pixwidth * current_level);
854 rect.height = pixheight;
857 if (current_level > old_level) {
858 rect.x = 1 + pixrect.width;
859 /* colored/pixbuf got larger, just draw the new section */
860 rect.width = new_right - pixrect.width;
862 /* it got smaller, compute the difference */
863 rect.x = 1 + new_right;
864 /* rect.height is the old.x (smaller) minus the new.x (larger) */
865 rect.width = pixrect.width - new_right;
868 Cairo::RefPtr<Cairo::Region> region;
871 if (rect.height != 0) {
873 /* ok, first region to draw ... */
875 region = Cairo::Region::create (rect);
879 /* redraw the last place where the last peak hold bar was;
880 the next expose will draw the new one whether its part of
881 expose region or not.
884 if (last_peak_rect.width * last_peak_rect.height != 0) {
886 region = Cairo::Region::create ();
889 region->do_union (last_peak_rect);
892 if (hold_state && current_peak > 0) {
894 region = Cairo::Region::create ();
898 rect.height = pixheight;
899 const int xpos = floor (pixwidth * current_peak);
900 if (bright_hold || (_styleflags & 2)) {
901 rect.width = min(3, xpos);
903 rect.width = min(2, xpos);
905 rect.x = 1 + max(0, xpos - rect.width);
906 region->do_union (rect);
910 if (visible() && _bounding_box && _canvas) {
911 Cairo::RectangleInt iri = region->get_extents();
912 Rect ir (iri.x, iri.y, iri.x + iri.width, iri.y + iri.height);
913 _canvas->request_redraw (item_to_window (ir));
919 Meter::set_highlight (bool onoff)
921 if (highlight == onoff) {
925 if (orientation == Vertical) {
926 bgpattern = vertical_background (pixwidth + 2, pixheight + 2, highlight ? _bgh : _bgc, highlight);
928 bgpattern = horizontal_background (pixwidth + 2, pixheight + 2, highlight ? _bgh : _bgc, highlight);