remove whitespace at eol
[goodguy/history.git] / cinelerra-5.1 / guicast / bcmeter.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcbutton.h"
23 #include "bcmeter.h"
24 #include "bcpixmap.h"
25 #include "bcresources.h"
26 #include "bcwindow.h"
27 #include "colors.h"
28 #include "fonts.h"
29 #include "vframe.h"
30 #include <string.h>
31
32 // Which source image to replicate
33 #define METER_NORMAL 0
34 #define METER_GREEN 1
35 #define METER_RED 2
36 #define METER_YELLOW 3
37 #define METER_WHITE 4
38 #define METER_OVER 5
39 #define METER_DOWNMIX 6
40
41 // Region of source image to replicate
42 #define METER_LEFT 0
43 #define METER_MID 1
44 #define METER_RIGHT 3
45
46
47 BC_Meter::BC_Meter(int x,
48         int y,
49         int orientation,
50         int pixels,
51         int min,
52         int max,
53         int mode,
54         int use_titles,
55         int span,
56         int downmix)
57  : BC_SubWindow(x, y, -1, -1)
58 {
59         this->over_delay = 150;
60         this->peak_delay = 15;
61         this->use_titles = use_titles;
62         this->min = min;
63         this->max = max;
64         this->mode = mode;
65         this->orientation = orientation;
66         this->pixels = pixels;
67         this->span = span;
68         this->downmix = downmix;
69
70 //printf("BC_Meter::draw_face %d w=%d pixels=%d\n", __LINE__, w, pixels);
71         for(int i = 0; i < TOTAL_METER_IMAGES; i++) images[i] = 0;
72         db_titles.set_array_delete();
73 }
74
75 BC_Meter::~BC_Meter()
76 {
77         db_titles.remove_all_objects();
78         title_pixels.remove_all();
79         tick_pixels.remove_all();
80         for(int i = 0; i < TOTAL_METER_IMAGES; i++) delete images[i];
81 }
82
83 int BC_Meter::get_title_w()
84 {
85         return get_resources()->meter_title_w;
86 }
87
88 int BC_Meter::get_meter_w()
89 {
90         return get_resources()->ymeter_images[0]->get_w();
91 }
92
93
94 int BC_Meter::set_delays(int over_delay, int peak_delay)
95 {
96         this->over_delay = over_delay;
97         this->peak_delay = peak_delay;
98         return 0;
99 }
100
101 int BC_Meter::initialize()
102 {
103         peak_timer = 0;
104         level_pixel = peak_pixel = 0;
105         over_timer = 0;
106         over_count = 0;
107         peak = level = -100;
108
109         if(orientation == METER_VERT)
110         {
111                 set_images(get_resources()->ymeter_images);
112                 h = pixels;
113                 if(span < 0)
114                 {
115                         w = images[0]->get_w();
116                         if(use_titles) w += get_title_w();
117                 }
118                 else
119                         w = span;
120         }
121         else
122         {
123                 set_images(get_resources()->xmeter_images);
124                 h = images[0]->get_h();
125                 w = pixels;
126                 if(use_titles) h += get_title_w();
127         }
128
129 // calibrate the db titles
130         get_divisions();
131
132         BC_SubWindow::initialize();
133         draw_titles(0);
134         draw_face(0);
135         show_window(0);
136         return 0;
137 }
138
139 void BC_Meter::set_images(VFrame **data)
140 {
141         for(int i = 0; i < TOTAL_METER_IMAGES; i++) delete images[i];
142         for(int i = 0; i < TOTAL_METER_IMAGES; i++)
143                 images[i] = new BC_Pixmap(parent_window, data[i], PIXMAP_ALPHA);
144 }
145
146 int BC_Meter::reposition_window(int x, int y, int span, int pixels)
147 {
148         if(pixels < 0) pixels = this->pixels;
149         this->span = span;
150         this->pixels = pixels;
151         if(orientation == METER_VERT)
152                 BC_SubWindow::reposition_window(x,
153                         y,
154                         this->span < 0 ? w : span,
155                         pixels);
156         else
157                 BC_SubWindow::reposition_window(x, y, pixels, get_h());
158
159 //printf("BC_Meter::reposition_window 1 %d %d %d %d\n", x, y, w, h);
160         get_divisions();
161
162 //set_color(WHITE);
163 //draw_box(0, 0, w, h);
164 //flash();
165 //return 0;
166         draw_titles(0);
167         draw_face(0);
168         return 0;
169 }
170
171 int BC_Meter::reset(int dmix)
172 {
173         level = min;
174         peak = min;
175         level_pixel = peak_pixel = 0;
176         peak_timer = 0;
177         over_timer = 0;
178         over_count = 0;
179         if(dmix >= 0) downmix = dmix;
180         draw_face(1);
181         return 0;
182 }
183
184 int BC_Meter::button_press_event()
185 {
186         if(cursor_inside() && is_event_win())
187         {
188                 reset_over();
189                 return 1;
190         }
191         return 0;
192 }
193
194
195 int BC_Meter::reset_over()
196 {
197         over_timer = 0;
198         return 0;
199 }
200
201 int BC_Meter::change_format(int mode, int min, int max)
202 {
203         this->mode = mode;
204         this->min = min;
205         this->max = max;
206         reposition_window(get_x(), get_y(), w, pixels);
207         return 0;
208 }
209
210 int BC_Meter::level_to_pixel(float level)
211 {
212         int result;
213         if(mode == METER_DB)
214         {
215                 result = (int)(pixels *
216                         (level - min) /
217                         (max - min));
218                 if(level <= min) result = 0;
219         }
220         else
221         {
222 // Not implemented anymore
223                 result = 0;
224         }
225
226         return result;
227 }
228
229
230 void BC_Meter::get_divisions()
231 {
232         char string[BCTEXTLEN];
233         char *new_string;
234
235
236         db_titles.remove_all_objects();
237         title_pixels.remove_all();
238         tick_pixels.remove_all();
239
240         low_division = 0;
241         medium_division = 0;
242         high_division = pixels;
243
244         int current_pixel;
245 // Create tick marks and titles in one pass
246         for(int current = min; current <= max; current++)
247         {
248                 if(orientation == METER_VERT)
249                 {
250 // Create tick mark
251                         current_pixel = (pixels - METER_MARGIN * 2 - 2) *
252                                 (current - min) /
253                                 (max - min) + 2;
254                         tick_pixels.append(current_pixel);
255
256 // Create titles in selected positions
257                         if(current == min ||
258                                 current == max ||
259                                 current == 0 ||
260                                 (current - min > 4 && max - current > 4 && !(current % 5)))
261                         {
262                                 int title_pixel = (pixels -
263                                         METER_MARGIN * 2) *
264                                         (current - min) /
265                                         (max - min);
266                                 sprintf(string, "%ld", labs(current));
267                                 new_string = new char[strlen(string) + 1];
268                                 strcpy(new_string, string);
269                                 db_titles.append(new_string);
270                                 title_pixels.append(title_pixel);
271                         }
272                 }
273                 else
274                 {
275                         current_pixel = (pixels - METER_MARGIN * 2) *
276                                 (current - min) /
277                                 (max - min);
278                         tick_pixels.append(current_pixel);
279 // Titles not supported for horizontal
280                 }
281
282 // Create color divisions
283                 if(current == -20)
284                 {
285                         low_division = current_pixel;
286                 }
287                 else
288                 if(current == -5)
289                 {
290                         medium_division = current_pixel;
291                 }
292                 else
293                 if(current == 0)
294                 {
295                         high_division = current_pixel;
296                 }
297         }
298 // if(orientation == METER_VERT)
299 // printf("BC_Meter::get_divisions %d %d %d %d\n",
300 // low_division, medium_division, high_division, pixels);
301 }
302
303 void BC_Meter::draw_titles(int flush)
304 {
305         if(!use_titles) return;
306
307         set_font(get_resources()->meter_font);
308
309         if(orientation == METER_HORIZ)
310         {
311                 draw_top_background(parent_window, 0, 0, get_w(), get_title_w());
312
313                 for(int i = 0; i < db_titles.total; i++)
314                 {
315                         draw_text(0, title_pixels.values[i], db_titles.values[i]);
316                 }
317
318                 flash(0, 0, get_w(), get_title_w(), flush);
319         }
320         else
321         if(orientation == METER_VERT)
322         {
323                 draw_top_background(parent_window, 0, 0, get_title_w(), get_h());
324
325 // Titles
326                 for(int i = 0; i < db_titles.total; i++)
327                 {
328                         int title_y = pixels -
329                                 title_pixels.values[i];
330                         if(i == 0)
331                                 title_y -= get_text_descent(SMALLFONT_3D);
332                         else
333                         if(i == db_titles.total - 1)
334                                 title_y += get_text_ascent(SMALLFONT_3D);
335                         else
336                                 title_y += get_text_ascent(SMALLFONT_3D) / 2;
337
338                         set_color(get_resources()->meter_font_color);
339                         draw_text(0,
340                                 title_y,
341                                 db_titles.values[i]);
342                 }
343
344                 for(int i = 0; i < tick_pixels.total; i++)
345                 {
346 // Tick marks
347                         int tick_y = pixels - tick_pixels.values[i] - METER_MARGIN;
348                         set_color(get_resources()->meter_font_color);
349                         draw_line(get_title_w() - 10 - 1, tick_y, get_title_w() - 1, tick_y);
350                         if(get_resources()->meter_3d)
351                         {
352                                 set_color(BLACK);
353                                 draw_line(get_title_w() - 10, tick_y + 1, get_title_w(), tick_y + 1);
354                         }
355                 }
356
357                 flash(0, 0, get_title_w(), get_h(), flush);
358         }
359 }
360
361 int BC_Meter::region_pixel(int region)
362 {
363         VFrame **reference_images = get_resources()->xmeter_images;
364         int result;
365
366         if(region == METER_RIGHT)
367                 result = region * reference_images[0]->get_w() / 4;
368         else
369                 result = region * reference_images[0]->get_w() / 4;
370
371         return result;
372 }
373
374 int BC_Meter::region_pixels(int region)
375 {
376         int x1;
377         int x2;
378         int result;
379         VFrame **reference_images = get_resources()->xmeter_images;
380
381         x1 = region * reference_images[0]->get_w() / 4;
382         x2 = (region + 1) * reference_images[0]->get_w() / 4;
383         if(region == METER_MID)
384                 result = (x2 - x1) * 2;
385         else
386                 result = x2 - x1;
387         return result;
388 }
389
390 void BC_Meter::draw_face(int flush)
391 {
392         int level_pixel = level_to_pixel(level);
393         int peak_pixel2 = level_to_pixel(peak);
394         int peak_pixel1 = peak_pixel2 - 2;
395         int left_pixel = region_pixel(METER_MID);
396         int right_pixel = pixels - region_pixels(METER_RIGHT);
397         int pixel = 0;
398         int image_number = 0, region = 0;
399         int in_span, in_start;
400         int x = use_titles ? get_title_w() : 0;
401         int w = use_titles ? (this->w - get_title_w()) : this->w;
402
403         draw_top_background(parent_window, x, 0, w, h);
404
405 // printf("BC_Meter::draw_face %d span=%d this->w=%d get_title_w()=%d %d %d\n",
406 // __LINE__,
407 // span,
408 // this->w,
409 // get_title_w(),
410 // w,
411 // h);
412
413         while(pixel < pixels)
414         {
415 // Select image to draw
416                 if(pixel < level_pixel ||
417                         (pixel >= peak_pixel1 && pixel < peak_pixel2))
418                 {
419                         if(pixel < low_division)
420                                 image_number = METER_GREEN;
421                         else
422                         if(pixel < medium_division)
423                                 image_number = METER_YELLOW;
424                         else
425                         if(pixel < high_division)
426                                 image_number = METER_RED;
427                         else
428                                 image_number = METER_WHITE;
429                 }
430                 else
431                 {
432                         image_number = METER_NORMAL;
433                 }
434
435 // Select region of image to duplicate
436                 if(pixel < left_pixel)
437                 {
438                         region = METER_LEFT;
439                         in_start = pixel + region_pixel(region);
440                         in_span = region_pixels(region) - (in_start - region_pixel(region));
441                 }
442                 else
443                 if(pixel < right_pixel)
444                 {
445                         region = METER_MID;
446                         in_start = region_pixel(region);
447                         in_span = region_pixels(region);
448                 }
449                 else
450                 {
451                         region = METER_RIGHT;
452                         in_start = (pixel - right_pixel) + region_pixel(region);
453                         in_span = region_pixels(region) - (in_start - region_pixel(region));;
454                 }
455
456 //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);
457                 if(in_span > 0)
458                 {
459 // Clip length to peaks
460                         if(pixel < level_pixel && pixel + in_span > level_pixel)
461                                 in_span = level_pixel - pixel;
462                         else
463                         if(pixel < peak_pixel1 && pixel + in_span > peak_pixel1)
464                                 in_span = peak_pixel1 - pixel;
465                         else
466                         if(pixel < peak_pixel2 && pixel + in_span > peak_pixel2)
467                                 in_span = peak_pixel2 - pixel;
468
469 // Clip length to color changes
470                         if(image_number == METER_GREEN && pixel + in_span > low_division)
471                                 in_span = low_division - pixel;
472                         else
473                         if(image_number == METER_YELLOW && pixel + in_span > medium_division)
474                                 in_span = medium_division - pixel;
475                         else
476                         if(image_number == METER_RED && pixel + in_span > high_division)
477                                 in_span = high_division - pixel;
478
479 // Clip length to regions
480                         if(pixel < left_pixel && pixel + in_span > left_pixel)
481                                 in_span = left_pixel - pixel;
482                         else
483                         if(pixel < right_pixel && pixel + in_span > right_pixel)
484                                 in_span = right_pixel - pixel;
485
486 //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);
487 //printf("BC_Meter::draw_face %d %d %d %d\n", orientation, region, images[image_number]->get_h() - in_start - in_span);
488                         if(orientation == METER_HORIZ)
489                         {
490                                 draw_pixmap(images[image_number],
491                                         pixel,
492                                         x,
493                                         in_span + 1,
494                                         get_h(),
495                                         in_start,
496                                         0);
497                         }
498                         else
499                         {
500 //printf("BC_Meter::draw_face %d %d\n", __LINE__, span);
501                                 if(span < 0)
502                                 {
503                                         draw_pixmap(images[image_number],
504                                                 x,
505                                                 get_h() - pixel - in_span,
506                                                 get_w(),
507                                                 in_span + 1,
508                                                 0,
509                                                 images[image_number]->get_h() - in_start - in_span);
510                                 }
511                                 else
512                                 {
513                                         int total_w = get_w() - x;
514                                         int third = images[image_number]->get_w() / 3 + 1;
515
516
517                                         for(int x1 = 0; x1 < total_w; x1 += third)
518                                         {
519                                                 int in_x = 0;
520                                                 int in_w = third;
521                                                 if(x1 >= third) in_x = third;
522                                                 if(x1 >= total_w - third)
523                                                 {
524                                                         in_x = images[image_number]->get_w() -
525                                                                 (total_w - x1);
526                                                         in_w = total_w - x1;
527                                                 }
528
529                                                 int in_y = images[image_number]->get_h() - in_start - in_span;
530 //printf("BC_Meter::draw_face %d %d %d\n", __LINE__, get_w(), x + x1 + in_w, in_x, in_y, in_w, span);
531
532
533                                                 draw_pixmap(images[image_number],
534                                                         x + x1,
535                                                         get_h() - pixel - in_span,
536                                                         in_w,
537                                                         in_span + 1,
538                                                         in_x,
539                                                         in_y);
540                                         }
541                                 }
542                         }
543
544                         pixel += in_span;
545                 }
546                 else
547                 {
548 // Sanity check
549                         break;
550                 }
551         }
552
553         if(downmix) {
554                 if(orientation == METER_HORIZ)
555                         draw_pixmap(images[METER_DOWNMIX],
556                                 0,
557                                 0);
558                 else
559                         draw_pixmap(images[METER_DOWNMIX],
560                                 x,
561                                 get_h() - images[METER_DOWNMIX]->get_h()-1);
562         }
563
564         if(over_timer)
565         {
566                 if(orientation == METER_HORIZ)
567                         draw_pixmap(images[METER_OVER],
568                                 20,
569                                 2);
570                 else
571                         draw_pixmap(images[METER_OVER],
572                                 x,
573                                 get_h() - 100);
574
575                 over_timer--;
576         }
577
578         if(orientation == METER_HORIZ)
579                 flash(0, 0, pixels, get_h(), flush);
580         else
581                 flash(x, 0, w, pixels, flush);
582 }
583
584 int BC_Meter::update(float new_value, int over, int dmix)
585 {
586         peak_timer++;
587
588         if(mode == METER_DB)
589         {
590                 if(new_value == 0)
591                         level = min;
592                 else
593                         level = db.todb(new_value);        // db value
594         }
595
596         if(level > peak || peak_timer > peak_delay)
597         {
598                 peak = level;
599                 peak_timer = 0;
600         }
601
602 // if(orientation == METER_HORIZ)
603 // printf("BC_Meter::update %f\n", level);
604         if(over) over_timer = over_delay;
605 // only draw if window is visible
606         if(dmix >= 0) downmix = dmix;
607
608         draw_face(1);
609         return 0;
610 }