From 43bd836778451b032cc6accbb2618dc8e6ba00b3 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 25 Jul 2013 19:04:52 +0200 Subject: [PATCH] [re]implement horizontal meter --- libs/gtkmm2ext/fastmeter.cc | 353 +++++++++++++++++++++++++-- libs/gtkmm2ext/gtkmm2ext/fastmeter.h | 18 +- 2 files changed, 347 insertions(+), 24 deletions(-) diff --git a/libs/gtkmm2ext/fastmeter.cc b/libs/gtkmm2ext/fastmeter.cc index c930e60be3..c79b120624 100644 --- a/libs/gtkmm2ext/fastmeter.cc +++ b/libs/gtkmm2ext/fastmeter.cc @@ -44,6 +44,9 @@ bool FastMeter::no_rgba_overlay = false; FastMeter::Pattern10Map FastMeter::vm_pattern_cache; FastMeter::PatternBgMap FastMeter::vb_pattern_cache; +FastMeter::Pattern10Map FastMeter::hm_pattern_cache; +FastMeter::PatternBgMap FastMeter::hb_pattern_cache; + FastMeter::FastMeter (long hold, unsigned long dimen, Orientation o, int len, int clr0, int clr1, int clr2, int clr3, int clr4, int clr5, int clr6, int clr7, @@ -99,10 +102,18 @@ FastMeter::FastMeter (long hold, unsigned long dimen, Orientation o, int len, if (!len) { len = 250; } - fgpattern = request_vertical_meter(dimen, len, _clr, _stp, _styleflags); - bgpattern = request_vertical_background (dimen, len, _bgc, false); - pixheight = len; - pixwidth = dimen; + if (orientation == Vertical) { + pixheight = len; + pixwidth = dimen; + fgpattern = request_vertical_meter(pixwidth + 2, pixheight + 2, _clr, _stp, _styleflags); + bgpattern = request_vertical_background (pixwidth + 2, pixheight + 2, _bgc, false); + + } else { + pixheight = dimen; + pixwidth = len; + fgpattern = request_horizontal_meter(pixwidth + 2, pixheight + 2, _clr, _stp, _styleflags); + bgpattern = request_horizontal_background (pixwidth + 2, pixheight + 2, _bgc, false); + } pixrect.width = pixwidth; pixrect.height = pixheight; @@ -119,7 +130,7 @@ FastMeter::~FastMeter () Cairo::RefPtr FastMeter::generate_meter_pattern ( - int width, int height, int *clr, float *stp, int styleflags) + int width, int height, int *clr, float *stp, int styleflags, bool horiz) { guint8 r,g,b,a; double knee; @@ -221,6 +232,24 @@ FastMeter::generate_meter_pattern ( cairo_surface_destroy (surface); } + if (horiz) { + cairo_surface_t* surface; + cairo_t* tc = 0; + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width); + tc = cairo_create (surface); + + cairo_matrix_t m; + cairo_matrix_init_rotate (&m, -M_PI/2.0); + cairo_matrix_translate (&m, -height, 0); + cairo_pattern_set_matrix (pat, &m); + cairo_set_source (tc, pat); + cairo_rectangle (tc, 0, 0, height, width); + cairo_fill (tc); + cairo_pattern_destroy (pat); + pat = cairo_pattern_create_for_surface (surface); + cairo_destroy (tc); + cairo_surface_destroy (surface); + } Cairo::RefPtr p (new Cairo::Pattern (pat, false)); return p; @@ -229,7 +258,7 @@ FastMeter::generate_meter_pattern ( Cairo::RefPtr FastMeter::generate_meter_background ( - int width, int height, int *clr, bool shade) + int width, int height, int *clr, bool shade, bool horiz) { guint8 r0,g0,b0,r1,g1,b1,a; @@ -270,6 +299,25 @@ FastMeter::generate_meter_background ( cairo_surface_destroy (surface); } + if (horiz) { + cairo_surface_t* surface; + cairo_t* tc = 0; + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width); + tc = cairo_create (surface); + + cairo_matrix_t m; + cairo_matrix_init_rotate (&m, -M_PI/2.0); + cairo_matrix_translate (&m, -height, 0); + cairo_pattern_set_matrix (pat, &m); + cairo_set_source (tc, pat); + cairo_rectangle (tc, 0, 0, height, width); + cairo_fill (tc); + cairo_pattern_destroy (pat); + pat = cairo_pattern_create_for_surface (surface); + cairo_destroy (tc); + cairo_surface_destroy (surface); + } + Cairo::RefPtr p (new Cairo::Pattern (pat, false)); return p; @@ -279,10 +327,8 @@ Cairo::RefPtr FastMeter::request_vertical_meter( int width, int height, int *clr, float *stp, int styleflags) { - if (height < min_pattern_metric_size) - height = min_pattern_metric_size; - if (height > max_pattern_metric_size) - height = max_pattern_metric_size; + height = max(height, min_pattern_metric_size); + height = min(height, max_pattern_metric_size); const Pattern10MapKey key (width, height, stp[0], stp[1], stp[2], stp[3], @@ -297,7 +343,7 @@ FastMeter::request_vertical_meter( // TODO flush pattern cache if it gets too large Cairo::RefPtr p = generate_meter_pattern ( - width, height, clr, stp, styleflags); + width, height, clr, stp, styleflags, false); vm_pattern_cache[key] = p; return p; @@ -307,10 +353,9 @@ Cairo::RefPtr FastMeter::request_vertical_background( int width, int height, int *bgc, bool shade) { - if (height < min_pattern_metric_size) - height = min_pattern_metric_size; - if (height > max_pattern_metric_size) - height = max_pattern_metric_size; + height = max(height, min_pattern_metric_size); + height = min(height, max_pattern_metric_size); + height += 2; const PatternBgMapKey key (width, height, bgc[0], bgc[1]); PatternBgMap::iterator i; @@ -320,12 +365,62 @@ FastMeter::request_vertical_background( // TODO flush pattern cache if it gets too large Cairo::RefPtr p = generate_meter_background ( - width, height, bgc, shade); + width, height, bgc, shade, false); vb_pattern_cache[key] = p; return p; } +Cairo::RefPtr +FastMeter::request_horizontal_meter( + int width, int height, int *clr, float *stp, int styleflags) +{ + width = max(width, min_pattern_metric_size); + width = min(width, max_pattern_metric_size); + + 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], styleflags); + + Pattern10Map::iterator i; + if ((i = hm_pattern_cache.find (key)) != hm_pattern_cache.end()) { + return i->second; + } + // TODO flush pattern cache if it gets too large + + Cairo::RefPtr p = generate_meter_pattern ( + height, width, clr, stp, styleflags, true); + + hm_pattern_cache[key] = p; + return p; +} + +Cairo::RefPtr +FastMeter::request_horizontal_background( + int width, int height, int *bgc, bool shade) +{ + width = max(width, min_pattern_metric_size); + width = min(width, max_pattern_metric_size); + width += 2; + + const PatternBgMapKey key (width, height, bgc[0], bgc[1]); + PatternBgMap::iterator i; + if ((i = hb_pattern_cache.find (key)) != hb_pattern_cache.end()) { + return i->second; + } + // TODO flush pattern cache if it gets too large + + Cairo::RefPtr p = generate_meter_background ( + height, width, bgc, shade, true); + + hb_pattern_cache[key] = p; + + return p; +} + + void FastMeter::set_hold_count (long val) @@ -343,6 +438,16 @@ FastMeter::set_hold_count (long val) void FastMeter::on_size_request (GtkRequisition* req) +{ + if (orientation == Vertical) { + vertical_size_request (req); + } else { + horizontal_size_request (req); + } +} + +void +FastMeter::vertical_size_request (GtkRequisition* req) { req->height = request_height; req->height = max(req->height, min_pattern_metric_size); @@ -352,8 +457,29 @@ FastMeter::on_size_request (GtkRequisition* req) req->width = request_width; } +void +FastMeter::horizontal_size_request (GtkRequisition* req) +{ + req->width = request_width; + req->width = max(req->width, min_pattern_metric_size); + req->width = min(req->width, max_pattern_metric_size); + req->width += 2; + + req->height = request_height; +} + void FastMeter::on_size_allocate (Gtk::Allocation &alloc) +{ + if (orientation == Vertical) { + vertical_size_allocate (alloc); + } else { + horizontal_size_allocate (alloc); + } +} + +void +FastMeter::vertical_size_allocate (Gtk::Allocation &alloc) { if (alloc.get_width() != request_width) { alloc.set_width (request_width); @@ -377,10 +503,39 @@ FastMeter::on_size_allocate (Gtk::Allocation &alloc) DrawingArea::on_size_allocate (alloc); } +void +FastMeter::horizontal_size_allocate (Gtk::Allocation &alloc) +{ + if (alloc.get_height() != request_height) { + alloc.set_height (request_height); + } + + int w = alloc.get_width(); + w = max (w, min_pattern_metric_size + 2); + w = min (w, max_pattern_metric_size + 2); + + if (w != alloc.get_width()) { + alloc.set_width (w); + } + + if (pixwidth != w) { + fgpattern = request_horizontal_meter (w, request_height, _clr, _stp, _styleflags); + bgpattern = request_horizontal_background (w, request_height, highlight ? _bgh : _bgc, highlight); + pixwidth = w - 2; + pixheight = request_height - 2; + } + + DrawingArea::on_size_allocate (alloc); +} + bool FastMeter::on_expose_event (GdkEventExpose* ev) { - return vertical_expose (ev); + if (orientation == Vertical) { + return vertical_expose (ev); + } else { + return horizontal_expose (ev); + } } bool @@ -397,7 +552,7 @@ FastMeter::vertical_expose (GdkEventExpose* ev) cairo_clip (cr); cairo_set_source_rgb (cr, 0, 0, 0); // black - rounded_rectangle (cr, 0, 0, pixrect.width + 2, pixheight + 2, 2); + rounded_rectangle (cr, 0, 0, pixwidth + 2, pixheight + 2, 2); cairo_stroke (cr); top_of_meter = (gint) floor (pixheight * current_level); @@ -439,7 +594,80 @@ FastMeter::vertical_expose (GdkEventExpose* ev) } cairo_set_source (cr, fgpattern->cobj()); - cairo_rectangle (cr, 1, last_peak_rect.y, pixwidth, last_peak_rect.height); + cairo_rectangle (cr, last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height); + + if (bright_hold && !no_rgba_overlay) { + cairo_fill_preserve (cr); + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.3); + } + cairo_fill (cr); + + } else { + last_peak_rect.width = 0; + last_peak_rect.height = 0; + } + + cairo_destroy (cr); + + return TRUE; +} + +bool +FastMeter::horizontal_expose (GdkEventExpose* ev) +{ + Glib::RefPtr 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); + + cairo_set_source_rgb (cr, 0, 0, 0); // black + rounded_rectangle (cr, 0, 0, pixwidth + 2, pixheight + 2, 2); + cairo_stroke (cr); + + right_of_meter = (gint) floor (pixwidth * current_level); + + /* reset the height & origin of the rect that needs to show the pixbuf + */ + + pixrect.width = right_of_meter; + + background.x = 1 + right_of_meter; + background.y = 1; + background.width = pixwidth - right_of_meter; + background.height = pixheight; + + if (gdk_rectangle_intersect (&background, &ev->area, &intersection)) { + cairo_set_source (cr, bgpattern->cobj()); + cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height); + cairo_fill (cr); + } + + if (gdk_rectangle_intersect (&pixrect, &ev->area, &intersection)) { + cairo_set_source (cr, fgpattern->cobj()); + cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height); + cairo_fill (cr); + } + + // draw peak bar + + if (hold_state) { + last_peak_rect.y = 1; + last_peak_rect.height = pixheight; + const int xpos = floor (pixwidth * current_peak); + if (bright_hold) { + last_peak_rect.width = min(4, xpos ); + } else { + last_peak_rect.width = min(2, xpos ); + } + last_peak_rect.x = 1 + max(0, xpos - last_peak_rect.width); + + cairo_set_source (cr, fgpattern->cobj()); + cairo_rectangle (cr, last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height); if (bright_hold && !no_rgba_overlay) { cairo_fill_preserve (cr); @@ -494,7 +722,11 @@ FastMeter::set (float lvl, float peak) return; } - queue_vertical_redraw (win, old_level); + if (orientation == Vertical) { + queue_vertical_redraw (win, old_level); + } else { + queue_horizontal_redraw (win, old_level); + } } void @@ -575,6 +807,81 @@ FastMeter::queue_vertical_redraw (const Glib::RefPtr& win, float ol } } +void +FastMeter::queue_horizontal_redraw (const Glib::RefPtr& win, float old_level) +{ +#if 1 + GdkRectangle rect; + + gint new_right = (gint) floor (pixwidth * current_level); + + rect.height = pixheight; + rect.y = 1; + + if (current_level > old_level) { + rect.x = 1 + pixrect.width; + /* colored/pixbuf got larger, just draw the new section */ + rect.width = new_right - pixrect.width; + } else { + /* it got smaller, compute the difference */ + rect.x = 1 + new_right; + /* rect.height is the old.x (smaller) minus the new.x (larger) */ + rect.width = pixrect.width - new_right; + } + + GdkRegion* region = 0; + bool queue = false; + + if (rect.height != 0) { + + /* ok, first region to draw ... */ + + region = gdk_region_rectangle (&rect); + queue = true; + } + + /* redraw the last place where the last peak hold bar was; + the next expose will draw the new one whether its part of + expose region or not. + */ + + if (last_peak_rect.width * last_peak_rect.height != 0) { + if (!queue) { + region = gdk_region_new (); + queue = true; + } + gdk_region_union_with_rect (region, &last_peak_rect); + } + + if (hold_state && current_peak > 0) { + if (!queue) { + region = gdk_region_new (); + queue = true; + } + rect.y = 1; + rect.height = pixheight; + const int xpos = floor (pixwidth * current_peak); + if (bright_hold) { + rect.width = min(4, xpos); + } else { + rect.width = min(2, xpos); + } + rect.x = 1 + max(0, xpos - rect.width); + gdk_region_union_with_rect (region, &rect); + } + + if (queue) { + gdk_window_invalidate_region (win->gobj(), region, true); + } + if (region) { + gdk_region_destroy(region); + region = 0; + } +#else + queue_draw (); +#endif +} + void FastMeter::set_highlight (bool onoff) { @@ -582,7 +889,11 @@ FastMeter::set_highlight (bool onoff) return; } highlight = onoff; - bgpattern = request_vertical_background (request_width, pixheight, highlight ? _bgh : _bgc, highlight); + if (orientation == Vertical) { + bgpattern = request_vertical_background (request_width, request_height, highlight ? _bgh : _bgc, highlight); + } else { + bgpattern = request_horizontal_background (request_width, request_height, highlight ? _bgh : _bgc, highlight); + } queue_draw (); } diff --git a/libs/gtkmm2ext/gtkmm2ext/fastmeter.h b/libs/gtkmm2ext/gtkmm2ext/fastmeter.h index 3a806262f1..602bea1b38 100644 --- a/libs/gtkmm2ext/gtkmm2ext/fastmeter.h +++ b/libs/gtkmm2ext/gtkmm2ext/fastmeter.h @@ -68,7 +68,6 @@ protected: bool on_expose_event (GdkEventExpose*); void on_size_request (GtkRequisition*); void on_size_allocate (Gtk::Allocation&); - private: Cairo::RefPtr fgpattern; @@ -96,19 +95,30 @@ private: bool highlight; bool vertical_expose (GdkEventExpose*); + void vertical_size_request (GtkRequisition*); + void vertical_size_allocate (Gtk::Allocation&); void queue_vertical_redraw (const Glib::RefPtr&, float); + bool horizontal_expose (GdkEventExpose*); + void horizontal_size_request (GtkRequisition*); + void horizontal_size_allocate (Gtk::Allocation&); + void queue_horizontal_redraw (const Glib::RefPtr&, float); + static bool no_rgba_overlay; static Cairo::RefPtr generate_meter_pattern ( - int, int, int *, float *, int); + int, int, int *, float *, int, bool); static Cairo::RefPtr request_vertical_meter ( int, int, int *, float *, int); + static Cairo::RefPtr request_horizontal_meter ( + int, int, int *, float *, int); static Cairo::RefPtr generate_meter_background ( - int, int, int *, bool); + int, int, int *, bool, bool); static Cairo::RefPtr request_vertical_background ( int, int, int *, bool); + static Cairo::RefPtr request_horizontal_background ( + int, int, int *, bool); struct Pattern10MapKey { Pattern10MapKey ( @@ -151,6 +161,8 @@ private: static Pattern10Map vm_pattern_cache; static PatternBgMap vb_pattern_cache; + static Pattern10Map hm_pattern_cache; + static PatternBgMap hb_pattern_cache; static int min_pattern_metric_size; // min dimension for axis that displays the meter level static int max_pattern_metric_size; // max dimension for axis that displays the meter level };