826f817f6d0de267afad55418b796331e156dfa3
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / gradient / gradient.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include <math.h>
23 #include <stdint.h>
24 #include <string.h>
25
26 #include "bccolors.h"
27 #include "bcdisplayinfo.h"
28 #include "clip.h"
29 #include "bchash.h"
30 #include "filexml.h"
31 #include "gradient.h"
32 #include "keyframe.h"
33 #include "language.h"
34 #include "overlayframe.h"
35 #include "theme.h"
36 #include "vframe.h"
37
38 REGISTER_PLUGIN(GradientMain)
39
40 GradientConfig::GradientConfig()
41 {
42         reset();
43 }
44
45 void GradientConfig::reset()
46 {
47         angle = 0;
48         in_radius = 0;
49         out_radius = 100;
50         in_r  = in_g  = in_b  = in_a  = 0xff;
51         out_r = out_g = out_b = out_a = 0x00;
52         shape = GradientConfig::LINEAR;
53         rate = GradientConfig::LINEAR;
54         center_x = 50;
55         center_y = 50;
56 }
57
58 int GradientConfig::equivalent(GradientConfig &that)
59 {
60         return (EQUIV(angle, that.angle) &&
61                 EQUIV(in_radius, that.in_radius) &&
62                 EQUIV(out_radius, that.out_radius) &&
63                 in_r == that.in_r &&
64                 in_g == that.in_g &&
65                 in_b == that.in_b &&
66                 in_a == that.in_a &&
67                 out_r == that.out_r &&
68                 out_g == that.out_g &&
69                 out_b == that.out_b &&
70                 out_a == that.out_a &&
71                 shape == that.shape &&
72                 rate == that.rate &&
73                 EQUIV(center_x, that.center_x) &&
74                 EQUIV(center_y, that.center_y));
75 }
76
77 void GradientConfig::copy_from(GradientConfig &that)
78 {
79         angle = that.angle;
80         in_radius = that.in_radius;
81         out_radius = that.out_radius;
82         in_r = that.in_r;
83         in_g = that.in_g;
84         in_b = that.in_b;
85         in_a = that.in_a;
86         out_r = that.out_r;
87         out_g = that.out_g;
88         out_b = that.out_b;
89         out_a = that.out_a;
90         shape = that.shape;
91         rate = that.rate;
92         center_x = that.center_x;
93         center_y = that.center_y;
94 }
95
96 void GradientConfig::interpolate(GradientConfig &prev, GradientConfig &next,
97                 long prev_frame, long next_frame, long current_frame)
98 {
99         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
100         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
101
102         this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale);
103         this->in_radius = (int)(prev.in_radius * prev_scale + next.in_radius * next_scale);
104         this->out_radius = (int)(prev.out_radius * prev_scale + next.out_radius * next_scale);
105         in_r = (int)(prev.in_r * prev_scale + next.in_r * next_scale);
106         in_g = (int)(prev.in_g * prev_scale + next.in_g * next_scale);
107         in_b = (int)(prev.in_b * prev_scale + next.in_b * next_scale);
108         in_a = (int)(prev.in_a * prev_scale + next.in_a * next_scale);
109         out_r = (int)(prev.out_r * prev_scale + next.out_r * next_scale);
110         out_g = (int)(prev.out_g * prev_scale + next.out_g * next_scale);
111         out_b = (int)(prev.out_b * prev_scale + next.out_b * next_scale);
112         out_a = (int)(prev.out_a * prev_scale + next.out_a * next_scale);
113         shape = prev.shape;
114         rate = prev.rate;
115         center_x = prev.center_x * prev_scale + next.center_x * next_scale;
116         center_y = prev.center_y * prev_scale + next.center_y * next_scale;
117 }
118
119 int GradientConfig::get_in_color()
120 {
121         int result = (in_r << 16) | (in_g << 8) | (in_b);
122         return result;
123 }
124
125 int GradientConfig::get_out_color()
126 {
127         int result = (out_r << 16) | (out_g << 8) | (out_b);
128         return result;
129 }
130
131 #define COLOR_W 100
132 #define COLOR_H 30
133
134 GradientWindow::GradientWindow(GradientMain *plugin)
135  : PluginClientWindow(plugin, 350, 290, 350, 290, 0)
136 {
137         this->plugin = plugin;
138         angle = 0;
139         angle_title = 0;
140         center_x = 0;
141         center_y = 0;
142         center_x_title = 0;
143         center_y_title = 0;
144         in_color_thread = 0;
145         out_color_thread = 0;
146 }
147 GradientWindow::~GradientWindow()
148 {
149         delete in_color_thread;
150         delete out_color_thread;
151 }
152
153
154 void GradientWindow::create_objects()
155 {
156         int margin = plugin->get_theme()->widget_border;
157         int x = 10, y = 10;
158         BC_Title *title;
159
160         add_subwindow(title = new BC_Title(x, y, _("Shape:")));
161         add_subwindow(shape = new GradientShape(plugin, this,
162                         x + title->get_w() + margin, y));
163         shape->create_objects();
164         y += shape->get_h() + margin;
165         shape_x = x;
166         shape_y = y;
167         y += BC_Pot::calculate_h() + margin;
168
169         add_subwindow(title = new BC_Title(x, y, _("Rate:")));
170         add_subwindow(rate = new GradientRate(plugin,
171                         x + title->get_w() + margin, y));
172         rate->create_objects();
173         y += rate->get_h() + margin;
174
175         int x1 = x, y1 = y;
176         BC_Title *title1;
177         add_subwindow(title1 = new BC_Title(x, y, _("Inner radius:")));
178         y += BC_Slider::get_span(0) + margin;
179         BC_Title *title2;
180         add_subwindow(title2 = new BC_Title(x, y, _("Outer radius:")));
181
182         add_subwindow(reset = new GradientReset(plugin, this, x, y+100));
183
184         y = y1;
185         x += MAX(title1->get_w(), title2->get_w()) + margin;
186         add_subwindow(in_radius = new GradientInRadius(plugin, x, y));
187         y += in_radius->get_h() + margin;
188         add_subwindow(out_radius = new GradientOutRadius(plugin, x, y));
189         y += out_radius->get_h() + margin;
190
191         x = x1;
192         y1 = y;
193         add_subwindow(in_color = new GradientInColorButton(plugin, this, x, y));
194         y += COLOR_H + margin;
195
196         add_subwindow(out_color = new GradientOutColorButton(plugin, this, x, y));
197         x += MAX(in_color->get_w(), out_color->get_w()) + margin;
198         y = y1;
199
200         in_color_x = x;
201         in_color_y = y;
202         y += COLOR_H + margin;
203         out_color_x = x;
204         out_color_y = y;
205         in_color_thread = new GradientInColorThread(plugin, this);
206         out_color_thread = new GradientOutColorThread(plugin, this);
207         update_in_color();
208         update_out_color();
209         update_shape();
210
211         draw_3d_border(in_color_x - 2, in_color_y - 2,
212                 COLOR_W + 4, COLOR_H + 4, 1);
213
214         draw_3d_border(out_color_x - 2, out_color_y - 2,
215                 COLOR_W + 4, COLOR_H + 4, 1);
216         show_window();
217 }
218
219 void GradientWindow::update_shape()
220 {
221         int x = shape_x, y = shape_y;
222
223         if( plugin->config.shape == GradientConfig::LINEAR ) {
224                 delete center_x_title;  center_x_title = 0;
225                 delete center_y_title;  center_y_title = 0;
226                 delete center_x;  center_x = 0;
227                 delete center_y;  center_y = 0;
228                 if( !angle ) {
229                         add_subwindow(angle_title = new BC_Title(x, y, _("Angle:")));
230                         add_subwindow(angle = new GradientAngle(plugin, x + angle_title->get_w() + 10, y));
231                 }
232         }
233         else {
234                 delete angle_title;  angle_title = 0;
235                 delete angle;  angle = 0;
236                 if( !center_x ) {
237                         add_subwindow(center_x_title = new BC_Title(x, y, _("Center X:")));
238                         add_subwindow(center_x = new GradientCenterX(plugin,
239                                 x + center_x_title->get_w() + 10, y));
240                         x += center_x_title->get_w() + 10 + center_x->get_w() + 10;
241                         add_subwindow(center_y_title = new BC_Title(x, y, _("Center Y:")));
242                         add_subwindow(center_y = new GradientCenterY(plugin,
243                                 x + center_y_title->get_w() + 10, y));
244                 }
245         }
246         show_window();
247 }
248
249 void GradientWindow::update_in_color()
250 {
251 //printf("GradientWindow::update_in_color 1 %08x\n", plugin->config.get_in_color());
252         set_color(plugin->config.get_in_color());
253         draw_box(in_color_x, in_color_y, COLOR_W, COLOR_H);
254         flash(in_color_x, in_color_y, COLOR_W, COLOR_H);
255 }
256
257 void GradientWindow::update_out_color()
258 {
259 //printf("GradientWindow::update_out_color 1 %08x\n", plugin->config.get_in_color());
260         set_color(plugin->config.get_out_color());
261         draw_box(out_color_x, out_color_y, COLOR_W, COLOR_H);
262         flash(out_color_x, out_color_y, COLOR_W, COLOR_H);
263 }
264
265 void GradientWindow::done_event(int result)
266 {
267         in_color_thread->close_window();
268         out_color_thread->close_window();
269 }
270
271 GradientShape::GradientShape(GradientMain *plugin, GradientWindow *gui, int x, int y)
272  : BC_PopupMenu(x, y, 100, to_text(plugin->config.shape), 1)
273 {
274         this->plugin = plugin;
275         this->gui = gui;
276 }
277 void GradientShape::create_objects()
278 {
279         add_item(new BC_MenuItem(to_text(GradientConfig::LINEAR)));
280         add_item(new BC_MenuItem(to_text(GradientConfig::RADIAL)));
281 }
282 char* GradientShape::to_text(int shape)
283 {
284         switch( shape ) {
285         case GradientConfig::LINEAR:    return _("Linear");
286         }
287         return _("Radial");
288 }
289 int GradientShape::from_text(char *text)
290 {
291         if( !strcmp(text, to_text(GradientConfig::LINEAR)) )
292                 return GradientConfig::LINEAR;
293         return GradientConfig::RADIAL;
294 }
295 int GradientShape::handle_event()
296 {
297         plugin->config.shape = from_text(get_text());
298         gui->update_shape();
299         plugin->send_configure_change();
300         return 1;
301 }
302
303
304 GradientCenterX::GradientCenterX(GradientMain *plugin, int x, int y)
305  : BC_FPot(x, y, plugin->config.center_x, 0, 100)
306 {
307         this->plugin = plugin;
308 }
309 int GradientCenterX::handle_event()
310 {
311         plugin->config.center_x = get_value();
312         plugin->send_configure_change();
313         return 1;
314 }
315
316
317 GradientCenterY::GradientCenterY(GradientMain *plugin, int x, int y)
318  : BC_FPot(x, y, plugin->config.center_y, 0, 100)
319 {
320         this->plugin = plugin;
321 }
322
323 int GradientCenterY::handle_event()
324 {
325         plugin->config.center_y = get_value();
326         plugin->send_configure_change();
327         return 1;
328 }
329
330
331 GradientAngle::GradientAngle(GradientMain *plugin, int x, int y)
332  : BC_FPot(x, y, plugin->config.angle, -180, 180)
333 {
334         this->plugin = plugin;
335 }
336
337 int GradientAngle::handle_event()
338 {
339         plugin->config.angle = get_value();
340         plugin->send_configure_change();
341         return 1;
342 }
343
344
345 GradientRate::GradientRate(GradientMain *plugin, int x, int y)
346  : BC_PopupMenu(x, y, 100, to_text(plugin->config.rate), 1)
347 {
348         this->plugin = plugin;
349 }
350 void GradientRate::create_objects()
351 {
352         add_item(new BC_MenuItem(to_text(GradientConfig::LINEAR)));
353         add_item(new BC_MenuItem(to_text(GradientConfig::LOG)));
354         add_item(new BC_MenuItem(to_text(GradientConfig::SQUARE)));
355 }
356 char* GradientRate::to_text(int shape)
357 {
358         switch( shape ) {
359         case GradientConfig::LINEAR:    return _("Linear");
360         case GradientConfig::LOG:       return _("Log");
361         }
362         return _("Square");
363 }
364 int GradientRate::from_text(char *text)
365 {
366         if( !strcmp(text, to_text(GradientConfig::LINEAR)) )
367                 return GradientConfig::LINEAR;
368         if( !strcmp(text, to_text(GradientConfig::LOG)) )
369                 return GradientConfig::LOG;
370         return GradientConfig::SQUARE;
371 }
372 int GradientRate::handle_event()
373 {
374         plugin->config.rate = from_text(get_text());
375         plugin->send_configure_change();
376         return 1;
377 }
378
379
380 GradientInRadius::GradientInRadius(GradientMain *plugin, int x, int y)
381  : BC_FSlider(x, y, 0, 200, 200,
382                 0.f, 100.f, (float)plugin->config.in_radius)
383 {
384         this->plugin = plugin;
385 }
386
387 int GradientInRadius::handle_event()
388 {
389         plugin->config.in_radius = get_value();
390         plugin->send_configure_change();
391         return 1;
392 }
393
394
395 GradientOutRadius::GradientOutRadius(GradientMain *plugin, int x, int y)
396  : BC_FSlider(x, y, 0, 200, 200,
397                 0.f, 100.f, (float)plugin->config.out_radius)
398 {
399         this->plugin = plugin;
400 }
401
402 int GradientOutRadius::handle_event()
403 {
404         plugin->config.out_radius = get_value();
405         plugin->send_configure_change();
406         return 1;
407 }
408
409 GradientInColorButton::GradientInColorButton(GradientMain *plugin, GradientWindow *window, int x, int y)
410  : BC_GenericButton(x, y, _("Inner color:"))
411 {
412         this->plugin = plugin;
413         this->window = window;
414 }
415
416 int GradientInColorButton::handle_event()
417 {
418         window->in_color_thread->start_window(
419                 plugin->config.get_in_color(),
420                 plugin->config.in_a);
421         return 1;
422 }
423
424
425 GradientOutColorButton::GradientOutColorButton(GradientMain *plugin, GradientWindow *window, int x, int y)
426  : BC_GenericButton(x, y, _("Outer color:"))
427 {
428         this->plugin = plugin;
429         this->window = window;
430 }
431
432 int GradientOutColorButton::handle_event()
433 {
434         window->out_color_thread->start_window(
435                 plugin->config.get_out_color(),
436                 plugin->config.out_a);
437         return 1;
438 }
439
440 GradientReset::GradientReset(GradientMain *plugin, GradientWindow *window, int x, int y)
441  : BC_GenericButton(x, y, _("Reset"))
442 {
443         this->plugin = plugin;
444         this->window = window;
445 }
446
447 int GradientReset::handle_event()
448 {
449         plugin->config.reset();
450         window->update_gui();
451         plugin->send_configure_change();
452         return 1;
453 }
454
455 GradientInColorThread::GradientInColorThread(GradientMain *plugin,
456         GradientWindow *window)
457  : ColorPicker(1, _("Inner color"))
458 {
459         this->plugin = plugin;
460         this->window = window;
461 }
462
463 int GradientInColorThread::handle_new_color(int output, int alpha)
464 {
465         plugin->config.in_r = (output & 0xff0000) >> 16;
466         plugin->config.in_g = (output & 0xff00) >> 8;
467         plugin->config.in_b = (output & 0xff);
468         plugin->config.in_a = alpha;
469
470         window->lock_window("GradientInColorThread::handle_new_color");
471         window->update_in_color();
472         window->flush();
473         window->unlock_window();
474         plugin->send_configure_change();
475 //printf("GradientInColorThread::handle_event 1 %d %d %d %d %d %d %d %d\n",
476 // plugin->config.in_r, plugin->config.in_g, plugin->config.in_b, plugin->config.in_a,
477 // plugin->config.out_r, plugin->config.out_g, plugin->config.out_b, plugin->config.out_a);
478         return 1;
479 }
480
481
482 GradientOutColorThread::GradientOutColorThread(GradientMain *plugin,
483         GradientWindow *window)
484  : ColorPicker(1, _("Outer color"))
485 {
486         this->plugin = plugin;
487         this->window = window;
488 }
489
490 int GradientOutColorThread::handle_new_color(int output, int alpha)
491 {
492         plugin->config.out_r = (output & 0xff0000) >> 16;
493         plugin->config.out_g = (output & 0xff00) >> 8;
494         plugin->config.out_b = (output & 0xff);
495         plugin->config.out_a = alpha;
496         window->lock_window("GradientOutColorThread::handle_new_color");
497         window->update_out_color();
498         window->flush();
499         window->unlock_window();
500         plugin->send_configure_change();
501 //printf("GradientOutColorThread::handle_event 1 %d %d %d %d %d %d %d %d\n",
502 // plugin->config.in_r, plugin->config.in_g, plugin->config.in_b, plugin->config.in_a,
503 // plugin->config.out_r, plugin->config.out_g, plugin->config.out_b, plugin->config.out_a);
504         return 1;
505 }
506
507
508 GradientMain::GradientMain(PluginServer *server)
509  : PluginVClient(server)
510 {
511
512         need_reconfigure = 1;
513         gradient = 0;
514         engine = 0;
515         overlayer = 0;
516         table = 0;
517         table_size = 0;
518 }
519
520 GradientMain::~GradientMain()
521 {
522         delete [] table;
523         delete gradient;
524         delete engine;
525         delete overlayer;
526 }
527
528 const char* GradientMain::plugin_title() { return N_("Gradient"); }
529 int GradientMain::is_realtime() { return 1; }
530
531
532 NEW_WINDOW_MACRO(GradientMain, GradientWindow)
533
534 LOAD_CONFIGURATION_MACRO(GradientMain, GradientConfig)
535
536 int GradientMain::is_synthesis()
537 {
538         return 1;
539 }
540
541
542 int GradientMain::process_buffer(VFrame *frame,
543         int64_t start_position,
544         double frame_rate)
545 {
546         this->input = frame;
547         this->output = frame;
548         float fw = input->get_w(), fh = input->get_h();
549         gradient_size = hypotf(fw, fh);
550         need_reconfigure = load_configuration();
551
552         int need_alpha = config.in_a != 0xff || config.out_a != 0xff;
553         if( need_alpha )
554                 read_frame(frame, 0, start_position, frame_rate, get_use_opengl());
555         if( get_use_opengl() ) return run_opengl();
556
557         int gradient_cmodel = input->get_color_model();
558         if( need_alpha && BC_CModels::components(gradient_cmodel) == 3 ) {
559                 switch( gradient_cmodel ) {
560                 case BC_RGB888:     gradient_cmodel = BC_RGBA8888;    break;
561                 case BC_YUV888:     gradient_cmodel = BC_YUVA8888;    break;
562                 case BC_RGB_FLOAT:  gradient_cmodel = BC_RGBA_FLOAT;  break;
563                 }
564         }
565
566         int bpp = BC_CModels::calculate_pixelsize(gradient_cmodel);
567         int comps = BC_CModels::components(gradient_cmodel);
568         int grad_size1 = gradient_size + 1;
569         int sz = 4 * (bpp / comps) * grad_size1;
570         if( table_size < sz ) {
571                 delete [] table;  table = 0;
572         }
573         if( !table ) {
574                 table = new uint8_t[table_size = sz];
575                 need_reconfigure = 1;
576         }
577
578         if( gradient && gradient->get_color_model() != gradient_cmodel ) {
579                 delete gradient;
580                 gradient = 0;
581         }
582
583         if( !gradient )
584                 gradient = new VFrame(input->get_w(), input->get_h(),
585                         gradient_cmodel, 0);
586
587         if( !engine )
588                 engine = new GradientServer(this,
589                         get_project_smp() + 1, get_project_smp() + 1);
590         engine->process_packages();
591
592 // Use overlay routine in GradientServer if mismatched colormodels
593         if( gradient->get_color_model() == output->get_color_model() ) {
594                 if( !overlayer ) overlayer = new OverlayFrame(get_project_smp() + 1);
595                 overlayer->overlay(output, gradient,
596                         0, 0, input->get_w(), input->get_h(),
597                         0, 0, input->get_w(), input->get_h(),
598                         1.0, TRANSFER_NORMAL, NEAREST_NEIGHBOR);
599         }
600
601         return 0;
602 }
603
604
605 void GradientMain::update_gui()
606 {
607         if( !thread ) return;
608         if( !load_configuration() ) return;
609         ((GradientWindow*)thread->window)->lock_window("GradientMain::update_gui");
610         GradientWindow *window = (GradientWindow *)thread->window;
611         window->update_gui();
612         window->unlock_window();
613 }
614
615 void GradientWindow::update_gui()
616 {
617         GradientConfig &config = plugin->config;
618         rate->set_text(GradientRate::to_text(config.rate));
619         in_radius->update(config.in_radius);
620         out_radius->update(config.out_radius);
621         shape->set_text(GradientShape::to_text(config.shape));
622         if( angle ) angle->update(config.angle);
623         if( center_x ) center_x->update(config.center_x);
624         if( center_y ) center_y->update(config.center_y);
625         update_in_color();
626         update_out_color();
627         update_shape();
628         unlock_window();
629         in_color_thread->update_gui(config.get_in_color(), config.in_a);
630         out_color_thread->update_gui(config.get_out_color(), config.out_a);
631         lock_window("GradientWindow::update_gui");
632 }
633
634
635 void GradientMain::save_data(KeyFrame *keyframe)
636 {
637         FileXML output;
638
639 // cause data to be stored directly in text
640         output.set_shared_output(keyframe->xbuf);
641         output.tag.set_title("GRADIENT");
642
643         output.tag.set_property("ANGLE", config.angle);
644         output.tag.set_property("IN_RADIUS", config.in_radius);
645         output.tag.set_property("OUT_RADIUS", config.out_radius);
646         output.tag.set_property("IN_R", config.in_r);
647         output.tag.set_property("IN_G", config.in_g);
648         output.tag.set_property("IN_B", config.in_b);
649         output.tag.set_property("IN_A", config.in_a);
650         output.tag.set_property("OUT_R", config.out_r);
651         output.tag.set_property("OUT_G", config.out_g);
652         output.tag.set_property("OUT_B", config.out_b);
653         output.tag.set_property("OUT_A", config.out_a);
654         output.tag.set_property("SHAPE", config.shape);
655         output.tag.set_property("RATE", config.rate);
656         output.tag.set_property("CENTER_X", config.center_x);
657         output.tag.set_property("CENTER_Y", config.center_y);
658         output.append_tag();
659         output.tag.set_title("/GRADIENT");
660         output.append_tag();
661         output.append_newline();
662         output.terminate_string();
663 }
664
665 void GradientMain::read_data(KeyFrame *keyframe)
666 {
667         FileXML input;
668
669         input.set_shared_input(keyframe->xbuf);
670
671         int result = 0;
672
673         while( !(result = input.read_tag()) ) {
674                 if( input.tag.title_is("GRADIENT") ) {
675                         config.angle = input.tag.get_property("ANGLE", config.angle);
676                         config.rate = input.tag.get_property("RATE", config.rate);
677                         config.in_radius = input.tag.get_property("IN_RADIUS", config.in_radius);
678                         config.out_radius = input.tag.get_property("OUT_RADIUS", config.out_radius);
679                         config.in_r = input.tag.get_property("IN_R", config.in_r);
680                         config.in_g = input.tag.get_property("IN_G", config.in_g);
681                         config.in_b = input.tag.get_property("IN_B", config.in_b);
682                         config.in_a = input.tag.get_property("IN_A", config.in_a);
683                         config.out_r = input.tag.get_property("OUT_R", config.out_r);
684                         config.out_g = input.tag.get_property("OUT_G", config.out_g);
685                         config.out_b = input.tag.get_property("OUT_B", config.out_b);
686                         config.out_a = input.tag.get_property("OUT_A", config.out_a);
687                         config.shape = input.tag.get_property("SHAPE", config.shape);
688                         config.center_x = input.tag.get_property("CENTER_X", config.center_x);
689                         config.center_y = input.tag.get_property("CENTER_Y", config.center_y);
690                 }
691         }
692 }
693
694 int GradientMain::handle_opengl()
695 {
696 #ifdef HAVE_GL
697         const char *head_frag =
698                 "uniform sampler2D tex;\n"
699                 "uniform vec2 tex_dimensions;\n"
700                 "uniform vec2 center;\n"
701                 "uniform vec2 angle;\n"
702                 "uniform float gradient_size;\n"
703                 "uniform vec4 out_color;\n"
704                 "uniform vec4 in_color;\n"
705                 "uniform float in_radius;\n"
706                 "uniform float out_radius;\n"
707                 "uniform float radius_diff;\n"
708                 "\n"
709                 "void main()\n"
710                 "{\n"
711                 "       vec2 out_coord = gl_TexCoord[0].st;\n"
712                 "       vec2 in_coord = out_coord * tex_dimensions - center;\n";
713
714         const char *linear_shape =
715                 "       float mag = 0.5 + dot(in_coord,angle)/gradient_size;\n";
716
717         const char *radial_shape =
718                 "       float mag = length(in_coord)/gradient_size;\n";
719
720 // No clamp function in NVidia
721         const char *linear_rate =
722                 "       mag = min(max(mag, in_radius), out_radius);\n"
723                 "       float opacity = (mag - in_radius) / radius_diff;\n";
724
725 // NVidia warns about exp, but exp is in the GLSL spec.
726         const char *log_rate =
727                 "       mag = max(mag, in_radius);\n"
728                 "       float opacity = 1.0 - \n"
729                 "               exp(1.0 * -(mag - in_radius) / radius_diff);\n";
730
731         const char *square_rate =
732                 "       mag = min(max(mag, in_radius), out_radius);\n"
733                 "       float opacity = pow((mag - in_radius) / radius_diff, 2.0);\n"
734                 "       opacity = min(opacity, 1.0);\n";
735
736         const char *tail_frag =
737                 "       vec4 color = mix(in_color, out_color, opacity);\n"
738                 "       vec4 bg_color = texture2D(tex, out_coord);\n"
739                 "       gl_FragColor.rgb = mix(bg_color.rgb, color.rgb, color.a);\n"
740                 "       gl_FragColor.a = mix(bg_color.a, 1., color.a);\n"
741                 "}\n";
742
743
744         const char *shader_stack[16];
745         memset(shader_stack,0, sizeof(shader_stack));
746         int current_shader = 0;
747
748         shader_stack[current_shader++] = head_frag;
749
750         const char *shape_frag = 0;
751         switch( config.shape ) {
752         case GradientConfig::LINEAR:
753                 shape_frag = linear_shape;
754                 break;
755         default:
756                 shape_frag = radial_shape;
757                 break;
758         }
759         if( shape_frag )
760                 shader_stack[current_shader++] = shape_frag;
761
762         const char *rate_frag = 0;
763         switch( config.rate ) {
764         case GradientConfig::LINEAR:
765                 rate_frag = linear_rate;
766                 break;
767         case GradientConfig::LOG:
768                 rate_frag = log_rate;
769                 break;
770         case GradientConfig::SQUARE:
771                 rate_frag = square_rate;
772                 break;
773         }
774         if( rate_frag )
775                 shader_stack[current_shader++] = rate_frag;
776
777         shader_stack[current_shader++] = tail_frag;
778
779 // Force frame to create texture without copying to it if full alpha.
780          if( config.in_a >= 0xff && config.out_a >= 0xff )
781                 get_output()->set_opengl_state(VFrame::TEXTURE);
782         get_output()->to_texture();
783         get_output()->enable_opengl();
784         get_output()->init_screen();
785         get_output()->bind_texture(0);
786
787         shader_stack[current_shader] = 0;
788         unsigned int shader = VFrame::make_shader(shader_stack);
789         if( shader > 0 ) {
790                 glUseProgram(shader);
791                 float w = get_output()->get_w();
792                 float h = get_output()->get_h();
793                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
794                 float texture_w = get_output()->get_texture_w();
795                 float texture_h = get_output()->get_texture_h();
796                 glUniform2f(glGetUniformLocation(shader, "tex_dimensions"),
797                         texture_w, texture_h);
798                 float u = config.shape == GradientConfig::LINEAR ?
799                         0.5f : config.center_x/100.f;
800                 float v = config.shape == GradientConfig::LINEAR ?
801                         0.5f : config.center_y/100.f;
802                 glUniform2f(glGetUniformLocation(shader, "center"),
803                         u * w, v * h);
804                 glUniform1f(glGetUniformLocation(shader, "gradient_size"),
805                         gradient_size);
806                 glUniform2f(glGetUniformLocation(shader, "angle"),
807                         -sin(config.angle * (M_PI / 180)), cos(config.angle * (M_PI / 180)));
808
809                 float in_radius = (float)config.in_radius/100;
810                 float out_radius = (float)config.out_radius/100;
811                 if( in_radius > out_radius ) {
812                         float r = in_radius;
813                         in_radius = out_radius;
814                         out_radius = r;
815                 }
816                 glUniform1f(glGetUniformLocation(shader, "in_radius"), in_radius);
817                 glUniform1f(glGetUniformLocation(shader, "out_radius"), out_radius);
818                 glUniform1f(glGetUniformLocation(shader, "radius_diff"),
819                         out_radius - in_radius);
820
821                 float in_r  = (float)config.in_r  / 0xff;
822                 float in_g  = (float)config.in_g  / 0xff;
823                 float in_b  = (float)config.in_b  / 0xff;
824                 float in_a  = (float)config.in_a  / 0xff;
825                 float out_r = (float)config.out_r / 0xff;
826                 float out_g = (float)config.out_g / 0xff;
827                 float out_b = (float)config.out_b / 0xff;
828                 float out_a = (float)config.out_a / 0xff;
829                 switch( get_output()->get_color_model() ) {
830                 case BC_YUV888:
831                 case BC_YUVA8888: {
832                         float in1, in2, in3, in4;
833                         float out1, out2, out3, out4;
834                         YUV::yuv.rgb_to_yuv_f(in_r,in_g,in_b, in1,in2,in3);
835                         in2  += 0.5;  in3 += 0.5;   in4 = in_a;
836                         YUV::yuv.rgb_to_yuv_f(out_r,out_g,out_b, out1,out2,out3);
837                         out2 += 0.5;  out3 += 0.5;  out4 = out_a;
838                         glUniform4f(glGetUniformLocation(shader, "out_color"),
839                                 out1, out2, out3, out4);
840                         glUniform4f(glGetUniformLocation(shader, "in_color"),
841                                 in1, in2, in3, in4);
842                         break; }
843
844                 default: {
845                         glUniform4f(glGetUniformLocation(shader, "out_color"),
846                                 out_r, out_g, out_b, out_a);
847                         glUniform4f(glGetUniformLocation(shader, "in_color"),
848                                 in_r, in_g, in_b, in_a);
849                         break; }
850                 }
851         }
852
853         get_output()->draw_texture();
854         glUseProgram(0);
855         get_output()->set_opengl_state(VFrame::SCREEN);
856
857 #endif
858         return 0;
859 }
860
861
862 GradientPackage::GradientPackage()
863  : LoadPackage()
864 {
865 }
866
867 GradientUnit::GradientUnit(GradientServer *server, GradientMain *plugin)
868  : LoadClient(server)
869 {
870         this->plugin = plugin;
871         this->server = server;
872 }
873
874
875 static float calculate_opacity(float mag,
876                 float in_radius, float out_radius, int rate)
877 {
878         float mag_diff = mag - in_radius;
879         float radius_diff = out_radius - in_radius;
880         if( !radius_diff ) return 1.0;
881         float opacity = 0.0;
882         switch( rate ) {
883         case GradientConfig::LINEAR:
884                 opacity = mag < in_radius ? 0.0 : mag >= out_radius ? 1.0 :
885                                 mag_diff / radius_diff;
886                 break;
887         case GradientConfig::LOG:
888                 opacity = mag < in_radius ? 0.0 : // Let this one decay beyond out_radius
889                                 1 - exp(1.0 * -mag_diff / radius_diff);
890                 break;
891         case GradientConfig::SQUARE:
892                 opacity = mag < in_radius ? 0.0 : mag >= out_radius ? 1.0 :
893                                 powf(mag_diff / radius_diff, 2.);
894                 break;
895         }
896         return opacity;
897 }
898
899 #define CREATE_GRADIENT(type, temp, components, max) { \
900 /* Synthesize linear gradient for lookups */ \
901         int grad_size = plugin->gradient_size; \
902         int n = sizeof(type) * (grad_size+1); \
903         void *r_table = (void*) (plugin->table + 0*n); \
904         void *g_table = (void*) (plugin->table + 1*n); \
905         void *b_table = (void*) (plugin->table + 2*n); \
906         void *a_table = (void*) (plugin->table + 3*n); \
907         double grad_size2 = grad_size / 2.; \
908  \
909         if( plugin->need_reconfigure ) { \
910                 for( int i = 0; i <= grad_size; i++ ) { \
911                         float opacity = calculate_opacity(i, in_radius, out_radius, plugin->config.rate); \
912                         float transparency = 1.0 - opacity; \
913                         ((type*)r_table)[i] = (type)(out1 * opacity + in1 * transparency); \
914                         ((type*)g_table)[i] = (type)(out2 * opacity + in2 * transparency); \
915                         ((type*)b_table)[i] = (type)(out3 * opacity + in3 * transparency); \
916                         ((type*)a_table)[i] = (type)(out4 * opacity + in4 * transparency); \
917                 } \
918         } \
919  \
920         for( int i = pkg->y1; i < pkg->y2; i++ ) { \
921                 double y = half_h - i; \
922                 type *gradient_row = (type*)plugin->gradient->get_rows()[i]; \
923                 type *out_row = (type*)plugin->get_output()->get_rows()[i]; \
924                 switch( plugin->config.shape ) { \
925                 case GradientConfig::LINEAR: \
926                         for( int j = 0; j < w; j++ ) { \
927                                 double x = j - half_w; \
928 /* Rotate by effect angle */ \
929                                 int mag = (grad_size2 - (x * sin_angle + y * cos_angle)) + 0.5; \
930 /* Get gradient value from these coords */ \
931                                 if( sizeof(type) == 4 ) { \
932                                         float opacity = calculate_opacity(mag, \
933                                                 in_radius, out_radius, plugin->config.rate); \
934                                         float transparency = 1.0 - opacity; \
935                                         gradient_row[0] = (type)(out1 * opacity + in1 * transparency); \
936                                         gradient_row[1] = (type)(out2 * opacity + in2 * transparency); \
937                                         gradient_row[2] = (type)(out3 * opacity + in3 * transparency); \
938                                         if( components == 4 ) \
939                                                 gradient_row[3] = (type)(out4 * opacity + in4 * transparency); \
940                                 } \
941                                 else if( mag < 0 ) { \
942                                         gradient_row[0] = in1; \
943                                         gradient_row[1] = in2; \
944                                         gradient_row[2] = in3; \
945                                         if( components == 4 ) \
946                                                 gradient_row[3] = in4; \
947                                 } \
948                                 else if( mag >= grad_size ) { \
949                                         gradient_row[0] = out1; \
950                                         gradient_row[1] = out2; \
951                                         gradient_row[2] = out3; \
952                                         if( components == 4 ) \
953                                                 gradient_row[3] = out4; \
954                                 } \
955                                 else { \
956                                         gradient_row[0] = ((type*)r_table)[mag]; \
957                                         gradient_row[1] = ((type*)g_table)[mag]; \
958                                         gradient_row[2] = ((type*)b_table)[mag]; \
959                                         if( components == 4 ) \
960                                                 gradient_row[3] = ((type*)a_table)[mag]; \
961                                 } \
962 /* no alpha output, Overlay mixed colormodels */ \
963                                 if( gradient_cmodel != output_cmodel ) { \
964                                         temp opacity = gradient_row[3]; \
965                                         temp transparency = max - opacity; \
966                                         out_row[0] = (transparency * out_row[0] + opacity * gradient_row[0]) / max; \
967                                         out_row[1] = (transparency * out_row[1] + opacity * gradient_row[1]) / max; \
968                                         out_row[2] = (transparency * out_row[2] + opacity * gradient_row[2]) / max; \
969                                         out_row += 3; \
970                                 } \
971                                 gradient_row += components; \
972                         } \
973                         break; \
974  \
975                 case GradientConfig::RADIAL: \
976                         for( int j = 0; j < w; j++ ) { \
977                                 double x = j - center_x, y = i - center_y; \
978                                 double magnitude = hypot(x, y); \
979                                 int mag = (int)magnitude; \
980                                 if( sizeof(type) == 4 ) { \
981                                         float opacity = calculate_opacity(mag, \
982                                                         in_radius, out_radius, plugin->config.rate); \
983                                         float transparency = 1.0 - opacity; \
984                                         gradient_row[0] = (type)(out1 * opacity + in1 * transparency); \
985                                         gradient_row[1] = (type)(out2 * opacity + in2 * transparency); \
986                                         gradient_row[2] = (type)(out3 * opacity + in3 * transparency); \
987                                         if( components == 4 ) \
988                                                 gradient_row[3] = (type)(out4 * opacity + in4 * transparency); \
989                                 } \
990                                 else { \
991                                         gradient_row[0] = ((type*)r_table)[mag]; \
992                                         gradient_row[1] = ((type*)g_table)[mag]; \
993                                         gradient_row[2] = ((type*)b_table)[mag]; \
994                                         if( components == 4 ) \
995                                                 gradient_row[3] = ((type*)a_table)[mag]; \
996                                 } \
997 /* Overlay mixed colormodels onto output */ \
998                                 if( gradient_cmodel != output_cmodel ) { \
999                                         temp opacity = gradient_row[3]; \
1000                                         temp transparency = max - opacity; \
1001                                         out_row[0] = (transparency * out_row[0] + opacity * gradient_row[0]) / max; \
1002                                         out_row[1] = (transparency * out_row[1] + opacity * gradient_row[1]) / max; \
1003                                         out_row[2] = (transparency * out_row[2] + opacity * gradient_row[2]) / max; \
1004                                         out_row += 3; \
1005                                 } \
1006                                 gradient_row += components; \
1007                         } \
1008                         break; \
1009                 } \
1010         } \
1011 }
1012
1013 void GradientUnit::process_package(LoadPackage *package)
1014 {
1015         GradientPackage *pkg = (GradientPackage*)package;
1016         int h = plugin->input->get_h();
1017         int w = plugin->input->get_w();
1018         int grad_size = plugin->gradient_size;
1019         int half_w = w / 2;
1020         int half_h = h / 2;
1021         int in_radius = (int)(plugin->config.in_radius / 100 * grad_size);
1022         int out_radius = (int)(plugin->config.out_radius / 100 * grad_size);
1023         double sin_angle = sin(plugin->config.angle * (M_PI / 180));
1024         double cos_angle = cos(plugin->config.angle * (M_PI / 180));
1025         double center_x = plugin->config.center_x * w / 100;
1026         double center_y = plugin->config.center_y * h / 100;
1027         int gradient_cmodel = plugin->gradient->get_color_model();
1028         int output_cmodel = plugin->get_output()->get_color_model();
1029
1030         if( in_radius > out_radius ) {
1031                 int r = in_radius;
1032                 in_radius = out_radius;
1033                 out_radius = r;
1034         }
1035
1036         switch( gradient_cmodel ) {
1037         case BC_RGB888: {
1038                 int in1 = plugin->config.in_r;
1039                 int in2 = plugin->config.in_g;
1040                 int in3 = plugin->config.in_b;
1041                 int in4 = plugin->config.in_a;
1042                 int out1 = plugin->config.out_r;
1043                 int out2 = plugin->config.out_g;
1044                 int out3 = plugin->config.out_b;
1045                 int out4 = plugin->config.out_a;
1046                 CREATE_GRADIENT(unsigned char, int, 3, 0xff)
1047                 break; }
1048
1049         case BC_RGBA8888: {
1050                 int in1 = plugin->config.in_r;
1051                 int in2 = plugin->config.in_g;
1052                 int in3 = plugin->config.in_b;
1053                 int in4 = plugin->config.in_a;
1054                 int out1 = plugin->config.out_r;
1055                 int out2 = plugin->config.out_g;
1056                 int out3 = plugin->config.out_b;
1057                 int out4 = plugin->config.out_a;
1058                 CREATE_GRADIENT(unsigned char, int, 4, 0xff)
1059                 break; }
1060
1061         case BC_RGB_FLOAT: {
1062                 float in1 = (float)plugin->config.in_r / 0xff;
1063                 float in2 = (float)plugin->config.in_g / 0xff;
1064                 float in3 = (float)plugin->config.in_b / 0xff;
1065                 float in4 = (float)plugin->config.in_a / 0xff;
1066                 float out1 = (float)plugin->config.out_r / 0xff;
1067                 float out2 = (float)plugin->config.out_g / 0xff;
1068                 float out3 = (float)plugin->config.out_b / 0xff;
1069                 float out4 = (float)plugin->config.out_a / 0xff;
1070                 CREATE_GRADIENT(float, float, 3, 1.0)
1071                 break; }
1072
1073         case BC_RGBA_FLOAT: {
1074                 float in1 = (float)plugin->config.in_r / 0xff;
1075                 float in2 = (float)plugin->config.in_g / 0xff;
1076                 float in3 = (float)plugin->config.in_b / 0xff;
1077                 float in4 = (float)plugin->config.in_a / 0xff;
1078                 float out1 = (float)plugin->config.out_r / 0xff;
1079                 float out2 = (float)plugin->config.out_g / 0xff;
1080                 float out3 = (float)plugin->config.out_b / 0xff;
1081                 float out4 = (float)plugin->config.out_a / 0xff;
1082                 CREATE_GRADIENT(float, float, 4, 1.0)
1083                 break; }
1084
1085         case BC_YUV888: {
1086                 int in_r = plugin->config.in_r;
1087                 int in_g = plugin->config.in_g;
1088                 int in_b = plugin->config.in_b;
1089                 int in1, in2, in3, in4;
1090                 int out1, out2, out3, out4;
1091                 YUV::yuv.rgb_to_yuv_8(in_r,in_g,in_b, in1,in2,in3);
1092                 in4 = plugin->config.in_a;
1093                 int out_r = plugin->config.in_r;
1094                 int out_g = plugin->config.in_g;
1095                 int out_b = plugin->config.in_b;
1096                 YUV::yuv.rgb_to_yuv_8(out_r,out_g,out_b, out1,out2,out3);
1097                 out4 = plugin->config.out_a;
1098                 CREATE_GRADIENT(unsigned char, int, 3, 0xff)
1099                 break; }
1100
1101         case BC_YUVA8888: {
1102                 int in_r = plugin->config.in_r;
1103                 int in_g = plugin->config.in_g;
1104                 int in_b = plugin->config.in_b;
1105                 int in1, in2, in3, in4;
1106                 int out1, out2, out3, out4;
1107                 YUV::yuv.rgb_to_yuv_8(in_r,in_g,in_b, in1,in2,in3);
1108                 in4 = plugin->config.in_a;
1109                 int out_r = plugin->config.in_r;
1110                 int out_g = plugin->config.in_g;
1111                 int out_b = plugin->config.in_b;
1112                 YUV::yuv.rgb_to_yuv_8(out_r,out_g,out_b, out1,out2,out3);
1113                 out4 = plugin->config.out_a;
1114                 CREATE_GRADIENT(unsigned char, int, 4, 0xff)
1115                 break; }
1116         }
1117 }
1118
1119
1120 GradientServer::GradientServer(GradientMain *plugin,
1121         int total_clients, int total_packages)
1122  : LoadServer(total_clients, total_packages)
1123 {
1124         this->plugin = plugin;
1125 }
1126
1127 void GradientServer::init_packages()
1128 {
1129         for( int i = 0; i < get_total_packages(); i++ ) {
1130                 GradientPackage *package = (GradientPackage*)get_package(i);
1131                 package->y1 = plugin->input->get_h() *
1132                         i /
1133                         get_total_packages();
1134                 package->y2 = plugin->input->get_h() *
1135                         (i + 1) /
1136                         get_total_packages();
1137         }
1138 }
1139
1140 LoadClient* GradientServer::new_client()
1141 {
1142         return new GradientUnit(this, plugin);
1143 }
1144
1145 LoadPackage* GradientServer::new_package()
1146 {
1147         return new GradientPackage;
1148 }
1149