fix aspect ratio on split screen drag handle, add videoscope to editpanel in cwindow...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / scopewindow.C
1 /*
2  * CINELERRA
3  * Copyright (C) 1997-2011 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include "bcsignals.h"
22 #include "bccolors.h"
23 #include "clip.h"
24 #include "cursors.h"
25 #include "language.h"
26 #include "scopewindow.h"
27 #include "theme.h"
28
29 #include <string.h>
30
31 ScopePackage::ScopePackage()
32  : LoadPackage()
33 {
34 }
35
36 ScopeUnit::ScopeUnit(ScopeGUI *gui,
37         ScopeEngine *server)
38  : LoadClient(server)
39 {
40         this->gui = gui;
41 }
42
43 void ScopeUnit::draw_point(unsigned char **rows,
44         int x, int y, int r, int g, int b)
45 {
46         unsigned char *pixel = rows[y] + x * 4;
47         pixel[0] = b;
48         pixel[1] = g;
49         pixel[2] = r;
50 }
51
52 #define PROCESS_PIXEL(column) { \
53 /* Calculate histogram */ \
54         if(use_hist) { \
55                 int v_i = (intensity - FLOAT_MIN) * (TOTAL_BINS / (FLOAT_MAX - FLOAT_MIN)); \
56                 CLAMP(v_i, 0, TOTAL_BINS - 1); \
57                 bins[3][v_i]++; \
58         } \
59         else if(use_hist_parade) { \
60                 int r_i = (r - FLOAT_MIN) * (TOTAL_BINS / (FLOAT_MAX - FLOAT_MIN)); \
61                 int g_i = (g - FLOAT_MIN) * (TOTAL_BINS / (FLOAT_MAX - FLOAT_MIN)); \
62                 int b_i = (b - FLOAT_MIN) * (TOTAL_BINS / (FLOAT_MAX - FLOAT_MIN)); \
63                 CLAMP(r_i, 0, TOTAL_BINS - 1); \
64                 CLAMP(g_i, 0, TOTAL_BINS - 1); \
65                 CLAMP(b_i, 0, TOTAL_BINS - 1); \
66                 bins[0][r_i]++; \
67                 bins[1][g_i]++; \
68                 bins[2][b_i]++; \
69         } \
70 /* Calculate waveform */ \
71         if(use_wave || use_wave_parade) { \
72                 int ix = (column) * wave_w / w; \
73                 if(ix >= 0 && ix < wave_w) { \
74                         if(use_wave_parade) { \
75                                 int iy = wave_h - (int)((r - FLOAT_MIN) /  \
76                                                 (FLOAT_MAX - FLOAT_MIN) * wave_h); \
77                                 if(iy >= 0 && iy < wave_h) \
78                                         draw_point(waveform_rows, ix / 3, iy, 0xff, 0x0, 0x0);  \
79                                 iy = wave_h - (int)((g - FLOAT_MIN) /  \
80                                                 (FLOAT_MAX - FLOAT_MIN) * wave_h); \
81                                 if(iy >= 0 && iy < wave_h)  \
82                                         draw_point(waveform_rows, ix / 3 + wave_w / 3, iy, 0x0, 0xff, 0x0);  \
83                                 iy = wave_h - (int)((b - FLOAT_MIN) /  \
84                                                 (FLOAT_MAX - FLOAT_MIN) * wave_h); \
85                                 if(iy >= 0 && iy < wave_h)  \
86                                         draw_point(waveform_rows, ix / 3 + wave_w / 3 * 2, iy, 0x0, 0x0, 0xff);  \
87                         } \
88                         else { \
89                                 int iy = wave_h - (int)((intensity - FLOAT_MIN) /  \
90                                                 (FLOAT_MAX - FLOAT_MIN) * wave_h); \
91                                 if(iy >= 0 && iy < wave_h)  \
92                                         draw_point(waveform_rows, ix, iy, 0xff, 0xff, 0xff);  \
93                         } \
94                 } \
95         } \
96 /* Calculate vectorscope */ \
97         if(use_vector) { \
98                 float adjacent = cos((h + 90) / 360 * 2 * M_PI); \
99                 float opposite = sin((h + 90) / 360 * 2 * M_PI); \
100                 int ix = vector_w / 2 + adjacent * (s) / (FLOAT_MAX) * radius; \
101                 int y = vector_h / 2 - opposite * (s) / (FLOAT_MAX) * radius; \
102                 CLAMP(ix, 0, vector_w - 1); \
103                 CLAMP(y, 0, vector_h - 1); \
104         /* Get color with full saturation & value */ \
105                 float r_f, g_f, b_f; \
106                 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, 1); \
107                 draw_point(vector_rows, ix, y, \
108                         (int)(CLIP(r_f, 0, 1) * 255),  \
109                         (int)(CLIP(g_f, 0, 1) * 255),  \
110                         (int)(CLIP(b_f, 0, 1) * 255)); \
111         } \
112 }
113
114 #define PROCESS_RGB_PIXEL(column, max) { \
115         r = (float)*row++ / max; \
116         g = (float)*row++ / max; \
117         b = (float)*row++ / max; \
118         HSV::rgb_to_hsv(r, g, b, h, s, v); \
119         intensity = v; \
120         PROCESS_PIXEL(column) \
121 }
122
123 #define PROCESS_BGR_PIXEL(column, max) { \
124         b = (float)*row++ / max; \
125         g = (float)*row++ / max; \
126         r = (float)*row++ / max; \
127         HSV::rgb_to_hsv(r, g, b, h, s, v); \
128         intensity = v; \
129         PROCESS_PIXEL(column) \
130 }
131
132 #define PROCESS_YUV_PIXEL(column, y_in, u_in, v_in) { \
133         YUV::yuv.yuv_to_rgb_f(r, g, b, (float)y_in / 255, (float)(u_in - 0x80) / 255, (float)(v_in - 0x80) / 255); \
134         HSV::rgb_to_hsv(r, g, b, h, s, v); \
135         intensity = v; \
136         PROCESS_PIXEL(column) \
137 }
138
139 void ScopeUnit::process_package(LoadPackage *package)
140 {
141         ScopePackage *pkg = (ScopePackage*)package;
142
143         float r, g, b;
144         float h, s, v;
145         float intensity;
146         int use_hist = gui->use_hist;
147         int use_hist_parade = gui->use_hist_parade;
148         int use_vector = gui->use_vector;
149         int use_wave = gui->use_wave;
150         int use_wave_parade = gui->use_wave_parade;
151         BC_Bitmap *waveform_bitmap = gui->waveform_bitmap;
152         BC_Bitmap *vector_bitmap = gui->vector_bitmap;
153         int wave_h = waveform_bitmap->get_h();
154         int wave_w = waveform_bitmap->get_w();
155         int vector_h = vector_bitmap->get_h();
156         int vector_w = vector_bitmap->get_w();
157
158         int w = gui->output_frame->get_w();
159         float radius = MIN(gui->vector_w / 2, gui->vector_h / 2);
160         unsigned char **waveform_rows = waveform_bitmap->get_row_pointers();
161         unsigned char **vector_rows = vector_bitmap->get_row_pointers();
162         unsigned char **rows = gui->output_frame->get_rows();
163
164         switch( gui->output_frame->get_color_model() ) {
165         case BC_RGB888:
166                 for( int y=pkg->row1; y<pkg->row2; ++y ) {
167                         unsigned char *row = rows[y];
168                         for( int x=0; x<w; ++x ) {
169                                 PROCESS_RGB_PIXEL(x, 255)
170                         }
171                 }
172                 break;
173         case BC_RGBA8888:
174                 for( int y=pkg->row1; y<pkg->row2; ++y ) {
175                         unsigned char *row = rows[y];
176                         for( int x=0; x<w; ++x ) {
177                                 PROCESS_RGB_PIXEL(x, 255)
178                                 ++row;
179                         }
180                 }
181                 break;
182         case BC_BGR888:
183                 for( int y=pkg->row1; y<pkg->row2; ++y ) {
184                         unsigned char *row = rows[y];
185                         for( int x=0; x<w; ++x ) {
186                                 PROCESS_BGR_PIXEL(x, 255)
187                         }
188                 }
189                 break;
190         case BC_BGR8888:
191                 for( int y=pkg->row1; y<pkg->row2; ++y ) {
192                         unsigned char *row = rows[y];
193                         for( int x=0; x<w; ++x ) {
194                                 PROCESS_BGR_PIXEL(x, 255)
195                                 ++row;
196                         }
197                 }
198                 break;
199         case BC_RGB_FLOAT:
200                 for( int y=pkg->row1; y<pkg->row2; ++y ) {
201                         float *row = (float*)rows[y];
202                         for( int x=0; x<w; ++x ) {
203                                 PROCESS_RGB_PIXEL(x, 1.0)
204                         }
205                 }
206                 break;
207         case BC_RGBA_FLOAT:
208                 for( int y=pkg->row1; y<pkg->row2; ++y ) {
209                         float *row = (float*)rows[y];
210                         for( int x=0; x<w; ++x ) {
211                                 PROCESS_RGB_PIXEL(x, 1.0)
212                                 ++row;
213                         }
214                 }
215                 break;
216         case BC_YUV888:
217                 for( int y=pkg->row1; y<pkg->row2; ++y ) {
218                         unsigned char *row = rows[y];
219                         for( int x=0; x<w; ++x ) {
220                                 PROCESS_YUV_PIXEL(x, row[0], row[1], row[2])
221                                 row += 3;
222                         }
223                 }
224                 break;
225
226         case BC_YUVA8888:
227                 for( int y=pkg->row1; y<pkg->row2; ++y ) {
228                         unsigned char *row = rows[y];
229                         for( int x=0; x<w; ++x ) {
230                                 PROCESS_YUV_PIXEL(x, row[0], row[1], row[2])
231                                 row += 4;
232                         }
233                 }
234                 break;
235         case BC_YUV420P: {
236                 unsigned char *yp = gui->output_frame->get_y();
237                 unsigned char *up = gui->output_frame->get_u();
238                 unsigned char *vp = gui->output_frame->get_v();
239                 for( int y=pkg->row1; y<pkg->row2; ++y ) {
240                         unsigned char *y_row = yp + y * w;
241                         unsigned char *u_row = up + (y / 2) * (w / 2);
242                         unsigned char *v_row = vp + (y / 2) * (w / 2);
243                         for( int x=0; x<w; x+=2 ) {
244                                 PROCESS_YUV_PIXEL(x, *y_row, *u_row, *v_row);      ++y_row;
245                                 PROCESS_YUV_PIXEL(x + 1, *y_row, *u_row, *v_row);  ++y_row;
246                                 ++u_row;  ++v_row;
247                         }
248                 }
249                 break; }
250         case BC_YUV422:
251                 for( int y=pkg->row1; y<pkg->row2; ++y ) {
252                         unsigned char *row = rows[y];
253                         for( int x=0; x<w; x+=2 ) {
254                                 PROCESS_YUV_PIXEL(x, row[0], row[1], row[3]);
255                                 PROCESS_YUV_PIXEL(x + 1, row[2], row[1], row[3]);
256                                 row += 4;
257                         }
258                 }
259                 break;
260
261         default:
262                 printf("ScopeUnit::process_package %d: color_model=%d unrecognized\n",
263                         __LINE__, gui->output_frame->get_color_model());
264                 break;
265         }
266 }
267
268
269 ScopeEngine::ScopeEngine(ScopeGUI *gui, int cpus)
270  : LoadServer(cpus, cpus)
271 {
272 //printf("ScopeEngine::ScopeEngine %d cpus=%d\n", __LINE__, cpus);
273         this->gui = gui;
274 }
275
276 ScopeEngine::~ScopeEngine()
277 {
278 }
279
280 void ScopeEngine::init_packages()
281 {
282         int y = 0, h = gui->output_frame->get_h();
283         for( int i=0,n=LoadServer::get_total_packages(); i<n; ) {
284                 ScopePackage *pkg = (ScopePackage*)get_package(i);
285                 pkg->row1 = y;
286                 pkg->row2 = y = (++i * h) / n;
287         }
288
289         for( int i=0,n=LoadServer::get_total_packages(); i<n; ++i ) {
290                 ScopeUnit *unit = (ScopeUnit*)get_client(i);
291                 for( int j=0; j<HIST_SECTIONS; ++j )
292                         bzero(unit->bins[j], sizeof(int) * TOTAL_BINS);
293         }
294 }
295
296
297 LoadClient* ScopeEngine::new_client()
298 {
299         return new ScopeUnit(gui, this);
300 }
301
302 LoadPackage* ScopeEngine::new_package()
303 {
304         return new ScopePackage;
305 }
306
307 void ScopeEngine::process()
308 {
309         process_packages();
310
311         for(int i = 0; i < HIST_SECTIONS; i++)
312                 bzero(gui->bins[i], sizeof(int) * TOTAL_BINS);
313
314         for(int i=0,n=get_total_clients(); i<n; ++i ) {
315                 ScopeUnit *unit = (ScopeUnit*)get_client(i);
316                 for( int j=0; j<HIST_SECTIONS; ++j ) {
317                         for( int k=0; k<TOTAL_BINS; ++k ) {
318                                 gui->bins[j][k] += unit->bins[j][k];
319                         }
320                 }
321         }
322 }
323
324
325 ScopeGUI::ScopeGUI(Theme *theme,
326         int x, int y, int w, int h, int cpus)
327  : PluginClientWindow(_(PROGRAM_NAME ": Scopes"),
328         x, y, w, h, MIN_SCOPE_W, MIN_SCOPE_H, 1)
329 {
330         this->x = x;
331         this->y = y;
332         this->w = w;
333         this->h = h;
334         this->theme = theme;
335         this->cpus = cpus;
336         reset();
337 }
338
339 ScopeGUI::ScopeGUI(PluginClient *plugin, int w, int h)
340  : PluginClientWindow(plugin, w, h, MIN_SCOPE_W, MIN_SCOPE_H, 1)
341 {
342         this->x = get_x();
343         this->y = get_y();
344         this->w = w;
345         this->h = h;
346         this->theme = plugin->get_theme();
347         this->cpus = plugin->PluginClient::smp + 1;
348         reset();
349 }
350
351 ScopeGUI::~ScopeGUI()
352 {
353         delete waveform_bitmap;
354         delete vector_bitmap;
355         delete engine;
356 }
357
358 void ScopeGUI::reset()
359 {
360         frame_w = 1;
361         waveform_bitmap = 0;
362         vector_bitmap = 0;
363         engine = 0;
364         use_hist = 0;
365         use_wave = 1;
366         use_vector = 1;
367         use_hist_parade = 0;
368         use_wave_parade = 0;
369         waveform = 0;
370         vectorscope = 0;
371         histogram = 0;
372         wave_w = wave_h = vector_w = vector_h = 0;
373 }
374
375
376 void ScopeGUI::create_objects()
377 {
378         if( use_hist && use_hist_parade )
379                 use_hist = 0;
380         if( use_wave && use_wave_parade )
381                 use_wave = 0;
382         if( !engine ) engine = new ScopeEngine(this, cpus);
383
384         lock_window("ScopeGUI::create_objects");
385         int x = theme->widget_border;
386         int y = theme->widget_border;
387         add_subwindow(hist_on = new ScopeToggle(this, x, y, &use_hist));
388         x += hist_on->get_w() + theme->widget_border;
389
390         add_subwindow(hist_parade_on = new ScopeToggle(this, x, y, &use_hist_parade));
391         x += hist_parade_on->get_w() + theme->widget_border;
392
393         add_subwindow(waveform_on = new ScopeToggle(this, x, y, &use_wave));
394         x += waveform_on->get_w() + theme->widget_border;
395         add_subwindow(waveform_parade_on = new ScopeToggle(this, x, y, &use_wave_parade));
396         x += waveform_parade_on->get_w() + theme->widget_border;
397
398         add_subwindow(vector_on = new ScopeToggle(this, x, y, &use_vector));
399         x += vector_on->get_w() + theme->widget_border;
400
401         add_subwindow(value_text = new BC_Title(x, y, ""));
402         x += value_text->get_w() + theme->widget_border;
403
404         y += vector_on->get_h() + theme->widget_border;
405
406         create_panels();
407
408         update_toggles();
409         show_window();
410         unlock_window();
411 }
412
413 void ScopeGUI::create_panels()
414 {
415         calculate_sizes(get_w(), get_h());
416         if( (use_wave || use_wave_parade) ) {
417                 if( !waveform ) {
418                         add_subwindow(waveform = new ScopeWaveform(this,
419                                 wave_x, wave_y, wave_w, wave_h));
420                         waveform->create_objects();
421                 }
422                 else {
423                         waveform->reposition_window(
424                                 wave_x, wave_y, wave_w, wave_h);
425                         waveform->clear_box(0, 0, wave_w, wave_h);
426                 }
427         }
428         else if( !(use_wave || use_wave_parade) && waveform ) {
429                 delete waveform;  waveform = 0;
430         }
431
432         if( use_vector ) {
433                 if( !vectorscope ) {
434                         add_subwindow(vectorscope = new ScopeVectorscope(this,
435                                 vector_x, vector_y, vector_w, vector_h));
436                         vectorscope->create_objects();
437                 }
438                 else {
439                         vectorscope->reposition_window(
440                                 vector_x, vector_y, vector_w, vector_h);
441                         vectorscope->clear_box(0, 0, vector_w, vector_h);
442                 }
443         }
444         else if( !use_vector && vectorscope ) {
445                 delete vectorscope;  vectorscope = 0;
446         }
447
448         if( (use_hist || use_hist_parade) ) {
449                 if( !histogram ) {
450 // printf("ScopeGUI::create_panels %d %d %d %d %d\n", __LINE__,
451 //  hist_x, hist_y, hist_w, hist_h);
452                         add_subwindow(histogram = new ScopeHistogram(this,
453                                 hist_x, hist_y, hist_w, hist_h));
454                         histogram->create_objects();
455                 }
456                 else {
457                         histogram->reposition_window(
458                                 hist_x, hist_y, hist_w, hist_h);
459                         histogram->clear_box(0, 0, hist_w, hist_h);
460                 }
461         }
462         else if( !(use_hist || use_hist_parade) ) {
463                 delete histogram;  histogram = 0;
464         }
465
466         allocate_bitmaps();
467         clear_points(0);
468         draw_overlays(1, 1, 0);
469 }
470
471 void ScopeGUI::clear_points(int flash)
472 {
473         if( histogram )
474                 histogram->clear_point();
475         if( waveform )
476                 waveform->clear_point();
477         if( vectorscope )
478                 vectorscope->clear_point();
479         if( histogram && flash )
480                 histogram->flash(0);
481         if( waveform && flash )
482                 waveform->flash(0);
483         if( vectorscope && flash )
484                 vectorscope->flash(0);
485 }
486
487 void ScopeGUI::toggle_event()
488 {
489 }
490
491 void ScopeGUI::calculate_sizes(int w, int h)
492 {
493         int margin = theme->widget_border;
494         int text_w = get_text_width(SMALLFONT, "000") + margin * 2;
495         int total_panels = ((use_hist || use_hist_parade) ? 1 : 0) +
496                 ((use_wave || use_wave_parade) ? 1 : 0) +
497                 (use_vector ? 1 : 0);
498         int x = margin;
499
500         int panel_w = (w - margin) / (total_panels > 0 ? total_panels : 1);
501 // Vectorscope determines the size of everything else
502 // Always last panel
503         vector_w = 0;
504         if( use_vector ) {
505                 vector_x = w - panel_w + text_w;
506                 vector_w = w - margin - vector_x;
507                 vector_y = vector_on->get_h() + margin * 2;
508                 vector_h = h - vector_y - margin;
509
510                 if( vector_w > vector_h ) {
511                         vector_w = vector_h;
512                         vector_x = w - theme->widget_border - vector_w;
513                 }
514                 --total_panels;
515                 if(total_panels > 0)
516                         panel_w = (vector_x - text_w - margin) / total_panels;
517         }
518
519 // Histogram is always 1st panel
520         if( use_hist || use_hist_parade ) {
521                 hist_x = x;
522                 hist_y = vector_on->get_h() + margin * 2;
523                 hist_w = panel_w - margin;
524                 hist_h = h - hist_y - margin;
525
526                 --total_panels;
527                 x += panel_w;
528         }
529
530         if( use_wave || use_wave_parade ) {
531                 wave_x = x + text_w;
532                 wave_y = vector_on->get_h() + margin * 2;
533                 wave_w = panel_w - margin - text_w;
534                 wave_h = h - wave_y - margin;
535         }
536
537 }
538
539
540 void ScopeGUI::allocate_bitmaps()
541 {
542         if(waveform_bitmap) delete waveform_bitmap;
543         if(vector_bitmap) delete vector_bitmap;
544
545         int w, h;
546 //printf("ScopeGUI::allocate_bitmaps %d %d %d %d %d\n", __LINE__,
547 // wave_w, wave_h, vector_w, vector_h);
548         int xs16 = xS(16), ys16 = yS(16);
549         w = MAX(wave_w, xs16);
550         h = MAX(wave_h, ys16);
551         waveform_bitmap = new_bitmap(w, h);
552         w = MAX(vector_w, xs16);
553         h = MAX(vector_h, ys16);
554         vector_bitmap = new_bitmap(w, h);
555 }
556
557
558 int ScopeGUI::resize_event(int w, int h)
559 {
560         clear_box(0, 0, w, h);
561         this->w = w;
562         this->h = h;
563         calculate_sizes(w, h);
564
565         if( waveform ) {
566                 waveform->reposition_window(wave_x, wave_y, wave_w, wave_h);
567                 waveform->clear_box(0, 0, wave_w, wave_h);
568         }
569
570         if( histogram ) {
571                 histogram->reposition_window(hist_x, hist_y, hist_w, hist_h);
572                 histogram->clear_box(0, 0, hist_w, hist_h);
573         }
574
575         if( vectorscope ) {
576                 vectorscope->reposition_window(vector_x, vector_y, vector_w, vector_h);
577                 vectorscope->clear_box(0, 0, vector_w, vector_h);
578         }
579
580         allocate_bitmaps();
581         clear_points(0);
582         draw_overlays(1, 1, 1);
583         return 1;
584 }
585
586 int ScopeGUI::translation_event()
587 {
588         x = get_x();
589         y = get_y();
590         PluginClientWindow::translation_event();
591         return 0;
592 }
593
594
595 void ScopeGUI::draw_overlays(int overlays, int borders, int flush)
596 {
597         BC_Resources *resources = BC_WindowBase::get_resources();
598         int text_color = GREEN;
599         if( resources->bg_color == 0xffffff ) {
600                 text_color = BLACK;
601         }
602
603         if( overlays && borders ) {
604                 clear_box(0, 0, get_w(), get_h());
605         }
606
607         if( overlays ) {
608                 set_line_dashes(1);
609                 set_color(text_color);
610                 set_font(SMALLFONT);
611
612                 if( histogram && (use_hist || use_hist_parade) ) {
613                         histogram->draw_line(hist_w * -FLOAT_MIN / (FLOAT_MAX - FLOAT_MIN), 0,
614                                         hist_w * -FLOAT_MIN / (FLOAT_MAX - FLOAT_MIN), hist_h);
615                         histogram->draw_line(hist_w * (1.0 - FLOAT_MIN) / (FLOAT_MAX - FLOAT_MIN), 0,
616                                         hist_w * (1.0 - FLOAT_MIN) / (FLOAT_MAX - FLOAT_MIN), hist_h);
617                         set_line_dashes(0);
618                         histogram->draw_point();
619                         set_line_dashes(1);
620                         histogram->flash(0);
621                 }
622
623 // Waveform overlay
624                 if( waveform && (use_wave || use_wave_parade) ) {
625                         set_color(text_color);
626                         for( int i=0; i<=WAVEFORM_DIVISIONS; ++i ) {
627                                 int y = wave_h * i / WAVEFORM_DIVISIONS;
628                                 int text_y = y + wave_y + get_text_ascent(SMALLFONT) / 2;
629                                 CLAMP(text_y, waveform->get_y() + get_text_ascent(SMALLFONT), waveform->get_y() + waveform->get_h() - 1);
630                                 char string[BCTEXTLEN];
631                                 sprintf(string, "%d", (int)((FLOAT_MAX -
632                                         i * (FLOAT_MAX - FLOAT_MIN) / WAVEFORM_DIVISIONS) * 100));
633                                 int text_x = wave_x - get_text_width(SMALLFONT, string) - theme->widget_border;
634                                 draw_text(text_x, text_y, string);
635
636                                 CLAMP(y, 0, waveform->get_h() - 1);
637                                 waveform->draw_line(0, y, wave_w, y);
638                                 //waveform->draw_rectangle(0, 0, wave_w, wave_h);
639                         }
640                         set_line_dashes(0);
641                         waveform->draw_point();
642                         set_line_dashes(1);
643                         waveform->flash(0);
644                 }
645
646
647 // Vectorscope overlay
648                 if( vectorscope && use_vector ) {
649                         set_color(text_color);
650                         int radius = MIN(vector_w / 2, vector_h / 2);
651                         for( int i=1; i<=VECTORSCOPE_DIVISIONS; i+=2 ) {
652                                 int x = vector_w / 2 - radius * i / VECTORSCOPE_DIVISIONS;
653                                 int y = vector_h / 2 - radius * i / VECTORSCOPE_DIVISIONS;
654                                 int text_y = y + vector_y + get_text_ascent(SMALLFONT) / 2;
655                                 int w = radius * i / VECTORSCOPE_DIVISIONS * 2;
656                                 int h = radius * i / VECTORSCOPE_DIVISIONS * 2;
657                                 char string[BCTEXTLEN];
658
659                                 sprintf(string, "%d",
660                                         (int)((FLOAT_MAX / VECTORSCOPE_DIVISIONS * i) * 100));
661                                 int text_x = vector_x - get_text_width(SMALLFONT, string) - theme->widget_border;
662                                 draw_text(text_x, text_y, string);
663 //printf("ScopeGUI::draw_overlays %d %d %d %s\n", __LINE__, text_x, text_y, string);
664
665                                 vectorscope->draw_circle(x, y, w, h);
666                 //vectorscope->draw_rectangle(0, 0, vector_w, vector_h);
667                         }
668                 //      vectorscope->draw_circle(vector_w / 2 - radius,
669                 //              vector_h / 2 - radius,
670                 //              radius * 2,
671                 //              radius * 2);
672
673                         set_line_dashes(0);
674                         vectorscope->draw_point();
675                         set_line_dashes(1);
676                         vectorscope->flash(0);
677                 }
678
679                 set_font(MEDIUMFONT);
680                 set_line_dashes(0);
681         }
682
683         if( borders ) {
684                 if( use_hist || use_hist_parade ) {
685                         draw_3d_border(hist_x - 2, hist_y - 2, hist_w + 4, hist_h + 4,
686                                 get_bg_color(), BLACK, MDGREY, get_bg_color());
687                 }
688                 if( use_wave || use_wave_parade ) {
689                         draw_3d_border(wave_x - 2, wave_y - 2, wave_w + 4, wave_h + 4,
690                                 get_bg_color(), BLACK, MDGREY, get_bg_color());
691                 }
692                 if( use_vector ) {
693                         draw_3d_border(vector_x - 2, vector_y - 2, vector_w + 4, vector_h + 4,
694                                 get_bg_color(), BLACK, MDGREY, get_bg_color());
695                 }
696         }
697
698         flash(0);
699         if(flush) this->flush();
700 }
701
702
703
704 void ScopeGUI::process(VFrame *output_frame)
705 {
706         lock_window("ScopeGUI::process");
707         this->output_frame = output_frame;
708         frame_w = output_frame->get_w();
709         //float radius = MIN(vector_w / 2, vector_h / 2);
710         bzero(waveform_bitmap->get_data(), waveform_bitmap->get_data_size());
711         bzero(vector_bitmap->get_data(), vector_bitmap->get_data_size());
712         engine->process();
713
714         if( histogram )
715                 histogram->draw(0, 0);
716         if( waveform )
717                 waveform->draw_bitmap(waveform_bitmap, 1, 0, 0);
718         if( vectorscope )
719                 vectorscope->draw_bitmap(vector_bitmap, 1, 0, 0);
720
721         draw_overlays(1, 0, 1);
722         unlock_window();
723 }
724
725
726 void ScopeGUI::update_toggles()
727 {
728         hist_parade_on->update(use_hist_parade);
729         hist_on->update(use_hist);
730         waveform_parade_on->update(use_wave_parade);
731         waveform_on->update(use_wave);
732         vector_on->update(use_vector);
733 }
734
735
736 ScopePanel::ScopePanel(ScopeGUI *gui, int x, int y, int w, int h)
737  : BC_SubWindow(x, y, w, h, BLACK)
738 {
739         this->gui = gui;
740         is_dragging = 0;
741 }
742
743 void ScopePanel::create_objects()
744 {
745         set_cursor(CROSS_CURSOR, 0, 0);
746         clear_box(0, 0, get_w(), get_h());
747 }
748
749 void ScopePanel::update_point(int x, int y)
750 {
751 }
752
753 void ScopePanel::draw_point()
754 {
755 }
756
757 void ScopePanel::clear_point()
758 {
759 }
760
761 int ScopePanel::button_press_event()
762 {
763         if( is_event_win() && cursor_inside() ) {
764                 gui->clear_points(1);
765                 is_dragging = 1;
766                 int x = get_cursor_x();
767                 int y = get_cursor_y();
768                 CLAMP(x, 0, get_w() - 1);
769                 CLAMP(y, 0, get_h() - 1);
770                 update_point(x, y);
771                 return 1;
772         }
773         return 0;
774 }
775
776
777 int ScopePanel::cursor_motion_event()
778 {
779         if( is_dragging ) {
780                 int x = get_cursor_x();
781                 int y = get_cursor_y();
782                 CLAMP(x, 0, get_w() - 1);
783                 CLAMP(y, 0, get_h() - 1);
784                 update_point(x, y);
785                 return 1;
786         }
787         return 0;
788 }
789
790
791 int ScopePanel::button_release_event()
792 {
793         if( is_dragging ) {
794                 is_dragging = 0;
795                 return 1;
796         }
797         return 0;
798 }
799
800
801 ScopeWaveform::ScopeWaveform(ScopeGUI *gui,
802                 int x, int y, int w, int h)
803  : ScopePanel(gui, x, y, w, h)
804 {
805         drag_x = -1;
806         drag_y = -1;
807 }
808
809 void ScopeWaveform::update_point(int x, int y)
810 {
811         draw_point();
812         drag_x = x;
813         drag_y = y;
814         int frame_x = x * gui->frame_w / get_w();
815
816         if( gui->use_wave_parade ) {
817                 if( x > get_w() / 3 * 2 )
818                         frame_x = (x - get_w() / 3 * 2) * gui->frame_w / (get_w() / 3);
819                 else if( x > get_w() / 3 )
820                         frame_x = (x - get_w() / 3) * gui->frame_w / (get_w() / 3);
821                 else
822                         frame_x = x * gui->frame_w / (get_w() / 3);
823         }
824
825         float value = ((float)get_h() - y) / get_h() * (FLOAT_MAX - FLOAT_MIN) + FLOAT_MIN;
826
827         char string[BCTEXTLEN];
828         sprintf(string, "X: %d Value: %.3f", frame_x, value);
829         gui->value_text->update(string, 0);
830
831         draw_point();
832         flash(1);
833 }
834
835 void ScopeWaveform::draw_point()
836 {
837         if( drag_x >= 0 ) {
838                 set_inverse();
839                 set_color(0xffffff);
840                 set_line_width(2);
841                 draw_line(0, drag_y, get_w(), drag_y);
842                 draw_line(drag_x, 0, drag_x, get_h());
843                 set_line_width(1);
844                 set_opaque();
845         }
846 }
847
848 void ScopeWaveform::clear_point()
849 {
850         draw_point();
851         drag_x = -1;
852         drag_y = -1;
853 }
854
855 ScopeVectorscope::ScopeVectorscope(ScopeGUI *gui,
856                 int x, int y, int w, int h)
857  : ScopePanel(gui, x, y, w, h)
858 {
859         drag_radius = 0;
860         drag_angle = 0;
861 }
862
863 void ScopeVectorscope::clear_point()
864 {
865 // Hide it
866         draw_point();
867         drag_radius = 0;
868         drag_angle = 0;
869 }
870
871 void ScopeVectorscope::update_point(int x, int y)
872 {
873 // Hide it
874         draw_point();
875
876         int radius = MIN(get_w() / 2, get_h() / 2);
877         drag_radius = sqrt(SQR(x - get_w() / 2) + SQR(y - get_h() / 2));
878         drag_angle = atan2(y - get_h() / 2, x - get_w() / 2);
879
880         drag_radius = MIN(drag_radius, radius);
881
882         float saturation = (float)drag_radius / radius * FLOAT_MAX;
883         float hue = -drag_angle * 360 / 2 / M_PI - 90;
884         if( hue < 0 ) hue += 360;
885
886         char string[BCTEXTLEN];
887         sprintf(string, "Hue: %.3f Sat: %.3f", hue, saturation);
888         gui->value_text->update(string, 0);
889
890 // Show it
891         draw_point();
892         flash(1);
893 }
894
895 void ScopeVectorscope::draw_point()
896 {
897         if( drag_radius > 0 ) {
898                 int radius = MIN(get_w() / 2, get_h() / 2);
899                 set_inverse();
900                 set_color(0xff0000);
901                 set_line_width(2);
902                 draw_circle(get_w() / 2 - drag_radius, get_h() / 2 - drag_radius,
903                         drag_radius * 2, drag_radius * 2);
904                 draw_line(get_w() / 2, get_h() / 2,
905                         get_w() / 2 + radius * cos(drag_angle),
906                         get_h() / 2 + radius * sin(drag_angle));
907                 set_line_width(1);
908                 set_opaque();
909         }
910 }
911
912
913
914 ScopeHistogram::ScopeHistogram(ScopeGUI *gui,
915                 int x, int y, int w, int h)
916  : ScopePanel(gui, x, y, w, h)
917 {
918         drag_x = -1;
919 }
920
921 void ScopeHistogram::clear_point()
922 {
923 // Hide it
924         draw_point();
925         drag_x = -1;
926 }
927
928 void ScopeHistogram::draw_point()
929 {
930         if( drag_x >= 0 ) {
931                 set_inverse();
932                 set_color(0xffffff);
933                 set_line_width(2);
934                 draw_line(drag_x, 0, drag_x, get_h());
935                 set_line_width(1);
936                 set_opaque();
937         }
938 }
939
940 void ScopeHistogram::update_point(int x, int y)
941 {
942         draw_point();
943         drag_x = x;
944         float value = (float)x / get_w() * (FLOAT_MAX - FLOAT_MIN) + FLOAT_MIN;
945
946         char string[BCTEXTLEN];
947         sprintf(string, "Value: %.3f", value);
948         gui->value_text->update(string, 0);
949
950         draw_point();
951         flash(1);
952 }
953
954
955
956 void ScopeHistogram::draw_mode(int mode, int color, int y, int h)
957 {
958 // Highest of all bins
959         int normalize = 0;
960         for( int i=0; i<TOTAL_BINS; ++i ) {
961                 if(gui->bins[mode][i] > normalize) normalize = gui->bins[mode][i];
962         }
963         set_color(color);
964         for( int i=0; i<get_w(); ++i ) {
965                 int accum_start = (int)(i * TOTAL_BINS / get_w());
966                 int accum_end = (int)((i + 1) * TOTAL_BINS / get_w());
967                 CLAMP(accum_start, 0, TOTAL_BINS);
968                 CLAMP(accum_end, 0, TOTAL_BINS);
969                 int max = 0;
970                 for(int k=accum_start; k<accum_end; ++k ) {
971                         max = MAX(gui->bins[mode][k], max);
972                 }
973 //              max = max * h / normalize;
974                 max = (int)(log(max) / log(normalize) * h);
975                 draw_line(i, y + h - max, i, y + h);
976         }
977 }
978
979 void ScopeHistogram::draw(int flash, int flush)
980 {
981         clear_box(0, 0, get_w(), get_h());
982
983         if( gui->use_hist_parade ) {
984                 draw_mode(0, 0xff0000, 0, get_h() / 3);
985                 draw_mode(1, 0x00ff00, get_h() / 3, get_h() / 3);
986                 draw_mode(2, 0x0000ff, get_h() / 3 * 2, get_h() / 3);
987         }
988         else {
989                 draw_mode(3, LTGREY, 0, get_h());
990         }
991
992         if(flash) this->flash(0);
993         if(flush) this->flush();
994 }
995
996 ScopeToggle::ScopeToggle(ScopeGUI *gui, int x, int y, int *value)
997  : BC_Toggle(x, y, get_image_set(gui, value), *value)
998 {
999         this->gui = gui;
1000         this->value = value;
1001         if( value == &gui->use_hist_parade ) {
1002                 set_tooltip(_("Histogram Parade"));
1003         }
1004         else if( value == &gui->use_hist )
1005         {
1006                 set_tooltip(_("Histogram"));
1007         }
1008         else if( value == &gui->use_wave_parade ) {
1009                 set_tooltip(_("Waveform Parade"));
1010         }
1011         else if( value == &gui->use_wave ) {
1012                 set_tooltip(_("Waveform"));
1013         }
1014         else {
1015                 set_tooltip(_("Vectorscope"));
1016         }
1017 }
1018
1019 VFrame** ScopeToggle::get_image_set(ScopeGUI *gui, int *value)
1020 {
1021         if( value == &gui->use_hist_parade ) {
1022                 return gui->theme->get_image_set("histogram_rgb_toggle");
1023         }
1024         else if( value == &gui->use_hist ) {
1025                 return gui->theme->get_image_set("histogram_toggle");
1026         }
1027         else if( value == &gui->use_wave_parade ) {
1028                 return gui->theme->get_image_set("waveform_rgb_toggle");
1029         }
1030         else if( value == &gui->use_wave ) {
1031                 return gui->theme->get_image_set("waveform_toggle");
1032         }
1033         else {
1034                 return gui->theme->get_image_set("scope_toggle");
1035         }
1036 }
1037
1038 int ScopeToggle::handle_event()
1039 {
1040         *value = get_value();
1041         if( value == &gui->use_hist_parade ) {
1042                 if( get_value() ) gui->use_hist = 0;
1043         }
1044         else if( value == &gui->use_hist )
1045         {
1046                 if( get_value() ) gui->use_hist_parade = 0;
1047         }
1048         else if( value == &gui->use_wave_parade ) {
1049                 if( get_value() ) gui->use_wave = 0;
1050         }
1051         else if( value == &gui->use_wave ) {
1052                 if( get_value() ) gui->use_wave_parade = 0;
1053         }
1054         gui->toggle_event();
1055         gui->update_toggles();
1056         gui->create_panels();
1057         gui->show_window();
1058         return 1;
1059 }
1060