dca56c2d48b1d97606ffd9f560edb6ae08ea411f
[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 #define MAXVALUE 100
98
99 RGBShiftLevel::RGBShiftLevel(RGBShiftEffect *plugin, int *output, int x, int y)
100  : BC_ISlider(x, y, 0, 200, 200, -MAXVALUE, MAXVALUE, *output)
101 {
102         this->plugin = plugin;
103         this->output = output;
104 }
105
106 int RGBShiftLevel::handle_event()
107 {
108         *output = get_value();
109         plugin->send_configure_change();
110         return 1;
111 }
112
113
114 RGBShiftReset::RGBShiftReset(RGBShiftEffect *plugin, RGBShiftWindow *window, int x, int y)
115  : BC_GenericButton(x, y, _("Reset"))
116 {
117         this->plugin = plugin;
118         this->window = window;
119 }
120 RGBShiftReset::~RGBShiftReset()
121 {
122 }
123 int RGBShiftReset::handle_event()
124 {
125         plugin->config.reset(RESET_ALL);
126         window->update_gui(RESET_ALL);
127         plugin->send_configure_change();
128         return 1;
129 }
130
131
132 RGBShiftSliderClr::RGBShiftSliderClr(RGBShiftEffect *plugin, RGBShiftWindow *window, int x, int y, int w, int clear)
133  : BC_GenericButton(x, y, w, _("⌂"))
134 {
135         this->plugin = plugin;
136         this->window = window;
137         this->clear = clear;
138 }
139 RGBShiftSliderClr::~RGBShiftSliderClr()
140 {
141 }
142 int RGBShiftSliderClr::handle_event()
143 {
144         // clear==1 ==> r_dx slider --- clear==2 ==> r_dy slider
145         // clear==3 ==> g_dx slider --- clear==4 ==> g_dy slider
146         // clear==5 ==> b_dx slider --- clear==6 ==> b_dy slider
147         plugin->config.reset(clear);
148         window->update_gui(clear);
149         plugin->send_configure_change();
150         return 1;
151 }
152
153
154 RGBShiftWindow::RGBShiftWindow(RGBShiftEffect *plugin)
155  : PluginClientWindow(plugin, 320, 230, 320, 230, 0)
156 {
157         this->plugin = plugin;
158 }
159
160 void RGBShiftWindow::create_objects()
161 {
162         int x = 10, y = 10, x1 = 50;
163         int x2 = 0; int clrBtn_w = 50;
164
165         add_subwindow(new BC_Title(x, y, _("R_dx:")));
166         add_subwindow(r_dx = new RGBShiftLevel(plugin, &plugin->config.r_dx, x1, y));
167         x2 = x1 + r_dx->get_w() + 10;
168         add_subwindow(r_dxClr = new RGBShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_R_DX));
169
170         y += 30;
171         add_subwindow(new BC_Title(x, y, _("R_dy:")));
172         add_subwindow(r_dy = new RGBShiftLevel(plugin, &plugin->config.r_dy, x1, y));
173         add_subwindow(r_dyClr = new RGBShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_R_DY));
174
175         y += 30;
176         add_subwindow(new BC_Title(x, y, _("G_dx:")));
177         add_subwindow(g_dx = new RGBShiftLevel(plugin, &plugin->config.g_dx, x1, y));
178         add_subwindow(g_dxClr = new RGBShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_G_DX));
179
180         y += 30;
181         add_subwindow(new BC_Title(x, y, _("G_dy:")));
182         add_subwindow(g_dy = new RGBShiftLevel(plugin, &plugin->config.g_dy, x1, y));
183         add_subwindow(g_dyClr = new RGBShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_G_DY));
184
185         y += 30;
186         add_subwindow(new BC_Title(x, y, _("B_dx:")));
187         add_subwindow(b_dx = new RGBShiftLevel(plugin, &plugin->config.b_dx, x1, y));
188         add_subwindow(b_dxClr = new RGBShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_B_DX));
189
190         y += 30;
191         add_subwindow(new BC_Title(x, y, _("B_dy:")));
192         add_subwindow(b_dy = new RGBShiftLevel(plugin, &plugin->config.b_dy, x1, y));
193         add_subwindow(b_dyClr = new RGBShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_B_DY));
194
195         y += 40;
196         add_subwindow(reset = new RGBShiftReset(plugin, this, x, y));
197
198         show_window();
199         flush();
200 }
201
202
203 // for Reset button
204 void RGBShiftWindow::update_gui(int clear)
205 {
206         switch(clear) {
207                 case RESET_R_DX : r_dx->update(plugin->config.r_dx);
208                         break;
209                 case RESET_R_DY : r_dy->update(plugin->config.r_dy);
210                         break;
211                 case RESET_G_DX : g_dx->update(plugin->config.g_dx);
212                         break;
213                 case RESET_G_DY : g_dy->update(plugin->config.g_dy);
214                         break;
215                 case RESET_B_DX : b_dx->update(plugin->config.b_dx);
216                         break;
217                 case RESET_B_DY : b_dy->update(plugin->config.b_dy);
218                         break;
219                 case RESET_ALL :
220                 default:
221                         r_dx->update(plugin->config.r_dx);
222                         r_dy->update(plugin->config.r_dy);
223                         g_dx->update(plugin->config.g_dx);
224                         g_dy->update(plugin->config.g_dy);
225                         b_dx->update(plugin->config.b_dx);
226                         b_dy->update(plugin->config.b_dy);
227                         break;
228         }
229 }
230
231
232
233
234
235
236 RGBShiftEffect::RGBShiftEffect(PluginServer *server)
237  : PluginVClient(server)
238 {
239         temp_frame = 0;
240 }
241 RGBShiftEffect::~RGBShiftEffect()
242 {
243         delete temp_frame;
244 }
245
246 const char* RGBShiftEffect::plugin_title() { return N_("RGBShift"); }
247 int RGBShiftEffect::is_realtime() { return 1; }
248
249
250 NEW_WINDOW_MACRO(RGBShiftEffect, RGBShiftWindow)
251 LOAD_CONFIGURATION_MACRO(RGBShiftEffect, RGBShiftConfig)
252
253 void RGBShiftEffect::update_gui()
254 {
255         if(thread)
256         {
257                 RGBShiftWindow *yuv_wdw = (RGBShiftWindow*)thread->window;
258                 yuv_wdw->lock_window("RGBShiftEffect::update_gui");
259                 load_configuration();
260                 yuv_wdw->r_dx->update(config.r_dx);
261                 yuv_wdw->r_dy->update(config.r_dy);
262                 yuv_wdw->g_dx->update(config.g_dx);
263                 yuv_wdw->g_dy->update(config.g_dy);
264                 yuv_wdw->b_dx->update(config.b_dx);
265                 yuv_wdw->b_dy->update(config.b_dy);
266                 yuv_wdw->unlock_window();
267         }
268 }
269
270 void RGBShiftEffect::save_data(KeyFrame *keyframe)
271 {
272         FileXML output;
273         output.set_shared_output(keyframe->xbuf);
274         output.tag.set_title("RGBSHIFT");
275         output.tag.set_property("R_DX", config.r_dx);
276         output.tag.set_property("R_DY", config.r_dy);
277         output.tag.set_property("G_DX", config.g_dx);
278         output.tag.set_property("G_DY", config.g_dy);
279         output.tag.set_property("B_DX", config.b_dx);
280         output.tag.set_property("B_DY", config.b_dy);
281         output.append_tag();
282         output.tag.set_title("/RGBSHIFT");
283         output.append_tag();
284         output.append_newline();
285         output.terminate_string();
286 }
287
288 void RGBShiftEffect::read_data(KeyFrame *keyframe)
289 {
290         FileXML input;
291         input.set_shared_input(keyframe->xbuf);
292         while(!input.read_tag())
293         {
294                 if(input.tag.title_is("RGBSHIFT"))
295                 {
296                         config.r_dx = input.tag.get_property("R_DX", config.r_dx);
297                         config.r_dy = input.tag.get_property("R_DY", config.r_dy);
298                         config.g_dx = input.tag.get_property("G_DX", config.g_dx);
299                         config.g_dy = input.tag.get_property("G_DY", config.g_dy);
300                         config.b_dx = input.tag.get_property("B_DX", config.b_dx);
301                         config.b_dy = input.tag.get_property("B_DY", config.b_dy);
302                 }
303         }
304 }
305
306 #define RGB_MACRO(type, temp_type, components) \
307 { \
308         for(int i = 0; i < h; i++) { \
309                 int ri = i + config.r_dy, gi = i + config.g_dy, bi = i + config.b_dy; \
310                 type *in_r = ri >= 0 && ri < h ? (type *)frame->get_rows()[ri] : 0; \
311                 type *in_g = gi >= 0 && gi < h ? (type *)frame->get_rows()[gi] : 0; \
312                 type *in_b = bi >= 0 && bi < h ? (type *)frame->get_rows()[bi] : 0; \
313                 type *out_row = (type *)output->get_rows()[i]; \
314                 for(int j = 0; j < w; j++) { \
315                         int rj = j + config.r_dx, gj = j + config.g_dx, bj = j + config.b_dx; \
316                         type *rp = in_r && rj >= 0 && rj < w ? in_r + rj*components: 0; \
317                         type *gp = in_g && gj >= 0 && gj < w ? in_g + gj*components: 0; \
318                         type *bp = in_b && bj >= 0 && bj < w ? in_b + bj*components: 0; \
319                         out_row[0] = rp ? rp[0] : 0; \
320                         out_row[1] = gp ? gp[1] : 0; \
321                         out_row[2] = bp ? bp[2] : 0; \
322                         out_row += components; \
323                 } \
324         } \
325 }
326
327 #define YUV_MACRO(type, temp_type, components) \
328 { \
329         for(int i = 0; i < h; i++) { \
330                 int ri = i + config.r_dy, gi = i + config.g_dy, bi = i + config.b_dy; \
331                 uint8_t *in_r = ri >= 0 && ri < h ? (uint8_t *)frame->get_rows()[ri] : 0; \
332                 uint8_t *in_g = gi >= 0 && gi < h ? (uint8_t *)frame->get_rows()[gi] : 0; \
333                 uint8_t *in_b = bi >= 0 && bi < h ? (uint8_t *)frame->get_rows()[bi] : 0; \
334                 type *out_row = (type *)output->get_rows()[i]; \
335                 for(int j = 0; j < w; j++) { \
336                         int rj = j + config.r_dx, gj = j + config.g_dx, bj = j + config.b_dx; \
337                         uint8_t *rp = in_r && rj >= 0 && rj < w ? in_r + rj*3: 0; \
338                         uint8_t *gp = in_g && gj >= 0 && gj < w ? in_g + gj*3: 0; \
339                         uint8_t *bp = in_b && bj >= 0 && bj < w ? in_b + bj*3: 0; \
340                         temp_type y, u, v; \
341                         temp_type r = rp ? rp[0] : 0; \
342                         temp_type g = gp ? gp[1] : 0; \
343                         temp_type b = bp ? bp[2] : 0; \
344                         if( sizeof(type) == 4 ) \
345                                 YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v); \
346                         else if( sizeof(type) == 2 ) \
347                                 YUV::yuv.rgb_to_yuv_16(r, g, b, y, u, v); \
348                         else \
349                                 YUV::yuv.rgb_to_yuv_8(r, g, b, y, u, v); \
350                         out_row[0] = y; \
351                         out_row[1] = u; \
352                         out_row[2] = v; \
353                         out_row += components; \
354                 } \
355         } \
356 }
357
358 int RGBShiftEffect::process_realtime(VFrame *input, VFrame *output)
359 {
360         load_configuration();
361
362         if( EQUIV(config.r_dx, 0) && EQUIV(config.g_dx, 0) && EQUIV(config.b_dx, 0) &&
363                 EQUIV(config.r_dy, 0) && EQUIV(config.g_dy, 0) && EQUIV(config.b_dy, 0) ) {
364                 if(input->get_rows()[0] != output->get_rows()[0])
365                         output->copy_from(input);
366                 return 0;
367         }
368
369         int w = input->get_w(), h = input->get_h();
370         int color_model = input->get_color_model();
371         int is_yuv = BC_CModels::is_yuv(color_model);
372         if( is_yuv ) color_model = BC_RGB888;
373         VFrame *frame = input;
374         if( input->get_rows()[0] == output->get_rows()[0] || !is_yuv ) {
375                 if( temp_frame && ( temp_frame->get_color_model() != color_model ||
376                         temp_frame->get_w() != w || temp_frame->get_h() != h ) ) {
377                         delete temp_frame;  temp_frame = 0;
378                 }
379                 if( !temp_frame )
380                         temp_frame = new VFrame(w, h, color_model, 0);
381                 frame = temp_frame;
382                 if( color_model != input->get_color_model() )
383                         BC_CModels::transfer(frame->get_rows(), input->get_rows(),
384                                 frame->get_y(), frame->get_u(), frame->get_v(),
385                                 input->get_y(), input->get_u(), input->get_v(),
386                                 0, 0, input->get_w(), input->get_h(),
387                                 0, 0, frame->get_w(), frame->get_h(),
388                                 input->get_color_model(), frame->get_color_model(), 0,
389                                 input->get_bytes_per_line(), w);
390                 else
391                         frame->copy_from(input);
392         }
393
394         switch( input->get_color_model() ) {
395         case BC_YUV888:       YUV_MACRO(unsigned char, int, 3);  break;
396         case BC_YUV161616:    YUV_MACRO(uint16_t, int, 3);       break;
397         case BC_YUVA8888:     YUV_MACRO(unsigned char, int, 4);  break;
398         case BC_YUVA16161616: YUV_MACRO(uint16_t, int, 4);       break;
399         case BC_RGB_FLOAT:    RGB_MACRO(float, float, 3);        break;
400         case BC_RGB888:       RGB_MACRO(unsigned char, int, 3);  break;
401         case BC_RGB161616:    RGB_MACRO(uint16_t, int, 3);       break;
402         case BC_RGBA_FLOAT:   RGB_MACRO(float, float, 4);        break;
403         case BC_RGBA8888:     RGB_MACRO(unsigned char, int, 4);  break;
404         case BC_RGBA16161616: RGB_MACRO(uint16_t, int, 4);       break;
405         }
406
407         return 0;
408 }
409
410