Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / plugins / yuvshift / yuvshift.C
diff --git a/cinelerra-5.1/plugins/yuvshift/yuvshift.C b/cinelerra-5.1/plugins/yuvshift/yuvshift.C
new file mode 100644 (file)
index 0000000..b1ab69c
--- /dev/null
@@ -0,0 +1,378 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+#include "bcdisplayinfo.h"
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "guicast.h"
+#include "language.h"
+#include "cicolors.h"
+#include "pluginvclient.h"
+#include "vframe.h"
+
+#include <stdint.h>
+#include <string.h>
+
+
+class YUVShiftEffect;
+
+
+class YUVShiftConfig
+{
+public:
+       YUVShiftConfig();
+
+       void copy_from(YUVShiftConfig &src);
+       int equivalent(YUVShiftConfig &src);
+       void interpolate(YUVShiftConfig &prev, 
+               YUVShiftConfig &next, 
+               long prev_frame, 
+               long next_frame, 
+               long current_frame);
+
+       int y_dx, y_dy, u_dx, u_dy, v_dx, v_dy;
+};
+
+class YUVShiftLevel : public BC_ISlider
+{
+public:
+       YUVShiftLevel(YUVShiftEffect *plugin, int *output, int x, int y);
+       int handle_event();
+       YUVShiftEffect *plugin;
+       int *output;
+};
+
+class YUVShiftWindow : public PluginClientWindow
+{
+public:
+       YUVShiftWindow(YUVShiftEffect *plugin);
+       void create_objects();
+       YUVShiftLevel *y_dx, *y_dy, *u_dx, *u_dy, *v_dx, *v_dy;
+       YUVShiftEffect *plugin;
+};
+
+
+
+class YUVShiftEffect : public PluginVClient
+{
+       VFrame *temp_frame;
+public:
+       YUVShiftEffect(PluginServer *server);
+       ~YUVShiftEffect();
+
+
+       PLUGIN_CLASS_MEMBERS(YUVShiftConfig)
+       int process_realtime(VFrame *input, VFrame *output);
+       int is_realtime();
+       void save_data(KeyFrame *keyframe);
+       void read_data(KeyFrame *keyframe);
+       void update_gui();
+};
+
+
+
+
+
+REGISTER_PLUGIN(YUVShiftEffect)
+
+
+
+
+
+
+
+YUVShiftConfig::YUVShiftConfig()
+{
+       y_dx = y_dy = 0;
+       u_dx = u_dy = 0;
+       v_dx = v_dy = 0;
+}
+
+void YUVShiftConfig::copy_from(YUVShiftConfig &src)
+{
+       y_dx = src.y_dx;  y_dy = src.y_dy;
+       u_dx = src.u_dx;  u_dy = src.u_dy;
+       v_dx = src.v_dx;  v_dy = src.v_dy;
+}
+
+int YUVShiftConfig::equivalent(YUVShiftConfig &src)
+{
+       return EQUIV(y_dx, src.y_dx) && EQUIV(u_dx, src.u_dx) && EQUIV(v_dx, src.v_dx) &&
+               EQUIV(y_dy, src.y_dy) && EQUIV(u_dy, src.u_dy) && EQUIV(v_dy, src.v_dy);
+}
+
+void YUVShiftConfig::interpolate(YUVShiftConfig &prev, 
+       YUVShiftConfig &next, 
+       long prev_frame, 
+       long next_frame, 
+       long current_frame)
+{
+       double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+       double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
+
+       y_dx = prev.y_dx * prev_scale + next.y_dx * next_scale;
+       y_dy = prev.y_dy * prev_scale + next.y_dy * next_scale;
+       u_dx = prev.u_dx * prev_scale + next.u_dx * next_scale;
+       u_dy = prev.u_dy * prev_scale + next.u_dy * next_scale;
+       v_dx = prev.v_dx * prev_scale + next.v_dx * next_scale;
+       v_dy = prev.v_dy * prev_scale + next.v_dy * next_scale;
+}
+
+
+
+
+
+
+#define MAXVALUE 100
+
+YUVShiftLevel::YUVShiftLevel(YUVShiftEffect *plugin, int *output, int x, int y)
+ : BC_ISlider(x, y, 0, 200, 200, -MAXVALUE, MAXVALUE, *output)
+{
+       this->plugin = plugin;
+       this->output = output;
+}
+
+int YUVShiftLevel::handle_event()
+{
+       *output = get_value();
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+YUVShiftWindow::YUVShiftWindow(YUVShiftEffect *plugin)
+ : PluginClientWindow(plugin, 300, 200, 300, 200, 0)
+{
+       this->plugin = plugin;
+}
+
+void YUVShiftWindow::create_objects()
+{
+       int x = 10, y = 10, x1 = 50;
+       add_subwindow(new BC_Title(x, y, _("Y_dx:")));
+       add_subwindow(y_dx = new YUVShiftLevel(plugin, &plugin->config.y_dx, x1, y));
+       y += 30;
+       add_subwindow(new BC_Title(x, y, _("Y_dy:")));
+       add_subwindow(y_dy = new YUVShiftLevel(plugin, &plugin->config.y_dy, x1, y));
+       y += 30;
+       add_subwindow(new BC_Title(x, y, _("U_dx:")));
+       add_subwindow(u_dx = new YUVShiftLevel(plugin, &plugin->config.u_dx, x1, y));
+       y += 30;
+       add_subwindow(new BC_Title(x, y, _("U_dy:")));
+       add_subwindow(u_dy = new YUVShiftLevel(plugin, &plugin->config.u_dy, x1, y));
+       y += 30;
+       add_subwindow(new BC_Title(x, y, _("V_dx:")));
+       add_subwindow(v_dx = new YUVShiftLevel(plugin, &plugin->config.v_dx, x1, y));
+       y += 30;
+       add_subwindow(new BC_Title(x, y, _("V_dy:")));
+       add_subwindow(v_dy = new YUVShiftLevel(plugin, &plugin->config.v_dy, x1, y));
+
+       show_window();
+       flush();
+}
+
+
+
+
+
+
+
+
+
+YUVShiftEffect::YUVShiftEffect(PluginServer *server)
+ : PluginVClient(server)
+{
+       temp_frame = 0;
+}
+YUVShiftEffect::~YUVShiftEffect()
+{
+       delete temp_frame;
+}
+
+const char* YUVShiftEffect::plugin_title() { return _("YUVShift"); }
+int YUVShiftEffect::is_realtime() { return 1; } 
+
+
+NEW_WINDOW_MACRO(YUVShiftEffect, YUVShiftWindow)
+LOAD_CONFIGURATION_MACRO(YUVShiftEffect, YUVShiftConfig)
+
+void YUVShiftEffect::update_gui()
+{
+       if(thread)
+       {
+               YUVShiftWindow *yuv_wdw = (YUVShiftWindow*)thread->window;
+               yuv_wdw->lock_window("YUVShiftEffect::update_gui");
+               load_configuration();
+               yuv_wdw->y_dx->update(config.y_dx);
+               yuv_wdw->y_dy->update(config.y_dy);
+               yuv_wdw->u_dx->update(config.u_dx);
+               yuv_wdw->u_dy->update(config.u_dy);
+               yuv_wdw->v_dx->update(config.v_dx);
+               yuv_wdw->v_dy->update(config.v_dy);
+               yuv_wdw->unlock_window();
+       }
+}
+
+void YUVShiftEffect::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+       output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
+       output.tag.set_title("YUVSHIFT");
+       output.tag.set_property("Y_DX", config.y_dx);
+       output.tag.set_property("Y_DY", config.y_dy);
+       output.tag.set_property("U_DX", config.u_dx);
+       output.tag.set_property("U_DY", config.u_dy);
+       output.tag.set_property("V_DX", config.v_dx);
+       output.tag.set_property("V_DY", config.v_dy);
+       output.append_tag();
+       output.tag.set_title("/YUVSHIFT");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+}
+
+void YUVShiftEffect::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+       input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
+       while(!input.read_tag())
+       {
+               if(input.tag.title_is("YUVSHIFT"))
+               {
+                       config.y_dx = input.tag.get_property("Y_DX", config.y_dx);
+                       config.y_dy = input.tag.get_property("Y_DY", config.y_dy);
+                       config.u_dx = input.tag.get_property("U_DX", config.u_dx);
+                       config.u_dy = input.tag.get_property("U_DY", config.u_dy);
+                       config.v_dx = input.tag.get_property("V_DX", config.v_dx);
+                       config.v_dy = input.tag.get_property("V_DY", config.v_dy);
+               }
+       }
+}
+
+
+static YUV yuv_static;
+
+#define YUV_MACRO(type, temp_type, components) \
+{ \
+       for(int i = 0; i < h; i++) { \
+               int yi = i + config.y_dy, ui = i + config.u_dy, vi = i + config.v_dy; \
+               type *in_y = yi >= 0 && yi < h ? (type *)frame->get_rows()[yi] : 0; \
+               type *in_u = ui >= 0 && ui < h ? (type *)frame->get_rows()[ui] : 0; \
+               type *in_v = vi >= 0 && vi < h ? (type *)frame->get_rows()[vi] : 0; \
+               type *out_row = (type *)output->get_rows()[i]; \
+               for(int j = 0; j < w; j++) { \
+                       int yj = j + config.y_dx, uj = j + config.u_dx, vj = j + config.v_dx; \
+                       type *yp = in_y && yj >= 0 && yj < w ? in_y + yj*components: 0; \
+                       type *up = in_u && uj >= 0 && uj < w ? in_u + uj*components: 0; \
+                       type *vp = in_v && vj >= 0 && vj < w ? in_v + vj*components: 0; \
+                       out_row[0] = yp ? yp[0] : 0; \
+                       out_row[1] = up ? up[1] : (1<<(8*sizeof(type)-1)); \
+                       out_row[2] = vp ? vp[2] : (1<<(8*sizeof(type)-1)); \
+                       out_row += components; \
+               } \
+       } \
+}
+
+#define RGB_MACRO(type, temp_type, components) \
+{ \
+       for(int i = 0; i < h; i++) { \
+               int yi = i + config.y_dy, ui = i + config.u_dy, vi = i + config.v_dy; \
+               uint8_t *in_y = yi >= 0 && yi < h ? (uint8_t *)frame->get_rows()[yi] : 0; \
+               uint8_t *in_u = ui >= 0 && ui < h ? (uint8_t *)frame->get_rows()[ui] : 0; \
+               uint8_t *in_v = vi >= 0 && vi < h ? (uint8_t *)frame->get_rows()[vi] : 0; \
+               type *out_row = (type *)output->get_rows()[i]; \
+               for(int j = 0; j < w; j++) { \
+                       int yj = j + config.y_dx, uj = j + config.u_dx, vj = j + config.v_dx; \
+                       uint8_t *yp = in_y && yj >= 0 && yj < w ? in_y + yj*3: 0; \
+                       uint8_t *up = in_u && uj >= 0 && uj < w ? in_u + uj*3: 0; \
+                       uint8_t *vp = in_v && vj >= 0 && vj < w ? in_v + vj*3: 0; \
+                       temp_type r, g, b; \
+                       temp_type y = yp ? yp[0] : 0x00; \
+                       temp_type u = up ? up[1] : 0x80; \
+                       temp_type v = vp ? vp[2] : 0x80; \
+                       if( sizeof(type) == 4 ) \
+                               yuv_static.yuv_to_rgb_f(r, g, b, y/255., (u-128.)/255., (v-128.)/255.); \
+                       else if( sizeof(type) == 2 ) \
+                               yuv_static.yuv_to_rgb_16(r, g, b, y, u, v); \
+                       else \
+                               yuv_static.yuv_to_rgb_8(r, g, b, y, u, v); \
+                       out_row[0] = r; \
+                       out_row[1] = g; \
+                       out_row[2] = b; \
+                       out_row += components; \
+               } \
+       } \
+}
+
+int YUVShiftEffect::process_realtime(VFrame *input, VFrame *output)
+{
+       load_configuration();
+
+       if( EQUIV(config.y_dx, 0) && EQUIV(config.u_dx, 0) && EQUIV(config.v_dx, 0) &&
+               EQUIV(config.y_dy, 0) && EQUIV(config.u_dy, 0) && EQUIV(config.v_dy, 0) ) {
+               if(input->get_rows()[0] != output->get_rows()[0])
+                       output->copy_from(input);
+               return 0;
+       }
+
+       int w = input->get_w(), h = input->get_h();
+       int color_model = input->get_color_model();
+       int is_yuv = BC_CModels::is_yuv(color_model);
+       if( !is_yuv ) color_model = BC_YUV888;
+       VFrame *frame = input;
+       if( input->get_rows()[0] == output->get_rows()[0] || !is_yuv ) {
+               if( temp_frame && ( temp_frame->get_color_model() != color_model ||
+                       temp_frame->get_w() != w || temp_frame->get_h() != h ) ) {
+                       delete temp_frame;  temp_frame = 0;
+               }
+               if( !temp_frame )
+                       temp_frame = new VFrame(w, h, color_model);
+               frame = temp_frame;
+               if( color_model != input->get_color_model() )
+                       BC_CModels::transfer(frame->get_rows(), input->get_rows(),
+                               frame->get_y(), frame->get_u(), frame->get_v(),
+                               input->get_y(), input->get_u(), input->get_v(),
+                               0, 0, input->get_w(), input->get_h(),
+                               0, 0, frame->get_w(), frame->get_h(),
+                               input->get_color_model(), frame->get_color_model(), 0,
+                               input->get_bytes_per_line(), w);
+               else
+                       frame->copy_from(input);
+       }
+
+       switch( input->get_color_model() ) {
+       case BC_YUV888:       YUV_MACRO(unsigned char, int, 3);  break;
+       case BC_YUV161616:    YUV_MACRO(uint16_t, int, 3);       break;
+       case BC_YUVA8888:     YUV_MACRO(unsigned char, int, 4);  break;
+       case BC_YUVA16161616: YUV_MACRO(uint16_t, int, 4);       break;
+       case BC_RGB_FLOAT:    RGB_MACRO(float, float, 3);        break;
+       case BC_RGB888:       RGB_MACRO(unsigned char, int, 3);  break;
+       case BC_RGB161616:    RGB_MACRO(uint16_t, int, 3);       break;
+       case BC_RGBA_FLOAT:   RGB_MACRO(float, float, 4);        break;
+       case BC_RGBA8888:     RGB_MACRO(unsigned char, int, 4);  break;
+       case BC_RGBA16161616: RGB_MACRO(uint16_t, int, 4);       break;
+       }
+
+       return 0;
+}
+
+