Credit Andrew - fix bug in render farm usage when using in/out pointers or selection
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / chromakeyavid / chromakey.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2012-2024 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 // original chromakey HSV author:
22 // https://www.mail-archive.com/[email protected]/msg00379.html
23 // http://jcornet.free.fr/linux/chromakey.html
24
25 // He was trying to replicate the Avid SpectraMatte:
26 // https://resources.avid.com/SupportFiles/attach/Symphony_Effects_and_CC_Guide_v5.5.pdf
27 // page 691
28 // but the problem seems to be harder than he thought
29
30 // A rewrite got a slightly different spill light processing
31 // but still not very useful:
32 // https://github.com/vanakala/cinelerra-cve/blob/master/plugins/chromakeyhsv/chromakey.C
33
34 // The current implementation is most of the Spectramatte algorithm.
35 // The only way to test it is to use the color swatch plugin.
36 // Fix brightness to test the saturation wedge.
37 // Fix saturation to test the brightness wedge.
38
39 // There are boundary artifacts caused by the color swatch showing a slice
40 // of a cube.
41 // If out slope > 0, constant 0% saturation or 0% brightness is all wedge.
42 // If out slope == 0, constant 0% saturation or constant 0% brightness has no wedge
43 // The general idea is if it's acting weird, switch between constant brightness
44 // & constant saturation.
45
46 // TODO: integrated color swatch & alpha blur, but it takes a lot of space in the GUI.
47
48 #include "bcdisplayinfo.h"
49 #include "bcsignals.h"
50 #include "chromakey.h"
51 #include "clip.h"
52 #include "bchash.h"
53 #include "filexml.h"
54 #include "guicast.h"
55 #include "keyframe.h"
56 #include "language.h"
57 #include "loadbalance.h"
58 #include "playback3d.h"
59 #include "bccolors.h"
60 #include "pluginvclient.h"
61 #include "vframe.h"
62
63 #include <stdint.h>
64 #include <string.h>
65 #include <unistd.h>
66
67 #define WINDOW_W xS(550)
68 #define WINDOW_H yS(600)
69 #define SLIDER_W xS(250)
70 #define MAX_SLOPE 100.0
71 #define MAX_VALUE 100.0
72
73 ChromaKeyConfig::ChromaKeyConfig ()
74 {
75   red   = 0.0;
76   green = 1.0;
77   blue  = 0.0;
78
79   min_brightness   =  50.0;
80   max_brightness   = 100.0;
81   tolerance        =  15.0;
82   saturation_start =   0.0;
83   saturation_line  =  50.0;
84
85   in_slope     = 2;
86   out_slope    = 2;
87   alpha_offset = 0;
88
89   spill_saturation = 0.0;
90   spill_angle      = 0.0;
91   desaturate_only  = 0;
92
93   show_mask = 0;
94
95   undo_red               = red;
96   undo_green             = green;
97   undo_blue              = blue;
98   undo_min_brightness    = min_brightness;
99   undo_max_brightness    = max_brightness;
100   undo_tolerance         = tolerance;
101   undo_saturation_start  = saturation_start;
102   undo_saturation_line   = saturation_line;
103   undo_in_slope          = in_slope;
104   undo_out_slope         = out_slope;
105   undo_alpha_offset      = alpha_offset;
106   undo_spill_saturation  = spill_saturation;
107   undo_spill_angle       = spill_angle;
108   undo_desaturate_only   = desaturate_only;
109   undo_show_mask         = show_mask;
110
111   store_red              = red;
112   store_green            = green;
113   store_blue             = blue;
114   store_min_brightness   = min_brightness;
115   store_max_brightness   = max_brightness;
116   store_tolerance        = tolerance;
117   store_saturation_start = saturation_start;
118   store_saturation_line  = saturation_line;
119   store_in_slope         = in_slope;
120   store_out_slope        = out_slope;
121   store_alpha_offset     = alpha_offset;
122   store_spill_saturation = spill_saturation;
123   store_spill_angle      = spill_angle;
124   store_desaturate_only  = desaturate_only;
125   store_show_mask        = show_mask;
126 }
127
128 void ChromaKeyConfig::reset(int clear)
129 {
130   switch(clear)
131   {
132   case RESET_RGB:
133     red   = 0.0;
134     green = 1.0;
135     blue  = 0.0;
136     break;
137   case RESET_MIN_BRIGHTNESS:
138     min_brightness = 50.0;
139     break;
140   case RESET_MAX_BRIGHTNESS:
141     max_brightness = 100.0;
142     break;
143   case RESET_TOLERANCE:
144     tolerance = 15.0;
145     break;
146   case RESET_SATURATION_START:
147     saturation_start = 0.0;
148     break;
149   case RESET_SATURATION_LINE:
150     saturation_line = 50.0;
151     break;
152   case RESET_IN_SLOPE:
153     in_slope = 2;
154     break;
155   case RESET_OUT_SLOPE:
156     out_slope = 2;
157     break;
158   case RESET_ALPHA_OFFSET:
159     alpha_offset = 0;
160     break;
161   case RESET_SPILL_SATURATION:
162     spill_saturation = 0.0;
163     break;
164   case RESET_SPILL_ANGLE:
165     spill_angle = 0.0;
166     break;
167   case RESET_ALL:
168   case RESET_DEFAULT_SETTINGS:
169   default:
170     undo_red              = red;
171     undo_green            = green;
172     undo_blue             = blue;
173     undo_min_brightness   = min_brightness;
174     undo_max_brightness   = max_brightness;
175     undo_tolerance        = tolerance;
176     undo_saturation_start = saturation_start;
177     undo_saturation_line  = saturation_line;
178     undo_in_slope         = in_slope;
179     undo_out_slope        = out_slope;
180     undo_alpha_offset     = alpha_offset;
181     undo_spill_saturation = spill_saturation;
182     undo_spill_angle      = spill_angle;
183     undo_desaturate_only  = desaturate_only;
184     undo_show_mask        = show_mask;
185
186     red   = 0.0;
187     green = 1.0;
188     blue  = 0.0;
189
190     min_brightness   =  50.0;
191     max_brightness   = 100.0;
192     tolerance        =  15.0;
193     saturation_start =   0.0;
194     saturation_line  =  50.0;
195
196     in_slope     = 2;
197     out_slope    = 2;
198     alpha_offset = 0;
199
200     spill_saturation = 0.0;
201     spill_angle      = 0.0;
202     desaturate_only  = 0;
203
204     show_mask = 0;
205     break;
206   }
207 }
208
209
210 void
211 ChromaKeyConfig::copy_from (ChromaKeyConfig & src)
212 {
213   red              = src.red;
214   green            = src.green;
215   blue             = src.blue;
216   spill_saturation = src.spill_saturation;
217   spill_angle      = src.spill_angle;
218   min_brightness   = src.min_brightness;
219   max_brightness   = src.max_brightness;
220   saturation_start = src.saturation_start;
221   saturation_line  = src.saturation_line;
222   tolerance        = src.tolerance;
223   in_slope         = src.in_slope;
224   out_slope        = src.out_slope;
225   alpha_offset     = src.alpha_offset;
226   show_mask        = src.show_mask;
227   desaturate_only  = src.desaturate_only;
228
229   undo_red              = src.red;
230   undo_green            = src.green;
231   undo_blue             = src.blue;
232   undo_min_brightness   = src.min_brightness;
233   undo_max_brightness   = src.max_brightness;
234   undo_tolerance        = src.tolerance;
235   undo_saturation_start = src.saturation_start;
236   undo_saturation_line  = src.saturation_line;
237   undo_in_slope         = src.in_slope;
238   undo_out_slope        = src.out_slope;
239   undo_alpha_offset     = src.alpha_offset;
240   undo_spill_saturation = src.spill_saturation;
241   undo_spill_angle      = src.spill_angle;
242   undo_desaturate_only  = src.desaturate_only;
243   undo_show_mask        = src.show_mask;
244 }
245
246 int
247 ChromaKeyConfig::equivalent (ChromaKeyConfig & src)
248 {
249   return (EQUIV (red,              src.red)              &&
250           EQUIV (green,            src.green)            &&
251           EQUIV (blue,             src.blue)             &&
252           EQUIV (spill_saturation, src.spill_saturation) &&
253           EQUIV (spill_angle,      src.spill_angle)      &&
254           EQUIV (min_brightness,   src.min_brightness)   &&
255           EQUIV (max_brightness,   src.max_brightness)   &&
256           EQUIV (saturation_start, src.saturation_start) &&
257           EQUIV (saturation_line,  src.saturation_line)  &&
258           EQUIV (tolerance,        src.tolerance)        &&
259           EQUIV (in_slope,         src.in_slope)         &&
260           EQUIV (out_slope,        src.out_slope)        &&
261           EQUIV (alpha_offset,     src.alpha_offset)     &&
262           show_mask       == src.show_mask &&
263           desaturate_only == src.desaturate_only);
264 }
265
266 void
267 ChromaKeyConfig::interpolate (ChromaKeyConfig & prev,
268                               ChromaKeyConfig & next,
269                               int64_t prev_frame,
270                               int64_t next_frame,
271                               int64_t current_frame)
272 {
273   double next_scale =
274     (double) (current_frame - prev_frame) / (next_frame - prev_frame);
275   double prev_scale =
276     (double) (next_frame - current_frame) / (next_frame - prev_frame);
277
278   this->red   = prev.red   * prev_scale + next.red   * next_scale;
279   this->green = prev.green * prev_scale + next.green * next_scale;
280   this->blue  = prev.blue  * prev_scale + next.blue  * next_scale;
281   this->spill_saturation =
282     prev.spill_saturation * prev_scale + next.spill_saturation * next_scale;
283   this->spill_angle =
284     prev.spill_angle * prev_scale + next.tolerance * next_scale;
285   this->min_brightness =
286     prev.min_brightness * prev_scale + next.min_brightness * next_scale;
287   this->max_brightness =
288     prev.max_brightness * prev_scale + next.max_brightness * next_scale;
289   this->saturation_start =
290     prev.saturation_start * prev_scale + next.saturation_start * next_scale;
291   this->saturation_line =
292     prev.saturation_line * prev_scale + next.saturation_line * next_scale;
293   this->tolerance = prev.tolerance * prev_scale + next.tolerance * next_scale;
294   this->in_slope  = prev.in_slope  * prev_scale + next.in_slope  * next_scale;
295   this->out_slope = prev.out_slope * prev_scale + next.out_slope * next_scale;
296   this->alpha_offset =
297     prev.alpha_offset * prev_scale + next.alpha_offset * next_scale;
298   this->show_mask       = prev.show_mask;
299   this->desaturate_only = prev.desaturate_only;
300
301   this->undo_red              = this->red;
302   this->undo_green            = this->green;
303   this->undo_blue             = this->blue;
304   this->undo_min_brightness   = this->min_brightness;
305   this->undo_max_brightness   = this->max_brightness;
306   this->undo_tolerance        = this->tolerance;
307   this->undo_saturation_start = this->saturation_start;
308   this->undo_saturation_line  = this->saturation_line;
309   this->undo_in_slope         = this->in_slope;
310   this->undo_out_slope        = this->out_slope;
311   this->undo_alpha_offset     = this->alpha_offset;
312   this->undo_spill_saturation = this->spill_saturation;
313   this->undo_spill_angle      = this->spill_angle;
314   this->undo_desaturate_only  = this->desaturate_only;
315   this->undo_show_mask        = this->show_mask;
316 }
317
318 int
319 ChromaKeyConfig::get_color ()
320 {
321   int red   = (int) (CLIP (this->red,   0, 1) * 0xff);
322   int green = (int) (CLIP (this->green, 0, 1) * 0xff);
323   int blue  = (int) (CLIP (this->blue,  0, 1) * 0xff);
324   return (red << 16) | (green << 8) | blue;
325 }
326
327
328
329 ChromaKeyWindow::ChromaKeyWindow (ChromaKeyAvid * plugin)
330   : PluginClientWindow(plugin,
331                        WINDOW_W, 
332                        WINDOW_H, 
333                        WINDOW_W, 
334                        WINDOW_H, 
335                        0)
336 {
337   this->plugin = plugin;
338   color_thread = 0;
339 }
340
341 ChromaKeyWindow::~ChromaKeyWindow ()
342 {
343   delete color_thread;
344 }
345
346 void ChromaKeyWindow::create_objects ()
347 {
348   int xs10 = xS(10), xs20 = xS(20), xs100 = xS(100), wslid = SLIDER_W;
349   int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys40 = yS(40), ys50 = yS(50);
350   int y = ys10, x2 = xS(160), x3 = xS(260), xs5 = xS(5);
351   int x = xs10;
352   int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
353
354   BC_Title *title;
355   BC_Bar *bar;
356   BC_TitleBar *title_bar;
357
358 // Color section
359   add_subwindow (title_bar =
360                  new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Color")));
361   y += ys20;
362
363   add_subwindow (color = new ChromaKeyColor (plugin, this, x, y));
364   // Info for Sample rectangle:       x_slider w_slider w_sample
365   //                                        \       |      /    y,   w,     h
366   add_subwindow (sample = new BC_SubWindow (x3 + wslid - xs100, y, xs100, ys50));
367   y += ys30;
368   add_subwindow (use_colorpicker = new ChromaKeyUseColorPicker (plugin, this, x, y));
369   y += ys30;
370   add_subwindow (show_mask =
371                  new ChromaKeyToggle (plugin, x, y, &plugin->config.show_mask,
372                                       _("Show Mask")));
373
374 // Key parameters section
375   y += ys30;
376   add_subwindow (title_bar =
377                  new BC_TitleBar (x, y, get_w()-2*x, xs20, xs10,
378                                   _("Key parameters")));
379   y += ys20;
380   add_subwindow (title = new BC_Title (x, y, _("Hue Tolerance:")));
381   tolerance_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_VALUE,
382                                       &plugin->config.tolerance);
383   tolerance_text->create_objects();
384   tolerance = new ChromaKeySlider (plugin, tolerance_text, x3, y, wslid,
385                                    0, MAX_VALUE, &plugin->config.tolerance);
386   add_subwindow (tolerance);
387   tolerance_text->slider = tolerance;
388   clr_x = x3 + tolerance->get_w() + x;
389   add_subwindow (tolerance_clr =
390                  new ChromaKeyClr (plugin, this, clr_x, y, RESET_TOLERANCE));
391   y += ys30;
392
393   add_subwindow (title = new BC_Title (x, y, _("Min. Brightness:")));
394   min_brightness_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
395                                            0, MAX_VALUE,
396                                            &plugin->config.min_brightness);
397   min_brightness_text->create_objects();
398   min_brightness = new ChromaKeySlider (plugin, min_brightness_text,
399                                         x3, y, wslid, 0, MAX_VALUE,
400                                         &plugin->config.min_brightness);
401   add_subwindow(min_brightness);
402   min_brightness_text->slider = min_brightness;
403   add_subwindow (min_brightness_clr =
404                  new ChromaKeyClr (plugin, this, clr_x, y,
405                                    RESET_MIN_BRIGHTNESS));
406   y += ys30;
407
408   add_subwindow (title = new BC_Title (x, y, _("Max. Brightness:")));
409   max_brightness_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
410                                            0, MAX_VALUE,
411                                            &plugin->config.max_brightness);
412   max_brightness_text->create_objects();
413   max_brightness = new ChromaKeySlider (plugin, max_brightness_text,
414                                         x3, y, wslid, 0, MAX_VALUE,
415                                         &plugin->config.max_brightness);
416   add_subwindow(max_brightness);
417   max_brightness_text->slider = max_brightness;
418   add_subwindow (max_brightness_clr =
419                  new ChromaKeyClr (plugin, this, clr_x, y,
420                                    RESET_MAX_BRIGHTNESS));
421   y += ys30;
422
423   add_subwindow (title = new BC_Title (x, y, _("Saturation Start:")));
424   saturation_start_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
425                                              0, MAX_VALUE,
426                                              &plugin->config.saturation_start);
427   saturation_start_text->create_objects();
428   saturation_start = new ChromaKeySlider (plugin, saturation_start_text,
429                                           x3, y, wslid, 0, MAX_VALUE,
430                                           &plugin->config.saturation_start);
431   add_subwindow(saturation_start);
432   saturation_start_text->slider = saturation_start;
433   add_subwindow (saturation_start_clr =
434                  new ChromaKeyClr (plugin, this, clr_x, y,
435                                    RESET_SATURATION_START));
436   y += ys30;
437
438   add_subwindow (title = new BC_Title (x, y, _("Saturation Line:")));
439   saturation_line_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
440                                             0, MAX_VALUE,
441                                             &plugin->config.saturation_line);
442   saturation_line_text->create_objects();
443   saturation_line = new ChromaKeySlider (plugin, saturation_line_text,
444                                          x3, y, wslid, 0, MAX_VALUE,
445                                          &plugin->config.saturation_line);
446   add_subwindow(saturation_line);
447   saturation_line_text->slider = saturation_line;
448   add_subwindow (saturation_line_clr =
449                  new ChromaKeyClr (plugin, this, clr_x, y,
450                                    RESET_SATURATION_LINE));
451   y += ys40;
452
453 // Mask tweaking section
454   add_subwindow (title_bar =
455                  new BC_TitleBar (x, y, get_w()-2*x, xs20, xs10,
456                                   _("Mask tweaking")));
457   y += ys20;
458   add_subwindow (title = new BC_Title (x, y, _("In Slope:")));
459   in_slope_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_SLOPE,
460                                      &plugin->config.in_slope);
461   in_slope_text->create_objects();
462   in_slope = new ChromaKeySlider (plugin, in_slope_text, x3, y, wslid,
463                                   0, MAX_SLOPE, &plugin->config.in_slope);
464   add_subwindow(in_slope);
465   in_slope_text->slider = in_slope;
466   add_subwindow (in_slope_clr =
467                  new ChromaKeyClr (plugin, this, clr_x, y, RESET_IN_SLOPE));
468   y += ys30;
469
470   add_subwindow (title = new BC_Title (x, y, _("Out Slope:")));
471   out_slope_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_SLOPE,
472                                       &plugin->config.out_slope);
473   out_slope_text->create_objects();
474   out_slope = new ChromaKeySlider (plugin, out_slope_text, x3, y, wslid,
475                                    0, MAX_SLOPE, &plugin->config.out_slope);
476   add_subwindow(out_slope);
477   out_slope_text->slider = out_slope;
478   add_subwindow (out_slope_clr =
479                  new ChromaKeyClr (plugin, this, clr_x, y, RESET_OUT_SLOPE));
480   y += ys30;
481
482   add_subwindow (title = new BC_Title (x, y, _("Alpha Offset:")));
483   alpha_offset_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
484                                          -MAX_VALUE, MAX_VALUE,
485                                          &plugin->config.alpha_offset);
486   alpha_offset_text->create_objects();
487   alpha_offset = new ChromaKeySlider (plugin, alpha_offset_text, x3, y, wslid,
488                                       -MAX_VALUE, MAX_VALUE,
489                                       &plugin->config.alpha_offset);
490   add_subwindow(alpha_offset);
491   alpha_offset_text->slider = alpha_offset;
492   add_subwindow (alpha_offset_clr =
493                  new ChromaKeyClr (plugin, this, clr_x, y, RESET_ALPHA_OFFSET));
494   y += ys40;
495
496 // Spill light control section
497   add_subwindow (title_bar =
498                  new BC_TitleBar (x, y, get_w()-2*x, xs20, xs10,
499                                   _("Spill light control")));
500   y += ys20;
501   add_subwindow (title = new BC_Title (x, y, _("Spill Saturation:")));
502   spill_saturation_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
503                                              0, MAX_VALUE,
504                                              &plugin->config.spill_saturation);
505   spill_saturation_text->create_objects();
506   spill_saturation = new ChromaKeySlider (plugin, spill_saturation_text,
507                                           x3, y, wslid, 0, MAX_VALUE,
508                                           &plugin->config.spill_saturation);
509   add_subwindow(spill_saturation);
510   spill_saturation_text->slider = spill_saturation;
511   add_subwindow (spill_saturation_clr =
512                  new ChromaKeyClr (plugin, this, clr_x, y,
513                                    RESET_SPILL_SATURATION));
514   y += ys30;
515
516   add_subwindow (title = new BC_Title (x, y, _("Spill Angle:")));
517   spill_angle_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_VALUE,
518                                         &plugin->config.spill_angle);
519   spill_angle_text->create_objects();
520   spill_angle = new ChromaKeySlider (plugin, spill_angle_text, x3, y, wslid,
521                                      0, MAX_VALUE, &plugin->config.spill_angle);
522   add_subwindow(spill_angle);
523   spill_angle_text->slider = spill_angle;
524   add_subwindow (spill_angle_clr =
525                  new ChromaKeyClr (plugin, this, clr_x, y, RESET_SPILL_ANGLE));
526   y += ys30;
527
528   add_subwindow(desaturate_only =
529                 new ChromaKeyToggle(plugin, x, y,
530                                     &plugin->config.desaturate_only, 
531                                     _("Desaturate Only")));
532   y += ys40;
533
534 // Global buttons section
535   add_subwindow (bar = new BC_Bar(x, y, get_w()-2*x));
536   y += ys20;
537   add_subwindow (store = new ChromaKeyStore (plugin, this, x, y));
538   x += store->get_w() + xs5;
539   add_subwindow (recall = new ChromaKeyRecall (plugin, this, x, y));
540   x += recall->get_w() + xs5;
541   add_subwindow (exchange = new ChromaKeyExchange (plugin, this, x, y));
542   x += exchange->get_w() + xs5;
543   add_subwindow (undo = new ChromaKeyUndo (plugin, this, x, y));
544   x += undo->get_w() + xs5;
545   add_subwindow (reset = new ChromaKeyReset (plugin, this, x, y));
546
547   color_thread = new ChromaKeyColorThread (plugin, this);
548
549   update_sample ();
550   show_window ();
551 }
552
553 void
554 ChromaKeyWindow::update_sample ()
555 {
556   sample->set_color (plugin->config.get_color ());
557   sample->draw_box (0, 0, sample->get_w (), sample->get_h ());
558   sample->set_color (BLACK);
559   sample->draw_rectangle (0, 0, sample->get_w (), sample->get_h ());
560   sample->flash ();
561 }
562
563 void ChromaKeyWindow::done_event(int result)
564 {
565   color_thread->close_window();
566 }
567
568
569 ChromaKeyColor::ChromaKeyColor (ChromaKeyAvid * plugin,
570                                 ChromaKeyWindow * gui, int x, int y)
571   : BC_GenericButton (x, y, _("Color..."))
572 {
573   this->plugin = plugin;
574   this->gui = gui;
575 }
576
577 int
578 ChromaKeyColor::handle_event ()
579 {
580   gui->color_thread->start_window (plugin->config.get_color (), 0xff);
581   return 1;
582 }
583
584
585
586 ChromaKeyText::ChromaKeyText(ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
587                              ChromaKeySlider *slider, int x, int y,
588                              float min, float max, float *output)
589   : BC_TumbleTextBox(gui, *output, min, max, x, y, xS(60), 2)
590 {
591   this->plugin = plugin;
592   this->gui = gui;
593   this->slider = slider;
594   this->min = min;
595   this->max = max;
596   this->output = output;
597   set_increment(0.01);
598 }
599
600 ChromaKeyText::~ChromaKeyText()
601 {
602 }
603
604 int ChromaKeyText::handle_event()
605 {
606   *output = atof(get_text());
607   if(*output > max) *output = max;
608   if(*output < min) *output = min;
609   slider->update(*output);
610   plugin->send_configure_change();
611   return 1;
612 }
613
614 ChromaKeySlider::ChromaKeySlider(ChromaKeyAvid *plugin, ChromaKeyText *text,
615                                  int x, int y, int w,
616                                  float min, float max, float *output)
617   : BC_FSlider(x, y, 0, w, w, min, max, *output)
618 {
619   this->plugin = plugin;
620   this->text = text;
621   this->output = output;
622   set_precision(0.01);
623   enable_show_value(0); // Hide caption
624 }
625
626 ChromaKeySlider::~ChromaKeySlider()
627 {
628 }
629
630 int ChromaKeySlider::handle_event()
631 {
632   *output = get_value();
633   text->update(*output);
634   plugin->send_configure_change();
635   return 1;
636 }
637
638 ChromaKeyClr::ChromaKeyClr(ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
639                            int x, int y, int clear)
640   : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
641 {
642   this->plugin = plugin;
643   this->gui = gui;
644   this->clear = clear;
645 }
646
647 ChromaKeyClr::~ChromaKeyClr()
648 {
649 }
650
651 int ChromaKeyClr::handle_event()
652 {
653   plugin->config.reset(clear);
654   gui->update_gui(clear);
655   plugin->send_configure_change();
656   return 1;
657 }
658
659 ChromaKeyToggle::ChromaKeyToggle (ChromaKeyAvid * plugin,
660                                   int x,
661                                   int y,
662                                   bool *output,
663                                   const char *caption)
664   : BC_CheckBox (x,
665                  y,
666                  *output ? 1 : 0,
667                  caption)
668 {
669   this->plugin = plugin;
670   this->output = output;
671 }
672
673 int
674 ChromaKeyToggle::handle_event ()
675 {
676   *output = get_value() ? true : false;
677   plugin->send_configure_change ();
678   return 1;
679 }
680
681
682 ChromaKeyReset::ChromaKeyReset (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
683                                 int x, int y)
684   : BC_GenericButton(x, y, _("Reset"))
685 {
686   this->plugin = plugin;
687   this->gui = gui;
688 }
689
690 int ChromaKeyReset::handle_event ()
691 {
692   plugin->config.reset(RESET_DEFAULT_SETTINGS);
693   gui->update_gui(RESET_DEFAULT_SETTINGS);
694   plugin->send_configure_change();
695   return 1;
696 }
697
698 ChromaKeyStore::ChromaKeyStore (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
699                                 int x, int y)
700   : BC_GenericButton(x, y, _("Store"))
701 {
702   this->plugin = plugin;
703   this->gui = gui;
704 }
705
706 int ChromaKeyStore::handle_event ()
707 {
708   plugin->config.store_red              = plugin->config.red;
709   plugin->config.store_green            = plugin->config.green;
710   plugin->config.store_blue             = plugin->config.blue;
711   plugin->config.store_min_brightness   = plugin->config.min_brightness;
712   plugin->config.store_max_brightness   = plugin->config.max_brightness;
713   plugin->config.store_tolerance        = plugin->config.tolerance;
714   plugin->config.store_saturation_start = plugin->config.saturation_start;
715   plugin->config.store_saturation_line  = plugin->config.saturation_line;
716   plugin->config.store_in_slope         = plugin->config.in_slope;
717   plugin->config.store_out_slope        = plugin->config.out_slope;
718   plugin->config.store_alpha_offset     = plugin->config.alpha_offset;
719   plugin->config.store_spill_saturation = plugin->config.spill_saturation;
720   plugin->config.store_spill_angle      = plugin->config.spill_angle;
721   plugin->config.store_desaturate_only  = plugin->config.desaturate_only;
722   plugin->config.store_show_mask        = plugin->config.show_mask;
723   return 1;
724 }
725
726 ChromaKeyRecall::ChromaKeyRecall (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
727                                   int x, int y)
728   : BC_GenericButton(x, y, _("Recall"))
729 {
730   this->plugin = plugin;
731   this->gui = gui;
732 }
733
734 int ChromaKeyRecall::handle_event ()
735 {
736   plugin->config.undo_red              = plugin->config.red;
737   plugin->config.undo_green            = plugin->config.green;
738   plugin->config.undo_blue             = plugin->config.blue;
739   plugin->config.undo_min_brightness   = plugin->config.min_brightness;
740   plugin->config.undo_max_brightness   = plugin->config.max_brightness;
741   plugin->config.undo_tolerance        = plugin->config.tolerance;
742   plugin->config.undo_saturation_start = plugin->config.saturation_start;
743   plugin->config.undo_saturation_line  = plugin->config.saturation_line;
744   plugin->config.undo_in_slope         = plugin->config.in_slope;
745   plugin->config.undo_out_slope        = plugin->config.out_slope;
746   plugin->config.undo_alpha_offset     = plugin->config.alpha_offset;
747   plugin->config.undo_spill_saturation = plugin->config.spill_saturation;
748   plugin->config.undo_spill_angle      = plugin->config.spill_angle;
749   plugin->config.undo_desaturate_only  = plugin->config.desaturate_only;
750   plugin->config.undo_show_mask        = plugin->config.show_mask;
751
752   plugin->config.red              = plugin->config.store_red;
753   plugin->config.green            = plugin->config.store_green;
754   plugin->config.blue             = plugin->config.store_blue;
755   plugin->config.min_brightness   = plugin->config.store_min_brightness;
756   plugin->config.max_brightness   = plugin->config.store_max_brightness;
757   plugin->config.tolerance        = plugin->config.store_tolerance;
758   plugin->config.saturation_start = plugin->config.store_saturation_start;
759   plugin->config.saturation_line  = plugin->config.store_saturation_line;
760   plugin->config.in_slope         = plugin->config.store_in_slope;
761   plugin->config.out_slope        = plugin->config.store_out_slope;
762   plugin->config.alpha_offset     = plugin->config.store_alpha_offset;
763   plugin->config.spill_saturation = plugin->config.store_spill_saturation;
764   plugin->config.spill_angle      = plugin->config.store_spill_angle;
765   plugin->config.desaturate_only  = plugin->config.store_desaturate_only;
766   plugin->config.show_mask        = plugin->config.store_show_mask;
767
768   gui->update_gui(RESET_DEFAULT_SETTINGS);
769   plugin->send_configure_change();
770   return 1;
771 }
772
773 ChromaKeyExchange::ChromaKeyExchange (ChromaKeyAvid *plugin,
774                                       ChromaKeyWindow *gui, int x, int y)
775   : BC_GenericButton(x, y, _("Exchange"))
776 {
777   this->plugin = plugin;
778   this->gui = gui;
779 }
780
781 #define CHROMAKEY_SWAP(what,typ,elem) \
782 tmp_##typ = plugin->config.what##_##elem; \
783 plugin->config.what##_##elem = plugin->config.elem; \
784 plugin->config.elem = tmp_##typ;
785
786 int ChromaKeyExchange::handle_event ()
787 {
788   float tmp_flt;
789   bool tmp_bool;
790
791   plugin->config.undo_red              = plugin->config.red;
792   plugin->config.undo_green            = plugin->config.green;
793   plugin->config.undo_blue             = plugin->config.blue;
794   plugin->config.undo_min_brightness   = plugin->config.min_brightness;
795   plugin->config.undo_max_brightness   = plugin->config.max_brightness;
796   plugin->config.undo_tolerance        = plugin->config.tolerance;
797   plugin->config.undo_saturation_start = plugin->config.saturation_start;
798   plugin->config.undo_saturation_line  = plugin->config.saturation_line;
799   plugin->config.undo_in_slope         = plugin->config.in_slope;
800   plugin->config.undo_out_slope        = plugin->config.out_slope;
801   plugin->config.undo_alpha_offset     = plugin->config.alpha_offset;
802   plugin->config.undo_spill_saturation = plugin->config.spill_saturation;
803   plugin->config.undo_spill_angle      = plugin->config.spill_angle;
804   plugin->config.undo_desaturate_only  = plugin->config.desaturate_only;
805   plugin->config.undo_show_mask        = plugin->config.show_mask;
806
807   CHROMAKEY_SWAP (store, flt,  red)
808   CHROMAKEY_SWAP (store, flt,  green)
809   CHROMAKEY_SWAP (store, flt,  blue)
810   CHROMAKEY_SWAP (store, flt,  min_brightness)
811   CHROMAKEY_SWAP (store, flt,  max_brightness)
812   CHROMAKEY_SWAP (store, flt,  tolerance)
813   CHROMAKEY_SWAP (store, flt,  saturation_start)
814   CHROMAKEY_SWAP (store, flt,  saturation_line)
815   CHROMAKEY_SWAP (store, flt,  in_slope)
816   CHROMAKEY_SWAP (store, flt,  out_slope)
817   CHROMAKEY_SWAP (store, flt,  alpha_offset)
818   CHROMAKEY_SWAP (store, flt,  spill_saturation)
819   CHROMAKEY_SWAP (store, flt,  spill_angle)
820   CHROMAKEY_SWAP (store, bool, desaturate_only)
821   CHROMAKEY_SWAP (store, bool, show_mask)
822
823   gui->update_gui(RESET_DEFAULT_SETTINGS);
824   plugin->send_configure_change();
825   return 1;
826 }
827
828 ChromaKeyUndo::ChromaKeyUndo (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
829                               int x, int y)
830   : BC_GenericButton(x, y, _("Undo"))
831 {
832   this->plugin = plugin;
833   this->gui = gui;
834 }
835
836 int ChromaKeyUndo::handle_event ()
837 {
838   //float tmp_flt;
839   //bool tmp_bool;
840
841   //CHROMAKEY_SWAP (undo, flt,  red)
842   //CHROMAKEY_SWAP (undo, flt,  green)
843   //CHROMAKEY_SWAP (undo, flt,  blue)
844   //CHROMAKEY_SWAP (undo, flt,  min_brightness)
845   //CHROMAKEY_SWAP (undo, flt,  max_brightness)
846   //CHROMAKEY_SWAP (undo, flt,  tolerance)
847   //CHROMAKEY_SWAP (undo, flt,  saturation_start)
848   //CHROMAKEY_SWAP (undo, flt,  saturation_line)
849   //CHROMAKEY_SWAP (undo, flt,  in_slope)
850   //CHROMAKEY_SWAP (undo, flt,  out_slope)
851   //CHROMAKEY_SWAP (undo, flt,  alpha_offset)
852   //CHROMAKEY_SWAP (undo, flt,  spill_saturation)
853   //CHROMAKEY_SWAP (undo, flt,  spill_angle)
854   //CHROMAKEY_SWAP (undo, bool, desaturate_only)
855   //CHROMAKEY_SWAP (undo, bool, show_mask)
856
857   // better not to swap but restore undo values
858
859   plugin->config.red              = plugin->config.undo_red;
860   plugin->config.green            = plugin->config.undo_green;
861   plugin->config.blue             = plugin->config.undo_blue;
862   plugin->config.min_brightness   = plugin->config.undo_min_brightness;
863   plugin->config.max_brightness   = plugin->config.undo_max_brightness;
864   plugin->config.tolerance        = plugin->config.undo_tolerance;
865   plugin->config.saturation_start = plugin->config.undo_saturation_start;
866   plugin->config.saturation_line  = plugin->config.undo_saturation_line;
867   plugin->config.in_slope         = plugin->config.undo_in_slope;
868   plugin->config.out_slope        = plugin->config.undo_out_slope;
869   plugin->config.alpha_offset     = plugin->config.undo_alpha_offset;
870   plugin->config.spill_saturation = plugin->config.undo_spill_saturation;
871   plugin->config.spill_angle      = plugin->config.undo_spill_angle;
872   plugin->config.desaturate_only  = plugin->config.undo_desaturate_only;
873   plugin->config.show_mask        = plugin->config.undo_show_mask;
874
875   gui->update_gui(RESET_DEFAULT_SETTINGS);
876   plugin->send_configure_change();
877   return 1;
878 }
879
880 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker (ChromaKeyAvid * plugin,
881                                                   ChromaKeyWindow * gui,
882                                                   int x, int y)
883   : BC_GenericButton (x, y, _("Use color picker"))
884 {
885   this->plugin = plugin;
886   this->gui = gui;
887 }
888
889 int
890 ChromaKeyUseColorPicker::handle_event ()
891 {
892   plugin->config.red = plugin->get_red ();
893   plugin->config.green = plugin->get_green ();
894   plugin->config.blue = plugin->get_blue ();
895
896   gui->update_sample ();
897
898   plugin->send_configure_change ();
899   return 1;
900 }
901
902 ChromaKeyColorThread::ChromaKeyColorThread (ChromaKeyAvid * plugin,
903                                             ChromaKeyWindow * gui)
904   : ColorPicker (0, _("Color"))
905 {
906   this->plugin = plugin;
907   this->gui = gui;
908 }
909
910 int
911 ChromaKeyColorThread::handle_new_color (int output, int alpha)
912 {
913   plugin->config.red   = (float) (output & 0xff0000) / 0xff0000;
914   plugin->config.green = (float) (output & 0xff00) / 0xff00;
915   plugin->config.blue  = (float) (output & 0xff) / 0xff;
916
917   get_gui()->unlock_window();
918   gui->lock_window("ChromaKeyColorThread::handle_new_color");
919   gui->update_sample();
920   gui->unlock_window();
921   get_gui()->lock_window("ChromaKeyColorThread::handle_new_color");
922
923   plugin->send_configure_change ();
924   return 1;
925 }
926
927
928
929 ChromaKeyServer::ChromaKeyServer (ChromaKeyAvid * plugin)
930   : LoadServer (plugin->PluginClient::smp + 1,
931                 plugin->PluginClient::smp + 1)
932 // DEBUG
933 //  : LoadServer (1, 1)
934 {
935   this->plugin = plugin;
936 }
937
938 void
939 ChromaKeyServer::init_packages ()
940 {
941   for (int i = 0; i < get_total_packages (); i++)
942   {
943     ChromaKeyPackage *pkg = (ChromaKeyPackage *) get_package (i);
944     pkg->y1 = plugin->input->get_h () * i / get_total_packages ();
945     pkg->y2 = plugin->input->get_h () * (i + 1) / get_total_packages ();
946   }
947 }
948
949 LoadClient *
950 ChromaKeyServer::new_client ()
951 {
952   return new ChromaKeyUnit (plugin, this);
953 }
954
955 LoadPackage *
956 ChromaKeyServer::new_package ()
957 {
958   return new ChromaKeyPackage;
959 }
960
961 ChromaKeyPackage::ChromaKeyPackage ():LoadPackage ()
962 {
963 }
964
965 ChromaKeyUnit::ChromaKeyUnit (ChromaKeyAvid * plugin, ChromaKeyServer * server)
966   : LoadClient (server)
967 {
968   this->plugin = plugin;
969 }
970
971
972
973 #define ABS(a) (((a) < 0) ? -(a) : (a))
974
975
976
977 // Compute the same values in the opengl version
978 #define COMMON_VARIABLES \
979     float red = plugin->config.red; \
980     float green = plugin->config.green; \
981     float blue = plugin->config.blue; \
982  \
983     float in_slope = plugin->config.in_slope / MAX_SLOPE; \
984     float out_slope = plugin->config.out_slope / MAX_SLOPE; \
985  \
986 /* Convert RGB key to HSV key */ \
987     float hue_key, saturation_key, value_key; \
988     HSV::rgb_to_hsv(red,        \
989                 green, \
990                 blue, \
991                 hue_key, \
992                 saturation_key, \
993                 value_key); \
994  \
995 /* hue range */ \
996     float tolerance = plugin->config.tolerance * 180 / MAX_VALUE; \
997     float tolerance_in = tolerance - in_slope * 180; \
998     tolerance_in = MAX(tolerance_in, 0); \
999     float tolerance_out = tolerance + out_slope * 180; \
1000     tolerance_out = MIN(tolerance_out, 180); \
1001  \
1002 /* distance of wedge point from center */ \
1003     float sat_distance = plugin->config.saturation_start / MAX_VALUE; \
1004 /* XY shift of input color to get wedge point */ \
1005     float sat_x = -cos(TO_RAD(hue_key)) * sat_distance; \
1006     float sat_y = -sin(TO_RAD(hue_key)) * sat_distance; \
1007 /* minimum saturation after wedge point */ \
1008     float min_s = plugin->config.saturation_line / MAX_VALUE; \
1009     float min_s_in = min_s + in_slope; \
1010     float min_s_out = min_s - out_slope; \
1011  \
1012     float min_v = plugin->config.min_brightness / MAX_VALUE; \
1013     float min_v_in = min_v + in_slope; \
1014     float min_v_out = min_v - out_slope; \
1015 /* ignore min_brightness 0 */ \
1016     if(plugin->config.min_brightness == 0) min_v_in = 0; \
1017  \
1018     float max_v = plugin->config.max_brightness / MAX_VALUE; \
1019     float max_v_in = max_v - in_slope; \
1020     float max_v_out = max_v + out_slope; \
1021 /* handle wedge boundaries crossing over */ \
1022     if(max_v_in < min_v_in) max_v_in = min_v_in = (max_v_in + min_v_in) / 2; \
1023 /* ignore max_brightness 100% */ \
1024     if(plugin->config.max_brightness == MAX_VALUE) max_v_in = 1.0; \
1025  \
1026 /* distance of spill wedge point from center */ \
1027     float spill_distance = sat_distance * (1.0 - plugin->config.spill_saturation / MAX_VALUE); \
1028 /* XY shift of input color to get spill wedge point */ \
1029     float spill_x = -cos(TO_RAD(hue_key)) * spill_distance; \
1030     float spill_y = -sin(TO_RAD(hue_key)) * spill_distance; \
1031 /* tolerance of the spill wedge */ \
1032     float spill_tolerance = tolerance + plugin->config.spill_angle * 90.0 / MAX_VALUE; \
1033 /* borders of the spill wedge */ \
1034     float min_h = hue_key - spill_tolerance; \
1035     float max_h = hue_key + spill_tolerance; \
1036     int desaturate_only = plugin->config.desaturate_only; \
1037  \
1038     float alpha_offset = plugin->config.alpha_offset / MAX_VALUE;
1039
1040 // shortest distance between 2 hues
1041 static float hue_distance(float h1, float h2)
1042 {
1043   float result = h1 - h2;
1044   if(result < -180) result += 360;
1045   else if(result > 180) result -= 360;
1046   return result;
1047 }
1048
1049 // shift H & S based on an X & Y offset
1050 static void shift_hs(float &h_shifted, 
1051                      float &s_shifted, 
1052                      float h, 
1053                      float s, 
1054                      float x_offset,
1055                      float y_offset)
1056 {
1057   float h_rad = TO_RAD(h);
1058   float x = cos(h_rad) * s;
1059   float y = sin(h_rad) * s;
1060   x += x_offset;
1061   y += y_offset;
1062   h_shifted = TO_DEG(atan2(y, x));
1063   s_shifted = hypot(x, y);
1064 }
1065
1066 template <typename component_type> 
1067 void ChromaKeyUnit::process_chromakey(int components, 
1068                                       component_type max, 
1069                                       bool use_yuv, 
1070                                       ChromaKeyPackage *pkg) 
1071 {
1072   COMMON_VARIABLES
1073
1074   int w = plugin->input->get_w();
1075
1076 // printf("ChromaKeyUnit::process_chromakey %d hue_key=%f min_h=%f max_h=%f\n", 
1077 // __LINE__, 
1078 // hue_key,
1079 // min_h,
1080 // max_h);
1081 // printf("ChromaKeyUnit::process_chromakey %d tolerance_in=%f tolerance=%f tolerance_out=%f\n", 
1082 // __LINE__, 
1083 // tolerance_in,
1084 // tolerance,
1085 // tolerance_out);
1086 // printf("ChromaKeyUnit::process_chromakey %d min_s_in=%f min_s=%f min_s_out=%f\n", 
1087 // __LINE__, 
1088 // min_s_in,
1089 // min_s,
1090 // min_s_out);
1091 // printf("ChromaKeyUnit::process_chromakey %d min_v_in=%f min_v=%f min_v_out=%f\n", 
1092 // __LINE__, 
1093 // min_v_in,
1094 // min_v,
1095 // min_v_out);
1096 // printf("ChromaKeyUnit::process_chromakey %d max_v_in=%f max_v=%f max_v_out=%f\n", 
1097 // __LINE__, 
1098 // max_v_in,
1099 // max_v,
1100 // max_v_out);
1101
1102   for (int i = pkg->y1; i < pkg->y2; i++)
1103   {
1104     component_type *row = (component_type *) plugin->input->get_rows ()[i];
1105
1106     for (int j = 0; j < w; j++)
1107     {
1108       float r, g, b, a = 1;
1109       if (!use_yuv)
1110       {
1111         r = (float) row[0] / max;
1112         g = (float) row[1] / max;
1113         b = (float) row[2] / max;
1114       }
1115       else              /* Convert pixel to RGB float */
1116         YUV::yuv.yuv_to_rgb_f (r, g, b, row[0], row[1], row[2]);
1117
1118 // the input color
1119       float h, s, v;
1120
1121 // alpha contribution of each component of the HSV space
1122       float ah = 1, as = 1, av = 1;
1123
1124       HSV::rgb_to_hsv (r, g, b, h, s, v);
1125
1126 // shift the input color in XY to get the wedge point
1127       float h_shifted, s_shifted;
1128       if(!EQUIV(plugin->config.saturation_start, 0))
1129       {
1130         shift_hs(h_shifted, 
1131                  s_shifted, 
1132                  h, 
1133                  s, 
1134                  sat_x,
1135                  sat_y);
1136       }
1137       else
1138       {
1139         h_shifted = h;
1140         s_shifted = s;
1141       }
1142
1143 /* Get the difference between the shifted input color & the unshifted wedge */
1144       float h_diff = hue_distance(h_shifted, hue_key);
1145       h_diff = ABS(h_diff);
1146
1147 // alpha contribution from hue difference
1148 // outside wedge < tolerance_out < tolerance_in < inside wedge < tolerance_in < tolerance_out < outside wedge
1149       if (tolerance_out > 0)
1150       {
1151 // completely inside the wedge
1152         if (h_diff < tolerance_in)
1153           ah = 0;
1154         else
1155 // between the outer & inner slope
1156           if(h_diff < tolerance_out)
1157             ah = (h_diff - tolerance_in) / (tolerance_out - tolerance_in);
1158         if(ah > 1) ah = 1;
1159       }
1160
1161 // alpha contribution from saturation
1162 // outside wedge < min_s_out < min_s_in < inside wedge
1163       if(s_shifted > min_s_out)
1164       {
1165 // saturation with offset applied
1166 // completely inside the wedge
1167         if(s_shifted > min_s_in)
1168           as = 0;
1169 // inside the gradient
1170         if(s_shifted >= min_s_out)
1171           as = (min_s_in - s_shifted) / (min_s_in - min_s_out);
1172       }
1173
1174 // alpha contribution from brightness range
1175 // brightness range is defined by 4 in/out variables
1176 // outside wedge < min_v_out < min_v_in < inside wedge < max_v_in < max_v_out < outside wedge
1177       if(v > min_v_out)
1178       {
1179         if(v < min_v_in || max_v_in >= 1.0)
1180           av = (min_v_in - v) / (min_v_in - min_v_out);
1181         else if(v <= max_v_in)
1182           av = 0;
1183         else if(v <= max_v_out)
1184           av = (v - max_v_in) / (max_v_out - max_v_in);
1185       }
1186
1187 // combine the alpha contribution of every component into a single alpha
1188       a = MAX(as, ah);
1189       a = MAX(a, av);
1190
1191 // Spill light processing
1192       if(spill_tolerance > 0)
1193       {
1194 // get the difference between the shifted input color to the unshifted spill wedge
1195         if(!EQUIV(spill_distance, 0))
1196         {
1197           shift_hs(h_shifted, 
1198                    s_shifted, 
1199                    h, 
1200                    s, 
1201                    spill_x,
1202                    spill_y);
1203         }
1204         else
1205         {
1206           h_shifted = h;
1207           s_shifted = s;
1208         }
1209
1210 // Difference between the shifted hue & the unshifted hue key
1211         h_diff = hue_distance(h_shifted, hue_key);
1212
1213 // inside the wedge
1214         if(ABS(h_diff) < spill_tolerance)
1215         {
1216           if(!desaturate_only)
1217           {
1218 // the shifted input color in the unshifted wedge
1219 // gives 2 unshifted border colors & the weighting
1220             float blend = 0.5 + h_diff / spill_tolerance / 2;
1221 // shift the 2 border colors to the output wedge
1222             float min_h_shifted;
1223             float min_s_shifted;
1224             shift_hs(min_h_shifted, 
1225                      min_s_shifted, 
1226                      min_h, 
1227                      s_shifted, 
1228                      -spill_x,
1229                      -spill_y);
1230             float max_h_shifted;
1231             float max_s_shifted;
1232             shift_hs(max_h_shifted, 
1233                      max_s_shifted, 
1234                      max_h, 
1235                      s_shifted, 
1236                      -spill_x,
1237                      -spill_y);
1238
1239 // blend the shifted border colors using the unshifted weighting
1240 // the only thing which doesn't restore the key color & doesn't make an edge is
1241 // fading the saturation to 0 in the middle
1242             if(blend > 0.5)
1243             {
1244               h = max_h_shifted;
1245               s = max_s_shifted;
1246     //                        s = max_s_shifted * (blend - 0.5) / 0.5;
1247     // DEBUG
1248     //h = 0;
1249     //s = blend; 
1250             }
1251             else
1252             {
1253               h = min_h_shifted;
1254               s = min_s_shifted;
1255     //                        s = min_s_shifted * (0.5 - blend) / 0.5;
1256     // DEBUG
1257     //h = 0;
1258     //s = blend; 
1259             }
1260           } 
1261           else // !desaturate_only
1262           {
1263
1264 // fade the saturation to 0 in the middle
1265             s *= ABS(h_diff) / spill_tolerance;
1266           }
1267
1268           if(h < 0) h += 360;
1269           HSV::hsv_to_rgb (r, g, b, h, s, v);
1270
1271 // store new color
1272           if (!use_yuv)
1273           {
1274             row[0] = (component_type) ((float) r * max);
1275             row[1] = (component_type) ((float) g * max);
1276             row[2] = (component_type) ((float) b * max);
1277           }
1278           else
1279             YUV::yuv.rgb_to_yuv_f(r, g, b, row[0], row[1], row[2]);
1280         }
1281       }
1282
1283       a += alpha_offset;
1284       CLAMP (a, 0.0, 1.0);
1285
1286       if (plugin->config.show_mask)
1287       {
1288         if (use_yuv)
1289         {
1290           row[0] = (component_type) ((float) a * max);
1291           row[1] = (component_type) ((float) max / 2);
1292           row[2] = (component_type) ((float) max / 2);
1293         }
1294         else
1295         {
1296           row[0] = (component_type) ((float) a * max);
1297           row[1] = (component_type) ((float) a * max);
1298           row[2] = (component_type) ((float) a * max);
1299         }
1300       }
1301
1302       /* Multiply alpha and put back in frame */
1303       if (components == 4)
1304       {
1305         row[3] = MIN ((component_type) (a * max), row[3]);
1306       }
1307       else if (use_yuv)
1308       {
1309         row[0] = (component_type) ((float) a * row[0]);
1310         row[1] = (component_type) ((float) a * (row[1] - (max / 2 + 1)) +
1311                                    max / 2 + 1);
1312         row[2] = (component_type) ((float) a * (row[2] - (max / 2 + 1)) +
1313                                    max / 2 + 1);
1314       }
1315       else
1316       {
1317         row[0] = (component_type) ((float) a * row[0]);
1318         row[1] = (component_type) ((float) a * row[1]);
1319         row[2] = (component_type) ((float) a * row[2]);
1320       }
1321
1322       row += components;
1323     }
1324   }
1325 }
1326
1327
1328
1329 void ChromaKeyUnit::process_package(LoadPackage *package)
1330 {
1331   ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
1332
1333   switch(plugin->input->get_color_model())
1334   {
1335   case BC_RGB_FLOAT:
1336     process_chromakey<float> (3, 1.0, 0, pkg);
1337     break;
1338   case BC_RGBA_FLOAT:
1339     process_chromakey<float> ( 4, 1.0, 0, pkg);
1340     break;
1341   case BC_RGB888:
1342     process_chromakey<unsigned char> ( 3, 0xff, 0, pkg);
1343     break;
1344   case BC_RGBA8888:
1345     process_chromakey<unsigned char> ( 4, 0xff, 0, pkg);
1346     break;
1347   case BC_YUV888:
1348     process_chromakey<unsigned char> ( 3, 0xff, 1, pkg);
1349     break;
1350   case BC_YUVA8888:
1351     process_chromakey<unsigned char> ( 4, 0xff, 1, pkg);
1352     break;
1353   case BC_YUV161616:
1354     process_chromakey<uint16_t> (3, 0xffff, 1, pkg);
1355     break;
1356   case BC_YUVA16161616:
1357     process_chromakey<uint16_t> (4, 0xffff, 1, pkg);
1358     break;
1359   }
1360 }
1361
1362
1363
1364 REGISTER_PLUGIN(ChromaKeyAvid)
1365
1366
1367
1368 ChromaKeyAvid::ChromaKeyAvid(PluginServer *server)
1369   : PluginVClient(server)
1370 {
1371   engine = 0;
1372 }
1373
1374 ChromaKeyAvid::~ChromaKeyAvid()
1375 {
1376   if(engine) delete engine;
1377 }
1378
1379
1380 int ChromaKeyAvid::process_buffer(VFrame *frame,
1381                                   int64_t start_position,
1382                                   double frame_rate)
1383 {
1384   load_configuration();
1385   this->input = frame;
1386   this->output = frame;
1387
1388   read_frame(frame,
1389              0,
1390              start_position,
1391              frame_rate,
1392              get_use_opengl());
1393   if(get_use_opengl()) return run_opengl();
1394
1395   if(!engine) engine = new ChromaKeyServer(this);
1396   engine->process_packages();
1397
1398   return 0;
1399 }
1400
1401 const char* ChromaKeyAvid::plugin_title() { return N_("Chroma key (Avid)"); }
1402 int ChromaKeyAvid::is_realtime() { return 1; }
1403
1404
1405 LOAD_CONFIGURATION_MACRO(ChromaKeyAvid, ChromaKeyConfig)
1406
1407
1408 void ChromaKeyAvid::save_data(KeyFrame * keyframe)
1409 {
1410   FileXML output;
1411   output.set_shared_output(keyframe->xbuf);
1412   output.tag.set_title("CHROMAKEY_AVID");
1413   output.tag.set_property("RED", config.red);
1414   output.tag.set_property("GREEN", config.green);
1415   output.tag.set_property("BLUE", config.blue);
1416   output.tag.set_property("MIN_BRIGHTNESS", config.min_brightness);
1417   output.tag.set_property("MAX_BRIGHTNESS", config.max_brightness);
1418   output.tag.set_property("SATURATION_START", config.saturation_start);
1419   output.tag.set_property("SATURATION_LINE", config.saturation_line);
1420   output.tag.set_property("TOLERANCE", config.tolerance);
1421   output.tag.set_property("IN_SLOPE", config.in_slope);
1422   output.tag.set_property("OUT_SLOPE", config.out_slope);
1423   output.tag.set_property("ALPHA_OFFSET", config.alpha_offset);
1424   output.tag.set_property("SPILL_SATURATION", config.spill_saturation);
1425   output.tag.set_property("SPILL_ANGLE", config.spill_angle);
1426   output.tag.set_property("DESATURATE_ONLY", config.desaturate_only);
1427   output.tag.set_property("SHOW_MASK", config.show_mask);
1428   output.append_tag();
1429   output.tag.set_title("/CHROMAKEY_AVID");
1430   output.append_tag();
1431   output.append_newline();
1432   output.terminate_string();
1433 }
1434
1435 void ChromaKeyAvid::read_data(KeyFrame * keyframe)
1436 {
1437   FileXML input;
1438
1439   input.set_shared_input(keyframe->xbuf);
1440
1441   while( !input.read_tag() )
1442   {
1443     if( input.tag.title_is("CHROMAKEY_AVID") )
1444     {
1445       config.red   = input.tag.get_property("RED",   config.red);
1446       config.green = input.tag.get_property("GREEN", config.green);
1447       config.blue  = input.tag.get_property("BLUE",  config.blue);
1448       config.min_brightness =
1449         input.tag.get_property("MIN_BRIGHTNESS", config.min_brightness);
1450       config.max_brightness =
1451         input.tag.get_property("MAX_BRIGHTNESS", config.max_brightness);
1452       config.saturation_start =
1453         input.tag.get_property("SATURATION_START", config.saturation_start);
1454       config.saturation_line =
1455         input.tag.get_property("SATURATION_LINE", config.saturation_line);
1456       config.tolerance =
1457         input.tag.get_property("TOLERANCE", config.tolerance);
1458       config.in_slope =
1459         input.tag.get_property("IN_SLOPE", config.in_slope);
1460       config.out_slope =
1461         input.tag.get_property("OUT_SLOPE", config.out_slope);
1462       config.alpha_offset =
1463         input.tag.get_property("ALPHA_OFFSET", config.alpha_offset);
1464       config.spill_saturation =
1465         input.tag.get_property("SPILL_SATURATION", config.spill_saturation);
1466       config.spill_angle =
1467         input.tag.get_property("SPILL_ANGLE", config.spill_angle);
1468       config.desaturate_only =
1469         input.tag.get_property("DESATURATE_ONLY", config.desaturate_only);
1470       config.show_mask =
1471         input.tag.get_property("SHOW_MASK", config.show_mask);
1472
1473       config.undo_red              = config.red;
1474       config.undo_green            = config.green;
1475       config.undo_blue             = config.blue;
1476       config.undo_min_brightness   = config.min_brightness;
1477       config.undo_max_brightness   = config.max_brightness;
1478       config.undo_tolerance        = config.tolerance;
1479       config.undo_saturation_start = config.saturation_start;
1480       config.undo_saturation_line  = config.saturation_line;
1481       config.undo_in_slope         = config.in_slope;
1482       config.undo_out_slope        = config.out_slope;
1483       config.undo_alpha_offset     = config.alpha_offset;
1484       config.undo_spill_saturation = config.spill_saturation;
1485       config.undo_spill_angle      = config.spill_angle;
1486       config.undo_desaturate_only  = config.desaturate_only;
1487       config.undo_show_mask        = config.show_mask;
1488     }
1489   }
1490 }
1491
1492
1493 NEW_WINDOW_MACRO(ChromaKeyAvid, ChromaKeyWindow)
1494
1495
1496 void ChromaKeyAvid::update_gui()
1497 {
1498   if( thread )
1499   {
1500     load_configuration();
1501     ChromaKeyWindow *window = (ChromaKeyWindow*)thread->window;
1502     window->lock_window();
1503     window->update_gui(RESET_ALL);
1504     window->unlock_window();
1505   }
1506 }
1507
1508 void ChromaKeyWindow::update_gui(int clear)
1509 {
1510   ChromaKeyConfig &config = plugin->config;
1511   switch(clear)
1512   {
1513   case RESET_RGB:
1514     update_sample();
1515     break;
1516   case RESET_MIN_BRIGHTNESS:
1517     min_brightness->update(config.min_brightness);
1518     min_brightness_text->update(config.min_brightness);
1519     break;
1520   case RESET_MAX_BRIGHTNESS:
1521     max_brightness->update(config.max_brightness);
1522     max_brightness_text->update(config.max_brightness);
1523     break;
1524   case RESET_TOLERANCE:
1525     tolerance->update(config.tolerance);
1526     tolerance_text->update(config.tolerance);
1527     break;
1528   case RESET_SATURATION_START:
1529     saturation_start->update(config.saturation_start);
1530     saturation_start_text->update(config.saturation_start);
1531     break;
1532   case RESET_SATURATION_LINE:
1533     saturation_line->update(config.saturation_line);
1534     saturation_line_text->update(config.saturation_line);
1535     break;
1536   case RESET_IN_SLOPE:
1537     in_slope->update(config.in_slope);
1538     in_slope_text->update(config.in_slope);
1539     break;
1540   case RESET_OUT_SLOPE:
1541     out_slope->update(config.out_slope);
1542     out_slope_text->update(config.out_slope);
1543     break;
1544   case RESET_ALPHA_OFFSET:
1545     alpha_offset->update(config.alpha_offset);
1546     alpha_offset_text->update(config.alpha_offset);
1547     break;
1548   case RESET_SPILL_SATURATION:
1549     spill_saturation->update(config.spill_saturation);
1550     spill_saturation_text->update(config.spill_saturation);
1551     break;
1552   case RESET_SPILL_ANGLE:
1553     spill_angle->update(config.spill_angle);
1554     spill_angle_text->update(config.spill_angle);
1555     break;
1556   case RESET_ALL:
1557   case RESET_DEFAULT_SETTINGS:
1558   default:
1559     min_brightness->update(config.min_brightness);
1560     min_brightness_text->update(config.min_brightness);
1561     max_brightness->update(config.max_brightness);
1562     max_brightness_text->update(config.max_brightness);
1563     saturation_start->update(config.saturation_start);
1564     saturation_start_text->update(config.saturation_start);
1565     saturation_line->update(config.saturation_line);
1566     saturation_line_text->update(config.saturation_line);
1567     tolerance->update(config.tolerance);
1568     tolerance_text->update(config.tolerance);
1569     in_slope->update(config.in_slope);
1570     in_slope_text->update(config.in_slope);
1571     out_slope->update(config.out_slope);
1572     out_slope_text->update(config.out_slope);
1573     alpha_offset->update(config.alpha_offset);
1574     alpha_offset_text->update(config.alpha_offset);
1575     spill_saturation->update(config.spill_saturation);
1576     spill_saturation_text->update(config.spill_saturation);
1577     spill_angle->update(config.spill_angle);
1578     spill_angle_text->update(config.spill_angle);
1579     desaturate_only->update(config.desaturate_only);
1580     show_mask->update(config.show_mask);
1581     update_sample();
1582     break;
1583   }
1584 }
1585
1586
1587
1588 int ChromaKeyAvid::handle_opengl()
1589 {
1590 #ifdef HAVE_GL
1591 // For macro
1592         ChromaKeyAvid *plugin = this;
1593         COMMON_VARIABLES
1594
1595         static const char *yuv_shader =
1596                 "const vec3 black = vec3(0.0, 0.5, 0.5);\n"
1597                 "uniform mat3 yuv_to_rgb_matrix;\n"
1598                 "uniform mat3 rgb_to_yuv_matrix;\n"
1599                 "uniform float yminf;\n"
1600                 "\n"
1601                 "vec4 yuv_to_rgb(vec4 color)\n"
1602                 "{\n"
1603                 "       color.rgb -= vec3(yminf, 0.5, 0.5);\n"
1604                 "       color.rgb = yuv_to_rgb_matrix * color.rgb;\n"
1605                 "       return color;\n"
1606                 "}\n"
1607                 "\n"
1608                 "vec4 rgb_to_yuv(vec4 color)\n"
1609                 "{\n"
1610                 "       color.rgb = rgb_to_yuv_matrix * color.rgb;\n"
1611                 "       color.rgb += vec3(yminf, 0.5, 0.5);\n"
1612                 "       return color;\n"
1613                 "}\n";
1614
1615         static const char *rgb_shader =
1616                 "const vec3 black = vec3(0.0, 0.0, 0.0);\n"
1617                 "\n"
1618                 "vec4 yuv_to_rgb(vec4 color)\n"
1619                 "{\n"
1620                 "       return color;\n"
1621                 "}\n"
1622                 "vec4 rgb_to_yuv(vec4 color)\n"
1623                 "{\n"
1624                 "       return color;\n"
1625                 "}\n";
1626
1627         static const char *hsv_shader =
1628                 "vec4 rgb_to_hsv(vec4 color)\n"
1629                 "{\n"
1630                         RGB_TO_HSV_FRAG("color")
1631                 "       return color;\n"
1632                 "}\n"
1633                 "\n"
1634                 "vec4 hsv_to_rgb(vec4 color)\n"
1635                 "{\n"
1636                         HSV_TO_RGB_FRAG("color")
1637                 "       return color;\n"
1638                 "}\n"
1639                 "\n";
1640
1641         static const char *show_rgbmask_shader =
1642                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1643                 "{\n"
1644                 "       return vec4(1.0, 1.0, 1.0, min(color.a, color2.a));"
1645                 "}\n";
1646
1647         static const char *show_yuvmask_shader =
1648                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1649                 "{\n"
1650                 "       return vec4(1.0, 0.5, 0.5, min(color.a, color2.a));"
1651                 "}\n";
1652
1653         static const char *nomask_shader =
1654                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1655                 "{\n"
1656                 "       return vec4(color.rgb, min(color.a, color2.a));"
1657                 "}\n";
1658
1659         get_output()->to_texture();
1660         get_output()->enable_opengl();
1661         get_output()->init_screen();
1662
1663         const char *shader_stack[16];
1664         memset(shader_stack,0, sizeof(shader_stack));
1665         int current_shader = 0;
1666
1667         shader_stack[current_shader++] = \
1668                  !BC_CModels::is_yuv(get_output()->get_color_model()) ?
1669                         rgb_shader : yuv_shader;
1670         shader_stack[current_shader++] = hsv_shader;
1671         shader_stack[current_shader++] = !config.show_mask ? nomask_shader :
1672                  !BC_CModels::is_yuv(get_output()->get_color_model()) ?
1673                         show_rgbmask_shader : show_yuvmask_shader ;
1674         extern unsigned char _binary_chromakeyavid_sl_start[];
1675         static const char *shader_frag = (char*)_binary_chromakeyavid_sl_start;
1676         shader_stack[current_shader++] = shader_frag;
1677
1678         shader_stack[current_shader] = 0;
1679         unsigned int shader = VFrame::make_shader(shader_stack);
1680         if( shader > 0 ) {
1681                 glUseProgram(shader);
1682                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
1683                 glUniform1f(glGetUniformLocation(shader, "red"), red);
1684                 glUniform1f(glGetUniformLocation(shader, "green"), green);
1685                 glUniform1f(glGetUniformLocation(shader, "blue"), blue);
1686                 glUniform1f(glGetUniformLocation(shader, "in_slope"), in_slope);
1687                 glUniform1f(glGetUniformLocation(shader, "out_slope"), out_slope);
1688                 glUniform1f(glGetUniformLocation(shader, "tolerance"), tolerance);
1689                 glUniform1f(glGetUniformLocation(shader, "tolerance_in"), tolerance_in);
1690                 glUniform1f(glGetUniformLocation(shader, "tolerance_out"), tolerance_out);
1691                 glUniform1f(glGetUniformLocation(shader, "sat_x"), sat_x);
1692                 glUniform1f(glGetUniformLocation(shader, "sat_y"), sat_y);
1693                 glUniform1f(glGetUniformLocation(shader, "min_s"), min_s);
1694                 glUniform1f(glGetUniformLocation(shader, "min_s_in"), min_s_in);
1695                 glUniform1f(glGetUniformLocation(shader, "min_s_out"), min_s_out);
1696                 glUniform1f(glGetUniformLocation(shader, "min_v"), min_v);
1697                 glUniform1f(glGetUniformLocation(shader, "min_v_in"), min_v_in);
1698                 glUniform1f(glGetUniformLocation(shader, "min_v_out"), min_v_out);
1699                 glUniform1f(glGetUniformLocation(shader, "max_v"), max_v);
1700                 glUniform1f(glGetUniformLocation(shader, "max_v_in"), max_v_in);
1701                 glUniform1f(glGetUniformLocation(shader, "max_v_out"), max_v_out);
1702                 glUniform1f(glGetUniformLocation(shader, "spill_distance"), spill_distance);
1703                 glUniform1f(glGetUniformLocation(shader, "spill_x"), spill_x);
1704                 glUniform1f(glGetUniformLocation(shader, "spill_y"), spill_y);
1705                 glUniform1f(glGetUniformLocation(shader, "spill_tolerance"), spill_tolerance);
1706                 glUniform1f(glGetUniformLocation(shader, "min_h"), min_h);
1707                 glUniform1f(glGetUniformLocation(shader, "max_h"), max_h);
1708                 glUniform1i(glGetUniformLocation(shader, "desaturate_only"), desaturate_only);
1709                 glUniform1f(glGetUniformLocation(shader, "alpha_offset"), alpha_offset);
1710                 glUniform1f(glGetUniformLocation(shader, "hue_key"), hue_key);
1711                 glUniform1f(glGetUniformLocation(shader, "saturation_key"), saturation_key);
1712                 glUniform1f(glGetUniformLocation(shader, "value_key"), value_key);
1713                 if( BC_CModels::is_yuv(get_output()->get_color_model()) )
1714                         BC_GL_COLORS(shader);
1715         }
1716
1717         get_output()->bind_texture(0);
1718         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1719         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1720
1721         if(BC_CModels::components(get_output()->get_color_model()) == 3)
1722         {
1723                 glEnable(GL_BLEND);
1724                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1725                 get_output()->clear_pbuffer();
1726         }
1727         get_output()->draw_texture();
1728
1729         glUseProgram(0);
1730         get_output()->set_opengl_state(VFrame::SCREEN);
1731         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1732         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1733         glDisable(GL_BLEND);
1734
1735
1736 #endif
1737         return 0;
1738 }