Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / guicast / bcmeter.C
diff --git a/cinelerra-5.1/guicast/bcmeter.C b/cinelerra-5.1/guicast/bcmeter.C
new file mode 100644 (file)
index 0000000..be4a1f0
--- /dev/null
@@ -0,0 +1,610 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+#include "bcbutton.h"
+#include "bcmeter.h"
+#include "bcpixmap.h"
+#include "bcresources.h"
+#include "bcwindow.h"
+#include "colors.h"
+#include "fonts.h"
+#include "vframe.h"
+#include <string.h>
+
+// Which source image to replicate
+#define METER_NORMAL 0
+#define METER_GREEN 1
+#define METER_RED 2
+#define METER_YELLOW 3
+#define METER_WHITE 4
+#define METER_OVER 5
+#define METER_DOWNMIX 6
+
+// Region of source image to replicate
+#define METER_LEFT 0
+#define METER_MID 1
+#define METER_RIGHT 3
+
+
+BC_Meter::BC_Meter(int x, 
+       int y, 
+       int orientation, 
+       int pixels, 
+       int min, 
+       int max,
+       int mode, 
+       int use_titles,
+       int span,
+       int downmix)
+ : BC_SubWindow(x, y, -1, -1)
+{
+       this->over_delay = 150;
+       this->peak_delay = 15;
+       this->use_titles = use_titles;
+       this->min = min;
+       this->max = max;
+       this->mode = mode;
+       this->orientation = orientation;
+       this->pixels = pixels;
+       this->span = span;
+       this->downmix = downmix;
+
+//printf("BC_Meter::draw_face %d w=%d pixels=%d\n", __LINE__, w, pixels);
+       for(int i = 0; i < TOTAL_METER_IMAGES; i++) images[i] = 0;
+       db_titles.set_array_delete();
+}
+
+BC_Meter::~BC_Meter()
+{
+       db_titles.remove_all_objects();
+       title_pixels.remove_all();
+       tick_pixels.remove_all();
+       for(int i = 0; i < TOTAL_METER_IMAGES; i++) delete images[i];
+}
+
+int BC_Meter::get_title_w()
+{
+       return get_resources()->meter_title_w;
+}
+
+int BC_Meter::get_meter_w()
+{
+       return get_resources()->ymeter_images[0]->get_w();
+}
+
+
+int BC_Meter::set_delays(int over_delay, int peak_delay)
+{
+       this->over_delay = over_delay;
+       this->peak_delay = peak_delay;
+       return 0;
+}
+
+int BC_Meter::initialize()
+{
+       peak_timer = 0;
+       level_pixel = peak_pixel = 0;
+       over_timer = 0;
+       over_count = 0;
+       peak = level = -100;
+
+       if(orientation == METER_VERT)
+       {
+               set_images(get_resources()->ymeter_images);
+               h = pixels;
+               if(span < 0) 
+               {
+                       w = images[0]->get_w();
+                       if(use_titles) w += get_title_w();
+               }
+               else 
+                       w = span;
+       }
+       else
+       {
+               set_images(get_resources()->xmeter_images);
+               h = images[0]->get_h();
+               w = pixels;
+               if(use_titles) h += get_title_w();
+       }
+
+// calibrate the db titles
+       get_divisions();
+
+       BC_SubWindow::initialize();
+       draw_titles(0);
+       draw_face(0);
+       show_window(0);
+       return 0;
+}
+
+void BC_Meter::set_images(VFrame **data)
+{
+       for(int i = 0; i < TOTAL_METER_IMAGES; i++) delete images[i];
+       for(int i = 0; i < TOTAL_METER_IMAGES; i++) 
+               images[i] = new BC_Pixmap(parent_window, data[i], PIXMAP_ALPHA);
+}
+
+int BC_Meter::reposition_window(int x, int y, int span, int pixels)
+{
+       if(pixels < 0) pixels = this->pixels;
+       this->span = span;
+       this->pixels = pixels;
+       if(orientation == METER_VERT)
+               BC_SubWindow::reposition_window(x, 
+                       y, 
+                       this->span < 0 ? w : span, 
+                       pixels);
+       else
+               BC_SubWindow::reposition_window(x, y, pixels, get_h());
+
+//printf("BC_Meter::reposition_window 1 %d %d %d %d\n", x, y, w, h);
+       get_divisions();
+
+//set_color(WHITE);
+//draw_box(0, 0, w, h);
+//flash();     
+//return 0;
+       draw_titles(0);
+       draw_face(0);
+       return 0;
+}
+
+int BC_Meter::reset(int dmix)
+{
+       level = min;
+       peak = min;
+       level_pixel = peak_pixel = 0;
+       peak_timer = 0;
+       over_timer = 0;
+       over_count = 0;
+       if(dmix >= 0) downmix = dmix;
+       draw_face(1);
+       return 0;
+}
+
+int BC_Meter::button_press_event()
+{
+       if(cursor_inside() && is_event_win())
+       {
+               reset_over();
+               return 1;
+       }
+       return 0;
+}
+
+
+int BC_Meter::reset_over()
+{
+       over_timer = 0;
+       return 0;
+}
+
+int BC_Meter::change_format(int mode, int min, int max)
+{
+       this->mode = mode;
+       this->min = min;
+       this->max = max;
+       reposition_window(get_x(), get_y(), w, pixels);
+       return 0;
+}
+
+int BC_Meter::level_to_pixel(float level)
+{
+       int result;
+       if(mode == METER_DB)
+       {
+               result = (int)(pixels * 
+                       (level - min) / 
+                       (max - min));
+               if(level <= min) result = 0;
+       }
+       else
+       {
+// Not implemented anymore
+               result = 0;
+       }
+       
+       return result;
+}
+
+
+void BC_Meter::get_divisions()
+{
+       char string[BCTEXTLEN];
+       char *new_string;
+
+
+       db_titles.remove_all_objects();
+       title_pixels.remove_all();
+       tick_pixels.remove_all();
+
+       low_division = 0;
+       medium_division = 0;
+       high_division = pixels;
+
+       int current_pixel;
+// Create tick marks and titles in one pass
+       for(int current = min; current <= max; current++)
+       {
+               if(orientation == METER_VERT)
+               {
+// Create tick mark
+                       current_pixel = (pixels - METER_MARGIN * 2 - 2) * 
+                               (current - min) /
+                               (max - min) + 2;
+                       tick_pixels.append(current_pixel);
+
+// Create titles in selected positions
+                       if(current == min || 
+                               current == max ||
+                               current == 0 ||
+                               (current - min > 4 && max - current > 4 && !(current % 5)))
+                       {
+                               int title_pixel = (pixels - 
+                                       METER_MARGIN * 2) * 
+                                       (current - min) /
+                                       (max - min);
+                               sprintf(string, "%ld", labs(current));
+                               new_string = new char[strlen(string) + 1];
+                               strcpy(new_string, string);
+                               db_titles.append(new_string);
+                               title_pixels.append(title_pixel);
+                       }
+               }
+               else
+               {
+                       current_pixel = (pixels - METER_MARGIN * 2) *
+                               (current - min) /
+                               (max - min);
+                       tick_pixels.append(current_pixel);
+// Titles not supported for horizontal
+               }
+
+// Create color divisions
+               if(current == -20)
+               {
+                       low_division = current_pixel;
+               }
+               else
+               if(current == -5)
+               {
+                       medium_division = current_pixel;
+               }
+               else
+               if(current == 0)
+               {
+                       high_division = current_pixel;
+               }
+       }
+// if(orientation == METER_VERT)
+// printf("BC_Meter::get_divisions %d %d %d %d\n",
+// low_division, medium_division, high_division, pixels);
+}
+
+void BC_Meter::draw_titles(int flush)
+{
+       if(!use_titles) return;
+
+       set_font(get_resources()->meter_font);
+
+       if(orientation == METER_HORIZ)
+       {
+               draw_top_background(parent_window, 0, 0, get_w(), get_title_w());
+
+               for(int i = 0; i < db_titles.total; i++)
+               {
+                       draw_text(0, title_pixels.values[i], db_titles.values[i]);
+               }
+
+               flash(0, 0, get_w(), get_title_w(), flush);
+       }
+       else
+       if(orientation == METER_VERT)
+       {
+               draw_top_background(parent_window, 0, 0, get_title_w(), get_h());
+
+// Titles
+               for(int i = 0; i < db_titles.total; i++)
+               {
+                       int title_y = pixels - 
+                               title_pixels.values[i];
+                       if(i == 0) 
+                               title_y -= get_text_descent(SMALLFONT_3D);
+                       else
+                       if(i == db_titles.total - 1)
+                               title_y += get_text_ascent(SMALLFONT_3D);
+                       else
+                               title_y += get_text_ascent(SMALLFONT_3D) / 2;
+
+                       set_color(get_resources()->meter_font_color);
+                       draw_text(0, 
+                               title_y,
+                               db_titles.values[i]);
+               }
+
+               for(int i = 0; i < tick_pixels.total; i++)
+               {
+// Tick marks
+                       int tick_y = pixels - tick_pixels.values[i] - METER_MARGIN;
+                       set_color(get_resources()->meter_font_color);
+                       draw_line(get_title_w() - 10 - 1, tick_y, get_title_w() - 1, tick_y);
+                       if(get_resources()->meter_3d)
+                       {
+                               set_color(BLACK);
+                               draw_line(get_title_w() - 10, tick_y + 1, get_title_w(), tick_y + 1);
+                       }
+               }
+
+               flash(0, 0, get_title_w(), get_h(), flush);
+       }
+}
+
+int BC_Meter::region_pixel(int region)
+{
+       VFrame **reference_images = get_resources()->xmeter_images;
+       int result;
+
+       if(region == METER_RIGHT) 
+               result = region * reference_images[0]->get_w() / 4;
+       else
+               result = region * reference_images[0]->get_w() / 4;
+
+       return result;
+}
+
+int BC_Meter::region_pixels(int region)
+{
+       int x1;
+       int x2;
+       int result;
+       VFrame **reference_images = get_resources()->xmeter_images;
+       
+       x1 = region * reference_images[0]->get_w() / 4;
+       x2 = (region + 1) * reference_images[0]->get_w() / 4;
+       if(region == METER_MID) 
+               result = (x2 - x1) * 2;
+       else 
+               result = x2 - x1;
+       return result;
+}
+
+void BC_Meter::draw_face(int flush)
+{
+       int level_pixel = level_to_pixel(level);
+       int peak_pixel2 = level_to_pixel(peak);
+       int peak_pixel1 = peak_pixel2 - 2;
+       int left_pixel = region_pixel(METER_MID);
+       int right_pixel = pixels - region_pixels(METER_RIGHT);
+       int pixel = 0;
+       int image_number = 0, region = 0;
+       int in_span, in_start;
+       int x = use_titles ? get_title_w() : 0;
+       int w = use_titles ? (this->w - get_title_w()) : this->w;
+
+       draw_top_background(parent_window, x, 0, w, h);
+
+// printf("BC_Meter::draw_face %d span=%d this->w=%d get_title_w()=%d %d %d\n", 
+// __LINE__, 
+// span, 
+// this->w,
+// get_title_w(),
+// w, 
+// h);
+
+       while(pixel < pixels)
+       {
+// Select image to draw
+               if(pixel < level_pixel ||
+                       (pixel >= peak_pixel1 && pixel < peak_pixel2))
+               {
+                       if(pixel < low_division)
+                               image_number = METER_GREEN;
+                       else
+                       if(pixel < medium_division)
+                               image_number = METER_YELLOW;
+                       else
+                       if(pixel < high_division)
+                               image_number = METER_RED;
+                       else
+                               image_number = METER_WHITE;
+               }
+               else
+               {
+                       image_number = METER_NORMAL;
+               }
+
+// Select region of image to duplicate
+               if(pixel < left_pixel)
+               {
+                       region = METER_LEFT;
+                       in_start = pixel + region_pixel(region);
+                       in_span = region_pixels(region) - (in_start - region_pixel(region));
+               }
+               else
+               if(pixel < right_pixel)
+               {
+                       region = METER_MID;
+                       in_start = region_pixel(region);
+                       in_span = region_pixels(region);
+               }
+               else
+               {
+                       region = METER_RIGHT;
+                       in_start = (pixel - right_pixel) + region_pixel(region);
+                       in_span = region_pixels(region) - (in_start - region_pixel(region));;
+               }
+
+//printf("BC_Meter::draw_face region %d pixel %d pixels %d in_start %d in_span %d\n", region, pixel, pixels, in_start, in_span);
+               if(in_span > 0)
+               {
+// Clip length to peaks
+                       if(pixel < level_pixel && pixel + in_span > level_pixel)
+                               in_span = level_pixel - pixel;
+                       else
+                       if(pixel < peak_pixel1 && pixel + in_span > peak_pixel1)
+                               in_span = peak_pixel1 - pixel;
+                       else
+                       if(pixel < peak_pixel2 && pixel + in_span > peak_pixel2) 
+                               in_span = peak_pixel2 - pixel;
+
+// Clip length to color changes
+                       if(image_number == METER_GREEN && pixel + in_span > low_division)
+                               in_span = low_division - pixel;
+                       else
+                       if(image_number == METER_YELLOW && pixel + in_span > medium_division)
+                               in_span = medium_division - pixel;
+                       else
+                       if(image_number == METER_RED && pixel + in_span > high_division)
+                               in_span = high_division - pixel;
+
+// Clip length to regions
+                       if(pixel < left_pixel && pixel + in_span > left_pixel)
+                               in_span = left_pixel - pixel;
+                       else
+                       if(pixel < right_pixel && pixel + in_span > right_pixel)
+                               in_span = right_pixel - pixel;
+
+//printf("BC_Meter::draw_face image_number %d pixel %d pixels %d in_start %d in_span %d\n", image_number, pixel, pixels, in_start, in_span);
+//printf("BC_Meter::draw_face %d %d %d %d\n", orientation, region, images[image_number]->get_h() - in_start - in_span);
+                       if(orientation == METER_HORIZ)
+                       {
+                               draw_pixmap(images[image_number], 
+                                       pixel, 
+                                       x, 
+                                       in_span + 1, 
+                                       get_h(), 
+                                       in_start, 
+                                       0);
+                       }
+                       else
+                       {
+//printf("BC_Meter::draw_face %d %d\n", __LINE__, span);
+                               if(span < 0)
+                               {
+                                       draw_pixmap(images[image_number],
+                                               x,
+                                               get_h() - pixel - in_span,
+                                               get_w(),
+                                               in_span + 1,
+                                               0,
+                                               images[image_number]->get_h() - in_start - in_span);
+                               }
+                               else
+                               {
+                                       int total_w = get_w() - x;
+                                       int third = images[image_number]->get_w() / 3 + 1;
+
+
+                                       for(int x1 = 0; x1 < total_w; x1 += third)
+                                       {
+                                               int in_x = 0;
+                                               int in_w = third;
+                                               if(x1 >= third) in_x = third;
+                                               if(x1 >= total_w - third)
+                                               {
+                                                       in_x = images[image_number]->get_w() - 
+                                                               (total_w - x1);
+                                                       in_w = total_w - x1;
+                                               }
+
+                                               int in_y = images[image_number]->get_h() - in_start - in_span;
+//printf("BC_Meter::draw_face %d %d %d\n", __LINE__, get_w(), x + x1 + in_w, in_x, in_y, in_w, span);
+
+
+                                               draw_pixmap(images[image_number],
+                                                       x + x1,
+                                                       get_h() - pixel - in_span,
+                                                       in_w,
+                                                       in_span + 1,
+                                                       in_x,
+                                                       in_y);
+                                       }
+                               }
+                       }
+
+                       pixel += in_span;
+               }
+               else
+               {
+// Sanity check
+                       break;
+               }
+       }
+
+       if(downmix) {
+               if(orientation == METER_HORIZ)
+                       draw_pixmap(images[METER_DOWNMIX],
+                               0,
+                               0);
+               else
+                       draw_pixmap(images[METER_DOWNMIX],
+                               x,
+                               get_h() - images[METER_DOWNMIX]->get_h()-1);
+       }
+
+       if(over_timer)
+       {
+               if(orientation == METER_HORIZ)
+                       draw_pixmap(images[METER_OVER], 
+                               20, 
+                               2);
+               else
+                       draw_pixmap(images[METER_OVER],
+                               x,
+                               get_h() - 100);
+
+               over_timer--;
+       }
+
+       if(orientation == METER_HORIZ)
+               flash(0, 0, pixels, get_h(), flush);
+       else
+               flash(x, 0, w, pixels, flush);
+}
+
+int BC_Meter::update(float new_value, int over, int dmix)
+{
+       peak_timer++;
+
+       if(mode == METER_DB)
+       {
+               if(new_value == 0) 
+                       level = min;
+               else
+                       level = db.todb(new_value);        // db value
+       }
+
+       if(level > peak || peak_timer > peak_delay)
+       {
+               peak = level;
+               peak_timer = 0;
+       }
+
+// if(orientation == METER_HORIZ)
+// printf("BC_Meter::update %f\n", level);
+       if(over) over_timer = over_delay;
+// only draw if window is visible
+       if(dmix >= 0) downmix = dmix;
+
+       draw_face(1);
+       return 0;
+}