fe5a0c9acc534ebddcad7c6e23d9069067bc3bce
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / yuvshift / yuvshift.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 "yuvshift.h"
23
24
25
26 REGISTER_PLUGIN(YUVShiftEffect)
27
28
29
30
31
32
33 YUVShiftConfig::YUVShiftConfig()
34 {
35         reset(RESET_ALL);
36 }
37
38 void YUVShiftConfig::reset(int clear)
39 {
40         switch(clear) {
41                 case RESET_Y_DX : y_dx = 0;
42                         break;
43                 case RESET_Y_DY : y_dy = 0;
44                         break;
45                 case RESET_U_DX : u_dx = 0;
46                         break;
47                 case RESET_U_DY : u_dy = 0;
48                         break;
49                 case RESET_V_DX : v_dx = 0;
50                         break;
51                 case RESET_V_DY : v_dy = 0;
52                         break;
53                 case RESET_ALL :
54                 default:
55                         y_dx = y_dy = 0;
56                         u_dx = u_dy = 0;
57                         v_dx = v_dy = 0;
58                         break;
59         }
60 }
61
62 void YUVShiftConfig::copy_from(YUVShiftConfig &src)
63 {
64         y_dx = src.y_dx;  y_dy = src.y_dy;
65         u_dx = src.u_dx;  u_dy = src.u_dy;
66         v_dx = src.v_dx;  v_dy = src.v_dy;
67 }
68
69 int YUVShiftConfig::equivalent(YUVShiftConfig &src)
70 {
71         return EQUIV(y_dx, src.y_dx) && EQUIV(u_dx, src.u_dx) && EQUIV(v_dx, src.v_dx) &&
72                 EQUIV(y_dy, src.y_dy) && EQUIV(u_dy, src.u_dy) && EQUIV(v_dy, src.v_dy);
73 }
74
75 void YUVShiftConfig::interpolate(YUVShiftConfig &prev,
76         YUVShiftConfig &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         y_dx = prev.y_dx * prev_scale + next.y_dx * next_scale;
85         y_dy = prev.y_dy * prev_scale + next.y_dy * next_scale;
86         u_dx = prev.u_dx * prev_scale + next.u_dx * next_scale;
87         u_dy = prev.u_dy * prev_scale + next.u_dy * next_scale;
88         v_dx = prev.v_dx * prev_scale + next.v_dx * next_scale;
89         v_dy = prev.v_dy * prev_scale + next.v_dy * next_scale;
90 }
91
92
93
94
95
96
97 #define MAXVALUE 100
98
99 YUVShiftLevel::YUVShiftLevel(YUVShiftEffect *plugin, int *output, int x, int y)
100  : BC_ISlider(x, y, 0, xS(200), yS(200), -MAXVALUE, MAXVALUE, *output)
101 {
102         this->plugin = plugin;
103         this->output = output;
104 }
105
106 int YUVShiftLevel::handle_event()
107 {
108         *output = get_value();
109         plugin->send_configure_change();
110         return 1;
111 }
112
113
114 YUVShiftReset::YUVShiftReset(YUVShiftEffect *plugin, YUVShiftWindow *window, int x, int y)
115  : BC_GenericButton(x, y, _("Reset"))
116 {
117         this->plugin = plugin;
118         this->window = window;
119 }
120 YUVShiftReset::~YUVShiftReset()
121 {
122 }
123 int YUVShiftReset::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 YUVShiftSliderClr::YUVShiftSliderClr(YUVShiftEffect *plugin, YUVShiftWindow *window, int x, int y, int w, int clear)
133  : BC_Button(x, y, w, plugin->get_theme()->get_image_set("reset_button"))
134 {
135         this->plugin = plugin;
136         this->window = window;
137         this->clear = clear;
138 }
139 YUVShiftSliderClr::~YUVShiftSliderClr()
140 {
141 }
142 int YUVShiftSliderClr::handle_event()
143 {
144         // clear==1 ==> y_dx slider --- clear==2 ==> y_dy slider
145         // clear==3 ==> u_dx slider --- clear==4 ==> u_dy slider
146         // clear==5 ==> v_dx slider --- clear==6 ==> v_dy slider
147         plugin->config.reset(clear);
148         window->update_gui(clear);
149         plugin->send_configure_change();
150         return 1;
151 }
152
153
154 YUVShiftWindow::YUVShiftWindow(YUVShiftEffect *plugin)
155  : PluginClientWindow(plugin, xS(320), yS(230), xS(320), yS(230), 0)
156 {
157         this->plugin = plugin;
158 }
159
160 void YUVShiftWindow::create_objects()
161 {
162         int xs10 = xS(10), xs50 = xS(50);
163         int ys10 = yS(10), ys30 = yS(30), ys40 = yS(40);
164         int x = xs10, y = ys10, x1 = xs50;
165         int x2 = 0; int clrBtn_w = xs50;
166
167         add_subwindow(new BC_Title(x, y, _("Y_dx:")));
168         add_subwindow(y_dx = new YUVShiftLevel(plugin, &plugin->config.y_dx, x1, y));
169         x2 = x1 + y_dx->get_w() + xs10;
170         add_subwindow(y_dxClr = new YUVShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_Y_DX));
171
172         y += ys30;
173         add_subwindow(new BC_Title(x, y, _("Y_dy:")));
174         add_subwindow(y_dy = new YUVShiftLevel(plugin, &plugin->config.y_dy, x1, y));
175         add_subwindow(y_dyClr = new YUVShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_Y_DY));
176
177         y += ys30;
178         add_subwindow(new BC_Title(x, y, _("U_dx:")));
179         add_subwindow(u_dx = new YUVShiftLevel(plugin, &plugin->config.u_dx, x1, y));
180         add_subwindow(u_dxClr = new YUVShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_U_DX));
181
182         y += ys30;
183         add_subwindow(new BC_Title(x, y, _("U_dy:")));
184         add_subwindow(u_dy = new YUVShiftLevel(plugin, &plugin->config.u_dy, x1, y));
185         add_subwindow(u_dyClr = new YUVShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_U_DY));
186
187         y += ys30;
188         add_subwindow(new BC_Title(x, y, _("V_dx:")));
189         add_subwindow(v_dx = new YUVShiftLevel(plugin, &plugin->config.v_dx, x1, y));
190         add_subwindow(v_dxClr = new YUVShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_V_DX));
191
192         y += ys30;
193         add_subwindow(new BC_Title(x, y, _("V_dy:")));
194         add_subwindow(v_dy = new YUVShiftLevel(plugin, &plugin->config.v_dy, x1, y));
195         add_subwindow(v_dyClr = new YUVShiftSliderClr(plugin, this, x2, y, clrBtn_w, RESET_V_DY));
196
197         y += ys40;
198         add_subwindow(reset = new YUVShiftReset(plugin, this, x, y));
199
200         show_window();
201         flush();
202 }
203
204
205 // for Reset button
206 void YUVShiftWindow::update_gui(int clear)
207 {
208         switch(clear) {
209                 case RESET_Y_DX : y_dx->update(plugin->config.y_dx);
210                         break;
211                 case RESET_Y_DY : y_dy->update(plugin->config.y_dy);
212                         break;
213                 case RESET_U_DX : u_dx->update(plugin->config.u_dx);
214                         break;
215                 case RESET_U_DY : u_dy->update(plugin->config.u_dy);
216                         break;
217                 case RESET_V_DX : v_dx->update(plugin->config.v_dx);
218                         break;
219                 case RESET_V_DY : v_dy->update(plugin->config.v_dy);
220                         break;
221                 case RESET_ALL :
222                 default:
223                         y_dx->update(plugin->config.y_dx);
224                         y_dy->update(plugin->config.y_dy);
225                         u_dx->update(plugin->config.u_dx);
226                         u_dy->update(plugin->config.u_dy);
227                         v_dx->update(plugin->config.v_dx);
228                         v_dy->update(plugin->config.v_dy);
229                         break;
230         }
231 }
232
233
234
235
236
237
238 YUVShiftEffect::YUVShiftEffect(PluginServer *server)
239  : PluginVClient(server)
240 {
241         temp_frame = 0;
242 }
243 YUVShiftEffect::~YUVShiftEffect()
244 {
245         delete temp_frame;
246 }
247
248 const char* YUVShiftEffect::plugin_title() { return N_("YUVShift"); }
249 int YUVShiftEffect::is_realtime() { return 1; }
250
251
252 NEW_WINDOW_MACRO(YUVShiftEffect, YUVShiftWindow)
253 LOAD_CONFIGURATION_MACRO(YUVShiftEffect, YUVShiftConfig)
254
255 void YUVShiftEffect::update_gui()
256 {
257         if(thread)
258         {
259                 YUVShiftWindow *yuv_wdw = (YUVShiftWindow*)thread->window;
260                 yuv_wdw->lock_window("YUVShiftEffect::update_gui");
261                 load_configuration();
262                 yuv_wdw->y_dx->update(config.y_dx);
263                 yuv_wdw->y_dy->update(config.y_dy);
264                 yuv_wdw->u_dx->update(config.u_dx);
265                 yuv_wdw->u_dy->update(config.u_dy);
266                 yuv_wdw->v_dx->update(config.v_dx);
267                 yuv_wdw->v_dy->update(config.v_dy);
268                 yuv_wdw->unlock_window();
269         }
270 }
271
272 void YUVShiftEffect::save_data(KeyFrame *keyframe)
273 {
274         FileXML output;
275         output.set_shared_output(keyframe->xbuf);
276         output.tag.set_title("YUVSHIFT");
277         output.tag.set_property("Y_DX", config.y_dx);
278         output.tag.set_property("Y_DY", config.y_dy);
279         output.tag.set_property("U_DX", config.u_dx);
280         output.tag.set_property("U_DY", config.u_dy);
281         output.tag.set_property("V_DX", config.v_dx);
282         output.tag.set_property("V_DY", config.v_dy);
283         output.append_tag();
284         output.tag.set_title("/YUVSHIFT");
285         output.append_tag();
286         output.append_newline();
287         output.terminate_string();
288 }
289
290 void YUVShiftEffect::read_data(KeyFrame *keyframe)
291 {
292         FileXML input;
293         input.set_shared_input(keyframe->xbuf);
294         while(!input.read_tag())
295         {
296                 if(input.tag.title_is("YUVSHIFT"))
297                 {
298                         config.y_dx = input.tag.get_property("Y_DX", config.y_dx);
299                         config.y_dy = input.tag.get_property("Y_DY", config.y_dy);
300                         config.u_dx = input.tag.get_property("U_DX", config.u_dx);
301                         config.u_dy = input.tag.get_property("U_DY", config.u_dy);
302                         config.v_dx = input.tag.get_property("V_DX", config.v_dx);
303                         config.v_dy = input.tag.get_property("V_DY", config.v_dy);
304                 }
305         }
306 }
307
308
309 #define YUV_MACRO(type, temp_type, components) \
310 { \
311         for(int i = 0; i < h; i++) { \
312                 int yi = i + config.y_dy, ui = i + config.u_dy, vi = i + config.v_dy; \
313                 type *in_y = yi >= 0 && yi < h ? (type *)frame->get_rows()[yi] : 0; \
314                 type *in_u = ui >= 0 && ui < h ? (type *)frame->get_rows()[ui] : 0; \
315                 type *in_v = vi >= 0 && vi < h ? (type *)frame->get_rows()[vi] : 0; \
316                 type *out_row = (type *)output->get_rows()[i]; \
317                 for(int j = 0; j < w; j++) { \
318                         int yj = j + config.y_dx, uj = j + config.u_dx, vj = j + config.v_dx; \
319                         type *yp = in_y && yj >= 0 && yj < w ? in_y + yj*components: 0; \
320                         type *up = in_u && uj >= 0 && uj < w ? in_u + uj*components: 0; \
321                         type *vp = in_v && vj >= 0 && vj < w ? in_v + vj*components: 0; \
322                         out_row[0] = yp ? yp[0] : 0; \
323                         out_row[1] = up ? up[1] : (1<<(8*sizeof(type)-1)); \
324                         out_row[2] = vp ? vp[2] : (1<<(8*sizeof(type)-1)); \
325                         out_row += components; \
326                 } \
327         } \
328 }
329
330 #define RGB_MACRO(type, temp_type, components) \
331 { \
332         for(int i = 0; i < h; i++) { \
333                 int yi = i + config.y_dy, ui = i + config.u_dy, vi = i + config.v_dy; \
334                 uint8_t *in_y = yi >= 0 && yi < h ? (uint8_t *)frame->get_rows()[yi] : 0; \
335                 uint8_t *in_u = ui >= 0 && ui < h ? (uint8_t *)frame->get_rows()[ui] : 0; \
336                 uint8_t *in_v = vi >= 0 && vi < h ? (uint8_t *)frame->get_rows()[vi] : 0; \
337                 type *out_row = (type *)output->get_rows()[i]; \
338                 for(int j = 0; j < w; j++) { \
339                         int yj = j + config.y_dx, uj = j + config.u_dx, vj = j + config.v_dx; \
340                         uint8_t *yp = in_y && yj >= 0 && yj < w ? in_y + yj*3: 0; \
341                         uint8_t *up = in_u && uj >= 0 && uj < w ? in_u + uj*3: 0; \
342                         uint8_t *vp = in_v && vj >= 0 && vj < w ? in_v + vj*3: 0; \
343                         temp_type r, g, b; \
344                         temp_type y = yp ? yp[0] : 0x00; \
345                         temp_type u = up ? up[1] : 0x80; \
346                         temp_type v = vp ? vp[2] : 0x80; \
347                         if( sizeof(type) == 4 ) \
348                                 YUV::yuv.yuv_to_rgb_f(r, g, b, y/255., (u-128.)/255., (v-128.)/255.); \
349                         else if( sizeof(type) == 2 ) \
350                                 YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v); \
351                         else \
352                                 YUV::yuv.yuv_to_rgb_8(r, g, b, y, u, v); \
353                         out_row[0] = r; \
354                         out_row[1] = g; \
355                         out_row[2] = b; \
356                         out_row += components; \
357                 } \
358         } \
359 }
360
361 int YUVShiftEffect::process_realtime(VFrame *input, VFrame *output)
362 {
363         load_configuration();
364
365         if( EQUIV(config.y_dx, 0) && EQUIV(config.u_dx, 0) && EQUIV(config.v_dx, 0) &&
366                 EQUIV(config.y_dy, 0) && EQUIV(config.u_dy, 0) && EQUIV(config.v_dy, 0) ) {
367                 if(input->get_rows()[0] != output->get_rows()[0])
368                         output->copy_from(input);
369                 return 0;
370         }
371
372         int w = input->get_w(), h = input->get_h();
373         int color_model = input->get_color_model();
374         int is_yuv = BC_CModels::is_yuv(color_model);
375         if( !is_yuv ) color_model = BC_YUV888;
376         VFrame *frame = input;
377         if( input->get_rows()[0] == output->get_rows()[0] || !is_yuv ) {
378                 if( temp_frame && ( temp_frame->get_color_model() != color_model ||
379                         temp_frame->get_w() != w || temp_frame->get_h() != h ) ) {
380                         delete temp_frame;  temp_frame = 0;
381                 }
382                 if( !temp_frame )
383                         temp_frame = new VFrame(w, h, color_model, 0);
384                 frame = temp_frame;
385                 if( color_model != input->get_color_model() )
386                         BC_CModels::transfer(frame->get_rows(), input->get_rows(),
387                                 frame->get_y(), frame->get_u(), frame->get_v(),
388                                 input->get_y(), input->get_u(), input->get_v(),
389                                 0, 0, input->get_w(), input->get_h(),
390                                 0, 0, frame->get_w(), frame->get_h(),
391                                 input->get_color_model(), frame->get_color_model(), 0,
392                                 input->get_bytes_per_line(), w);
393                 else
394                         frame->copy_from(input);
395         }
396
397         switch( input->get_color_model() ) {
398         case BC_YUV888:       YUV_MACRO(unsigned char, int, 3);  break;
399         case BC_YUV161616:    YUV_MACRO(uint16_t, int, 3);       break;
400         case BC_YUVA8888:     YUV_MACRO(unsigned char, int, 4);  break;
401         case BC_YUVA16161616: YUV_MACRO(uint16_t, int, 4);       break;
402         case BC_RGB_FLOAT:    RGB_MACRO(float, float, 3);        break;
403         case BC_RGB888:       RGB_MACRO(unsigned char, int, 3);  break;
404         case BC_RGB161616:    RGB_MACRO(uint16_t, int, 3);       break;
405         case BC_RGBA_FLOAT:   RGB_MACRO(float, float, 4);        break;
406         case BC_RGBA8888:     RGB_MACRO(unsigned char, int, 4);  break;
407         case BC_RGBA16161616: RGB_MACRO(uint16_t, int, 4);       break;
408         }
409
410         return 0;
411 }
412
413