reverse Fade In, Fade Out patch from Freelancer
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / rgbshift / rgbshift.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 "rgbshift.h"
23
24
25
26 REGISTER_PLUGIN(RGBShiftEffect)
27
28
29
30
31
32
33 RGBShiftConfig::RGBShiftConfig()
34 {
35         reset(RESET_ALL);
36 }
37
38 void RGBShiftConfig::reset(int clear)
39 {
40         switch(clear) {
41                 case RESET_R_DX : r_dx = 0;
42                         break;
43                 case RESET_R_DY : r_dy = 0;
44                         break;
45                 case RESET_G_DX : g_dx = 0;
46                         break;
47                 case RESET_G_DY : g_dy = 0;
48                         break;
49                 case RESET_B_DX : b_dx = 0;
50                         break;
51                 case RESET_B_DY : b_dy = 0;
52                         break;
53                 case RESET_ALL :
54                 default:
55                         r_dx = r_dy = 0;
56                         g_dx = g_dy = 0;
57                         b_dx = b_dy = 0;
58                         break;
59         }
60 }
61
62 void RGBShiftConfig::copy_from(RGBShiftConfig &src)
63 {
64         r_dx = src.r_dx;  r_dy = src.r_dy;
65         g_dx = src.g_dx;  g_dy = src.g_dy;
66         b_dx = src.b_dx;  b_dy = src.b_dy;
67 }
68
69 int RGBShiftConfig::equivalent(RGBShiftConfig &src)
70 {
71         return EQUIV(r_dx, src.r_dx) && EQUIV(g_dx, src.g_dx) && EQUIV(b_dx, src.b_dx) &&
72                 EQUIV(r_dy, src.r_dy) && EQUIV(g_dy, src.g_dy) && EQUIV(b_dy, src.b_dy);
73 }
74
75 void RGBShiftConfig::interpolate(RGBShiftConfig &prev,
76         RGBShiftConfig &next,
77         long prev_frame,
78         long next_frame,
79         long current_frame)
80 {
81         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
82         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
83
84         r_dx = prev.r_dx * prev_scale + next.r_dx * next_scale;
85         r_dy = prev.r_dy * prev_scale + next.r_dy * next_scale;
86         g_dx = prev.g_dx * prev_scale + next.g_dx * next_scale;
87         g_dy = prev.g_dy * prev_scale + next.g_dy * next_scale;
88         b_dx = prev.b_dx * prev_scale + next.b_dx * next_scale;
89         b_dy = prev.b_dy * prev_scale + next.b_dy * next_scale;
90 }
91
92
93
94
95
96
97
98 RGBShiftIText::RGBShiftIText(RGBShiftWindow *window, RGBShiftEffect *plugin,
99         RGBShiftISlider *slider, int *output, int x, int y, int min, int max)
100  : BC_TumbleTextBox(window, *output,
101         min, max, x, y, xS(60), 0)
102 {
103         this->window = window;
104         this->plugin = plugin;
105         this->output = output;
106         this->slider = slider;
107         this->min = min;
108         this->max = max;
109         set_increment(1);
110 }
111
112 RGBShiftIText::~RGBShiftIText()
113 {
114 }
115
116 int RGBShiftIText::handle_event()
117 {
118         *output = atoi(get_text());
119         if(*output > max) *output = max;
120         if(*output < min) *output = min;
121         slider->update(*output);
122         plugin->send_configure_change();
123         return 1;
124 }
125
126 RGBShiftISlider::RGBShiftISlider(RGBShiftEffect *plugin, RGBShiftIText *text, int *output, int x, int y)
127  : BC_ISlider(x, y, 0, xS(200), xS(200), -MAXVALUE, MAXVALUE, *output)
128 {
129         this->plugin = plugin;
130         this->output = output;
131         this->text = text;
132         enable_show_value(0); // Hide caption
133 }
134
135 int RGBShiftISlider::handle_event()
136 {
137         *output = get_value();
138         text->update((int64_t)*output);
139         plugin->send_configure_change();
140         return 1;
141 }
142
143
144 RGBShiftReset::RGBShiftReset(RGBShiftEffect *plugin, RGBShiftWindow *window, int x, int y)
145  : BC_GenericButton(x, y, _("Reset"))
146 {
147         this->plugin = plugin;
148         this->window = window;
149 }
150 RGBShiftReset::~RGBShiftReset()
151 {
152 }
153 int RGBShiftReset::handle_event()
154 {
155         plugin->config.reset(RESET_ALL);
156         window->update_gui(RESET_ALL);
157         plugin->send_configure_change();
158         return 1;
159 }
160
161
162 RGBShiftClr::RGBShiftClr(RGBShiftEffect *plugin, RGBShiftWindow *window, int x, int y, int clear)
163  : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
164 {
165         this->plugin = plugin;
166         this->window = window;
167         this->clear = clear;
168 }
169 RGBShiftClr::~RGBShiftClr()
170 {
171 }
172 int RGBShiftClr::handle_event()
173 {
174         // clear==1 ==> r_dx slider --- clear==2 ==> r_dy slider
175         // clear==3 ==> g_dx slider --- clear==4 ==> g_dy slider
176         // clear==5 ==> b_dx slider --- clear==6 ==> b_dy slider
177         plugin->config.reset(clear);
178         window->update_gui(clear);
179         plugin->send_configure_change();
180         return 1;
181 }
182
183
184 RGBShiftWindow::RGBShiftWindow(RGBShiftEffect *plugin)
185  : PluginClientWindow(plugin, xS(420), yS(250), xS(420), yS(250), 0)
186 {
187         this->plugin = plugin;
188 }
189
190 void RGBShiftWindow::create_objects()
191 {
192         int xs10 = xS(10);
193         int ys10 = yS(10), ys30 = yS(30), ys40 = yS(40);
194         int x = xs10, y = ys10;
195         int x2 = xS(80), x3 = xS(180);
196         int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
197
198         BC_Bar *bar;
199
200         y += ys10;
201         add_subwindow(new BC_Title(x, y, _("R_dx:")));
202         r_dx_text = new RGBShiftIText(this, plugin,
203                 0, &plugin->config.r_dx, (x + x2), y, -MAXVALUE, MAXVALUE);
204         r_dx_text->create_objects();
205         r_dx_slider = new RGBShiftISlider(plugin,
206                 r_dx_text, &plugin->config.r_dx, x3, y);
207         add_subwindow(r_dx_slider);
208         r_dx_text->slider = r_dx_slider;
209         clr_x = x3 + r_dx_slider->get_w() + x;
210         add_subwindow(r_dx_Clr = new RGBShiftClr(plugin, this, clr_x, y, RESET_R_DX));
211         y += ys30;
212
213         add_subwindow(new BC_Title(x, y, _("R_dy:")));
214         r_dy_text = new RGBShiftIText(this, plugin,
215                 0, &plugin->config.r_dy, (x + x2), y, -MAXVALUE, MAXVALUE);
216         r_dy_text->create_objects();
217         r_dy_slider = new RGBShiftISlider(plugin,
218                 r_dy_text, &plugin->config.r_dy, x3, y);
219         add_subwindow(r_dy_slider);
220         r_dy_text->slider = r_dy_slider;
221         add_subwindow(r_dy_Clr = new RGBShiftClr(plugin, this, clr_x, y, RESET_R_DY));
222         y += ys30;
223
224         add_subwindow(new BC_Title(x, y, _("G_dx:")));
225         g_dx_text = new RGBShiftIText(this, plugin,
226                 0, &plugin->config.g_dx, (x + x2), y, -MAXVALUE, MAXVALUE);
227         g_dx_text->create_objects();
228         g_dx_slider = new RGBShiftISlider(plugin,
229                 g_dx_text, &plugin->config.g_dx, x3, y);
230         add_subwindow(g_dx_slider);
231         g_dx_text->slider = g_dx_slider;
232         add_subwindow(g_dx_Clr = new RGBShiftClr(plugin, this, clr_x, y, RESET_G_DX));
233         y += ys30;
234
235         add_subwindow(new BC_Title(x, y, _("G_dy:")));
236         g_dy_text = new RGBShiftIText(this, plugin,
237                 0, &plugin->config.g_dy, (x + x2), y, -MAXVALUE, MAXVALUE);
238         g_dy_text->create_objects();
239         g_dy_slider = new RGBShiftISlider(plugin,
240                 g_dy_text, &plugin->config.g_dy, x3, y);
241         add_subwindow(g_dy_slider);
242         g_dy_text->slider = g_dy_slider;
243         add_subwindow(g_dy_Clr = new RGBShiftClr(plugin, this, clr_x, y, RESET_G_DY));
244         y += ys30;
245
246         add_subwindow(new BC_Title(x, y, _("B_dx:")));
247         b_dx_text = new RGBShiftIText(this, plugin,
248                 0, &plugin->config.b_dx, (x + x2), y, -MAXVALUE, MAXVALUE);
249         b_dx_text->create_objects();
250         b_dx_slider = new RGBShiftISlider(plugin,
251                 b_dx_text, &plugin->config.b_dx, x3, y);
252         add_subwindow(b_dx_slider);
253         b_dx_text->slider = b_dx_slider;
254         add_subwindow(b_dx_Clr = new RGBShiftClr(plugin, this, clr_x, y, RESET_B_DX));
255         y += ys30;
256
257         add_subwindow(new BC_Title(x, y, _("B_dy:")));
258         b_dy_text = new RGBShiftIText(this, plugin,
259                 0, &plugin->config.b_dy, (x + x2), y, -MAXVALUE, MAXVALUE);
260         b_dy_text->create_objects();
261         b_dy_slider = new RGBShiftISlider(plugin,
262                 b_dy_text, &plugin->config.b_dy, x3, y);
263         add_subwindow(b_dy_slider);
264         b_dy_text->slider = b_dy_slider;
265         add_subwindow(b_dy_Clr = new RGBShiftClr(plugin, this, clr_x, y, RESET_B_DY));
266         y += ys40;
267
268 // Reset section
269         add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
270         y += ys10;
271         add_subwindow(reset = new RGBShiftReset(plugin, this, x, y));
272
273         show_window();
274         flush();
275 }
276
277
278 // for Reset button
279 void RGBShiftWindow::update_gui(int clear)
280 {
281         switch(clear) {
282                 case RESET_R_DX :
283                         r_dx_text->update((int64_t)plugin->config.r_dx);
284                         r_dx_slider->update(plugin->config.r_dx);
285                         break;
286                 case RESET_R_DY :
287                         r_dy_text->update((int64_t)plugin->config.r_dy);
288                         r_dy_slider->update(plugin->config.r_dy);
289                         break;
290                 case RESET_G_DX :
291                         g_dx_text->update((int64_t)plugin->config.g_dx);
292                         g_dx_slider->update(plugin->config.g_dx);
293                         break;
294                 case RESET_G_DY :
295                         g_dy_text->update((int64_t)plugin->config.g_dy);
296                         g_dy_slider->update(plugin->config.g_dy);
297                         break;
298                 case RESET_B_DX :
299                         b_dx_text->update((int64_t)plugin->config.b_dx);
300                         b_dx_slider->update(plugin->config.b_dx);
301                         break;
302                 case RESET_B_DY :
303                         b_dy_text->update((int64_t)plugin->config.b_dy);
304                         b_dy_slider->update(plugin->config.b_dy);
305                         break;
306                 case RESET_ALL :
307                 default:
308                         r_dx_text->update((int64_t)plugin->config.r_dx);
309                         r_dx_slider->update(plugin->config.r_dx);
310                         r_dy_text->update((int64_t)plugin->config.r_dy);
311                         r_dy_slider->update(plugin->config.r_dy);
312                         g_dx_text->update((int64_t)plugin->config.g_dx);
313                         g_dx_slider->update(plugin->config.g_dx);
314                         g_dy_text->update((int64_t)plugin->config.g_dy);
315                         g_dy_slider->update(plugin->config.g_dy);
316                         b_dx_text->update((int64_t)plugin->config.b_dx);
317                         b_dx_slider->update(plugin->config.b_dx);
318                         b_dy_text->update((int64_t)plugin->config.b_dy);
319                         b_dy_slider->update(plugin->config.b_dy);
320                         break;
321         }
322 }
323
324
325
326
327
328
329 RGBShiftEffect::RGBShiftEffect(PluginServer *server)
330  : PluginVClient(server)
331 {
332         temp_frame = 0;
333 }
334 RGBShiftEffect::~RGBShiftEffect()
335 {
336         delete temp_frame;
337 }
338
339 const char* RGBShiftEffect::plugin_title() { return N_("RGBShift"); }
340 int RGBShiftEffect::is_realtime() { return 1; }
341
342
343 NEW_WINDOW_MACRO(RGBShiftEffect, RGBShiftWindow)
344 LOAD_CONFIGURATION_MACRO(RGBShiftEffect, RGBShiftConfig)
345
346 void RGBShiftEffect::update_gui()
347 {
348         if(thread)
349         {
350                 RGBShiftWindow *yuv_wdw = (RGBShiftWindow*)thread->window;
351                 yuv_wdw->lock_window("RGBShiftEffect::update_gui");
352                 load_configuration();
353                 yuv_wdw->r_dx_text->update((int64_t)config.r_dx);
354                 yuv_wdw->r_dx_slider->update(config.r_dx);
355                 yuv_wdw->r_dy_text->update((int64_t)config.r_dy);
356                 yuv_wdw->r_dy_slider->update(config.r_dy);
357                 yuv_wdw->g_dx_text->update((int64_t)config.g_dx);
358                 yuv_wdw->g_dx_slider->update(config.g_dx);
359                 yuv_wdw->g_dy_text->update((int64_t)config.g_dy);
360                 yuv_wdw->g_dy_slider->update(config.g_dy);
361                 yuv_wdw->b_dx_text->update((int64_t)config.b_dx);
362                 yuv_wdw->b_dx_slider->update(config.b_dx);
363                 yuv_wdw->b_dy_text->update((int64_t)config.b_dy);
364                 yuv_wdw->b_dy_slider->update(config.b_dy);
365                 yuv_wdw->unlock_window();
366         }
367 }
368
369 void RGBShiftEffect::save_data(KeyFrame *keyframe)
370 {
371         FileXML output;
372         output.set_shared_output(keyframe->xbuf);
373         output.tag.set_title("RGBSHIFT");
374         output.tag.set_property("R_DX", config.r_dx);
375         output.tag.set_property("R_DY", config.r_dy);
376         output.tag.set_property("G_DX", config.g_dx);
377         output.tag.set_property("G_DY", config.g_dy);
378         output.tag.set_property("B_DX", config.b_dx);
379         output.tag.set_property("B_DY", config.b_dy);
380         output.append_tag();
381         output.tag.set_title("/RGBSHIFT");
382         output.append_tag();
383         output.append_newline();
384         output.terminate_string();
385 }
386
387 void RGBShiftEffect::read_data(KeyFrame *keyframe)
388 {
389         FileXML input;
390         input.set_shared_input(keyframe->xbuf);
391         while(!input.read_tag())
392         {
393                 if(input.tag.title_is("RGBSHIFT"))
394                 {
395                         config.r_dx = input.tag.get_property("R_DX", config.r_dx);
396                         config.r_dy = input.tag.get_property("R_DY", config.r_dy);
397                         config.g_dx = input.tag.get_property("G_DX", config.g_dx);
398                         config.g_dy = input.tag.get_property("G_DY", config.g_dy);
399                         config.b_dx = input.tag.get_property("B_DX", config.b_dx);
400                         config.b_dy = input.tag.get_property("B_DY", config.b_dy);
401                 }
402         }
403 }
404
405 #define RGB_MACRO(type, temp_type, components) \
406 { \
407         for(int i = 0; i < h; i++) { \
408                 int ri = i + config.r_dy, gi = i + config.g_dy, bi = i + config.b_dy; \
409                 type *in_r = ri >= 0 && ri < h ? (type *)frame->get_rows()[ri] : 0; \
410                 type *in_g = gi >= 0 && gi < h ? (type *)frame->get_rows()[gi] : 0; \
411                 type *in_b = bi >= 0 && bi < h ? (type *)frame->get_rows()[bi] : 0; \
412                 type *out_row = (type *)output->get_rows()[i]; \
413                 for(int j = 0; j < w; j++) { \
414                         int rj = j + config.r_dx, gj = j + config.g_dx, bj = j + config.b_dx; \
415                         type *rp = in_r && rj >= 0 && rj < w ? in_r + rj*components: 0; \
416                         type *gp = in_g && gj >= 0 && gj < w ? in_g + gj*components: 0; \
417                         type *bp = in_b && bj >= 0 && bj < w ? in_b + bj*components: 0; \
418                         out_row[0] = rp ? rp[0] : 0; \
419                         out_row[1] = gp ? gp[1] : 0; \
420                         out_row[2] = bp ? bp[2] : 0; \
421                         out_row += components; \
422                 } \
423         } \
424 }
425
426 #define YUV_MACRO(type, temp_type, components) \
427 { \
428         for(int i = 0; i < h; i++) { \
429                 int ri = i + config.r_dy, gi = i + config.g_dy, bi = i + config.b_dy; \
430                 uint8_t *in_r = ri >= 0 && ri < h ? (uint8_t *)frame->get_rows()[ri] : 0; \
431                 uint8_t *in_g = gi >= 0 && gi < h ? (uint8_t *)frame->get_rows()[gi] : 0; \
432                 uint8_t *in_b = bi >= 0 && bi < h ? (uint8_t *)frame->get_rows()[bi] : 0; \
433                 type *out_row = (type *)output->get_rows()[i]; \
434                 for(int j = 0; j < w; j++) { \
435                         int rj = j + config.r_dx, gj = j + config.g_dx, bj = j + config.b_dx; \
436                         uint8_t *rp = in_r && rj >= 0 && rj < w ? in_r + rj*3: 0; \
437                         uint8_t *gp = in_g && gj >= 0 && gj < w ? in_g + gj*3: 0; \
438                         uint8_t *bp = in_b && bj >= 0 && bj < w ? in_b + bj*3: 0; \
439                         temp_type y, u, v; \
440                         temp_type r = rp ? rp[0] : 0; \
441                         temp_type g = gp ? gp[1] : 0; \
442                         temp_type b = bp ? bp[2] : 0; \
443                         if( sizeof(type) == 4 ) \
444                                 YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v); \
445                         else if( sizeof(type) == 2 ) \
446                                 YUV::yuv.rgb_to_yuv_16(r, g, b, y, u, v); \
447                         else \
448                                 YUV::yuv.rgb_to_yuv_8(r, g, b, y, u, v); \
449                         out_row[0] = y; \
450                         out_row[1] = u; \
451                         out_row[2] = v; \
452                         out_row += components; \
453                 } \
454         } \
455 }
456
457 int RGBShiftEffect::process_realtime(VFrame *input, VFrame *output)
458 {
459         load_configuration();
460
461         if( EQUIV(config.r_dx, 0) && EQUIV(config.g_dx, 0) && EQUIV(config.b_dx, 0) &&
462                 EQUIV(config.r_dy, 0) && EQUIV(config.g_dy, 0) && EQUIV(config.b_dy, 0) ) {
463                 if(input->get_rows()[0] != output->get_rows()[0])
464                         output->copy_from(input);
465                 return 0;
466         }
467
468         int w = input->get_w(), h = input->get_h();
469         int color_model = input->get_color_model();
470         int is_yuv = BC_CModels::is_yuv(color_model);
471         if( is_yuv ) color_model = BC_RGB888;
472         VFrame *frame = input;
473         if( input->get_rows()[0] == output->get_rows()[0] || !is_yuv ) {
474                 if( temp_frame && ( temp_frame->get_color_model() != color_model ||
475                         temp_frame->get_w() != w || temp_frame->get_h() != h ) ) {
476                         delete temp_frame;  temp_frame = 0;
477                 }
478                 if( !temp_frame )
479                         temp_frame = new VFrame(w, h, color_model, 0);
480                 frame = temp_frame;
481                 if( color_model != input->get_color_model() )
482                         BC_CModels::transfer(frame->get_rows(), input->get_rows(),
483                                 frame->get_y(), frame->get_u(), frame->get_v(),
484                                 input->get_y(), input->get_u(), input->get_v(),
485                                 0, 0, input->get_w(), input->get_h(),
486                                 0, 0, frame->get_w(), frame->get_h(),
487                                 input->get_color_model(), frame->get_color_model(), 0,
488                                 input->get_bytes_per_line(), w);
489                 else
490                         frame->copy_from(input);
491         }
492
493         switch( input->get_color_model() ) {
494         case BC_YUV888:       YUV_MACRO(unsigned char, int, 3);  break;
495         case BC_YUV161616:    YUV_MACRO(uint16_t, int, 3);       break;
496         case BC_YUVA8888:     YUV_MACRO(unsigned char, int, 4);  break;
497         case BC_YUVA16161616: YUV_MACRO(uint16_t, int, 4);       break;
498         case BC_RGB_FLOAT:    RGB_MACRO(float, float, 3);        break;
499         case BC_RGB888:       RGB_MACRO(unsigned char, int, 3);  break;
500         case BC_RGB161616:    RGB_MACRO(uint16_t, int, 3);       break;
501         case BC_RGBA_FLOAT:   RGB_MACRO(float, float, 4);        break;
502         case BC_RGBA8888:     RGB_MACRO(unsigned char, int, 4);  break;
503         case BC_RGBA16161616: RGB_MACRO(uint16_t, int, 4);       break;
504         }
505
506         return 0;
507 }
508
509