af60c04185cdf158cfd59c1bcd685c1fe977e554
[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();
36 }
37
38 void RGBShiftConfig::reset()
39 {
40         r_dx = r_dy = 0;
41         g_dx = g_dy = 0;
42         b_dx = b_dy = 0;
43 }
44
45 void RGBShiftConfig::copy_from(RGBShiftConfig &src)
46 {
47         r_dx = src.r_dx;  r_dy = src.r_dy;
48         g_dx = src.g_dx;  g_dy = src.g_dy;
49         b_dx = src.b_dx;  b_dy = src.b_dy;
50 }
51
52 int RGBShiftConfig::equivalent(RGBShiftConfig &src)
53 {
54         return EQUIV(r_dx, src.r_dx) && EQUIV(g_dx, src.g_dx) && EQUIV(b_dx, src.b_dx) &&
55                 EQUIV(r_dy, src.r_dy) && EQUIV(g_dy, src.g_dy) && EQUIV(b_dy, src.b_dy);
56 }
57
58 void RGBShiftConfig::interpolate(RGBShiftConfig &prev,
59         RGBShiftConfig &next,
60         long prev_frame,
61         long next_frame,
62         long current_frame)
63 {
64         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
65         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
66
67         r_dx = prev.r_dx * prev_scale + next.r_dx * next_scale;
68         r_dy = prev.r_dy * prev_scale + next.r_dy * next_scale;
69         g_dx = prev.g_dx * prev_scale + next.g_dx * next_scale;
70         g_dy = prev.g_dy * prev_scale + next.g_dy * next_scale;
71         b_dx = prev.b_dx * prev_scale + next.b_dx * next_scale;
72         b_dy = prev.b_dy * prev_scale + next.b_dy * next_scale;
73 }
74
75
76
77
78
79
80 #define MAXVALUE 100
81
82 RGBShiftLevel::RGBShiftLevel(RGBShiftEffect *plugin, int *output, int x, int y)
83  : BC_ISlider(x, y, 0, 200, 200, -MAXVALUE, MAXVALUE, *output)
84 {
85         this->plugin = plugin;
86         this->output = output;
87 }
88
89 int RGBShiftLevel::handle_event()
90 {
91         *output = get_value();
92         plugin->send_configure_change();
93         return 1;
94 }
95
96
97 RGBShiftReset::RGBShiftReset(RGBShiftEffect *plugin, RGBShiftWindow *window, int x, int y)
98  : BC_GenericButton(x, y, _("Reset"))
99 {
100         this->plugin = plugin;
101         this->window = window;
102 }
103 RGBShiftReset::~RGBShiftReset()
104 {
105 }
106 int RGBShiftReset::handle_event()
107 {
108         plugin->config.reset();
109         window->update();
110         plugin->send_configure_change();
111         return 1;
112 }
113
114
115 RGBShiftWindow::RGBShiftWindow(RGBShiftEffect *plugin)
116  : PluginClientWindow(plugin, 300, 230, 300, 230, 0)
117 {
118         this->plugin = plugin;
119 }
120
121 void RGBShiftWindow::create_objects()
122 {
123         int x = 10, y = 10, x1 = 50;
124         add_subwindow(new BC_Title(x, y, _("R_dx:")));
125         add_subwindow(r_dx = new RGBShiftLevel(plugin, &plugin->config.r_dx, x1, y));
126         y += 30;
127         add_subwindow(new BC_Title(x, y, _("R_dy:")));
128         add_subwindow(r_dy = new RGBShiftLevel(plugin, &plugin->config.r_dy, x1, y));
129         y += 30;
130         add_subwindow(new BC_Title(x, y, _("G_dx:")));
131         add_subwindow(g_dx = new RGBShiftLevel(plugin, &plugin->config.g_dx, x1, y));
132         y += 30;
133         add_subwindow(new BC_Title(x, y, _("G_dy:")));
134         add_subwindow(g_dy = new RGBShiftLevel(plugin, &plugin->config.g_dy, x1, y));
135         y += 30;
136         add_subwindow(new BC_Title(x, y, _("B_dx:")));
137         add_subwindow(b_dx = new RGBShiftLevel(plugin, &plugin->config.b_dx, x1, y));
138         y += 30;
139         add_subwindow(new BC_Title(x, y, _("B_dy:")));
140         add_subwindow(b_dy = new RGBShiftLevel(plugin, &plugin->config.b_dy, x1, y));
141
142         y += 40;
143         add_subwindow(reset = new RGBShiftReset(plugin, this, x, y));
144
145         show_window();
146         flush();
147 }
148
149
150 // for Reset button
151 void RGBShiftWindow::update()
152 {
153         r_dx->update(plugin->config.r_dx);
154         r_dy->update(plugin->config.r_dy);
155         g_dx->update(plugin->config.g_dx);
156         g_dy->update(plugin->config.g_dy);
157         b_dx->update(plugin->config.b_dx);
158         b_dy->update(plugin->config.b_dy);
159 }
160
161
162
163
164
165
166 RGBShiftEffect::RGBShiftEffect(PluginServer *server)
167  : PluginVClient(server)
168 {
169         temp_frame = 0;
170 }
171 RGBShiftEffect::~RGBShiftEffect()
172 {
173         delete temp_frame;
174 }
175
176 const char* RGBShiftEffect::plugin_title() { return N_("RGBShift"); }
177 int RGBShiftEffect::is_realtime() { return 1; }
178
179
180 NEW_WINDOW_MACRO(RGBShiftEffect, RGBShiftWindow)
181 LOAD_CONFIGURATION_MACRO(RGBShiftEffect, RGBShiftConfig)
182
183 void RGBShiftEffect::update_gui()
184 {
185         if(thread)
186         {
187                 RGBShiftWindow *yuv_wdw = (RGBShiftWindow*)thread->window;
188                 yuv_wdw->lock_window("RGBShiftEffect::update_gui");
189                 load_configuration();
190                 yuv_wdw->r_dx->update(config.r_dx);
191                 yuv_wdw->r_dy->update(config.r_dy);
192                 yuv_wdw->g_dx->update(config.g_dx);
193                 yuv_wdw->g_dy->update(config.g_dy);
194                 yuv_wdw->b_dx->update(config.b_dx);
195                 yuv_wdw->b_dy->update(config.b_dy);
196                 yuv_wdw->unlock_window();
197         }
198 }
199
200 void RGBShiftEffect::save_data(KeyFrame *keyframe)
201 {
202         FileXML output;
203         output.set_shared_output(keyframe->xbuf);
204         output.tag.set_title("RGBSHIFT");
205         output.tag.set_property("R_DX", config.r_dx);
206         output.tag.set_property("R_DY", config.r_dy);
207         output.tag.set_property("G_DX", config.g_dx);
208         output.tag.set_property("G_DY", config.g_dy);
209         output.tag.set_property("B_DX", config.b_dx);
210         output.tag.set_property("B_DY", config.b_dy);
211         output.append_tag();
212         output.tag.set_title("/RGBSHIFT");
213         output.append_tag();
214         output.append_newline();
215         output.terminate_string();
216 }
217
218 void RGBShiftEffect::read_data(KeyFrame *keyframe)
219 {
220         FileXML input;
221         input.set_shared_input(keyframe->xbuf);
222         while(!input.read_tag())
223         {
224                 if(input.tag.title_is("RGBSHIFT"))
225                 {
226                         config.r_dx = input.tag.get_property("R_DX", config.r_dx);
227                         config.r_dy = input.tag.get_property("R_DY", config.r_dy);
228                         config.g_dx = input.tag.get_property("G_DX", config.g_dx);
229                         config.g_dy = input.tag.get_property("G_DY", config.g_dy);
230                         config.b_dx = input.tag.get_property("B_DX", config.b_dx);
231                         config.b_dy = input.tag.get_property("B_DY", config.b_dy);
232                 }
233         }
234 }
235
236 #define RGB_MACRO(type, temp_type, components) \
237 { \
238         for(int i = 0; i < h; i++) { \
239                 int ri = i + config.r_dy, gi = i + config.g_dy, bi = i + config.b_dy; \
240                 type *in_r = ri >= 0 && ri < h ? (type *)frame->get_rows()[ri] : 0; \
241                 type *in_g = gi >= 0 && gi < h ? (type *)frame->get_rows()[gi] : 0; \
242                 type *in_b = bi >= 0 && bi < h ? (type *)frame->get_rows()[bi] : 0; \
243                 type *out_row = (type *)output->get_rows()[i]; \
244                 for(int j = 0; j < w; j++) { \
245                         int rj = j + config.r_dx, gj = j + config.g_dx, bj = j + config.b_dx; \
246                         type *rp = in_r && rj >= 0 && rj < w ? in_r + rj*components: 0; \
247                         type *gp = in_g && gj >= 0 && gj < w ? in_g + gj*components: 0; \
248                         type *bp = in_b && bj >= 0 && bj < w ? in_b + bj*components: 0; \
249                         out_row[0] = rp ? rp[0] : 0; \
250                         out_row[1] = gp ? gp[1] : 0; \
251                         out_row[2] = bp ? bp[2] : 0; \
252                         out_row += components; \
253                 } \
254         } \
255 }
256
257 #define YUV_MACRO(type, temp_type, components) \
258 { \
259         for(int i = 0; i < h; i++) { \
260                 int ri = i + config.r_dy, gi = i + config.g_dy, bi = i + config.b_dy; \
261                 uint8_t *in_r = ri >= 0 && ri < h ? (uint8_t *)frame->get_rows()[ri] : 0; \
262                 uint8_t *in_g = gi >= 0 && gi < h ? (uint8_t *)frame->get_rows()[gi] : 0; \
263                 uint8_t *in_b = bi >= 0 && bi < h ? (uint8_t *)frame->get_rows()[bi] : 0; \
264                 type *out_row = (type *)output->get_rows()[i]; \
265                 for(int j = 0; j < w; j++) { \
266                         int rj = j + config.r_dx, gj = j + config.g_dx, bj = j + config.b_dx; \
267                         uint8_t *rp = in_r && rj >= 0 && rj < w ? in_r + rj*3: 0; \
268                         uint8_t *gp = in_g && gj >= 0 && gj < w ? in_g + gj*3: 0; \
269                         uint8_t *bp = in_b && bj >= 0 && bj < w ? in_b + bj*3: 0; \
270                         temp_type y, u, v; \
271                         temp_type r = rp ? rp[0] : 0; \
272                         temp_type g = gp ? gp[1] : 0; \
273                         temp_type b = bp ? bp[2] : 0; \
274                         if( sizeof(type) == 4 ) \
275                                 YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v); \
276                         else if( sizeof(type) == 2 ) \
277                                 YUV::yuv.rgb_to_yuv_16(r, g, b, y, u, v); \
278                         else \
279                                 YUV::yuv.rgb_to_yuv_8(r, g, b, y, u, v); \
280                         out_row[0] = y; \
281                         out_row[1] = u; \
282                         out_row[2] = v; \
283                         out_row += components; \
284                 } \
285         } \
286 }
287
288 int RGBShiftEffect::process_realtime(VFrame *input, VFrame *output)
289 {
290         load_configuration();
291
292         if( EQUIV(config.r_dx, 0) && EQUIV(config.g_dx, 0) && EQUIV(config.b_dx, 0) &&
293                 EQUIV(config.r_dy, 0) && EQUIV(config.g_dy, 0) && EQUIV(config.b_dy, 0) ) {
294                 if(input->get_rows()[0] != output->get_rows()[0])
295                         output->copy_from(input);
296                 return 0;
297         }
298
299         int w = input->get_w(), h = input->get_h();
300         int color_model = input->get_color_model();
301         int is_yuv = BC_CModels::is_yuv(color_model);
302         if( is_yuv ) color_model = BC_RGB888;
303         VFrame *frame = input;
304         if( input->get_rows()[0] == output->get_rows()[0] || !is_yuv ) {
305                 if( temp_frame && ( temp_frame->get_color_model() != color_model ||
306                         temp_frame->get_w() != w || temp_frame->get_h() != h ) ) {
307                         delete temp_frame;  temp_frame = 0;
308                 }
309                 if( !temp_frame )
310                         temp_frame = new VFrame(w, h, color_model, 0);
311                 frame = temp_frame;
312                 if( color_model != input->get_color_model() )
313                         BC_CModels::transfer(frame->get_rows(), input->get_rows(),
314                                 frame->get_y(), frame->get_u(), frame->get_v(),
315                                 input->get_y(), input->get_u(), input->get_v(),
316                                 0, 0, input->get_w(), input->get_h(),
317                                 0, 0, frame->get_w(), frame->get_h(),
318                                 input->get_color_model(), frame->get_color_model(), 0,
319                                 input->get_bytes_per_line(), w);
320                 else
321                         frame->copy_from(input);
322         }
323
324         switch( input->get_color_model() ) {
325         case BC_YUV888:       YUV_MACRO(unsigned char, int, 3);  break;
326         case BC_YUV161616:    YUV_MACRO(uint16_t, int, 3);       break;
327         case BC_YUVA8888:     YUV_MACRO(unsigned char, int, 4);  break;
328         case BC_YUVA16161616: YUV_MACRO(uint16_t, int, 4);       break;
329         case BC_RGB_FLOAT:    RGB_MACRO(float, float, 3);        break;
330         case BC_RGB888:       RGB_MACRO(unsigned char, int, 3);  break;
331         case BC_RGB161616:    RGB_MACRO(uint16_t, int, 3);       break;
332         case BC_RGBA_FLOAT:   RGB_MACRO(float, float, 4);        break;
333         case BC_RGBA8888:     RGB_MACRO(unsigned char, int, 4);  break;
334         case BC_RGBA16161616: RGB_MACRO(uint16_t, int, 4);       break;
335         }
336
337         return 0;
338 }
339
340