2 Copyright (C) 2003-2006 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.
26 #include <gdkmm/rectangle.h>
27 #include <gtkmm2ext/fastmeter.h>
28 #include <gtkmm2ext/utils.h>
30 #define UINT_TO_RGB(u,r,g,b) { (*(r)) = ((u)>>16)&0xff; (*(g)) = ((u)>>8)&0xff; (*(b)) = (u)&0xff; }
31 #define UINT_TO_RGBA(u,r,g,b,a) { UINT_TO_RGB(((u)>>8),r,g,b); (*(a)) = (u)&0xff; }
35 using namespace Gtkmm2ext;
38 int FastMeter::min_pattern_metric_size = 10;
39 int FastMeter::max_pattern_metric_size = 1024;
41 FastMeter::Pattern10Map FastMeter::vm_pattern_cache;
42 FastMeter::PatternBgMap FastMeter::vb_pattern_cache;
44 FastMeter::FastMeter (long hold, unsigned long dimen, Orientation o, int len,
45 int clr0, int clr1, int clr2, int clr3,
46 int clr4, int clr5, int clr6, int clr7,
47 int clr8, int clr9, int bgc0, int bgc1,
48 float stp0, float stp1,
49 float stp2, float stp3
58 last_peak_rect.width = 0;
59 last_peak_rect.height = 0;
80 set_events (BUTTON_PRESS_MASK|BUTTON_RELEASE_MASK);
88 fgpattern = request_vertical_meter(dimen, len, _clr, _stp);
89 bgpattern = request_vertical_background (dimen, len, _bgc);
93 pixrect.width = pixwidth;
94 pixrect.height = pixheight;
96 request_width = pixrect.width + 2;
97 request_height= pixrect.height + 2;
100 FastMeter::~FastMeter ()
104 Cairo::RefPtr<Cairo::Pattern>
105 FastMeter::generate_meter_pattern (
106 int width, int height, int *clr, float *stp)
111 cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, width, height);
114 Cairo coordinate space goes downwards as y value goes up, so invert
115 knee-based positions by using (1.0 - y)
118 UINT_TO_RGBA (clr[9], &r, &g, &b, &a); // top/clip
119 cairo_pattern_add_color_stop_rgb (pat, 0.0,
120 r/255.0, g/255.0, b/255.0);
122 knee = (int)floor((float)height * stp[3] / 115.0f); // -0dB
124 UINT_TO_RGBA (clr[8], &r, &g, &b, &a);
125 cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height),
126 r/255.0, g/255.0, b/255.0);
128 UINT_TO_RGBA (clr[7], &r, &g, &b, &a);
129 cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height),
130 r/255.0, g/255.0, b/255.0);
132 knee = (int)floor((float)height * stp[2]/ 115.0f); // -3dB || -2dB
134 UINT_TO_RGBA (clr[6], &r, &g, &b, &a);
135 cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height),
136 r/255.0, g/255.0, b/255.0);
138 UINT_TO_RGBA (clr[5], &r, &g, &b, &a);
139 cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height),
140 r/255.0, g/255.0, b/255.0);
142 knee = (int)floor((float)height * stp[1] / 115.0f); // -9dB
144 UINT_TO_RGBA (clr[4], &r, &g, &b, &a);
145 cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height),
146 r/255.0, g/255.0, b/255.0);
148 UINT_TO_RGBA (clr[3], &r, &g, &b, &a);
149 cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height),
150 r/255.0, g/255.0, b/255.0);
152 knee = (int)floor((float)height * stp[0] / 115.0f); // -18dB
154 UINT_TO_RGBA (clr[2], &r, &g, &b, &a);
155 cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height),
156 r/255.0, g/255.0, b/255.0);
158 UINT_TO_RGBA (clr[1], &r, &g, &b, &a);
159 cairo_pattern_add_color_stop_rgb (pat, 1.0 - (knee/(double)height),
160 r/255.0, g/255.0, b/255.0);
162 UINT_TO_RGBA (clr[0], &r, &g, &b, &a); // bottom
163 cairo_pattern_add_color_stop_rgb (pat, 1.0,
164 r/255.0, g/255.0, b/255.0);
166 Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
172 Cairo::RefPtr<Cairo::Pattern>
173 FastMeter::generate_meter_background (
174 int width, int height, int *clr)
176 guint8 r0,g0,b0,r1,g1,b1,a;
178 cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, width, height);
180 UINT_TO_RGBA (clr[0], &r0, &g0, &b0, &a);
181 UINT_TO_RGBA (clr[1], &r1, &g1, &b1, &a);
183 cairo_pattern_add_color_stop_rgb (pat, 0.0,
184 r1/255.0, g1/255.0, b1/255.0);
186 cairo_pattern_add_color_stop_rgb (pat, 1.0,
187 r0/255.0, g0/255.0, b0/255.0);
189 Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
194 Cairo::RefPtr<Cairo::Pattern>
195 FastMeter::request_vertical_meter(
196 int width, int height, int *clr, float *stp)
198 if (height < min_pattern_metric_size)
199 height = min_pattern_metric_size;
200 if (height > max_pattern_metric_size)
201 height = max_pattern_metric_size;
203 const Pattern10MapKey key (width, height,
204 stp[0], stp[1], stp[2], stp[3],
205 clr[0], clr[1], clr[2], clr[3],
206 clr[4], clr[5], clr[6], clr[7],
208 Pattern10Map::iterator i;
209 if ((i = vm_pattern_cache.find (key)) != vm_pattern_cache.end()) {
213 Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
214 width, height, clr, stp);
215 vm_pattern_cache[key] = p;
220 Cairo::RefPtr<Cairo::Pattern>
221 FastMeter::request_vertical_background(
222 int width, int height, int *bgc)
224 if (height < min_pattern_metric_size)
225 height = min_pattern_metric_size;
226 if (height > max_pattern_metric_size)
227 height = max_pattern_metric_size;
229 const PatternBgMapKey key (width, height, bgc[0], bgc[1]);
230 PatternBgMap::iterator i;
231 if ((i = vb_pattern_cache.find (key)) != vb_pattern_cache.end()) {
235 Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (
237 vb_pattern_cache[key] = p;
244 FastMeter::set_hold_count (long val)
258 FastMeter::on_size_request (GtkRequisition* req)
260 req->height = request_height;
261 req->height = max(req->height, min_pattern_metric_size);
262 req->height = min(req->height, max_pattern_metric_size);
265 req->width = request_width;
269 FastMeter::on_size_allocate (Gtk::Allocation &alloc)
271 if (alloc.get_width() != request_width) {
272 alloc.set_width (request_width);
275 int h = alloc.get_height();
276 h = max (h, min_pattern_metric_size + 2);
277 h = min (h, max_pattern_metric_size + 2);
279 if (h != alloc.get_height()) {
280 alloc.set_height (h);
283 if (pixheight != h) {
284 fgpattern = request_vertical_meter (request_width, h, _clr, _stp);
285 bgpattern = request_vertical_background (request_width, h, _bgc);
287 pixwidth = request_width - 2;
290 DrawingArea::on_size_allocate (alloc);
295 FastMeter::on_expose_event (GdkEventExpose* ev)
297 return vertical_expose (ev);
301 FastMeter::vertical_expose (GdkEventExpose* ev)
303 Glib::RefPtr<Gdk::Window> win = get_window ();
305 GdkRectangle intersection;
306 GdkRectangle background;
308 cairo_t* cr = gdk_cairo_create (get_window ()->gobj());
311 cairo_set_source_rgb (cr, 0, 0, 0); // black
312 rounded_rectangle (cr, 0, 0, pixrect.width + 2, pixheight + 2, 2);
316 cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
319 top_of_meter = (gint) floor (pixheight * current_level);
321 /* reset the height & origin of the rect that needs to show the pixbuf
324 pixrect.height = top_of_meter;
325 pixrect.y = 1 + pixheight - top_of_meter;
329 background.width = pixrect.width;
330 background.height = pixheight - top_of_meter;
332 if (gdk_rectangle_intersect (&background, &ev->area, &intersection)) {
333 cairo_set_source (cr, bgpattern->cobj());
334 cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
338 if (gdk_rectangle_intersect (&pixrect, &ev->area, &intersection)) {
339 // draw the part of the meter image that we need. the area we draw is bounded "in reverse" (top->bottom)
340 cairo_set_source (cr, fgpattern->cobj());
341 cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
348 last_peak_rect.x = 1;
349 last_peak_rect.width = pixwidth;
350 last_peak_rect.y = 1 + pixheight - (gint) floor (pixheight * current_peak);
351 last_peak_rect.height = min(2, pixheight - last_peak_rect.y);
353 cairo_set_source (cr, fgpattern->cobj());
354 cairo_rectangle (cr, 1, last_peak_rect.y, pixwidth, last_peak_rect.height);
358 last_peak_rect.width = 0;
359 last_peak_rect.height = 0;
368 FastMeter::set (float lvl)
370 float old_level = current_level;
371 float old_peak = current_peak;
375 if (lvl > current_peak) {
377 hold_state = hold_cnt;
380 if (hold_state > 0) {
381 if (--hold_state == 0) {
386 if (current_level == old_level && current_peak == old_peak && hold_state == 0) {
391 Glib::RefPtr<Gdk::Window> win;
393 if ((win = get_window()) == 0) {
398 queue_vertical_redraw (win, old_level);
402 FastMeter::queue_vertical_redraw (const Glib::RefPtr<Gdk::Window>& win, float old_level)
406 gint new_top = (gint) floor (pixheight * current_level);
409 rect.width = pixwidth;
410 rect.height = new_top;
411 rect.y = 1 + pixheight - new_top;
413 if (current_level > old_level) {
414 /* colored/pixbuf got larger, just draw the new section */
415 /* rect.y stays where it is because of X coordinates */
416 /* height of invalidated area is between new.y (smaller) and old.y
418 X coordinates just make my brain hurt.
420 rect.height = pixrect.y - rect.y;
422 /* it got smaller, compute the difference */
423 /* rect.y becomes old.y (the smaller value) */
425 /* rect.height is the old.y (smaller) minus the new.y (larger)
427 rect.height = pixrect.height - rect.height;
430 GdkRegion* region = 0;
433 if (rect.height != 0) {
435 /* ok, first region to draw ... */
437 region = gdk_region_rectangle (&rect);
441 /* redraw the last place where the last peak hold bar was;
442 the next expose will draw the new one whether its part of
443 expose region or not.
446 if (last_peak_rect.width * last_peak_rect.height != 0) {
448 region = gdk_region_new ();
451 gdk_region_union_with_rect (region, &last_peak_rect);
455 gdk_window_invalidate_region (win->gobj(), region, true);
458 gdk_region_destroy(region);