--- /dev/null
+
+/*
+ * 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;
+}
+
+