merged hv7 mod
[goodguy/history.git] / cinelerra-5.1 / plugins / crikey / crikey.C
index ccbb216a34ca180373edfe4a14050e0e2ca1743b..b3e7592bddcd655eaa5494cbf4697b01ebea9041 100644 (file)
@@ -25,8 +25,9 @@
 
 #include "arraylist.h"
 #include "bccmodels.h"
-#include "cicolors.h"
+#include "bccolors.h"
 #include "clip.h"
+#include "edlsession.h"
 #include "filexml.h"
 #include "crikey.h"
 #include "crikeywindow.h"
@@ -48,25 +49,42 @@ void crikey_pgm(const char *fn,VFrame *vfrm)
 }
 #endif
 
+CriKeyPoint::CriKeyPoint(int t, int e, float x, float y)
+{
+       this->t = t;  this->e = e; this->x = x; this->y = y;
+}
+CriKeyPoint::~CriKeyPoint()
+{
+}
+
 CriKeyConfig::CriKeyConfig()
 {
        color = 0x000000;
        threshold = 0.5f;
        draw_mode = DRAW_ALPHA;
-       key_mode = KEY_SEARCH;
-       point_x = point_y = 0;
        drag = 0;
+       selected = 0;
+}
+CriKeyConfig::~CriKeyConfig()
+{
+       points.remove_all_objects();
 }
 
 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;
+       if( this->color != that.color ) return 0;
+       if( !EQUIV(this->threshold, that.threshold) ) return 0;
+       if( this->draw_mode != that.draw_mode ) return 0;
+       if( this->drag != that.drag ) return 0;
+       if( this->points.size() != that.points.size() ) return 0;
+       for( int i=0, n=points.size(); i<n; ++i ) {
+               CriKeyPoint *ap = this->points[i], *bp = that.points[i];
+               if( ap->t != bp->t ) return 0;
+               if( ap->e != bp->e ) return 0;
+               if( !EQUIV(ap->x, bp->x) ) return 0;
+               if( !EQUIV(ap->y, bp->y) ) return 0;
+       }
+       return 1;
 }
 
 void CriKeyConfig::copy_from(CriKeyConfig &that)
@@ -74,33 +92,47 @@ 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;
+       this->selected = that.selected;
+
+       points.remove_all_objects();
+       for( int i=0,n=that.points.size(); i<n; ++i ) {
+               CriKeyPoint *pt = that.points[i];
+               add_point(pt->t, pt->e, pt->x, pt->y);
+       }
 }
 
 void CriKeyConfig::interpolate(CriKeyConfig &prev, CriKeyConfig &next,
                long prev_frame, long next_frame, long current_frame)
 {
-       copy_from(prev);
+       this->color = prev.color;
+       this->draw_mode = prev.draw_mode;
+       this->drag = prev.drag;
+       this->selected = prev.selected;
+
        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; }
+ // interpolate rgb components
+       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];
+       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);
+
+       points.remove_all_objects();
+       int prev_sz = prev.points.size(), next_sz = next.points.size();
+       for( int i=0; i<prev_sz; ++i ) {
+               CriKeyPoint *pt = prev.points[i], *nt = 0;
+               float x = pt->x, y = pt->y;
+               int k = next_sz;  // associated by tag id in next
+               while( --k >= 0 && pt->t != (nt=next.points[k])->t );
+               if( k >= 0 ) {
+                       x = x * prev_scale + nt->x * next_scale;
+                       y = y * prev_scale + nt->y * next_scale;
+               }
+               add_point(pt->t, pt->e, x, y);
        }
 }
 
@@ -108,7 +140,25 @@ void CriKeyConfig::limits()
 {
        bclamp(threshold, 0.0f, 1.0f);
        bclamp(draw_mode, 0, DRAW_MODES-1);
-       bclamp(key_mode,  0, KEY_MODES-1);
+}
+
+int CriKeyConfig::add_point(int t, int e, float x, float y)
+{
+       int k = points.size();
+       if( t < 0 ) {
+               t = 0;
+               for( int i=k; --i>=0; ) {
+                       int n = points[i]->t;
+                       if( n >= t ) t = n + 1;
+               }
+       }
+       points.append(new CriKeyPoint(t, e, x, y));
+       return k;
+}
+
+void CriKeyConfig::del_point(int i)
+{
+       points.remove_object_number(i);
 }
 
 
@@ -127,20 +177,19 @@ class FillRegion
                stack.remove();
        }
  
-       int w, h, threshold;
+       int w, h;
        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; }
+       void run();
 
        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();
@@ -150,27 +199,37 @@ FillRegion::FillRegion(VFrame *d, VFrame *m)
 void FillRegion::fill(int x, int y)
 {
        push(y, x, x);
+}
 
-       do {
-               int ilt, irt, lt, rt;
+void FillRegion::run()
+{
+       while( stack.size() > 0 ) {
+               int y, ilt, irt;
                pop(y, ilt, irt);
                int ofs = y*w + ilt;
-               uint8_t *idp = data + ofs, *dp;
-               uint8_t *imp = mask + ofs, *mp;
+               uint8_t *idp = data + ofs;
+               uint8_t *imp = mask + ofs;
                for( int x=ilt; x<=irt; ++x,++imp,++idp ) {
-                       if( !*imp || edge_pixel(idp) ) continue;
+                       if( !*imp ) 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( edge_pixel(idp) ) continue;
+                       int lt = x, rt = x;
+                       uint8_t *ldp = idp, *lmp = imp;
+                       for( int i=lt; --i>=0; ) {
+                               if( !*--lmp ) break;
+                               *lmp = 0;  lt = i;
+                               if( edge_pixel(--ldp) ) break;
+                       }
+                       uint8_t *rdp = idp, *rmp = imp;
+                       for( int i=rt; ++i< w; rt=i,*rmp=0 ) {
+                               if( !*++rmp ) break;
+                               *rmp = 0;  rt = i;
+                               if( edge_pixel(++rdp) ) break;
+                       }
                        if( y+1 <  h ) push(y+1, lt, rt);
                        if( y-1 >= 0 ) push(y-1, lt, rt);
                }
-       } while( stack.size() > 0 );
+       }
 }
 
 
@@ -180,7 +239,6 @@ CriKey::CriKey(PluginServer *server)
        engine = 0;
        msk = 0;
        dst = 0;
-       diff_pixel = 0;
 }
 
 CriKey::~CriKey()
@@ -241,61 +299,20 @@ void CriKey::get_color(int x, int y)
        }
 }
 
-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)
 
+int CriKey::new_point()
+{
+       EDLSession *session = get_edlsession();
+       float x = !session ? 0.f : session->output_w / 2.f;
+       float y = !session ? 0.f : session->output_h / 2.f;
+       return config.add_point(-1, 0, x, y);
+}
+
 void CriKey::save_data(KeyFrame *keyframe)
 {
        FileXML output;
@@ -307,15 +324,26 @@ void CriKey::save_data(KeyFrame *keyframe)
        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.tag.set_property("SELECTED", config.selected);
        output.append_tag();
        output.append_newline();
        output.tag.set_title("/CRIKEY");
        output.append_tag();
        output.append_newline();
+       for( int i=0, n = config.points.size(); i<n; ++i ) {
+               CriKeyPoint *pt = config.points[i];
+               char point[BCSTRLEN];
+               sprintf(point,"/POINT_%d",pt->t);
+               output.tag.set_title(point+1);
+               output.tag.set_property("E", pt->e);
+               output.tag.set_property("X", pt->x);
+               output.tag.set_property("Y", pt->y);
+               output.append_tag();
+               output.tag.set_title(point+0);
+               output.append_tag();
+               output.append_newline();
+       }
        output.terminate_string();
 }
 
@@ -323,24 +351,28 @@ void CriKey::read_data(KeyFrame *keyframe)
 {
        FileXML input;
        input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
+       config.points.remove_all_objects();
        int result = 0;
 
        while( !(result=input.read_tag()) ) {
-               if(input.tag.title_is("CRIKEY")) {
+               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.selected = input.tag.get_property("SELECTED", 0);
                        config.limits();
                }
-               else if(input.tag.title_is("/CRIKEY")) {
-                       result = 1;
+               else if( !strncmp(input.tag.get_title(),"POINT_",6) ) {
+                       int t = atoi(input.tag.get_title() + 6);
+                       int e = input.tag.get_property("E", 0);
+                       float x = input.tag.get_property("X", 0.f);
+                       float y = input.tag.get_property("Y", 0.f);
+                       config.add_point(t, e, x, y);
                }
        }
 
+       if( !config.points.size() ) new_point();
 }
 
 void CriKey::update_gui()
@@ -488,6 +520,24 @@ void CriKey::draw_mask(VFrame *msk)
        }
 }
 
+void CriKey::draw_point(VFrame *src, CriKeyPoint *pt)
+{
+       int d = bmax(w,h) / 200 + 2;
+       int r = d/2+1, x = pt->x, y = pt->y;
+       src->draw_smooth(x-r,y+0, x-r, y-r, x+0,y-r);
+       src->draw_smooth(x+0,y-r, x+r, y-r, x+r,y+0);
+       src->draw_smooth(x+r,y+0, x+r, y+r, x+0,y+r);
+       src->draw_smooth(x+0,y+r, x-r, y+r, x-r,y+0);
+       if( pt->e ) {
+               src->set_pixel_color(RED);
+               src->draw_x(pt->x, pt->y, d);
+       }
+       else {
+               src->set_pixel_color(BLUE);
+               src->draw_t(pt->x, pt->y, d);
+       }
+}
+
 int CriKey::process_buffer(VFrame *frame, int64_t start_position, double frame_rate)
 {
        load_configuration();
@@ -499,26 +549,17 @@ int CriKey::process_buffer(VFrame *frame, int64_t start_position, double frame_r
         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;
-       }
+       set_target(is_yuv, config.color, target);
 
         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());
+       dst->clear_frame();
 
        if( !engine )
                engine = new CriKeyEngine(this,
@@ -532,47 +573,41 @@ int CriKey::process_buffer(VFrame *frame, int64_t start_position, double frame_r
        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;
+       if( config.draw_mode != DRAW_EDGE ) {
+               if( msk && ( msk->get_w() != w || msk->get_h() != h ) ) {
+                       delete msk;  msk = 0;
                }
-               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;
-       }
+               if( !msk )
+                       msk = new VFrame(w, h, BC_A8);
+               memset(msk->get_data(), 0xff, msk->get_data_size());
+
+               FillRegion fill_region(dst, msk);
+               for( int i=0, n=config.points.size(); i<n; ++i ) {
+                       CriKeyPoint *pt = config.points[i];
+                       if( !pt->e ) continue;
+                       float x = pt->x, y = pt->y;
+                       if( x >= 0 && x < w && y >= 0 && y < h )
+                               fill_region.fill(x, y);
+               }
+               fill_region.run();
+
 //crikey_pgm("/tmp/msk.pgm",msk);
 
-       if( config.draw_mode == DRAW_MASK ) {
-               draw_mask(msk);
-               return 0;
+               if( config.draw_mode == DRAW_MASK )
+                       draw_mask(msk);
+               else
+                       draw_alpha(msk);
        }
+       else
+               draw_mask(dst);
 
-       draw_alpha(msk);
+       if( config.drag ) {
+               for( int i=0, n=config.points.size(); i<n; ++i ) {
+                       CriKeyPoint *pt = config.points[i];
+                       src->set_pixel_color(config.selected == i ? GREEN : WHITE);
+                       draw_point(src, pt);
+               }
+       }
        return 0;
 }
 
@@ -618,10 +653,10 @@ LoadClient* CriKeyEngine::new_client()
                        } \
                        v = 0; \
                        float a = bmin(bmin(a00, a01), bmin(a10, a11)); \
-                       if( a < threshold ) continue; \
+                       if( a > threshold ) continue; \
                        float b = bmax(bmax(a00, a01), bmax(a10, a11)); \
-                       if( b <= threshold ) continue; \
-                       v = (b-a)*254 + 1; \
+                       if( threshold >= b ) continue; \
+                       v = 255; \
                } \
        } \
 } break