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