int y = margin;
int x2 = 70;
lock_window("CWindowEyedropGUI::create_objects");
- BC_Title *title1, *title2, *title3, *title4, *title5, *title6, *title7;
+ BC_Title *title0, *title1, *title2, *title3, *title4, *title5, *title6, *title7;
+ add_subwindow(title0 = new BC_Title(x, y,_("X,Y:")));
+ y += title0->get_h() + margin;
add_subwindow(title7 = new BC_Title(x, y, _("Radius:")));
y += BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1) + margin;
y += title5->get_h() + margin;
add_subwindow(title6 = new BC_Title(x, y, "V:"));
+ add_subwindow(current = new BC_Title(x2, title0->get_y(), ""));
radius = new CWindowCoord(this, x2, title7->get_y(),
mwindow->edl->session->eyedrop_radius);
void CWindowEyedropGUI::update()
{
+ char string[BCTEXTLEN];
+ sprintf(string, "%d, %d",
+ thread->gui->eyedrop_x,
+ thread->gui->eyedrop_y);
+ current->update(string);
+
radius->update((int64_t)mwindow->edl->session->eyedrop_radius);
red->update(mwindow->edl->local_session->red);
void create_objects();
void update();
+ BC_Title *current;
CWindowCoord *radius;
BC_Title *red, *green, *blue, *y, *u, *v;
BC_SubWindow *sample;
}
BC_TumbleTextBoxText::BC_TumbleTextBoxText(BC_TumbleTextBox *popup,
- float default_value, int x, int y)
- : BC_TextBox(x, y, popup->text_w, 1, default_value)
+ float default_value, int x, int y, int precision)
+ : BC_TextBox(x, y, popup->text_w, 1, default_value, 1, MEDIUMFONT, precision)
{
this->popup = popup;
}
{
int x = this->x, y = this->y;
- if(use_float)
- {
- parent_window->add_subwindow(textbox = new BC_TumbleTextBoxText(this,
- default_value_f, x, y));
- textbox->set_precision(precision);
- }
- else
- parent_window->add_subwindow(textbox = new BC_TumbleTextBoxText(this,
- default_value, x, y));
+ textbox = use_float ?
+ new BC_TumbleTextBoxText(this, default_value_f, x, y, precision) :
+ new BC_TumbleTextBoxText(this, default_value, x, y);
+ parent_window->add_subwindow(textbox);
x += textbox->get_w();
- if(use_float)
- parent_window->add_subwindow(tumbler = new BC_FTumbler(textbox,
- min_f,
- max_f,
- x,
- y));
- else
- parent_window->add_subwindow(tumbler = new BC_ITumbler(textbox,
- min,
- max,
- x,
- y));
-
+ tumbler = use_float ?
+ (BC_Tumbler *)new BC_FTumbler(textbox, min_f, max_f, x, y) :
+ (BC_Tumbler *)new BC_ITumbler(textbox, min, max, x, y);
+ parent_window->add_subwindow(tumbler);
tumbler->set_increment(increment);
return 0;
}
{
public:
BC_TumbleTextBoxText(BC_TumbleTextBox *popup, int64_t default_value, int x, int y);
- BC_TumbleTextBoxText(BC_TumbleTextBox *popup, float default_value, int x, int y);
+ BC_TumbleTextBoxText(BC_TumbleTextBox *popup, float default_value, int x, int y, int precision);
virtual ~BC_TumbleTextBoxText();
int handle_event();
int button_press_event();
unsharp videoscope wave zoomblur
plugin_dirs += blending
-blending := chromakeyhsv chromakey diffkey
+blending := crikey chromakeyhsv chromakey diffkey
plugin_dirs += tv_effects
tv_effects := deinterlace-cv deinterlace ivtc liveaudio livevideo rgb601 \
color3way \
colorbalance \
compressor \
+ crikey \
crossfade \
dcoffset \
decimate \
--- /dev/null
+include ../../plugin_defs
+
+OBJS := \
+ $(OBJDIR)/crikey.o \
+ $(OBJDIR)/crikeywindow.o
+
+PLUGIN = crikey
+
+include ../../plugin_config
+
+$(OBJDIR)/crikey.o: crikey.C
+$(OBJDIR)/crikeywindow.o: crikeywindow.C
+
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2015 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<stdio.h>
+#include<stdint.h>
+#include<math.h>
+#include<string.h>
+
+#include "arraylist.h"
+#include "bccmodels.h"
+#include "cicolors.h"
+#include "clip.h"
+#include "filexml.h"
+#include "crikey.h"
+#include "crikeywindow.h"
+#include "language.h"
+#include "vframe.h"
+
+// chroma interpolated key, crikey
+
+REGISTER_PLUGIN(CriKey)
+
+#if 0
+void crikey_pgm(const char *fn,VFrame *vfrm)
+{
+ FILE *fp = fopen(fn,"w");
+ int w = vfrm->get_w(), h = vfrm->get_h();
+ fprintf(fp,"P5\n%d %d\n255\n",w,h);
+ fwrite(vfrm->get_data(),w,h,fp);
+ fclose(fp);
+}
+#endif
+
+CriKeyConfig::CriKeyConfig()
+{
+ color = 0x000000;
+ threshold = 0.5f;
+ draw_mode = DRAW_ALPHA;
+ key_mode = KEY_SEARCH;
+ point_x = point_y = 0;
+ drag = 0;
+}
+
+int CriKeyConfig::equivalent(CriKeyConfig &that)
+{
+ return this->color != that.color ||
+ !EQUIV(this->threshold, that.threshold) ||
+ this->draw_mode != that.draw_mode ||
+ this->key_mode != that.key_mode ||
+ !EQUIV(this->point_x, that.point_x) ||
+ !EQUIV(this->point_y, that.point_y) ||
+ this->drag != that.drag ? 0 : 1;
+}
+
+void CriKeyConfig::copy_from(CriKeyConfig &that)
+{
+ this->color = that.color;
+ this->threshold = that.threshold;
+ this->draw_mode = that.draw_mode;
+ this->key_mode = that.key_mode;
+ this->point_x = that.point_x;
+ this->point_y = that.point_y;
+ this->drag = that.drag;
+}
+
+void CriKeyConfig::interpolate(CriKeyConfig &prev, CriKeyConfig &next,
+ long prev_frame, long next_frame, long current_frame)
+{
+ copy_from(prev);
+ double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+ double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
+ this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
+ switch( prev.key_mode ) {
+ case KEY_POINT: {
+ this->point_x = prev.point_x * prev_scale + next.point_x * next_scale;
+ this->point_y = prev.point_y * prev_scale + next.point_y * next_scale;
+ break; }
+ case KEY_SEARCH:
+ case KEY_SEARCH_ALL: {
+ float prev_target[3]; set_target(0, prev.color, prev_target);
+ float next_target[3]; set_target(0, next.color, next_target);
+ float target[3]; // interpolates rgb components
+ for( int i=0; i<3; ++i )
+ target[i] = prev_target[i] * prev_scale + next_target[i] * next_scale;
+ set_color(0, target, this->color);
+ break; }
+ }
+}
+
+void CriKeyConfig::limits()
+{
+ bclamp(threshold, 0.0f, 1.0f);
+ bclamp(draw_mode, 0, DRAW_MODES-1);
+ bclamp(key_mode, 0, KEY_MODES-1);
+}
+
+
+class FillRegion
+{
+ class segment { public: int y, lt, rt; };
+ ArrayList<segment> stack;
+
+ void push(int y, int lt, int rt) {
+ segment &seg = stack.append();
+ seg.y = y; seg.lt = lt; seg.rt = rt;
+ }
+ void pop(int &y, int <, int &rt) {
+ segment &seg = stack.last();
+ y = seg.y; lt = seg.lt; rt = seg.rt;
+ stack.remove();
+ }
+
+ int w, h, threshold;
+ uint8_t *data, *mask;
+ bool edge_pixel(uint8_t *dp) { return *dp; }
+
+public:
+ void fill(int x, int y);
+ void set_threshold(float v) { threshold = v; }
+
+ FillRegion(VFrame *d, VFrame *m);
+};
+
+FillRegion::FillRegion(VFrame *d, VFrame *m)
+{
+ threshold = 128;
+ w = d->get_w();
+ h = d->get_h();
+ data = d->get_data();
+ mask = m->get_data();
+}
+
+void FillRegion::fill(int x, int y)
+{
+ push(y, x, x);
+
+ do {
+ int ilt, irt, lt, rt;
+ pop(y, ilt, irt);
+ int ofs = y*w + ilt;
+ uint8_t *idp = data + ofs, *dp;
+ uint8_t *imp = mask + ofs, *mp;
+ for( int x=ilt; x<=irt; ++x,++imp,++idp ) {
+ if( !*imp || edge_pixel(idp) ) continue;
+ *imp = 0;
+ lt = rt = x;
+ dp = idp; mp = imp;
+ for( int i=lt; --i>=0; lt=i,*mp=0 )
+ if( !*--mp || edge_pixel(--dp) ) break;
+ dp = idp; mp = imp;
+ for( int i=rt; ++i< w; rt=i,*mp=0 )
+ if( !*++mp || edge_pixel(++dp) ) break;
+ if( y+1 < h ) push(y+1, lt, rt);
+ if( y-1 >= 0 ) push(y-1, lt, rt);
+ }
+ } while( stack.size() > 0 );
+}
+
+
+CriKey::CriKey(PluginServer *server)
+ : PluginVClient(server)
+{
+ engine = 0;
+ msk = 0;
+ dst = 0;
+ diff_pixel = 0;
+}
+
+CriKey::~CriKey()
+{
+ delete engine;
+ delete msk;
+ delete dst;
+}
+
+void CriKeyConfig::set_target(int is_yuv, int color, float *target)
+{
+ float r = ((color>>16) & 0xff) / 255.0f;
+ float g = ((color>> 8) & 0xff) / 255.0f;
+ float b = ((color>> 0) & 0xff) / 255.0f;
+ if( is_yuv ) {
+ float y, u, v;
+ YUV::rgb_to_yuv_f(r,g,b, y,u,v);
+ target[0] = y;
+ target[1] = u + 0.5f;
+ target[2] = v + 0.5f;
+ }
+ else {
+ target[0] = r;
+ target[1] = g;
+ target[2] = b;
+ }
+}
+void CriKeyConfig::set_color(int is_yuv, float *target, int &color)
+{
+ float r = target[0];
+ float g = target[1];
+ float b = target[2];
+ if( is_yuv ) {
+ float y = r, u = g-0.5f, v = b-0.5f;
+ YUV::yuv_to_rgb_f(y,u,v, r,g,b);
+ }
+ int ir = r >= 1 ? 0xff : r < 0 ? 0 : (int)(r * 256);
+ int ig = g >= 1 ? 0xff : g < 0 ? 0 : (int)(g * 256);
+ int ib = b >= 1 ? 0xff : b < 0 ? 0 : (int)(b * 256);
+ color = (ir << 16) | (ig << 8) | (ib << 0);
+}
+
+void CriKey::get_color(int x, int y)
+{
+ if( x < 0 || x >= w ) return;
+ if( y < 0 || y >= h ) return;
+ uint8_t **src_rows = src->get_rows();
+ uint8_t *sp = src_rows[y] + x*bpp;
+ if( is_float ) {
+ float *fp = (float *)sp;
+ for( int i=0; i<comp; ++i,++fp )
+ target[i] = *fp;
+ }
+ else {
+ float scale = 1./255;
+ for( int i=0; i<comp; ++i,++sp )
+ target[i] = *sp * scale;
+ }
+}
+
+float CriKey::diff_uint8(uint8_t *dp)
+{
+ float scale = 1./255., v = 0;
+ for( int i=0; i<comp; ++i,++dp )
+ v += fabs(*dp * scale - target[i]);
+ return v / comp;
+}
+float CriKey::diff_float(uint8_t *dp)
+{
+ float *fp = (float *)dp, v = 0;
+ for( int i=0; i<comp; ++i,++fp )
+ v += fabs(*fp - target[i]);
+ return v / comp;
+}
+
+void CriKey::min_key(int &ix, int &iy)
+{
+ float mv = 1;
+ uint8_t **src_rows = src->get_rows();
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y];
+ for( int x=0; x<w; ++x,sp+=bpp ) {
+ float v = (this->*diff_pixel)(sp);
+ if( v >= mv ) continue;
+ mv = v; ix = x; iy = y;
+ }
+ }
+}
+bool CriKey::find_key(int &ix, int &iy, float thr)
+{
+ uint8_t **src_rows = src->get_rows();
+ uint8_t **msk_rows = msk->get_rows();
+ int x = ix, y = iy;
+ for( ; y<h; ++y ) {
+ uint8_t *sp = src_rows[y] + x*bpp;
+ uint8_t *mp = msk_rows[y] + x;
+ for( ; x<w; ++x,++mp,sp+=bpp ) {
+ if( !*mp ) continue;
+ float v = (this->*diff_pixel)(sp);
+ if( v < thr ) {
+ ix = x; iy = y;
+ return true;
+ }
+ }
+ x = 0;
+ }
+ return false;
+}
+
+const char* CriKey::plugin_title() { return _("CriKey"); }
+int CriKey::is_realtime() { return 1; }
+
+NEW_WINDOW_MACRO(CriKey, CriKeyWindow);
+LOAD_CONFIGURATION_MACRO(CriKey, CriKeyConfig)
+
+void CriKey::save_data(KeyFrame *keyframe)
+{
+ FileXML output;
+
+// cause data to be stored directly in text
+ output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
+
+ output.tag.set_title("CRIKEY");
+ output.tag.set_property("COLOR", config.color);
+ output.tag.set_property("THRESHOLD", config.threshold);
+ output.tag.set_property("DRAW_MODE", config.draw_mode);
+ output.tag.set_property("KEY_MODE", config.key_mode);
+ output.tag.set_property("POINT_X", config.point_x);
+ output.tag.set_property("POINT_Y", config.point_y);
+ output.tag.set_property("DRAG", config.drag);
+ output.append_tag();
+ output.append_newline();
+ output.tag.set_title("/CRIKEY");
+ output.append_tag();
+ output.append_newline();
+ output.terminate_string();
+}
+
+void CriKey::read_data(KeyFrame *keyframe)
+{
+ FileXML input;
+ input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
+ int result = 0;
+
+ while( !(result=input.read_tag()) ) {
+ if(input.tag.title_is("CRIKEY")) {
+ config.color = input.tag.get_property("COLOR", config.color);
+ config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
+ config.draw_mode = input.tag.get_property("DRAW_MODE", config.draw_mode);
+ config.key_mode = input.tag.get_property("KEY_MODE", config.key_mode);
+ config.point_x = input.tag.get_property("POINT_X", config.point_x);
+ config.point_y = input.tag.get_property("POINT_Y", config.point_y);
+ config.drag = input.tag.get_property("DRAG", config.drag);
+ config.limits();
+ }
+ else if(input.tag.title_is("/CRIKEY")) {
+ result = 1;
+ }
+ }
+
+}
+
+void CriKey::update_gui()
+{
+ if( !thread ) return;
+ if( !load_configuration() ) return;
+ thread->window->lock_window("CriKey::update_gui");
+ CriKeyWindow *window = (CriKeyWindow*)thread->window;
+ window->update_gui();
+ window->flush();
+ thread->window->unlock_window();
+}
+
+void CriKey::draw_alpha(VFrame *msk)
+{
+ uint8_t **src_rows = src->get_rows();
+ uint8_t **msk_rows = msk->get_rows();
+ switch( color_model ) {
+ case BC_RGB_FLOAT:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ if( *mp ) continue;
+ float *px = (float *)sp;
+ px[0] = px[1] = px[2] = 0;
+ }
+ }
+ break;
+ case BC_RGBA_FLOAT:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ if( *mp ) continue;
+ float *px = (float *)sp;
+ px[3] = 0;
+ }
+ }
+ break;
+ case BC_RGB888:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ if( *mp ) continue;
+ uint8_t *px = sp;
+ px[0] = px[1] = px[2] = 0;
+ }
+ }
+ break;
+ case BC_YUV888:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ if( *mp ) continue;
+ uint8_t *px = sp;
+ px[0] = 0;
+ px[1] = px[2] = 0x80;
+ }
+ }
+ break;
+ case BC_RGBA8888:
+ case BC_YUVA8888:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ if( *mp ) continue;
+ uint8_t *px = sp;
+ px[3] = 0;
+ }
+ }
+ break;
+ }
+}
+
+void CriKey::draw_mask(VFrame *msk)
+{
+ uint8_t **src_rows = src->get_rows();
+ uint8_t **msk_rows = msk->get_rows();
+ float scale = 1 / 255.0f;
+ switch( color_model ) {
+ case BC_RGB_FLOAT:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ float a = *mp * scale;
+ float *px = (float *)sp;
+ px[0] = px[1] = px[2] = a;
+ }
+ }
+ break;
+ case BC_RGBA_FLOAT:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ float a = *mp * scale;
+ float *px = (float *)sp;
+ px[0] = px[1] = px[2] = a;
+ px[3] = 1.0f;
+ }
+ }
+ break;
+ case BC_RGB888:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ float a = *mp * scale;
+ uint8_t *px = sp;
+ px[0] = px[1] = px[2] = a * 255;
+ }
+ }
+ break;
+ case BC_RGBA8888:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ float a = *mp * scale;
+ uint8_t *px = sp;
+ px[0] = px[1] = px[2] = a * 255;
+ px[3] = 0xff;
+ }
+ }
+ break;
+ case BC_YUV888:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ float a = *mp * scale;
+ uint8_t *px = sp;
+ px[0] = a * 255;
+ px[1] = px[2] = 0x80;
+ }
+ }
+ break;
+ case BC_YUVA8888:
+ for( int y=0; y<h; ++y ) {
+ uint8_t *sp = src_rows[y], *mp = msk_rows[y];
+ for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
+ float a = *mp * scale;
+ uint8_t *px = sp;
+ px[0] = a * 255;
+ px[1] = px[2] = 0x80;
+ px[3] = 0xff;
+ }
+ }
+ break;
+ }
+}
+
+int CriKey::process_buffer(VFrame *frame, int64_t start_position, double frame_rate)
+{
+ load_configuration();
+ src = frame;
+ w = src->get_w(), h = src->get_h();
+ color_model = src->get_color_model();
+ bpp = BC_CModels::calculate_pixelsize(color_model);
+ comp = BC_CModels::components(color_model);
+ if( comp > 3 ) comp = 3;
+ is_yuv = BC_CModels::is_yuv(color_model);
+ is_float = BC_CModels::is_float(color_model);
+ diff_pixel = is_float ? &CriKey::diff_float : &CriKey::diff_uint8;
+
+ read_frame(src, 0, start_position, frame_rate, 0);
+
+ switch( config.key_mode ) {
+ case KEY_SEARCH:
+ case KEY_SEARCH_ALL:
+ set_target(is_yuv, config.color, target);
+ break;
+ case KEY_POINT:
+ get_color(config.point_x, config.point_y);
+ break;
+ }
+
+ if( dst && ( dst->get_w() != w || src->get_h() != h ) ) {
+ delete dst; dst = 0;
+ }
+ if( !dst )
+ dst = new VFrame(w, h, BC_A8);
+ memset(dst->get_data(), 0x00, dst->get_data_size());
+
+ if( !engine )
+ engine = new CriKeyEngine(this,
+ PluginClient::get_project_smp() + 1,
+ PluginClient::get_project_smp() + 1);
+ engine->process_packages();
+// copy fill btm/rt edges
+ int w1 = w-1, h1 = h-1;
+ uint8_t *dp = dst->get_data();
+ if( w1 > 0 ) for( int y=0; y<h1; ++y,dp+=w ) dp[w1] = dp[w1-1];
+ if( h1 > 0 ) for( int x=0; x<w; ++x ) dp[x] = dp[x-w];
+//crikey_pgm("/tmp/dst.pgm",dst);
+
+ if( config.draw_mode == DRAW_EDGE ) {
+ draw_mask(dst);
+ return 0;
+ }
+
+ if( msk && ( msk->get_w() != w || msk->get_h() != h ) ) {
+ delete msk; msk = 0;
+ }
+ if( !msk )
+ msk = new VFrame(w, h, BC_A8);
+ memset(msk->get_data(), 0xff, msk->get_data_size());
+
+ FillRegion fill_region(dst, msk);
+ fill_region.set_threshold(config.threshold);
+
+ int x = 0, y = 0;
+ switch( config.key_mode ) {
+ case KEY_SEARCH:
+ min_key(x, y);
+ fill_region.fill(x, y);
+ break;
+ case KEY_SEARCH_ALL:
+ while( find_key(x, y, config.threshold) ) {
+ fill_region.fill(x, y);
+ ++x;
+ }
+ break;
+ case KEY_POINT:
+ x = config.point_x, y = config.point_y;
+ if( x >= 0 && x < w && y >= 0 && y < h )
+ fill_region.fill(x, y);
+ break;
+ }
+//crikey_pgm("/tmp/msk.pgm",msk);
+
+ if( config.draw_mode == DRAW_MASK ) {
+ draw_mask(msk);
+ return 0;
+ }
+
+ draw_alpha(msk);
+ return 0;
+}
+
+
+void CriKeyEngine::init_packages()
+{
+ int y = 0, h1 = plugin->h-1;
+ for(int i = 0; i < get_total_packages(); ) {
+ CriKeyPackage *pkg = (CriKeyPackage*)get_package(i++);
+ pkg->y1 = y;
+ y = h1 * i / LoadServer::get_total_packages();
+ pkg->y2 = y;
+ }
+}
+
+LoadPackage* CriKeyEngine::new_package()
+{
+ return new CriKeyPackage();
+}
+
+LoadClient* CriKeyEngine::new_client()
+{
+ return new CriKeyUnit(this);
+}
+
+#define EDGE_MACRO(type, max, components, is_yuv) \
+{ \
+ uint8_t **src_rows = src->get_rows(); \
+ int comps = MIN(components, 3); \
+ float scale = 1.0f/max; \
+ for( int y=y1; y<y2; ++y ) { \
+ uint8_t *row0 = src_rows[y], *row1 = src_rows[y+1]; \
+ uint8_t *outp = dst_rows[y]; \
+ for( int v,x=x1; x<x2; *outp++=v,++x,row0+=bpp,row1+=bpp ) { \
+ type *r0 = (type*)row0, *r1 = (type*)row1; \
+ float a00 = 0, a01 = 0, a10 = 0, a11 = 0; \
+ for( int i=0; i<comps; ++i,++r0,++r1 ) { \
+ float t = target[i]; \
+ a00 += fabs(t - r0[0]*scale); \
+ a01 += fabs(t - r0[components]*scale); \
+ a10 += fabs(t - r1[0]*scale); \
+ a11 += fabs(t - r1[components]*scale); \
+ } \
+ v = 0; \
+ float a = bmin(bmin(a00, a01), bmin(a10, a11)); \
+ if( a < threshold ) continue; \
+ float b = bmax(bmax(a00, a01), bmax(a10, a11)); \
+ if( b <= threshold ) continue; \
+ v = (b-a)*254 + 1; \
+ } \
+ } \
+} break
+
+
+void CriKeyUnit::process_package(LoadPackage *package)
+{
+ VFrame *src = server->plugin->src;
+ int bpp = server->plugin->bpp;
+ VFrame *dst = server->plugin->dst;
+ uint8_t **dst_rows = dst->get_rows();
+ float *target = server->plugin->target;
+ float threshold = server->plugin->config.threshold;
+ CriKeyPackage *pkg = (CriKeyPackage*)package;
+ int x1 = 0, x2 = server->plugin->w-1;
+ int y1 = pkg->y1, y2 = pkg->y2;
+
+ switch( src->get_color_model() ) {
+ case BC_RGB_FLOAT: EDGE_MACRO(float, 1, 3, 0);
+ case BC_RGBA_FLOAT: EDGE_MACRO(float, 1, 4, 0);
+ case BC_RGB888: EDGE_MACRO(unsigned char, 0xff, 3, 0);
+ case BC_YUV888: EDGE_MACRO(unsigned char, 0xff, 3, 1);
+ case BC_RGBA8888: EDGE_MACRO(unsigned char, 0xff, 4, 0);
+ case BC_YUVA8888: EDGE_MACRO(unsigned char, 0xff, 4, 1);
+ }
+}
+
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2014 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
+ *
+ */
+
+
+
+#ifndef EDGE_H
+#define EDGE_H
+
+#include "loadbalance.h"
+#include "pluginvclient.h"
+
+class CriKeyEngine;
+class CriKey;
+
+#define DRAW_ALPHA 0
+#define DRAW_EDGE 1
+#define DRAW_MASK 2
+#define DRAW_MODES 3
+
+#define KEY_SEARCH 0
+#define KEY_SEARCH_ALL 1
+#define KEY_POINT 2
+#define KEY_MODES 3
+
+class CriKeyConfig
+{
+public:
+ CriKeyConfig();
+
+ int equivalent(CriKeyConfig &that);
+ void copy_from(CriKeyConfig &that);
+ void interpolate(CriKeyConfig &prev, CriKeyConfig &next,
+ long prev_frame, long next_frame, long current_frame);
+ void limits();
+ static void set_target(int is_yuv, int color, float *target);
+ static void set_color(int is_yuv, float *target, int &color);
+
+ int color;
+ float threshold;
+ int draw_mode, key_mode;
+ float point_x, point_y;
+ int drag;
+};
+
+class CriKeyPackage : public LoadPackage
+{
+public:
+ CriKeyPackage() : LoadPackage() {}
+ int y1, y2;
+};
+
+class CriKeyEngine : public LoadServer
+{
+public:
+ CriKeyEngine(CriKey *plugin, int total_clients, int total_packages)
+ : LoadServer(total_clients, total_packages) { this->plugin = plugin; }
+ ~CriKeyEngine() {}
+
+ void init_packages();
+ LoadPackage* new_package();
+ LoadClient* new_client();
+
+ CriKey *plugin;
+};
+
+class CriKeyUnit : public LoadClient
+{
+public:
+ CriKeyUnit(CriKeyEngine *server)
+ : LoadClient(server) { this->server = server; }
+ ~CriKeyUnit() {}
+
+ float edge_detect(float *data, float max, int do_max);
+ void process_package(LoadPackage *package);
+ CriKeyEngine *server;
+};
+
+
+class CriKey : public PluginVClient
+{
+public:
+ CriKey(PluginServer *server);
+ ~CriKey();
+// required for all realtime plugins
+ PLUGIN_CLASS_MEMBERS2(CriKeyConfig)
+ int is_realtime();
+ void update_gui();
+ void save_data(KeyFrame *keyframe);
+ void read_data(KeyFrame *keyframe);
+ int process_buffer(VFrame *frame, int64_t start_position, double frame_rate);
+ void draw_alpha(VFrame *msk);
+ void draw_mask(VFrame *msk);
+ float diff_uint8(uint8_t *tp);
+ float diff_float(uint8_t *tp);
+ float (CriKey::*diff_pixel)(uint8_t *dp);
+ void min_key(int &ix, int &iy);
+ bool find_key(int &ix, int &iy, float threshold);
+ static void set_target(int is_yuv, int color, float *target) {
+ CriKeyConfig::set_target(is_yuv, color, target);
+ }
+ static void set_color(int is_yuv, float *target, int &color) {
+ CriKeyConfig::set_color(is_yuv, target, color);
+ }
+
+ CriKeyEngine *engine;
+ VFrame *src, *dst, *msk;
+ int w, h, color_model, bpp, comp, is_yuv, is_float;
+
+ void get_color(int x, int y);
+ float target[3];
+};
+
+#endif
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 2014 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 "automation.h"
+#include "bcdisplayinfo.h"
+#include "clip.h"
+#include "crikey.h"
+#include "crikeywindow.h"
+#include "cwindow.h"
+#include "cwindowgui.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "language.h"
+#include "mwindow.h"
+#include "plugin.h"
+#include "pluginserver.h"
+#include "theme.h"
+#include "track.h"
+
+#define COLOR_W 50
+#define COLOR_H 30
+
+CriKeyNum::CriKeyNum(CriKeyWindow *gui, int x, int y, float &output)
+ : BC_TumbleTextBox(gui, output, -32767.0f, 32767.0f, x, y, 80)
+{
+ this->gui = gui;
+ this->output = &output;
+ set_increment(1);
+ set_precision(1);
+}
+
+CriKeyNum::~CriKeyNum()
+{
+}
+
+int CriKeyNum::handle_event()
+{
+ *output = atof(get_text());
+ gui->plugin->send_configure_change();
+ return 1;
+}
+
+int CriKeyDrawModeItem::handle_event()
+{
+ ((CriKeyDrawMode *)get_popup_menu())->update(id);
+ return 1;
+}
+CriKeyDrawMode::CriKeyDrawMode(CriKeyWindow *gui, int x, int y)
+ : BC_PopupMenu(x, y, 100, "", 1)
+{
+ this->gui = gui;
+ draw_modes[DRAW_ALPHA] = _("Alpha");
+ draw_modes[DRAW_EDGE] = _("Edge");
+ draw_modes[DRAW_MASK] = _("Mask");
+ mode = -1;
+}
+void CriKeyDrawMode::create_objects()
+{
+ for( int i=0; i<DRAW_MODES; ++i )
+ add_item(new CriKeyDrawModeItem(draw_modes[i], i));
+ update(gui->plugin->config.draw_mode);
+}
+void CriKeyDrawMode::update(int mode)
+{
+ if( this->mode == mode ) return;
+ this->mode = mode;
+ set_text(draw_modes[mode]);
+ gui->plugin->config.draw_mode = mode;
+ gui->plugin->send_configure_change();
+}
+
+int CriKeyKeyModeItem::handle_event()
+{
+ ((CriKeyKeyMode *)get_popup_menu())->update(id);
+ return 1;
+}
+CriKeyKeyMode::CriKeyKeyMode(CriKeyWindow *gui, int x, int y)
+ : BC_PopupMenu(x, y, 100, "", 1)
+{
+ this->gui = gui;
+ key_modes[KEY_SEARCH] = _("Search");
+ key_modes[KEY_SEARCH_ALL] = _("Search all");
+ key_modes[KEY_POINT] = _("Point");
+ this->mode = -1;
+}
+void CriKeyKeyMode::create_objects()
+{
+ for( int i=0; i<KEY_MODES; ++i )
+ add_item(new CriKeyKeyModeItem(key_modes[i], i));
+ update(gui->plugin->config.key_mode);
+}
+void CriKeyKeyMode::update(int mode)
+{
+ if( this->mode == mode ) return;
+ this->mode = mode;
+ set_text(key_modes[mode]);
+ gui->draw_key(mode);
+ gui->plugin->send_configure_change();
+}
+
+CriKeyColorButton::CriKeyColorButton(CriKeyWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Color"))
+{
+ this->gui = gui;
+}
+int CriKeyColorButton::handle_event()
+{
+ gui->start_color_thread();
+ return 1;
+}
+
+CriKeyColorPicker::CriKeyColorPicker(CriKeyColorButton *color_button)
+ : ColorPicker(0, _("Color"))
+{
+ this->color_button = color_button;
+}
+
+void CriKeyColorPicker::start(int color)
+{
+ start_window(this->color = color, 0, 1);
+}
+
+void CriKeyColorPicker::handle_done_event(int result)
+{
+ if( !result ) {
+ CriKeyWindow *gui = color_button->gui;
+ gui->lock_window("CriKeyColorPicker::handle_done_event");
+ gui->update_color(color);
+ gui->plugin->config.color = color;
+ gui->plugin->send_configure_change();
+ gui->unlock_window();
+ }
+}
+
+int CriKeyColorPicker::handle_new_color(int color, int alpha)
+{
+ CriKeyWindow *gui = color_button->gui;
+ gui->lock_window("CriKeyColorPicker::handle_new_color");
+ gui->update_color(this->color = color);
+ gui->flush();
+ gui->unlock_window();
+ return 1;
+}
+
+
+void CriKeyWindow::start_color_thread()
+{
+ unlock_window();
+ delete color_picker;
+ color_picker = new CriKeyColorPicker(color_button);
+ color_picker->start(plugin->config.color);
+ lock_window("CriKeyWindow::start_color_thread");
+}
+
+
+CriKeyWindow::CriKeyWindow(CriKey *plugin)
+ : PluginClientWindow(plugin, 320, 220, 320, 220, 0)
+{
+ this->plugin = plugin;
+ this->color_button = 0;
+ this->color_picker = 0;
+ this->title_x = 0; this->point_x = 0;
+ this->title_y = 0; this->point_y = 0;
+ this->dragging = 0; this->drag = 0;
+}
+
+CriKeyWindow::~CriKeyWindow()
+{
+ delete color_picker;
+}
+
+void CriKeyWindow::create_objects()
+{
+ int x = 10, y = 10;
+ int margin = plugin->get_theme()->widget_border;
+ BC_Title *title;
+ add_subwindow(title = new BC_Title(x, y, _("Threshold:")));
+ y += title->get_h() + margin;
+ add_subwindow(threshold = new CriKeyThreshold(this, x, y, get_w() - x * 2));
+ y += threshold->get_h() + margin;
+ add_subwindow(title = new BC_Title(x, y+5, _("Draw mode:")));
+ int x1 = x + title->get_w() + 10 + margin;
+ add_subwindow(draw_mode = new CriKeyDrawMode(this, x1, y));
+ draw_mode->create_objects();
+ y += draw_mode->get_h() + margin;
+ add_subwindow(title = new BC_Title(x, y+5, _("Key mode:")));
+ add_subwindow(key_mode = new CriKeyKeyMode(this, x1, y));
+ y += key_mode->get_h() + margin;
+ key_x = x + 10 + margin; key_y = y + 10 + margin;
+ key_mode->create_objects();
+
+ if( plugin->config.drag )
+ grab(plugin->server->mwindow->cwindow->gui);
+ show_window(1);
+}
+
+int CriKeyWindow::grab_event(XEvent *event)
+{
+ if( key_mode->mode != KEY_POINT ) return 0;
+
+ switch( event->type ) {
+ case ButtonPress:
+ if( dragging ) return 0;
+ dragging = 1;
+ break;
+ case ButtonRelease:
+ if( !dragging ) return 0;
+ dragging = 0;
+ return 1;
+ case MotionNotify:
+ if( dragging ) break;
+ default:
+ return 0;
+ }
+ MWindow *mwindow = plugin->server->mwindow;
+ CWindowGUI *cwindow_gui = mwindow->cwindow->gui;
+ CWindowCanvas *canvas = cwindow_gui->canvas;
+ float cursor_x = canvas->get_canvas()->get_relative_cursor_x();
+ float cursor_y = canvas->get_canvas()->get_relative_cursor_y();
+ canvas->canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
+ int64_t position = plugin->get_source_position();
+ float projector_x, projector_y, projector_z;
+ Track *track = plugin->server->plugin->track;
+ int track_w = track->track_w, track_h = track->track_h;
+ track->automation->get_projector(
+ &projector_x, &projector_y, &projector_z,
+ position, PLAY_FORWARD);
+ projector_x += mwindow->edl->session->output_w / 2;
+ projector_y += mwindow->edl->session->output_h / 2;
+ float output_x = (cursor_x - projector_x) / projector_z + track_w / 2;
+ float output_y = (cursor_y - projector_y) / projector_z + track_h / 2;
+ point_x->update((int64_t)(plugin->config.point_x = output_x));
+ point_y->update((int64_t)(plugin->config.point_y = output_y));
+ plugin->send_configure_change();
+ return 1;
+}
+
+void CriKeyWindow::done_event(int result)
+{
+ ungrab(client->server->mwindow->cwindow->gui);
+ if( color_picker ) color_picker->close_window();
+}
+
+void CriKeyWindow::draw_key(int mode)
+{
+ int margin = plugin->get_theme()->widget_border;
+ int x = key_x, y = key_y;
+ delete color_picker; color_picker = 0;
+ delete color_button; color_button = 0;
+ delete title_x; title_x = 0;
+ delete point_x; point_x = 0;
+ delete title_y; title_y = 0;
+ delete point_y; point_y = 0;
+ delete drag; drag = 0;
+
+ clear_box(x,y, get_w()-x,get_h()-y);
+ flash(x,y, get_w()-x,get_h()-y);
+
+ switch( mode ) {
+ case KEY_SEARCH:
+ case KEY_SEARCH_ALL:
+ add_subwindow(color_button = new CriKeyColorButton(this, x, y));
+ x += color_button->get_w() + margin;
+ color_x = x; color_y = y;
+ update_color(plugin->config.color);
+ break;
+ case KEY_POINT:
+ add_subwindow(title_x = new BC_Title(x, y, _("X:")));
+ int x1 = x + title_x->get_w() + margin, y1 = y;
+ point_x = new CriKeyNum(this, x1, y, plugin->config.point_x);
+ point_x->create_objects();
+ y += point_x->get_h() + margin;
+ add_subwindow(title_y = new BC_Title(x, y, _("Y:")));
+ point_y = new CriKeyNum(this, x1, y, plugin->config.point_y);
+ point_y->create_objects();
+ x1 += point_x->get_w() + margin;
+ add_subwindow(drag = new CriKeyDrag(this, x1, y1));
+ break;
+ }
+ plugin->config.key_mode = mode;
+ show_window(1);
+}
+
+void CriKeyWindow::update_color(int color)
+{
+ set_color(color);
+ draw_box(color_x, color_y, COLOR_W, COLOR_H);
+ set_color(BLACK);
+ draw_rectangle(color_x, color_y, COLOR_W, COLOR_H);
+ flash(color_x, color_y, COLOR_W, COLOR_H);
+}
+
+void CriKeyWindow::update_gui()
+{
+ threshold->update(plugin->config.threshold);
+ draw_mode->update(plugin->config.draw_mode);
+ key_mode->update(plugin->config.key_mode);
+ switch( plugin->config.key_mode ) {
+ case KEY_POINT:
+ point_x->update(plugin->config.point_x);
+ point_y->update(plugin->config.point_y);
+ break;
+ case KEY_SEARCH:
+ case KEY_SEARCH_ALL:
+ update_color(plugin->config.color);
+ break;
+ }
+}
+
+
+CriKeyThreshold::CriKeyThreshold(CriKeyWindow *gui, int x, int y, int w)
+ : BC_FSlider(x, y, 0, w, w, 0, 1, gui->plugin->config.threshold, 0)
+{
+ this->gui = gui;
+ set_precision(0.005);
+}
+
+int CriKeyThreshold::handle_event()
+{
+ gui->plugin->config.threshold = get_value();
+ gui->plugin->send_configure_change();
+ return 1;
+}
+
+
+CriKeyDrag::CriKeyDrag(CriKeyWindow *gui, int x, int y)
+ : BC_CheckBox(x, y, gui->plugin->config.drag, _("Drag"))
+{
+ this->gui = gui;
+}
+int CriKeyDrag::handle_event()
+{
+ int value = get_value();
+ gui->plugin->config.drag = value;
+ CWindowGUI *cwindow_gui = gui->plugin->server->mwindow->cwindow->gui;
+ if( value )
+ gui->grab(cwindow_gui);
+ else
+ gui->ungrab(cwindow_gui);
+ gui->plugin->send_configure_change();
+ return 1;
+}
+
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2015 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
+ *
+ */
+
+#ifndef __CRIKEYWINDOW_H__
+#define __CRIKEYWINDOW_H__
+
+#include "guicast.h"
+#include "colorpicker.h"
+
+class CriKey;
+class CriKeyWindow;
+class CriKeyNum;
+class CriKeyColorButton;
+class CriKeyColorPicker;
+class CriKeyDrawMode;
+class CriKeyDrawModeItem;
+class CriKeyKeyMode;
+class CriKeyKeyModeItem;
+class CriKeyThreshold;
+
+class CriKeyNum : public BC_TumbleTextBox
+{
+public:
+ CriKeyWindow *gui;
+ float *output;
+ int handle_event();
+
+ CriKeyNum(CriKeyWindow *gui, int x, int y, float &output);
+ ~CriKeyNum();
+};
+
+class CriKeyColorButton : public BC_GenericButton
+{
+public:
+ CriKeyColorButton(CriKeyWindow *gui, int x, int y);
+
+ int handle_event();
+ CriKeyWindow *gui;
+};
+
+class CriKeyColorPicker : public ColorPicker
+{
+public:
+ CriKeyColorPicker(CriKeyColorButton *color_button);
+
+ void start(int color);
+ int handle_new_color(int color, int alpha);
+ void handle_done_event(int result);
+
+ CriKeyColorButton *color_button;
+ int color;
+};
+
+class CriKeyDrawMode : public BC_PopupMenu
+{
+ const char *draw_modes[DRAW_MODES];
+public:
+ CriKeyDrawMode(CriKeyWindow *gui, int x, int y);
+
+ void create_objects();
+ void update(int mode);
+ CriKeyWindow *gui;
+ int mode;
+};
+class CriKeyDrawModeItem : public BC_MenuItem
+{
+public:
+ CriKeyDrawModeItem(const char *txt, int id)
+ : BC_MenuItem(txt) { this->id = id; }
+
+ int handle_event();
+ CriKeyWindow *gui;
+ int id;
+};
+
+class CriKeyKeyMode : public BC_PopupMenu
+{
+ const char *key_modes[KEY_MODES];
+public:
+ CriKeyKeyMode(CriKeyWindow *gui, int x, int y);
+
+ void create_objects();
+ void update(int mode);
+ CriKeyWindow *gui;
+ int mode;
+};
+class CriKeyKeyModeItem : public BC_MenuItem
+{
+public:
+ CriKeyKeyModeItem(const char *text, int id)
+ : BC_MenuItem(text) { this->id = id; }
+
+ int handle_event();
+ CriKeyWindow *gui;
+ int id;
+};
+
+class CriKeyThreshold : public BC_FSlider
+{
+public:
+ CriKeyThreshold(CriKeyWindow *gui, int x, int y, int w);
+ int handle_event();
+ CriKeyWindow *gui;
+};
+
+class CriKeyDrag : public BC_CheckBox
+{
+public:
+ CriKeyDrag(CriKeyWindow *gui, int x, int y);
+
+ int handle_event();
+ CriKeyWindow *gui;
+};
+
+class CriKeyWindow : public PluginClientWindow
+{
+public:
+ CriKeyWindow(CriKey *plugin);
+ ~CriKeyWindow();
+
+ void create_objects();
+ void draw_key(int mode);
+ void update_color(int color);
+ void update_gui();
+ void start_color_thread();
+ int grab_event(XEvent *event);
+ void done_event(int result);
+
+ CriKey *plugin;
+ CriKeyThreshold *threshold;
+ CriKeyDrawMode *draw_mode;
+ CriKeyKeyMode *key_mode;
+
+ CriKeyColorButton *color_button;
+ CriKeyColorPicker *color_picker;
+ int color_x, color_y, key_x, key_y;
+ BC_Title *title_x, *title_y;
+ CriKeyNum *point_x, *point_y;
+ int dragging;
+ CriKeyDrag *drag;
+};
+
+#endif
+
#include "transportque.inc"
#include <string.h>
-// Edge detection from the Gimp
-
REGISTER_PLUGIN(Edge)
EdgeConfig::EdgeConfig()
this->amount = that.amount;
}
-void EdgeConfig::interpolate(
- EdgeConfig &prev,
- EdgeConfig &next,
- long prev_frame,
- long next_frame,
- long current_frame)
+void EdgeConfig::interpolate( EdgeConfig &prev, EdgeConfig &next,
+ long prev_frame, long next_frame, long current_frame)
{
copy_from(next);
}
: PluginVClient(server)
{
engine = 0;
- temp = 0;
+ dst = 0;
}
Edge::~Edge()
{
- if(engine) delete engine;
- if(temp) delete temp;
+ delete engine;
+ delete dst;
}
const char* Edge::plugin_title() { return _("Edge"); }
void Edge::read_data(KeyFrame *keyframe)
{
FileXML input;
-
input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
-
int result = 0;
- while(!result)
- {
- result = input.read_tag();
-
- if(!result)
- {
- if(input.tag.title_is("EDGE"))
- {
- config.amount = input.tag.get_property("AMOUNT", config.amount);
- config.limits();
-
- }
- else
- if(input.tag.title_is("/EDGE"))
- {
- result = 1;
- }
+
+ while( !(result=input.read_tag()) ) {
+ if(input.tag.title_is("EDGE")) {
+ config.amount = input.tag.get_property("AMOUNT", config.amount);
+ config.limits();
+ }
+ else if(input.tag.title_is("/EDGE")) {
+ result = 1;
}
}
void Edge::update_gui()
{
- if(thread)
- {
- if(load_configuration())
- {
- thread->window->lock_window("Edge::update_gui");
- EdgeWindow *window = (EdgeWindow*)thread->window;
- window->flush();
- thread->window->unlock_window();
- }
- }
+ if( !thread ) return;
+ if( !load_configuration() ) return;
+ thread->window->lock_window("Edge::update_gui");
+ EdgeWindow *window = (EdgeWindow*)thread->window;
+ window->flush();
+ thread->window->unlock_window();
}
-
-
-int Edge::process_buffer(VFrame *frame,
- int64_t start_position,
- double frame_rate)
+int Edge::process_buffer(VFrame *frame, int64_t start_position, double frame_rate)
{
-
-// int need_reconfigure =
load_configuration();
- int w = frame->get_w();
- int h = frame->get_h();
- int color_model = frame->get_color_model();
+ src = frame;
+ w = src->get_w(), h = src->get_h();
+ color_model = frame->get_color_model();
+ bpp = BC_CModels::calculate_pixelsize(color_model);
+
+ if( dst && (dst->get_w() != w || dst->get_h() != h ||
+ dst->get_color_model() != color_model ) ) {
+ delete dst; dst = 0;
+ }
+ if( !dst )
+ dst = new VFrame(0, -1, w, h, color_model, -1);
-// initialize everything
- if(!temp)
- {
+ if( !engine )
engine = new EdgeEngine(this,
PluginClient::get_project_smp() + 1,
PluginClient::get_project_smp() + 1);
- temp = new VFrame(0,
- -1,
- w,
- h,
- color_model,
- -1);
-
- }
-
- read_frame(frame,
- 0,
- start_position,
- frame_rate,
- 0);
- engine->process(temp, frame);
- frame->copy_from(temp);
-
+ read_frame(frame, 0, start_position, frame_rate, 0);
+ engine->process_packages();
+ frame->copy_from(dst);
return 0;
}
-
-
EdgePackage::EdgePackage()
: LoadPackage()
{
{
}
-
-float EdgeUnit::edge_detect(float *data, float max, int do_max)
-{
- const float v_kernel[9] = { 0, 0, 0,
- 0, 2, -2,
- 0, 2, -2 };
- const float h_kernel[9] = { 0, 0, 0,
- 0, -2, -2,
- 0, 2, 2 };
- int i;
- float v_grad, h_grad;
- float amount = server->plugin->config.amount;
-
- for (i = 0, v_grad = 0, h_grad = 0; i < 9; i++)
- {
- v_grad += v_kernel[i] * data[i];
- h_grad += h_kernel[i] * data[i];
- }
-
- float result = sqrt (v_grad * v_grad * amount +
- h_grad * h_grad * amount);
- if(do_max)
- CLAMP(result, 0, max);
- return result;
-}
-
#define EDGE_MACRO(type, max, components, is_yuv) \
{ \
- type **input_rows = (type**)server->src->get_rows(); \
- type **output_rows = (type**)server->dst->get_rows(); \
int comps = MIN(components, 3); \
- for(int y = pkg->y1; y < pkg->y2; y++) \
- { \
- for(int x = 0; x < w; x++) \
- { \
-/* kernel is in bounds */ \
- if(y > 0 && x > 0 && y < h - 2 && x < w - 2) \
- { \
- for(int chan = 0; chan < comps; chan++) \
- { \
-/* load kernel */ \
- for(int kernel_y = 0; kernel_y < 3; kernel_y++) \
- { \
- for(int kernel_x = 0; kernel_x < 3; kernel_x++) \
- { \
- kernel[3 * kernel_y + kernel_x] = \
- (type)input_rows[y - 1 + kernel_y][(x - 1 + kernel_x) * components + chan]; \
- \
- if(is_yuv && chan > 0) \
- { \
- kernel[3 * kernel_y + kernel_x] -= 0x80; \
- } \
- \
- } \
- } \
-/* do the business */ \
- output_rows[y][x * components + chan] = edge_detect(kernel, max, sizeof(type) < 4); \
- if(is_yuv && chan > 0) \
- { \
- output_rows[y][x * components + chan] += 0x80; \
- } \
- \
- } \
- \
- if(components == 4) output_rows[y][x * components + 3] = \
- input_rows[y][x * components + 3]; \
+ float amounts = amount * amount / max; \
+ for( int y=y1; y<y2; ++y ) { \
+ uint8_t *row0 = input_rows[y], *row1 = input_rows[y+1]; \
+ uint8_t *outp = output_rows[y]; \
+ for( int x=x1; x<x2; ++x ) { \
+ type *r0 = (type *)row0, *r1 = (type *)row1, *op = (type *)outp; \
+ float h_grad = 0, v_grad = 0; \
+ for( int i=0; i<comps; ++i,++r0,++r1 ) { \
+ float dh = -r0[0] - r0[components] + r1[0] + r1[components]; \
+ if( (dh*=dh) > h_grad ) h_grad = dh; \
+ float dv = r0[0] - r0[components] + r1[0] - r1[components]; \
+ if( (dv*=dv) > v_grad ) v_grad = dv; \
} \
- else \
- { \
- for(int chan = 0; chan < comps; chan++) \
- { \
-/* load kernel */ \
- for(int kernel_y = 0; kernel_y < 3; kernel_y++) \
- { \
- for(int kernel_x = 0; kernel_x < 3; kernel_x++) \
- { \
- int in_y = y - 1 + kernel_y; \
- int in_x = x - 1 + kernel_x; \
- CLAMP(in_y, 0, h - 1); \
- CLAMP(in_x, 0, w - 1); \
- kernel[3 * kernel_y + kernel_x] = \
- (type)input_rows[in_y][in_x * components + chan]; \
- if(is_yuv && chan > 0) \
- { \
- kernel[3 * kernel_y + kernel_x] -= 0x80; \
- } \
- } \
- } \
-/* do the business */ \
- output_rows[y][x * components + chan] = edge_detect(kernel, max, sizeof(type) < 4); \
- if(is_yuv && chan > 0) \
- { \
- output_rows[y][x * components + chan] += 0x80; \
- } \
- } \
- if(components == 4) output_rows[y][x * components + 3] = \
- input_rows[y][x * components + 3]; \
+ float v = (h_grad + v_grad) * amounts; \
+ type t = v > max ? max : v; \
+ if( is_yuv ) { \
+ *op++ = t; *op++ = 0x80; *op++ = 0x80; \
} \
+ else { \
+ for( int i=0; i<comps; ++i ) *op++ = t; \
+ } \
+ if( components == 4 ) *op = *r0; \
+ row0 += bpp; row1 += bpp; outp += bpp; \
} \
} \
-}
+} break
void EdgeUnit::process_package(LoadPackage *package)
{
+ VFrame *src = server->plugin->src;
+ uint8_t **input_rows = src->get_rows();
+ VFrame *dst = server->plugin->dst;
+ uint8_t **output_rows = dst->get_rows();
+ float amount = (float)server->plugin->config.amount;
EdgePackage *pkg = (EdgePackage*)package;
- int w = server->src->get_w();
- int h = server->src->get_h();
- float kernel[9];
-
- switch(server->src->get_color_model())
- {
- case BC_RGB_FLOAT:
- EDGE_MACRO(float, 1, 3, 0);
- break;
- case BC_RGBA_FLOAT:
- EDGE_MACRO(float, 1, 4, 0);
- break;
- case BC_RGB888:
- EDGE_MACRO(unsigned char, 0xff, 3, 0);
- break;
- case BC_YUV888:
- EDGE_MACRO(unsigned char, 0xff, 3, 1);
- break;
- case BC_RGBA8888:
- EDGE_MACRO(unsigned char, 0xff, 4, 0);
- break;
- case BC_YUVA8888:
- EDGE_MACRO(unsigned char, 0xff, 4, 1);
- break;
+ int x1 = 0, x2 = server->plugin->w-1, bpp = server->plugin->bpp;
+ int y1 = pkg->y1, y2 = pkg->y2;
+
+ switch( server->plugin->color_model ) {
+ case BC_RGB_FLOAT: EDGE_MACRO(float, 1, 3, 0);
+ case BC_RGBA_FLOAT: EDGE_MACRO(float, 1, 4, 0);
+ case BC_RGB888: EDGE_MACRO(unsigned char, 0xff, 3, 0);
+ case BC_YUV888: EDGE_MACRO(unsigned char, 0xff, 3, 1);
+ case BC_RGBA8888: EDGE_MACRO(unsigned char, 0xff, 4, 0);
+ case BC_YUVA8888: EDGE_MACRO(unsigned char, 0xff, 4, 1);
}
}
-EdgeEngine::EdgeEngine(Edge *plugin,
- int total_clients,
- int total_packages)
+EdgeEngine::EdgeEngine(Edge *plugin, int total_clients, int total_packages)
: LoadServer(total_clients, total_packages)
{
this->plugin = plugin;
void EdgeEngine::init_packages()
{
- for(int i = 0; i < get_total_packages(); i++)
- {
- EdgePackage *pkg = (EdgePackage*)get_package(i);
- pkg->y1 = plugin->get_input(0)->get_h() * i / LoadServer::get_total_packages();
- pkg->y2 = plugin->get_input(0)->get_h() * (i + 1) / LoadServer::get_total_packages();
+ int y = 0, h1 = plugin->h-1;
+ for(int i = 0; i < get_total_packages(); ) {
+ EdgePackage *pkg = (EdgePackage*)get_package(i++);
+ pkg->y1 = y;
+ y = h1 * i / LoadServer::get_total_packages();
+ pkg->y2 = y;
}
}
-void EdgeEngine::process(VFrame *dst, VFrame *src)
-{
- this->dst = dst;
- this->src = src;
- process_packages();
-}
-
-
LoadClient* EdgeEngine::new_client()
{
return new EdgeUnit(this);
return new EdgePackage;
}
-
int equivalent(EdgeConfig &that);
void copy_from(EdgeConfig &that);
- void interpolate(EdgeConfig &prev,
- EdgeConfig &next,
- long prev_frame,
- long next_frame,
- long current_frame);
+ void interpolate(EdgeConfig &prev, EdgeConfig &next,
+ long prev_frame, long next_frame, long current_frame);
void limits();
-
int amount;
};
{
public:
EdgePackage();
- int y1;
- int y2;
+ int y1, y2;
};
class EdgeUnit : public LoadClient
~EdgeEngine();
void init_packages();
- void process(VFrame *dst, VFrame *src);
LoadClient* new_client();
LoadPackage* new_package();
- VFrame *src, *dst;
Edge *plugin;
};
double frame_rate);
EdgeEngine *engine;
- VFrame *temp;
+ VFrame *src, *dst;
+ int w, h, color_model, bpp;
};
-
-
#endif
-
-
-