yuv colorspace/range + prefs, ffmpeg colorrange probe, x11 direct force colormodel...
[goodguy/history.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 "bcdisplayinfo.h"
27 #include "clip.h"
28 #include "bchash.h"
29 #include "filexml.h"
30 #include "gradient.h"
31 #include "keyframe.h"
32 #include "language.h"
33 #include "overlayframe.h"
34 #include "theme.h"
35 #include "vframe.h"
36
37
38
39
40 REGISTER_PLUGIN(GradientMain)
41
42
43
44
45
46
47 GradientConfig::GradientConfig()
48 {
49         reset();
50 }
51
52 void GradientConfig::reset()
53
54 {
55         angle = 0;
56         in_radius = 0;
57         out_radius = 100;
58         in_r = 0xff;
59         in_g = 0xff;
60         in_b = 0xff;
61         in_a = 0xff;
62         out_r = 0x0;
63         out_g = 0x0;
64         out_b = 0x0;
65         out_a = 0x0;
66         shape = GradientConfig::LINEAR;
67         rate = GradientConfig::LINEAR;
68         center_x = 50;
69         center_y = 50;
70 }
71
72 int GradientConfig::equivalent(GradientConfig &that)
73 {
74         return (EQUIV(angle, that.angle) &&
75                 EQUIV(in_radius, that.in_radius) &&
76                 EQUIV(out_radius, that.out_radius) &&
77                 in_r == that.in_r &&
78                 in_g == that.in_g &&
79                 in_b == that.in_b &&
80                 in_a == that.in_a &&
81                 out_r == that.out_r &&
82                 out_g == that.out_g &&
83                 out_b == that.out_b &&
84                 out_a == that.out_a &&
85                 shape == that.shape &&
86                 rate == that.rate &&
87                 EQUIV(center_x, that.center_x) &&
88                 EQUIV(center_y, that.center_y));
89 }
90
91 void GradientConfig::copy_from(GradientConfig &that)
92 {
93         angle = that.angle;
94         in_radius = that.in_radius;
95         out_radius = that.out_radius;
96         in_r = that.in_r;
97         in_g = that.in_g;
98         in_b = that.in_b;
99         in_a = that.in_a;
100         out_r = that.out_r;
101         out_g = that.out_g;
102         out_b = that.out_b;
103         out_a = that.out_a;
104         shape = that.shape;
105         rate = that.rate;
106         center_x = that.center_x;
107         center_y = that.center_y;
108 }
109
110 void GradientConfig::interpolate(GradientConfig &prev,
111         GradientConfig &next,
112         long prev_frame,
113         long next_frame,
114         long current_frame)
115 {
116         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
117         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
118
119
120         this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale);
121         this->in_radius = (int)(prev.in_radius * prev_scale + next.in_radius * next_scale);
122         this->out_radius = (int)(prev.out_radius * prev_scale + next.out_radius * next_scale);
123         in_r = (int)(prev.in_r * prev_scale + next.in_r * next_scale);
124         in_g = (int)(prev.in_g * prev_scale + next.in_g * next_scale);
125         in_b = (int)(prev.in_b * prev_scale + next.in_b * next_scale);
126         in_a = (int)(prev.in_a * prev_scale + next.in_a * next_scale);
127         out_r = (int)(prev.out_r * prev_scale + next.out_r * next_scale);
128         out_g = (int)(prev.out_g * prev_scale + next.out_g * next_scale);
129         out_b = (int)(prev.out_b * prev_scale + next.out_b * next_scale);
130         out_a = (int)(prev.out_a * prev_scale + next.out_a * next_scale);
131         shape = prev.shape;
132         rate = prev.rate;
133         center_x = prev.center_x * prev_scale + next.center_x * next_scale;
134         center_y = prev.center_y * prev_scale + next.center_y * next_scale;
135 }
136
137 int GradientConfig::get_in_color()
138 {
139         int result = (in_r << 16) | (in_g << 8) | (in_b);
140         return result;
141 }
142
143 int GradientConfig::get_out_color()
144 {
145         int result = (out_r << 16) | (out_g << 8) | (out_b);
146         return result;
147 }
148
149
150
151
152
153
154
155
156
157
158
159 #define COLOR_W 100
160 #define COLOR_H 30
161
162 GradientWindow::GradientWindow(GradientMain *plugin)
163  : PluginClientWindow(plugin,
164         350,
165         290,
166         350,
167         290,
168         0)
169 {
170         this->plugin = plugin;
171         angle = 0;
172         angle_title = 0;
173         center_x = 0;
174         center_y = 0;
175         center_x_title = 0;
176         center_y_title = 0;
177 }
178
179 GradientWindow::~GradientWindow()
180 {
181         delete in_color_thread;
182         delete out_color_thread;
183 }
184
185 void GradientWindow::create_objects()
186 {
187         int margin = plugin->get_theme()->widget_border;
188         int x = 10, y = 10;
189         BC_Title *title;
190
191         add_subwindow(title = new BC_Title(x, y, _("Shape:")));
192         add_subwindow(shape = new GradientShape(plugin,
193                 this,
194                 x + title->get_w() + margin,
195                 y));
196         shape->create_objects();
197         y += shape->get_h() + margin;
198         shape_x = x;
199         shape_y = y;
200         y += BC_Pot::calculate_h() + margin;
201
202         add_subwindow(title = new BC_Title(x, y, _("Rate:")));
203         add_subwindow(rate = new GradientRate(plugin,
204                 x + title->get_w() + margin,
205                 y));
206         rate->create_objects();
207         y += rate->get_h() + margin;
208
209         int x1 = x;
210         int y1 = y;
211
212         BC_Title *title1;
213         add_subwindow(title1 = new BC_Title(x, y, _("Inner radius:")));
214
215         y += BC_Slider::get_span(0) + margin;
216
217         BC_Title *title2;
218         add_subwindow(title2 = new BC_Title(x, y, _("Outer radius:")));
219
220         add_subwindow(reset = new GradientReset(plugin, this, x, y+100));
221
222         y = y1;
223         x += MAX(title1->get_w(), title2->get_w()) + margin;
224
225         add_subwindow(in_radius = new GradientInRadius(plugin, x, y));
226         y += in_radius->get_h() + margin;
227
228         add_subwindow(out_radius = new GradientOutRadius(plugin, x, y));
229         y += out_radius->get_h() + margin;
230
231         x = x1;
232         y1 = y;
233         add_subwindow(in_color = new GradientInColorButton(plugin, this, x, y));
234         y += COLOR_H + margin;
235
236         add_subwindow(out_color = new GradientOutColorButton(plugin, this, x, y));
237         x += MAX(in_color->get_w(), out_color->get_w()) + margin;
238         y = y1;
239
240         in_color_x = x;
241         in_color_y = y;
242         y += COLOR_H + margin;
243         out_color_x = x;
244         out_color_y = y;
245         in_color_thread = new GradientInColorThread(plugin, this);
246         out_color_thread = new GradientOutColorThread(plugin, this);
247         update_in_color();
248         update_out_color();
249         update_shape();
250
251         draw_3d_border(in_color_x - 2,
252                 in_color_y - 2,
253                 COLOR_W + 4,
254                 COLOR_H + 4,
255                 1);
256
257         draw_3d_border(out_color_x - 2,
258                 out_color_y - 2,
259                 COLOR_W + 4,
260                 COLOR_H + 4,
261                 1);
262
263
264         show_window();
265 }
266
267 void GradientWindow::update_shape()
268 {
269         int x = shape_x, y = shape_y;
270
271         if(plugin->config.shape == GradientConfig::LINEAR)
272         {
273                 delete center_x_title;
274                 delete center_y_title;
275                 delete center_x;
276                 delete center_y;
277                 center_x_title = 0;
278                 center_y_title = 0;
279                 center_x = 0;
280                 center_y = 0;
281
282                 if(!angle)
283                 {
284                         add_subwindow(angle_title = new BC_Title(x, y, _("Angle:")));
285                         add_subwindow(angle = new GradientAngle(plugin, x + angle_title->get_w() + 10, y));
286                 }
287         }
288         else
289         {
290                 delete angle_title;
291                 delete angle;
292                 angle_title = 0;
293                 angle = 0;
294                 if(!center_x)
295                 {
296                         add_subwindow(center_x_title = new BC_Title(x, y, _("Center X:")));
297                         add_subwindow(center_x = new GradientCenterX(plugin,
298                                 x + center_x_title->get_w() + 10,
299                                 y));
300                         x += center_x_title->get_w() + 10 + center_x->get_w() + 10;
301                         add_subwindow(center_y_title = new BC_Title(x, y, _("Center Y:")));
302                         add_subwindow(center_y = new GradientCenterY(plugin,
303                                 x + center_y_title->get_w() + 10,
304                                 y));
305                 }
306         }
307         show_window();
308 }
309
310
311
312
313 void GradientWindow::update_in_color()
314 {
315 //printf("GradientWindow::update_in_color 1 %08x\n", plugin->config.get_in_color());
316         set_color(plugin->config.get_in_color());
317         draw_box(in_color_x, in_color_y, COLOR_W, COLOR_H);
318         flash(in_color_x, in_color_y, COLOR_W, COLOR_H);
319 }
320
321 void GradientWindow::update_out_color()
322 {
323 //printf("GradientWindow::update_out_color 1 %08x\n", plugin->config.get_in_color());
324         set_color(plugin->config.get_out_color());
325         draw_box(out_color_x, out_color_y, COLOR_W, COLOR_H);
326         flash(out_color_x, out_color_y, COLOR_W, COLOR_H);
327 }
328
329 void GradientWindow::done_event(int result)
330 {
331         in_color_thread->close_window();
332         out_color_thread->close_window();
333 }
334
335
336
337
338
339
340
341
342
343 GradientShape::GradientShape(GradientMain *plugin,
344         GradientWindow *gui,
345         int x,
346         int y)
347  : BC_PopupMenu(x, y, 100, to_text(plugin->config.shape), 1)
348 {
349         this->plugin = plugin;
350         this->gui = gui;
351 }
352 void GradientShape::create_objects()
353 {
354         add_item(new BC_MenuItem(to_text(GradientConfig::LINEAR)));
355         add_item(new BC_MenuItem(to_text(GradientConfig::RADIAL)));
356 }
357 char* GradientShape::to_text(int shape)
358 {
359         switch(shape)
360         {
361                 case GradientConfig::LINEAR:
362                         return _("Linear");
363                 default:
364                         return _("Radial");
365         }
366 }
367 int GradientShape::from_text(char *text)
368 {
369         if(!strcmp(text, to_text(GradientConfig::LINEAR)))
370                 return GradientConfig::LINEAR;
371         return GradientConfig::RADIAL;
372 }
373 int GradientShape::handle_event()
374 {
375         plugin->config.shape = from_text(get_text());
376         gui->update_shape();
377         plugin->send_configure_change();
378         return 1;
379 }
380
381
382
383
384 GradientCenterX::GradientCenterX(GradientMain *plugin, int x, int y)
385  : BC_FPot(x, y, plugin->config.center_x, 0, 100)
386 {
387         this->plugin = plugin;
388 }
389 int GradientCenterX::handle_event()
390 {
391         plugin->config.center_x = get_value();
392         plugin->send_configure_change();
393         return 1;
394 }
395
396
397
398 GradientCenterY::GradientCenterY(GradientMain *plugin, int x, int y)
399  : BC_FPot(x, y, plugin->config.center_y, 0, 100)
400 {
401         this->plugin = plugin;
402 }
403
404 int GradientCenterY::handle_event()
405 {
406         plugin->config.center_y = get_value();
407         plugin->send_configure_change();
408         return 1;
409 }
410
411
412
413
414 GradientAngle::GradientAngle(GradientMain *plugin, int x, int y)
415  : BC_FPot(x,
416         y,
417         plugin->config.angle,
418         -180,
419         180)
420 {
421         this->plugin = plugin;
422 }
423
424 int GradientAngle::handle_event()
425 {
426         plugin->config.angle = get_value();
427         plugin->send_configure_change();
428         return 1;
429 }
430
431
432 GradientRate::GradientRate(GradientMain *plugin, int x, int y)
433  : BC_PopupMenu(x,
434         y,
435         100,
436         to_text(plugin->config.rate),
437         1)
438 {
439         this->plugin = plugin;
440 }
441 void GradientRate::create_objects()
442 {
443         add_item(new BC_MenuItem(to_text(GradientConfig::LINEAR)));
444         add_item(new BC_MenuItem(to_text(GradientConfig::LOG)));
445         add_item(new BC_MenuItem(to_text(GradientConfig::SQUARE)));
446 }
447 char* GradientRate::to_text(int shape)
448 {
449         switch(shape)
450         {
451                 case GradientConfig::LINEAR:
452                         return _("Linear");
453                 case GradientConfig::LOG:
454                         return _("Log");
455                 default:
456                         return _("Square");
457         }
458 }
459 int GradientRate::from_text(char *text)
460 {
461         if(!strcmp(text, to_text(GradientConfig::LINEAR)))
462                 return GradientConfig::LINEAR;
463         if(!strcmp(text, to_text(GradientConfig::LOG)))
464                 return GradientConfig::LOG;
465         return GradientConfig::SQUARE;
466 }
467 int GradientRate::handle_event()
468 {
469         plugin->config.rate = from_text(get_text());
470         plugin->send_configure_change();
471         return 1;
472 }
473
474
475
476 GradientInRadius::GradientInRadius(GradientMain *plugin, int x, int y)
477  : BC_FSlider(x,
478         y,
479         0,
480         200,
481         200,
482         (float)0,
483         (float)100,
484         (float)plugin->config.in_radius)
485 {
486         this->plugin = plugin;
487 }
488
489 int GradientInRadius::handle_event()
490 {
491         plugin->config.in_radius = get_value();
492         plugin->send_configure_change();
493         return 1;
494 }
495
496
497 GradientOutRadius::GradientOutRadius(GradientMain *plugin, int x, int y)
498  : BC_FSlider(x,
499         y,
500         0,
501         200,
502         200,
503         (float)0,
504         (float)100,
505         (float)plugin->config.out_radius)
506 {
507         this->plugin = plugin;
508 }
509
510 int GradientOutRadius::handle_event()
511 {
512         plugin->config.out_radius = get_value();
513         plugin->send_configure_change();
514         return 1;
515 }
516
517 GradientInColorButton::GradientInColorButton(GradientMain *plugin, GradientWindow *window, int x, int y)
518  : BC_GenericButton(x, y, _("Inner color:"))
519 {
520         this->plugin = plugin;
521         this->window = window;
522 }
523
524 int GradientInColorButton::handle_event()
525 {
526         window->in_color_thread->start_window(
527                 plugin->config.get_in_color(),
528                 plugin->config.in_a);
529         return 1;
530 }
531
532
533 GradientOutColorButton::GradientOutColorButton(GradientMain *plugin, GradientWindow *window, int x, int y)
534  : BC_GenericButton(x, y, _("Outer color:"))
535 {
536         this->plugin = plugin;
537         this->window = window;
538 }
539
540 int GradientOutColorButton::handle_event()
541 {
542         window->out_color_thread->start_window(
543                 plugin->config.get_out_color(),
544                 plugin->config.out_a);
545         return 1;
546 }
547
548 GradientReset::GradientReset(GradientMain *plugin, GradientWindow *window, int x, int y)
549  : BC_GenericButton(x, y, _("Reset"))
550 {
551         this->plugin = plugin;
552         this->window = window;
553 }
554
555 int GradientReset::handle_event()
556 {
557         plugin->config.reset();
558         window->update_gui();
559         plugin->send_configure_change();
560         return 1;
561 }
562
563 GradientInColorThread::GradientInColorThread(GradientMain *plugin,
564         GradientWindow *window)
565  : ColorPicker(1, _("Inner color"))
566 {
567         this->plugin = plugin;
568         this->window = window;
569 }
570
571 int GradientInColorThread::handle_new_color(int output, int alpha)
572 {
573         plugin->config.in_r = (output & 0xff0000) >> 16;
574         plugin->config.in_g = (output & 0xff00) >> 8;
575         plugin->config.in_b = (output & 0xff);
576         plugin->config.in_a = alpha;
577
578         window->lock_window("GradientInColorThread::handle_new_color");
579         window->update_in_color();
580         window->flush();
581         window->unlock_window();
582         plugin->send_configure_change();
583 // printf("GradientInColorThread::handle_event 1 %d %d %d %d %d %d %d %d\n",
584 // plugin->config.in_r,
585 // plugin->config.in_g,
586 // plugin->config.in_b,
587 // plugin->config.in_a,
588 // plugin->config.out_r,
589 // plugin->config.out_g,
590 // plugin->config.out_b,
591 // plugin->config.out_a);
592
593         return 1;
594 }
595
596
597
598 GradientOutColorThread::GradientOutColorThread(GradientMain *plugin,
599         GradientWindow *window)
600  : ColorPicker(1, _("Outer color"))
601 {
602         this->plugin = plugin;
603         this->window = window;
604 }
605
606 int GradientOutColorThread::handle_new_color(int output, int alpha)
607 {
608         plugin->config.out_r = (output & 0xff0000) >> 16;
609         plugin->config.out_g = (output & 0xff00) >> 8;
610         plugin->config.out_b = (output & 0xff);
611         plugin->config.out_a = alpha;
612         window->lock_window("GradientOutColorThread::handle_new_color");
613         window->update_out_color();
614         window->flush();
615         window->unlock_window();
616         plugin->send_configure_change();
617 // printf("GradientOutColorThread::handle_event 1 %d %d %d %d %d %d %d %d\n",
618 // plugin->config.in_r,
619 // plugin->config.in_g,
620 // plugin->config.in_b,
621 // plugin->config.in_a,
622 // plugin->config.out_r,
623 // plugin->config.out_g,
624 // plugin->config.out_b,
625 // plugin->config.out_a);
626         return 1;
627 }
628
629
630
631
632
633
634
635
636
637
638
639
640 GradientMain::GradientMain(PluginServer *server)
641  : PluginVClient(server)
642 {
643
644         need_reconfigure = 1;
645         gradient = 0;
646         engine = 0;
647         overlayer = 0;
648 }
649
650 GradientMain::~GradientMain()
651 {
652
653
654         if(gradient) delete gradient;
655         if(engine) delete engine;
656         if(overlayer) delete overlayer;
657 }
658
659 const char* GradientMain::plugin_title() { return _("Gradient"); }
660 int GradientMain::is_realtime() { return 1; }
661
662
663 NEW_WINDOW_MACRO(GradientMain, GradientWindow)
664
665 LOAD_CONFIGURATION_MACRO(GradientMain, GradientConfig)
666
667 int GradientMain::is_synthesis()
668 {
669         return 1;
670 }
671
672
673 int GradientMain::process_buffer(VFrame *frame,
674         int64_t start_position,
675         double frame_rate)
676 {
677         this->input = frame;
678         this->output = frame;
679         need_reconfigure |= load_configuration();
680
681         int need_alpha = config.in_a != 0xff || config.out_a != 0xff;
682         if(need_alpha)
683                 read_frame(frame,
684                         0,
685                         start_position,
686                         frame_rate,
687                         get_use_opengl());
688         if(get_use_opengl()) return run_opengl();
689
690         int gradient_cmodel = input->get_color_model();
691         if(need_alpha && BC_CModels::components(gradient_cmodel) == 3)
692         {
693                 switch(gradient_cmodel)
694                 {
695                         case BC_RGB888:
696                                 gradient_cmodel = BC_RGBA8888;
697                                 break;
698                         case BC_RGB_FLOAT:
699                                 gradient_cmodel = BC_RGBA_FLOAT;
700                                 break;
701                         case BC_YUV888:
702                                 gradient_cmodel = BC_YUVA8888;
703                                 break;
704                 }
705         }
706
707         if(gradient && gradient->get_color_model() != gradient_cmodel)
708         {
709                 delete gradient;
710                 gradient = 0;
711         }
712
713         if(!gradient)
714                 gradient = new VFrame(input->get_w(), input->get_h(),
715                         gradient_cmodel, 0);
716
717         if(!engine) engine = new GradientServer(this,
718                 get_project_smp() + 1,
719                 get_project_smp() + 1);
720         engine->process_packages();
721
722 // Use overlay routine in GradientServer if mismatched colormodels
723         if(gradient->get_color_model() == output->get_color_model())
724         {
725                 if(!overlayer) overlayer = new OverlayFrame(get_project_smp() + 1);
726                 overlayer->overlay(output,
727                         gradient,
728                         0,
729                         0,
730                         input->get_w(),
731                         input->get_h(),
732                         0,
733                         0,
734                         input->get_w(),
735                         input->get_h(),
736                         1.0,
737                         TRANSFER_NORMAL,
738                         NEAREST_NEIGHBOR);
739         }
740
741
742         return 0;
743 }
744
745
746 void GradientMain::update_gui()
747 {
748         if( !thread ) return;
749         if( !load_configuration() ) return;
750         ((GradientWindow*)thread->window)->lock_window("GradientMain::update_gui");
751         GradientWindow *window = (GradientWindow *)thread->window;
752         window->update_gui();
753         window->unlock_window();
754 }
755
756 void GradientWindow::update_gui()
757 {
758         GradientConfig &config = plugin->config;
759         rate->set_text(GradientRate::to_text(config.rate));
760         in_radius->update(config.in_radius);
761         out_radius->update(config.out_radius);
762         shape->set_text(GradientShape::to_text(config.shape));
763         if( angle ) angle->update(config.angle);
764         if( center_x ) center_x->update(config.center_x);
765         if( center_y ) center_y->update(config.center_y);
766         update_in_color();
767         update_out_color();
768         update_shape();
769         unlock_window();
770         in_color_thread->update_gui(config.get_in_color(), config.in_a);
771         out_color_thread->update_gui(config.get_out_color(), config.out_a);
772         lock_window("GradientWindow::update_gui");
773 }
774
775
776 void GradientMain::save_data(KeyFrame *keyframe)
777 {
778         FileXML output;
779
780 // cause data to be stored directly in text
781         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
782         output.tag.set_title("GRADIENT");
783
784         output.tag.set_property("ANGLE", config.angle);
785         output.tag.set_property("IN_RADIUS", config.in_radius);
786         output.tag.set_property("OUT_RADIUS", config.out_radius);
787         output.tag.set_property("IN_R", config.in_r);
788         output.tag.set_property("IN_G", config.in_g);
789         output.tag.set_property("IN_B", config.in_b);
790         output.tag.set_property("IN_A", config.in_a);
791         output.tag.set_property("OUT_R", config.out_r);
792         output.tag.set_property("OUT_G", config.out_g);
793         output.tag.set_property("OUT_B", config.out_b);
794         output.tag.set_property("OUT_A", config.out_a);
795         output.tag.set_property("SHAPE", config.shape);
796         output.tag.set_property("RATE", config.rate);
797         output.tag.set_property("CENTER_X", config.center_x);
798         output.tag.set_property("CENTER_Y", config.center_y);
799         output.append_tag();
800         output.tag.set_title("/GRADIENT");
801         output.append_tag();
802         output.append_newline();
803         output.terminate_string();
804 }
805
806 void GradientMain::read_data(KeyFrame *keyframe)
807 {
808         FileXML input;
809
810         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
811
812         int result = 0;
813
814         while(!result)
815         {
816                 result = input.read_tag();
817
818                 if(!result)
819                 {
820                         if(input.tag.title_is("GRADIENT"))
821                         {
822                                 config.angle = input.tag.get_property("ANGLE", config.angle);
823                                 config.rate = input.tag.get_property("RATE", config.rate);
824                                 config.in_radius = input.tag.get_property("IN_RADIUS", config.in_radius);
825                                 config.out_radius = input.tag.get_property("OUT_RADIUS", config.out_radius);
826                                 config.in_r = input.tag.get_property("IN_R", config.in_r);
827                                 config.in_g = input.tag.get_property("IN_G", config.in_g);
828                                 config.in_b = input.tag.get_property("IN_B", config.in_b);
829                                 config.in_a = input.tag.get_property("IN_A", config.in_a);
830                                 config.out_r = input.tag.get_property("OUT_R", config.out_r);
831                                 config.out_g = input.tag.get_property("OUT_G", config.out_g);
832                                 config.out_b = input.tag.get_property("OUT_B", config.out_b);
833                                 config.out_a = input.tag.get_property("OUT_A", config.out_a);
834                                 config.shape = input.tag.get_property("SHAPE", config.shape);
835                                 config.center_x = input.tag.get_property("CENTER_X", config.center_x);
836                                 config.center_y = input.tag.get_property("CENTER_Y", config.center_y);
837                         }
838                 }
839         }
840 }
841
842 int GradientMain::handle_opengl()
843 {
844 #ifdef HAVE_GL
845         const char *head_frag =
846                 "uniform sampler2D tex;\n"
847                 "uniform float half_w;\n"
848                 "uniform float half_h;\n"
849                 "uniform float center_x;\n"
850                 "uniform float center_y;\n"
851                 "uniform float half_gradient_size;\n"
852                 "uniform float sin_angle;\n"
853                 "uniform float cos_angle;\n"
854                 "uniform vec4 out_color;\n"
855                 "uniform vec4 in_color;\n"
856                 "uniform float in_radius;\n"
857                 "uniform float out_radius;\n"
858                 "uniform float radius_diff;\n"
859                 "\n"
860                 "void main()\n"
861                 "{\n"
862                 "       vec2 out_coord = gl_TexCoord[0].st;\n";
863
864         const char *linear_shape =
865                 "       vec2 in_coord = vec2(out_coord.x - half_w, half_h - out_coord.y);\n"
866                 "       float mag = half_gradient_size - \n"
867                 "               (in_coord.x * sin_angle + in_coord.y * cos_angle);\n";
868
869         const char *radial_shape =
870                 "       vec2 in_coord = vec2(out_coord.x - center_x, out_coord.y - center_y);\n"
871                 "       float mag = length(vec2(in_coord.x, in_coord.y));\n";
872
873 // No clamp function in NVidia
874         const char *linear_rate =
875                 "       mag = min(max(mag, in_radius), out_radius);\n"
876                 "       float opacity = (mag - in_radius) / radius_diff;\n";
877
878 // NVidia warns about exp, but exp is in the GLSL spec.
879         const char *log_rate =
880                 "       mag = max(mag, in_radius);\n"
881                 "       float opacity = 1.0 - \n"
882                 "               exp(1.0 * -(mag - in_radius) / radius_diff);\n";
883
884         const char *square_rate =
885                 "       mag = min(max(mag, in_radius), out_radius);\n"
886                 "       float opacity = pow((mag - in_radius) / radius_diff, 2.0);\n"
887                 "       opacity = min(opacity, 1.0);\n";
888
889         const char *tail_frag =
890                 "       vec4 color = mix(in_color, out_color, opacity);\n"
891                 "       vec4 bg_color = texture2D(tex, out_coord);\n"
892                 "       gl_FragColor.rgb = mix(bg_color.rgb, color.rgb, color.a);\n"
893                 "       gl_FragColor.a = max(bg_color.a, color.a);\n"
894                 "}\n";
895
896
897         const char *shader_stack[5] = { 0, 0, 0, 0, 0 };
898         shader_stack[0] = head_frag;
899
900         switch(config.shape)
901         {
902                 case GradientConfig::LINEAR:
903                         shader_stack[1] = linear_shape;
904                         break;
905
906                 default:
907                         shader_stack[1] = radial_shape;
908                         break;
909         }
910
911         switch(config.rate)
912         {
913                 case GradientConfig::LINEAR:
914                         shader_stack[2] = linear_rate;
915                         break;
916                 case GradientConfig::LOG:
917                         shader_stack[2] = log_rate;
918                         break;
919                 case GradientConfig::SQUARE:
920                         shader_stack[2] = square_rate;
921                         break;
922         }
923
924         shader_stack[3] = tail_frag;
925 // Force frame to create texture without copying to it if full alpha.
926         if(config.in_a >= 0xff &&
927                 config.out_a >= 0xff)
928                 get_output()->set_opengl_state(VFrame::TEXTURE);
929         get_output()->to_texture();
930         get_output()->enable_opengl();
931         get_output()->init_screen();
932         get_output()->bind_texture(0);
933
934         unsigned int frag = VFrame::make_shader(0,
935                 shader_stack[0],
936                 shader_stack[1],
937                 shader_stack[2],
938                 shader_stack[3],
939                 0);
940
941         if(frag)
942         {
943                 glUseProgram(frag);
944                 float w = get_output()->get_w();
945                 float h = get_output()->get_h();
946                 float texture_w = get_output()->get_texture_w();
947                 float texture_h = get_output()->get_texture_h();
948                 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
949                 glUniform1f(glGetUniformLocation(frag, "half_w"), w / 2 / texture_w);
950                 glUniform1f(glGetUniformLocation(frag, "half_h"), h / 2 / texture_h);
951                 if(config.shape == GradientConfig::LINEAR)
952                 {
953                         glUniform1f(glGetUniformLocation(frag, "center_x"),
954                                 w / 2 / texture_w);
955                         glUniform1f(glGetUniformLocation(frag, "center_y"),
956                                 h / 2 / texture_h);
957                 }
958                 else
959                 {
960                         glUniform1f(glGetUniformLocation(frag, "center_x"),
961                                 (float)config.center_x * w / 100 / texture_w);
962                         glUniform1f(glGetUniformLocation(frag, "center_y"),
963                                 (float)config.center_y * h / 100 / texture_h);
964                 }
965                 float gradient_size = hypotf(w / texture_w, h / texture_h);
966                 glUniform1f(glGetUniformLocation(frag, "half_gradient_size"),
967                         gradient_size / 2);
968                 glUniform1f(glGetUniformLocation(frag, "sin_angle"),
969                         sin(config.angle * (M_PI / 180)));
970                 glUniform1f(glGetUniformLocation(frag, "cos_angle"),
971                         cos(config.angle * (M_PI / 180)));
972                 float in_radius = (float)config.in_radius / 100 * gradient_size;
973                 glUniform1f(glGetUniformLocation(frag, "in_radius"), in_radius);
974                 float out_radius = (float)config.out_radius / 100 * gradient_size;
975                 glUniform1f(glGetUniformLocation(frag, "out_radius"), out_radius);
976                 glUniform1f(glGetUniformLocation(frag, "radius_diff"),
977                         out_radius - in_radius);
978
979                 switch(get_output()->get_color_model())
980                 {
981                         case BC_YUV888:
982                         case BC_YUVA8888:
983                         {
984                                 float in1, in2, in3, in4;
985                                 float out1, out2, out3, out4;
986                                 YUV::yuv.rgb_to_yuv_f((float)config.in_r / 0xff,
987                                         (float)config.in_g / 0xff,
988                                         (float)config.in_b / 0xff,
989                                         in1,
990                                         in2,
991                                         in3);
992                                 in4 = (float)config.in_a / 0xff;
993                                 YUV::yuv.rgb_to_yuv_f((float)config.out_r / 0xff,
994                                         (float)config.out_g / 0xff,
995                                         (float)config.out_b / 0xff,
996                                         out1,
997                                         out2,
998                                         out3);
999                                 in2 += 0.5;
1000                                 in3 += 0.5;
1001                                 out2 += 0.5;
1002                                 out3 += 0.5;
1003                                 out4 = (float)config.out_a / 0xff;
1004                                 glUniform4f(glGetUniformLocation(frag, "out_color"),
1005                                         out1, out2, out3, out4);
1006                                 glUniform4f(glGetUniformLocation(frag, "in_color"),
1007                                         in1, in2, in3, in4);
1008                                 break;
1009                         }
1010
1011                         default:
1012                                 glUniform4f(glGetUniformLocation(frag, "out_color"),
1013                                         (float)config.out_r / 0xff,
1014                                         (float)config.out_g / 0xff,
1015                                         (float)config.out_b / 0xff,
1016                                         (float)config.out_a / 0xff);
1017                                 glUniform4f(glGetUniformLocation(frag, "in_color"),
1018                                         (float)config.in_r / 0xff,
1019                                         (float)config.in_g / 0xff,
1020                                         (float)config.in_b / 0xff,
1021                                         (float)config.in_a / 0xff);
1022                                 break;
1023                 }
1024         }
1025
1026         get_output()->draw_texture();
1027         glUseProgram(0);
1028         get_output()->set_opengl_state(VFrame::SCREEN);
1029
1030 #endif
1031         return 0;
1032 }
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044 GradientPackage::GradientPackage()
1045  : LoadPackage()
1046 {
1047 }
1048
1049
1050
1051
1052 GradientUnit::GradientUnit(GradientServer *server, GradientMain *plugin)
1053  : LoadClient(server)
1054 {
1055         this->plugin = plugin;
1056         this->server = server;
1057 }
1058
1059
1060
1061
1062 static float calculate_opacity(float mag,
1063         float in_radius, float out_radius, int rate)
1064 {
1065         float opacity = 0.0;
1066         switch(rate)
1067         {
1068                 case GradientConfig::LINEAR:
1069                         if(mag < in_radius)
1070                                 opacity = 0.0;
1071                         else
1072                         if(mag >= out_radius)
1073                                 opacity = 1.0;
1074                         else
1075                                 opacity = (float)(mag - in_radius) / (out_radius - in_radius);
1076                         break;
1077
1078                 case GradientConfig::LOG:
1079                         if(mag < in_radius)
1080                                 opacity = 0;
1081                         else
1082 // Let this one decay beyond out_radius
1083                                 opacity = 1 - exp(1.0 * -(float)(mag - in_radius) /
1084                                         (out_radius - in_radius));
1085                         break;
1086
1087                 case GradientConfig::SQUARE:
1088                         if(mag < in_radius)
1089                                 opacity = 0.0;
1090                         else
1091                         if(mag >= out_radius)
1092                                 opacity = 1.0;
1093                         else
1094                                 opacity = powf((float)(mag - in_radius) /
1095                                         (out_radius - in_radius), 2.0);
1096                         break;
1097         }
1098         CLAMP(opacity, 0.0, 1.0);
1099         return opacity;
1100 }
1101
1102 #define CREATE_GRADIENT(type, temp, components, max) \
1103 { \
1104 /* Synthesize linear gradient for lookups */ \
1105  \
1106         r_table = malloc(sizeof(type) * gradient_size); \
1107         g_table = malloc(sizeof(type) * gradient_size); \
1108         b_table = malloc(sizeof(type) * gradient_size); \
1109         a_table = malloc(sizeof(type) * gradient_size); \
1110  \
1111         for(int i = 0; i < gradient_size; i++) \
1112         { \
1113                 float opacity = calculate_opacity(i, in_radius, out_radius, plugin->config.rate); \
1114                 float transparency; \
1115  \
1116                 transparency = 1.0 - opacity; \
1117                 ((type*)r_table)[i] = (type)(out1 * opacity + in1 * transparency); \
1118                 ((type*)g_table)[i] = (type)(out2 * opacity + in2 * transparency); \
1119                 ((type*)b_table)[i] = (type)(out3 * opacity + in3 * transparency); \
1120                 ((type*)a_table)[i] = (type)(out4 * opacity + in4 * transparency); \
1121         } \
1122  \
1123         for(int i = pkg->y1; i < pkg->y2; i++) \
1124         { \
1125                 type *gradient_row = (type*)plugin->gradient->get_rows()[i]; \
1126                 type *out_row = (type*)plugin->get_output()->get_rows()[i]; \
1127  \
1128                 switch(plugin->config.shape) \
1129                 { \
1130                         case GradientConfig::LINEAR: \
1131                                 for(int j = 0; j < w; j++) \
1132                                 { \
1133                                         int x = j - half_w; \
1134                                         int y = -(i - half_h); \
1135                  \
1136 /* Rotate by effect angle */ \
1137                                         int mag = (int)(gradient_size / 2 - \
1138                                                 (x * sin_angle + y * cos_angle) + \
1139                                                 0.5); \
1140                  \
1141 /* Get gradient value from these coords */ \
1142                  \
1143                                         if(sizeof(type) == 4) \
1144                                         { \
1145                                                 float opacity = calculate_opacity(mag,  \
1146                                                         in_radius,  \
1147                                                         out_radius, \
1148                                                         plugin->config.rate); \
1149                                                 float transparency = 1.0 - opacity; \
1150                                                 gradient_row[0] = (type)(out1 * opacity + in1 * transparency); \
1151                                                 gradient_row[1] = (type)(out2 * opacity + in2 * transparency); \
1152                                                 gradient_row[2] = (type)(out3 * opacity + in3 * transparency); \
1153                                                 if(components == 4) gradient_row[3] = (type)(out4 * opacity + in4 * transparency); \
1154                                         } \
1155                                         else \
1156                                         if(mag < 0) \
1157                                         { \
1158                                                 gradient_row[0] = out1; \
1159                                                 gradient_row[1] = out2; \
1160                                                 gradient_row[2] = out3; \
1161                                                 if(components == 4) gradient_row[3] = out4; \
1162                                         } \
1163                                         else \
1164                                         if(mag >= gradient_size) \
1165                                         { \
1166                                                 gradient_row[0] = in1; \
1167                                                 gradient_row[1] = in2; \
1168                                                 gradient_row[2] = in3; \
1169                                                 if(components == 4) gradient_row[3] = in4; \
1170                                         } \
1171                                         else \
1172                                         { \
1173                                                 gradient_row[0] = ((type*)r_table)[mag]; \
1174                                                 gradient_row[1] = ((type*)g_table)[mag]; \
1175                                                 gradient_row[2] = ((type*)b_table)[mag]; \
1176                                                 if(components == 4) gradient_row[3] = ((type*)a_table)[mag]; \
1177                                         } \
1178  \
1179 /* Overlay mixed colormodels onto output */ \
1180                                         if(gradient_cmodel != output_cmodel) \
1181                                         { \
1182                                                 temp opacity = gradient_row[3]; \
1183                                                 temp transparency = max - opacity; \
1184                                                 out_row[0] = (transparency * out_row[0] + opacity * gradient_row[0]) / max; \
1185                                                 out_row[1] = (transparency * out_row[1] + opacity * gradient_row[1]) / max; \
1186                                                 out_row[2] = (transparency * out_row[2] + opacity * gradient_row[2]) / max; \
1187                                                 out_row += 3; \
1188                                         } \
1189  \
1190                                         gradient_row += components; \
1191                                 } \
1192                                 break; \
1193  \
1194                         case GradientConfig::RADIAL: \
1195                                 for(int j = 0; j < w; j++) \
1196                                 { \
1197                                         double x = j - center_x; \
1198                                         double y = i - center_y; \
1199                                         double magnitude = hypot(x, y); \
1200                                         int mag = (int)magnitude; \
1201                                         if(sizeof(type) == 4) \
1202                                         { \
1203                                                 float opacity = calculate_opacity(mag,  \
1204                                                         in_radius,  \
1205                                                         out_radius, \
1206                                                         plugin->config.rate); \
1207                                                 float transparency = 1.0 - opacity; \
1208                                                 gradient_row[0] = (type)(out1 * opacity + in1 * transparency); \
1209                                                 gradient_row[1] = (type)(out2 * opacity + in2 * transparency); \
1210                                                 gradient_row[2] = (type)(out3 * opacity + in3 * transparency); \
1211                                                 if(components == 4) gradient_row[3] = (type)(out4 * opacity + in4 * transparency); \
1212                                         } \
1213                                         else \
1214                                         { \
1215                                                 gradient_row[0] = ((type*)r_table)[mag]; \
1216                                                 gradient_row[1] = ((type*)g_table)[mag]; \
1217                                                 gradient_row[2] = ((type*)b_table)[mag]; \
1218                                                 if(components == 4) gradient_row[3] = ((type*)a_table)[mag]; \
1219                                         } \
1220  \
1221 /* Overlay mixed colormodels onto output */ \
1222                                         if(gradient_cmodel != output_cmodel) \
1223                                         { \
1224                                                 temp opacity = gradient_row[3]; \
1225                                                 temp transparency = max - opacity; \
1226                                                 out_row[0] = (transparency * out_row[0] + opacity * gradient_row[0]) / max; \
1227                                                 out_row[1] = (transparency * out_row[1] + opacity * gradient_row[1]) / max; \
1228                                                 out_row[2] = (transparency * out_row[2] + opacity * gradient_row[2]) / max; \
1229                                                 out_row += 3; \
1230                                         } \
1231  \
1232                                         gradient_row += components; \
1233                                 } \
1234                                 break; \
1235                 } \
1236         } \
1237 }
1238
1239 void GradientUnit::process_package(LoadPackage *package)
1240 {
1241         GradientPackage *pkg = (GradientPackage*)package;
1242         int h = plugin->input->get_h();
1243         int w = plugin->input->get_w();
1244         int half_w = w / 2;
1245         int half_h = h / 2;
1246         int gradient_size = (int)(ceil(hypot(w, h)));
1247         int in_radius = (int)(plugin->config.in_radius / 100 * gradient_size);
1248         int out_radius = (int)(plugin->config.out_radius / 100 * gradient_size);
1249         double sin_angle = sin(plugin->config.angle * (M_PI / 180));
1250         double cos_angle = cos(plugin->config.angle * (M_PI / 180));
1251         double center_x = plugin->config.center_x * w / 100;
1252         double center_y = plugin->config.center_y * h / 100;
1253         void *r_table = 0;
1254         void *g_table = 0;
1255         void *b_table = 0;
1256         void *a_table = 0;
1257         int gradient_cmodel = plugin->gradient->get_color_model();
1258         int output_cmodel = plugin->get_output()->get_color_model();
1259
1260         if(in_radius > out_radius)
1261         {
1262             in_radius ^= out_radius;
1263             out_radius ^= in_radius;
1264             in_radius ^= out_radius;
1265         }
1266
1267
1268         switch(gradient_cmodel)
1269         {
1270                 case BC_RGB888:
1271                 {
1272                         int in1 = plugin->config.in_r;
1273                         int in2 = plugin->config.in_g;
1274                         int in3 = plugin->config.in_b;
1275                         int in4 = plugin->config.in_a;
1276                         int out1 = plugin->config.out_r;
1277                         int out2 = plugin->config.out_g;
1278                         int out3 = plugin->config.out_b;
1279                         int out4 = plugin->config.out_a;
1280                         CREATE_GRADIENT(unsigned char, int, 3, 0xff)
1281                         break;
1282                 }
1283
1284                 case BC_RGBA8888:
1285                 {
1286                         int in1 = plugin->config.in_r;
1287                         int in2 = plugin->config.in_g;
1288                         int in3 = plugin->config.in_b;
1289                         int in4 = plugin->config.in_a;
1290                         int out1 = plugin->config.out_r;
1291                         int out2 = plugin->config.out_g;
1292                         int out3 = plugin->config.out_b;
1293                         int out4 = plugin->config.out_a;
1294                         CREATE_GRADIENT(unsigned char, int, 4, 0xff)
1295                         break;
1296                 }
1297
1298                 case BC_RGB_FLOAT:
1299                 {
1300                         float in1 = (float)plugin->config.in_r / 0xff;
1301                         float in2 = (float)plugin->config.in_g / 0xff;
1302                         float in3 = (float)plugin->config.in_b / 0xff;
1303                         float in4 = (float)plugin->config.in_a / 0xff;
1304                         float out1 = (float)plugin->config.out_r / 0xff;
1305                         float out2 = (float)plugin->config.out_g / 0xff;
1306                         float out3 = (float)plugin->config.out_b / 0xff;
1307                         float out4 = (float)plugin->config.out_a / 0xff;
1308                         CREATE_GRADIENT(float, float, 3, 1.0)
1309                         break;
1310                 }
1311
1312                 case BC_RGBA_FLOAT:
1313                 {
1314                         float in1 = (float)plugin->config.in_r / 0xff;
1315                         float in2 = (float)plugin->config.in_g / 0xff;
1316                         float in3 = (float)plugin->config.in_b / 0xff;
1317                         float in4 = (float)plugin->config.in_a / 0xff;
1318                         float out1 = (float)plugin->config.out_r / 0xff;
1319                         float out2 = (float)plugin->config.out_g / 0xff;
1320                         float out3 = (float)plugin->config.out_b / 0xff;
1321                         float out4 = (float)plugin->config.out_a / 0xff;
1322                         CREATE_GRADIENT(float, float, 4, 1.0)
1323                         break;
1324                 }
1325
1326                 case BC_YUV888:
1327                 {
1328                         int in1, in2, in3, in4;
1329                         int out1, out2, out3, out4;
1330                         yuv.rgb_to_yuv_8(plugin->config.in_r,
1331                                 plugin->config.in_g,
1332                                 plugin->config.in_b,
1333                                 in1,
1334                                 in2,
1335                                 in3);
1336                         in4 = plugin->config.in_a;
1337                         yuv.rgb_to_yuv_8(plugin->config.out_r,
1338                                 plugin->config.out_g,
1339                                 plugin->config.out_b,
1340                                 out1,
1341                                 out2,
1342                                 out3);
1343                         out4 = plugin->config.out_a;
1344                         CREATE_GRADIENT(unsigned char, int, 3, 0xff)
1345                         break;
1346                 }
1347
1348                 case BC_YUVA8888:
1349                 {
1350                         int in1, in2, in3, in4;
1351                         int out1, out2, out3, out4;
1352                         yuv.rgb_to_yuv_8(plugin->config.in_r,
1353                                 plugin->config.in_g,
1354                                 plugin->config.in_b,
1355                                 in1,
1356                                 in2,
1357                                 in3);
1358                         in4 = plugin->config.in_a;
1359                         yuv.rgb_to_yuv_8(plugin->config.out_r,
1360                                 plugin->config.out_g,
1361                                 plugin->config.out_b,
1362                                 out1,
1363                                 out2,
1364                                 out3);
1365                         out4 = plugin->config.out_a;
1366                         CREATE_GRADIENT(unsigned char, int, 4, 0xff)
1367                         break;
1368                 }
1369         }
1370
1371         if(r_table) free(r_table);
1372         if(g_table) free(g_table);
1373         if(b_table) free(b_table);
1374         if(a_table) free(a_table);
1375 }
1376
1377
1378
1379
1380
1381
1382 GradientServer::GradientServer(GradientMain *plugin,
1383         int total_clients,
1384         int total_packages)
1385  : LoadServer(total_clients, total_packages)
1386 {
1387         this->plugin = plugin;
1388 }
1389
1390 void GradientServer::init_packages()
1391 {
1392         for(int i = 0; i < get_total_packages(); i++)
1393         {
1394                 GradientPackage *package = (GradientPackage*)get_package(i);
1395                 package->y1 = plugin->input->get_h() *
1396                         i /
1397                         get_total_packages();
1398                 package->y2 = plugin->input->get_h() *
1399                         (i + 1) /
1400                         get_total_packages();
1401         }
1402 }
1403
1404 LoadClient* GradientServer::new_client()
1405 {
1406         return new GradientUnit(this, plugin);
1407 }
1408
1409 LoadPackage* GradientServer::new_package()
1410 {
1411         return new GradientPackage;
1412 }
1413
1414
1415
1416
1417