smooth lines, motion51, opengl pbuffer bit typo, misc fixes
authorGood Guy <[email protected]>
Thu, 19 Jan 2017 23:19:52 +0000 (16:19 -0700)
committerGood Guy <[email protected]>
Thu, 19 Jan 2017 23:19:52 +0000 (16:19 -0700)
24 files changed:
cinelerra-5.1/cinelerra/affine.C
cinelerra-5.1/cinelerra/file.C
cinelerra-5.1/cinelerra/fileffmpeg.C
cinelerra-5.1/cinelerra/fileffmpeg.h
cinelerra-5.1/cinelerra/pluginclient.C
cinelerra-5.1/cinelerra/pluginclient.h
cinelerra-5.1/ffmpeg/video/h264.mp4
cinelerra-5.1/guicast/bctextbox.C
cinelerra-5.1/guicast/bcwindow3d.C
cinelerra-5.1/guicast/bcwindowbase.h
cinelerra-5.1/guicast/clip.h
cinelerra-5.1/guicast/vframe.C
cinelerra-5.1/guicast/vframe.h
cinelerra-5.1/plugin_defs
cinelerra-5.1/plugins/Makefile
cinelerra-5.1/plugins/bluebanana/bluebananawindow.C
cinelerra-5.1/plugins/echocancel/echocancel.C
cinelerra-5.1/plugins/motion51/Makefile [new file with mode: 0644]
cinelerra-5.1/plugins/motion51/motion51.C [new file with mode: 0644]
cinelerra-5.1/plugins/motion51/motion51.h [new file with mode: 0644]
cinelerra-5.1/plugins/motion51/motion51.inc [new file with mode: 0644]
cinelerra-5.1/plugins/motion51/motionwindow51.C [new file with mode: 0644]
cinelerra-5.1/plugins/motion51/motionwindow51.h [new file with mode: 0644]
cinelerra-5.1/plugins/motion51/picon.png [new file with mode: 0644]

index 8c603bd60950f58de46a0c994fcf399a6a7e0820..09f81f2ed6b9542d4377f0e2a825423bb1ba160d 100644 (file)
@@ -372,6 +372,11 @@ void AffineUnit::process_package(LoadPackage *package)
                server->use_opengl)
        {
 #ifdef HAVE_GL
+               out_x1 -= pivot_offset_x;  out_y1 -= pivot_offset_y;
+               out_x2 -= pivot_offset_x;  out_y2 -= pivot_offset_y;
+               out_x3 -= pivot_offset_x;  out_y3 -= pivot_offset_y;
+               out_x4 -= pivot_offset_x;  out_y4 -= pivot_offset_y;
+
                server->output->to_texture();
                server->output->enable_opengl();
                server->output->init_screen();
index fbc03adf1dfc9460c92db0a7344381669f0fea20..a502b9f2a8503d5083046a58c08f45824bdfa58a 100644 (file)
@@ -1525,6 +1525,7 @@ int File::supports_audio(int format)
                case FILE_AIFF:
                case FILE_SND:
                case FILE_FFMPEG:
+               case FILE_RAWDV:
                        return 1;
        }
        return 0;
index 94205be8d6e2a7beefbb185a9b947d20c7a2033a..4d7c713b9304e202d45bd3d4ba79a51932f78c57 100644 (file)
@@ -111,6 +111,14 @@ int FFMpegVideoQuality::handle_event()
        return ret;
 }
 
+void FileFFMPEG::set_parameters(char *cp, int len, const char *bp)
+{
+       char *ep = cp + len-2, ch = 0;
+       while( cp < ep && *bp != 0 ) { ch = *bp++; *cp++ = ch; }
+       if( ch != '\n' ) *cp++ = '\n';
+       *cp = 0;
+}
+
 void FileFFMPEG::get_parameters(BC_WindowBase *parent_window,
                Asset *asset, BC_WindowBase *&format_window,
                int audio_options, int video_options)
@@ -120,7 +128,8 @@ void FileFFMPEG::get_parameters(BC_WindowBase *parent_window,
                format_window = window;
                window->create_objects();
                if( !window->run_window() )
-                       strcpy(asset->ff_audio_options, window->audio_options->get_text());
+                       set_parameters(asset->ff_audio_options, sizeof(asset->ff_audio_options),
+                                window->audio_options->get_text());
                delete window;
        }
        else if(video_options) {
@@ -128,7 +137,8 @@ void FileFFMPEG::get_parameters(BC_WindowBase *parent_window,
                format_window = window;
                window->create_objects();
                if( !window->run_window() )
-                       strcpy(asset->ff_video_options, window->video_options->get_text());
+                       set_parameters(asset->ff_video_options, sizeof(asset->ff_video_options),
+                                window->video_options->get_text());
                delete window;
        }
 }
@@ -476,12 +486,6 @@ FFAudioOptions::FFAudioOptions(FFMPEGConfigAudio *audio_popup,
        this->audio_popup = audio_popup;
 }
 
-int FFAudioOptions::handle_event()
-{
-       strcpy(audio_popup->asset->ff_audio_options, get_text());
-       return 1;
-}
-
 
 FFMPEGConfigAudioPopup::FFMPEGConfigAudioPopup(FFMPEGConfigAudio *popup, int x, int y)
  : BC_PopupTextBox(popup, &popup->presets, popup->asset->acodec, x, y, 300, 300)
@@ -643,12 +647,6 @@ FFVideoOptions::FFVideoOptions(FFMPEGConfigVideo *video_popup,
        this->video_popup = video_popup;
 }
 
-int FFVideoOptions::handle_event()
-{
-       strcpy(video_popup->asset->ff_video_options, get_text());
-       return 1;
-}
-
 
 FFMPEGConfigVideoPopup::FFMPEGConfigVideoPopup(FFMPEGConfigVideo *popup, int x, int y)
  : BC_PopupTextBox(popup, &popup->presets, popup->asset->vcodec, x, y, 300, 300)
index a4aea29705fc0ec1246062f15a2c091704594686..63de93c7a3b2867770fa3070a72f69eac84b4136 100644 (file)
@@ -31,6 +31,7 @@ public:
        static void ff_lock(const char *cp=0);
        static void ff_unlock();
 
+       static void set_parameters(char *cp, int len, const char *bp);
        static void get_parameters(BC_WindowBase *parent_window,Asset *asset,
           BC_WindowBase *&format_window,int audio_options,int video_options);
        static int check_sig(Asset *asset);
@@ -135,7 +136,6 @@ class FFAudioOptions : public BC_ScrollTextBox
 public:
        FFAudioOptions(FFMPEGConfigAudio *audio_popup,
                int x, int y, int w, int rows, int size, char *text);
-       int handle_event();
 
        FFMPEGConfigAudio *audio_popup;
 };
@@ -184,7 +184,6 @@ class FFVideoOptions : public BC_ScrollTextBox
 public:
        FFVideoOptions(FFMPEGConfigVideo *video_popup,
                int x, int y, int w, int rows, int size, char *text);
-       int handle_event();
 
        FFMPEGConfigVideo *video_popup;
 };
index 2f5d0e377f93b3873c7657161a2070ef47942906..6af3a3139a00c8fa67617869c84c553407c8a626 100644 (file)
 #include "bcsignals.h"
 #include "clip.h"
 #include "condition.h"
+#include "edits.h"
+#include "edit.h"
 #include "edl.h"
 #include "edlsession.h"
 #include "file.h"
 #include "filesystem.h"
+#include "indexable.h"
 #include "language.h"
 #include "localsession.h"
 #include "mainundo.h"
 #include "mwindow.h"
+#include "plugin.h"
 #include "pluginclient.h"
 #include "pluginserver.h"
 #include "preferences.h"
+#include "track.h"
 #include "transportque.inc"
 
 
@@ -683,6 +688,14 @@ double PluginClient::get_project_framerate()
        return server->get_project_framerate();
 }
 
+const char *PluginClient::get_source_path()
+{
+       int64_t source_position = server->plugin->startproject;
+       Edit *edit = server->plugin->track->edits->editof(source_position,PLAY_FORWARD,0);
+       Indexable *indexable = edit ? edit->get_source() : 0;
+       return indexable ? indexable->path : 0;
+}
+
 
 void PluginClient::update_display_title()
 {
index 83e0eeed66c926fe486d429a41141b08273d38b0..260a0edad2e8c1bb55bf5704a39df75a9707a91f 100644 (file)
@@ -414,6 +414,8 @@ public:
        int get_project_samplerate();
 // get framerate of EDL
        double get_project_framerate();
+// get asset path
+       const char *get_source_path();
 // Total number of processors - 1
        int get_project_smp();
        int get_aspect_ratio(float &aspect_w, float &aspect_h);
index 9486079312e037c9199d7f26a3b9eee4a7ee1213..c4daad777e7035be2ca1e962ac81ccb4b637af9c 100644 (file)
@@ -1,23 +1 @@
 mp4 libx264
-partitions=i8x8,i4x4,p8x8,b8x8
-me_method=hex
-subq=7
-me_range=16
-g=250
-keyint_min=25
-sc_threshold=40
-i_qfactor=0.71
-b_strategy=1
-qcomp=0.6
-qmin=10
-qmax=51
-qdiff=4
-bf=3
-refs=3
-directpred=1
-trellis=1
-mixed-refs=1
-weightp=2
-8x8dct=1
-fast-pskip=1
-mbtree=1
index f8777993c500f35a95096f8156b62392b06ff638..c76f67ef0a3fa653a6cc538f082162c270d026e5 100644 (file)
@@ -1231,21 +1231,23 @@ int BC_TextBox::keypress_event()
                int wlen =  -1;
                switch( last_keypress ) {
 //unicode active acitons
-               case ESC: {
-                       unicode_active = -1;
-                       result = 1;
-                       wlen = 0;
-                       break; }
                case RETURN: {
                        for( int i=highlight_letter1+1; i<highlight_letter2; ++i ) {
                                int ch = nib(wtext[i]);
                                if( ch < 0 ) return 1;
                                wch = (wch<<4) + ch;
                        }
+                       if( wch ) {
+                               dispatch_event = 1;
+                               unicode_active = -1;
+                               result = 1;
+                               wlen = 1;
+                               break;
+                       } } // fall through
+               case ESC: {
                        unicode_active = -1;
-                       dispatch_event = 1;
                        result = 1;
-                       wlen = 1;
+                       wlen = 0;
                        break; }
                case BACKSPACE: {
                        if(ibeam_letter > 0) {
index b3302fa5b4316df05c71baa75cedca31a9bb3c76..d3da09ea2bbb80d240be0b3f6545b5b7cf64eb43 100644 (file)
@@ -72,7 +72,7 @@ GLXFBConfig *BC_WindowBase::glx_window_fb_configs()
        if( !glx_fbcfgs_window ) {
                int fb_attrs[] = {
                        GLX_CONFIG_CAVEAT,      GLX_SLOW_CONFIG,
-                       GLX_DRAWABLE_TYPE,      GLX_WINDOW_BIT | GLX_PBUFFER | GLX_PIXMAP_BIT,
+                       GLX_DRAWABLE_TYPE,      GLX_WINDOW_BIT | GLX_PBUFFER_BIT | GLX_PIXMAP_BIT,
                        GLX_DOUBLEBUFFER,       True,
                        GLX_RENDER_TYPE,        GLX_RGBA_BIT,
                        GLX_ACCUM_RED_SIZE,     1,
@@ -118,7 +118,7 @@ GLXFBConfig *BC_WindowBase::glx_pbuffer_fb_configs()
        if( !glx_fbcfgs_pbuffer ) {
                int fb_attrs[] = {
                        GLX_CONFIG_CAVEAT,      GLX_SLOW_CONFIG,
-                       GLX_DRAWABLE_TYPE,      GLX_WINDOW_BIT | GLX_PBUFFER | GLX_PIXMAP_BIT,
+                       GLX_DRAWABLE_TYPE,      GLX_WINDOW_BIT | GLX_PBUFFER_BIT | GLX_PIXMAP_BIT,
                        GLX_DOUBLEBUFFER,       True, //False,
                        GLX_RENDER_TYPE,        GLX_RGBA_BIT,
                        GLX_ACCUM_RED_SIZE,     1,
@@ -143,7 +143,7 @@ GLXFBConfig *BC_WindowBase::glx_pixmap_fb_configs()
        if( !glx_fbcfgs_pixmap ) {
                static int fb_attrs[] = {
                        GLX_CONFIG_CAVEAT,      GLX_SLOW_CONFIG,
-                       GLX_DRAWABLE_TYPE,      GLX_PIXMAP_BIT | GLX_PBUFFER,
+                       GLX_DRAWABLE_TYPE,      GLX_PIXMAP_BIT | GLX_PBUFFER_BIT,
                        GLX_DOUBLEBUFFER,       True, //False,
                        GLX_RENDER_TYPE,        GLX_RGBA_BIT,
                        GLX_RED_SIZE,           8,
index c2ed7abc270bd98f01d17fbf51a62f441e264eca..2acfe5c3b9b6006d5bbd43fd493b64702cdfdab5 100644 (file)
@@ -201,6 +201,7 @@ public:
 // Wait until event loop is running
        void init_wait();
        int is_running() { return window_running; }
+       int is_hidden() { return hidden; }
 // Check if a hardware accelerated colormodel is available and reserve it
        int accel_available(int color_model, int lock_it);
        void get_input_context();
index 44a5f45052681d9cf01e5e97c4cb77b6834d6350..3b51b601be0ede49fae568e5aa157c4e8c57b0a7 100644 (file)
@@ -19,8 +19,8 @@
  *
  */
 
-#ifndef CLIP_H
-#define CLIP_H
+#ifndef __CLIP_H__
+#define __CLIP_H__
 
 // Math macros
 #undef SQR
 #define TO_RAD(x) ((x) * 2 * M_PI / 360)
 #define TO_DEG(x) ((x) * 360 / 2 / M_PI)
 
+static inline int bclip(int &iv, int imn, int imx) {
+        return iv < imn ? imn : iv > imx ? imx : iv;
+}
+static inline float bclip(float &fv, float fmn, float fmx) {
+        return fv < fmn ? fmn : fv > fmx ? fmx : fv;
+}
+static inline double bclip(double &dv, double dmn, double dmx) {
+        return dv < dmn ? dmn : dv > dmx ? dmx : dv;
+}
+static inline void bclamp(int &iv, int imn, int imx) {
+        if( iv < imn ) iv = imn; else if( iv > imx ) iv = imx;
+}
+static inline void bclamp(float &fv, float fmn, float fmx) {
+        if( fv < fmn ) fv = fmn; else if( fv > fmx ) fv = fmx;
+}
+static inline void bclamp(double &dv, double dmn, double dmx) {
+        if( dv < dmn ) dv = dmn; else if( dv > dmx ) dv = dmx;
+}
+
 #endif
index e6f0d9d5b9d5d56141c5a4e73399cb5cb137cf3a..941ec09e5f75ece70ddb5ae2a6066dee471d9446 100644 (file)
@@ -1371,58 +1371,282 @@ void VFrame::draw_pixel(int x, int y)
 }
 
 
+// Bresenham's
 void VFrame::draw_line(int x1, int y1, int x2, int y2)
 {
-       int w = labs(x2 - x1);
-       int h = labs(y2 - y1);
-//printf("FindObjectMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
+       if( y1 > y2 ) {
+               int tx = x1;  x1 = x2;  x2 = tx;
+               int ty = y1;  y1 = y2;  y2 = ty;
+       }
 
-       if(!w && !h)
-       {
-               draw_pixel(x1, y1);
+       int x = x1, y = y1;
+       int dx = x2-x1, dy = y2-y1;
+       int dx2 = 2*dx, dy2 = 2*dy;
+       if( dx < 0 ) dx = -dx;
+       int r = dx > dy ? dx : dy, n = r;
+       int dir = 0;
+       if( dx2 < 0 ) dir += 1;
+       if( dy >= dx ) {
+               if( dx2 >= 0 ) do {     /* +Y, +X */
+                       draw_pixel(x, y++);
+                       if( (r -= dx2) < 0 ) { r += dy2;  ++x; }
+               } while( --n >= 0 );
+               else do {               /* +Y, -X */
+                       draw_pixel(x, y++);
+                       if( (r += dx2) < 0 ) { r += dy2;  --x; }
+               } while( --n >= 0 );
        }
-       else
-       if(w > h)
-       {
-// Flip coordinates so x1 < x2
-               if(x2 < x1)
-               {
-                       y2 ^= y1;
-                       y1 ^= y2;
-                       y2 ^= y1;
-                       x1 ^= x2;
-                       x2 ^= x1;
-                       x1 ^= x2;
+       else {
+               if( dx2 >= 0 ) do {     /* +X, +Y */
+                       draw_pixel(x++, y);
+                       if( (r -= dy2) < 0 ) { r += dx2;  ++y; }
+               } while( --n >= 0 );
+               else do {               /* -X, +Y */
+                       draw_pixel(x--, y);
+                       if( (r -= dy2) < 0 ) { r -= dx2;  ++y; }
+               } while( --n >= 0 );
+       }
+}
+
+// g++ -dD -E - < /dev/null | grep DBL_EPSILON
+#ifndef __DBL_EPSILON__
+#define __DBL_EPSILON__ ((double)2.22044604925031308085e-16L)
+#endif
+// weakest fraction * graphics integer range
+#define RND_EPSILON (__DBL_EPSILON__*65536)
+
+class smooth_line {
+       int rnd(double v) { return round(v)+RND_EPSILON; }
+       VFrame *vframe;
+public:
+       int sx, sy, ex, ey;     /* current point, end point */
+       int xs, ys;             /* x/y quadrant sign -1/1 */
+       int64_t A, B, C;        /* quadratic coefficients */
+       int64_t r, dx, dy;      /* residual, dr/dx and dr/dy */
+       int xmxx, xmxy;         /* x,y at apex */
+       int done;
+
+       void init0(int x1,int y1, int x2,int y2, int x3,int y3, int top);
+       void init1(int x1,int y1, int x2,int y2, int x3,int y3);
+       int64_t rx() { return r + xs*8*dx + 4*A; }
+       void moveX(int64_t r) {
+               dx += xs*A;   dy -= xs*B;
+               this->r = r;  sx += xs;
+       }
+       int64_t ry() { return r + 8*dy + 4*C; }
+       void moveY(int64_t r) {
+               dx -= B;      dy += C;
+               this->r = r;  ++sy;
+       }
+       void draw();
+
+       smooth_line(VFrame *vframe) { this->vframe = vframe; this->done = 0; }
+};
+
+
+void smooth_line::draw()
+{
+       if( done ) return;
+       if( abs(dy) >= abs(dx) ) {
+               if( xs*(sx-xmxx) >= 0 ) {
+                       if( ys > 0 ) { done = 1;  return; }
+                       if( dy < 0 || ry() < 0 ) { moveY(ry()); goto xit; }
+                       xmxx = ex;  xmxy = ey;
+                       ys = 1;  xs = -xs;
                }
-               int numerator = y2 - y1;
-               int denominator = x2 - x1;
-               for(int i = x1; i <= x2; i++)
-               {
-                       int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
-                       draw_pixel(i, y);
+               moveX(rx());
+               int64_t rr = ry();
+               if( abs(rr) < abs(r) )
+                       moveY(rr);
+       }
+       else {
+               if( sy >= xmxy ) {
+                       if( ys > 0 ) { done = 1;  return; }
+                       xmxx = ex;  xmxy = ey;
+                       ys = 1;  xs = -xs;
                }
+               moveY(ry());
+               int64_t rr = rx();
+               if( abs(rr) < abs(r) )
+                       moveX(rr);
+       }
+xit:   vframe->draw_pixel(sx, sy);
+}
+
+void VFrame::draw_smooth(int x1, int y1, int x2, int y2, int x3, int y3)
+{
+       if( (x1 == x2 && y1 == y2) || (x2 == x3 && y2 == y3) )
+               draw_line(x1,y1, x3,y3);
+       else if( x1 == x3 && y1 == y3 )
+               draw_line(x1,y1, x2,y2);
+       else if( (x2-x1) * (y2-y3) == (x2-x3) * (y2-y1) ) {
+               // co-linear, draw line from min to max
+               if( x1 < x3 ) {
+                       if( x2 < x1 ) { x1 = x2;  y1 = y2; }
+                       if( x2 > x3 ) { x3 = x2;  y3 = y2; }
+               }
+               else {
+                       if( x2 > x1 ) { x1 = x2;  y1 = y2; }
+                       if( x2 < x3 ) { x3 = x2;  y3 = y2; }
+               }
+               draw_line(x1,y1, x3,y3);
        }
        else
-       {
-// Flip coordinates so y1 < y2
-               if(y2 < y1)
-               {
-                       y2 ^= y1;
-                       y1 ^= y2;
-                       y2 ^= y1;
-                       x1 ^= x2;
-                       x2 ^= x1;
-                       x1 ^= x2;
+               smooth_draw(x1, y1, x2, y2, x3, y3);
+}
+
+/*
+  Non-Parametric Smooth Curve Generation. Don Kelly 1984
+
+     P+-----+Q'= virtual
+     /     /       origin
+    /     /
+  Q+-----+R
+
+   Let the starting point be P. the ending point R. and the tangent vertex Q.
+   A general point Z on the curve is then
+        Z = (P + R - Q) + (Q - P) sin t + (Q - R) cos t
+
+   Expanding the Cartesian coordinates around (P + R - Q) gives
+        [x y] = Z - (P + R - Q)
+        [a c] = Q - P
+        [b d] = Q - R
+        x = a*sin(t) + b*cos(t)
+        y = c*sin(t) + d*cos(t)
+
+   from which t can now be eliminated via
+        c*x - a*y = (c*b - a*d)*cos(t)
+        d*x - b*y = (a*d - c*b)*sin(t)
+
+   giving the Cartesian equation for the ellipse as
+        f(x, y) = (c*x - a*y)**2 + (d*x - b*y)**2 - (a*d - c*b)**2 = 0
+
+   or:  f(x, y) = A*x**2 - 2*B*x*y + C*y**2 + B**2 - A*C = 0
+   where: A = c**2 + d**2,  B = a*c + b*d,  C = a**2 + b**2
+
+   The maximum y extent of the ellipse may now be derived as follows:
+        let df/dx = 0,  2*A*x = 2*B*y,  x = y*B/A
+        f(x, y) == B**2 * y**2 / A - 2*B**2 * y**2 / A + C*y**2 + B**2 - A*C = 0
+           (A*C - B**2)*y = (A*C - B**2)*A
+           max x = sqrt(C), at y = B/sqrt(C)
+           max y = sqrt(A), at x = B/sqrt(A)
+
+ */
+
+
+/* x1,y1 = P, x2,y2 = Q, x3,y3=R,
+ *  draw from P to Q to R   if top=0
+ *    or from P to (x,ymax) if top>0
+ *    or from Q to (x,ymax) if top<0
+ */
+void smooth_line::init0(int x1,int y1, int x2,int y2, int x3,int y3, int top)
+{
+       int x0 = x1+x3-x2, y0 = y1+y3-y2; // Q'
+
+       int a = x2-x1,  c = y2-y1;
+       int b = x2-x3,  d = y2-y3;
+       A = c*c + d*d;  C = a*a + b*b;  B = a*c + b*d;
+
+       sx = top >= 0 ? x1 : x3;
+       sy = top >= 0 ? y1 : y3;
+       xs = x2 > sx || (x2==sx && (x1+x3-sx)>=x2) ? 1 : -1;
+       int64_t px = sx-x0, py = sy-y0;
+       dx = A*px - B*py;  dy = C*py - B*px;
+       r = 0;
+
+       if( top ) {
+               double ymy = sqrt(A), ymx = B/ymy;
+               ex = x0 + rnd(ymx);
+               ey = y0 + rnd(ymy);
+       }
+       else {
+               ex = x3;  ey = y3;
+       }
+
+       ys = a*b > 0 && (!top || top*xs*(b*c - a*d) > 0) ? -1 : 1;
+       if( ys < 0 ) {
+               double xmx = xs*sqrt(C), xmy = B/xmx;
+               xmxx = x0 + rnd(xmx);
+               xmxy = y0 + rnd(xmy);
+       }
+       else {
+               xmxx = ex; xmxy = ey;
+       }
+}
+
+/*  x1,y1 = P, x2,y2 = Q, x3,y3=R,
+ *  draw from (x,ymax) to P
+ */
+void smooth_line::init1(int x1,int y1, int x2,int y2, int x3,int y3)
+{
+       int x0 = x1+x3-x2, y0 = y1+y3-y2; // Q'
+
+       int a = x2-x1,  c = y2-y1;
+       int b = x2-x3,  d = y2-y3;
+       A = c*c + d*d;  C = a*a + b*b;  B = a*c + b*d;
+
+       double ymy = -sqrt(A), ymx = B/ymy;
+       int64_t px = rnd(ymx), py = rnd(ymy);
+       sx = x0 + px;  ex = x1;
+       sy = y0 + py;  ey = y1;
+       xs = x2 > x1 || (x2==x1 && x3>=x2) ? 1 : -1;
+       dx = A*px - B*py;  dy = C*py - B*px;
+       r = 4 * (A*px*px - 2*B*px*py + C*py*py + B*B - A*C);
+
+       ys = a*b > 0 && xs*(b*c - a*d) < 0 ? -1 : 1;
+       if( ys < 0 ) {
+               double xmx = xs*sqrt(C), xmy = B/xmx;
+               xmxx = x0 + rnd(xmx);
+               xmxy = y0 + rnd(xmy);
+       }
+       else {
+               xs = -xs;
+               xmxx = ex; xmxy = ey;
+       }
+       if( xs > 0 )
+               vframe->draw_pixel(sx, sy);
+       while( xs*(sx-xmxx) < 0 && (xs*dx < 0 || rx() < 0) ) {
+               moveX(rx());
+               vframe->draw_pixel(sx, sy);
+       }
+}
+
+
+void VFrame::smooth_draw(int x1, int y1, int x2, int y2, int x3, int y3)
+{
+//printf("p smooth_draw( %d,%d, %d,%d, %d,%d )\n", x1,y1,x2,y2,x3,y3);
+       if( y1 > y3 ) {         // make y3 >= y1
+               int xt = x1;  x1 = x3;  x3 = xt;
+               int yt = y1;  y1 = y3;  y3 = yt;
+       }
+       if( y1 > y2 && y3 > y2 ) {
+               smooth_line lt(this), rt(this); // Q on bottom
+               lt.init1(x1, y1, x2, y2, x3, y3);
+               rt.init1(x3, y3, x2, y2, x1, y1);
+               while( !lt.done || !rt.done ) {
+                       lt.draw();
+                       rt.draw();
                }
-               int numerator = x2 - x1;
-               int denominator = y2 - y1;
-               for(int i = y1; i <= y2; i++)
-               {
-                       int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
-                       draw_pixel(x, i);
+       }
+       else if( y1 < y2 && y3 < y2 ) {
+               smooth_line lt(this), rt(this); // Q on top
+               lt.init0(x1, y1, x2, y2, x3, y3, 1);
+               draw_pixel(lt.sx, lt.sy);
+               rt.init0(x1, y1, x2, y2, x3, y3, -1);
+               draw_pixel(rt.sx, rt.sy);
+               while( !lt.done || !rt.done ) {
+                       lt.draw();
+                       rt.draw();
+               }
+       }
+       else {
+               smooth_line pt(this);           // Q in between
+               pt.init0(x1, y1, x2, y2, x3, y3, 0);
+               draw_pixel(pt.sx, pt.sy);
+               while( !pt.done ) {
+                       pt.draw();
                }
        }
-//printf("FindObjectMain::draw_line 2\n");
 }
 
 void VFrame::draw_rect(int x1, int y1, int x2, int y2)
@@ -1433,30 +1657,16 @@ void VFrame::draw_rect(int x1, int y1, int x2, int y2)
        draw_line(x1, y2 - 1, x1, y1 + 1);
 }
 
-#define ARROW_SIZE 10
-void VFrame::draw_arrow(int x1, int y1, int x2, int y2)
+void VFrame::draw_arrow(int x1, int y1, int x2, int y2, int sz)
 {
        double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
-       double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
-       double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
-       int x3;
-       int y3;
-       int x4;
-       int y4;
-       if(x2 < x1)
-       {
-               x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
-               y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
-               x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
-               y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
-       }
-       else
-       {
-               x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
-               y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
-               x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
-               y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
-       }
+       double angle1 = angle + (float)145 / 360 * 2 * M_PI;
+       double angle2 = angle - (float)145 / 360 * 2 * M_PI;
+       int s = x2 < x1 ? -1 : 1;
+       int x3 = x2 + s * (int)(sz * cos(angle1));
+       int y3 = y2 + s * (int)(sz * sin(angle1));
+       int x4 = x2 + s * (int)(sz * cos(angle2));
+       int y4 = y2 + s * (int)(sz * sin(angle2));
 
 // Main vector
        draw_line(x1, y1, x2, y2);
@@ -1468,6 +1678,15 @@ void VFrame::draw_arrow(int x1, int y1, int x2, int y2)
        if(abs(y2 - y1) || abs(x2 - x1)) draw_line(x2, y2, x4, y4);
 }
 
-
+void VFrame::draw_x(int x, int y, int sz)
+{
+       draw_line(x-sz,y-sz, x+sz,y+sz);
+       draw_line(x+sz,y-sz, x-sz,y+sz);
+}
+void VFrame::draw_t(int x, int y, int sz)
+{
+       draw_line(x,y-sz, x,y+sz);
+       draw_line(x+sz,y, x-sz,y);
+}
 
 
index adb8d792f1f7727c97028c925b4676872fcce554..73ec70da0b2bacdb40e601746419cde25e2eb39c 100644 (file)
@@ -344,10 +344,14 @@ public:
 // This clears the stacks and the param table
        void clear_stacks();
 
-       void draw_rect(int x1, int y1, int x2, int y2);
-       void draw_line(int x1, int y1, int x2, int y2);
        void draw_pixel(int x, int y);
-       void draw_arrow(int x1, int y1, int x2, int y2);
+       void draw_line(int x1, int y1, int x2, int y2);
+       void draw_smooth(int x1, int y1, int x2, int y2, int x3, int y3);
+       void smooth_draw(int x1, int y1, int x2, int y2, int x3, int y3);
+       void draw_rect(int x1, int y1, int x2, int y2);
+       void draw_arrow(int x1, int y1, int x2, int y2, int sz=10);
+       void draw_x(int x1, int y1, int sz=2);
+       void draw_t(int x1, int y1, int sz=2);
 
 // 3D scene graphs
 // Not integrated with shmem because that only affects codecs
index d8fdbbd0adf00452e632ca72bdeeee8de2ab1083..0530ee0885d97d19d1c1f3d089d171f848ee988b 100644 (file)
@@ -36,8 +36,9 @@ audio_tools := audioscope cdripper compressor dcoffset delayaudio \
 plugin_dirs += video_tools
 video_tools := blur decimate delayvideo denoisemjpeg denoisevideo downsample \
        fieldframe flash framefield freezeframe greycstoration interpolatepixels \
-       interpolatevideo invertvideo linearblur loopvideo motion2 motionblur \
-       motion motion-cv motion-hv overlay radialblur reframe reframert reroute \
+       interpolatevideo invertvideo linearblur loopvideo \
+       motion2 motionblur motion motion-cv motion-hv motion51 \
+       overlay radialblur reframe reframert reroute \
        reversevideo seltempavg sharpen spectrogram svg titler timeavg timefront \
        unsharp videoscope wave zoomblur
 
index 5bac9134d3d10848de34cec26c398394e036179b..c2000234e952352f83d64ccf1ae43a1c5742c244 100644 (file)
@@ -78,6 +78,7 @@ DIRS = \
        loopaudio \
        loopvideo \
        motion \
+       motion51 \
        motion-cv \
        motion-hv \
        motion2point \
index 8ef90b551b191d1b4b1c66baeda0255a71461510..191b635c2a84bd8e99c97ad1fe6822a9f6612590 100644 (file)
@@ -1350,10 +1350,9 @@ class BluebananaAAReadout : public BB_Tumble {
 
 class BluebananaAASlider : public BluebananaSliderSingle {
 public:
-  int hidden;
   BluebananaAASlider(BluebananaMain *plugin, BluebananaWindow *gui,
                      int x, int y, int w, int h)
-    : BluebananaSliderSingle(plugin,gui,x,y,w,h,0,100) { hidden = 0; }
+    : BluebananaSliderSingle(plugin,gui,x,y,w,h,0,100) {}
   virtual int handle_event() {
     plugin->config.Aadj_val = val;
     return 1;
@@ -1365,11 +1364,11 @@ public:
   void update(){
     val = plugin->config.Aadj_val;
     if( BC_CModels::has_alpha(plugin->colormodel) ) {
-      if( hidden ) { show_window();  hidden = 0; }
+      if( is_hidden() ) show_window();
     }else{
-      if( !hidden ) { hide_window();  hidden = 1; }
+      if( !is_hidden() ) hide_window();
     }
-    if( hidden ) return;
+    if( is_hidden() ) return;
     highlight = plugin->config.active && plugin->config.Aadj_active;
     gui->Aadj_readout->update(plugin->config.Aadj_val);
     gui->slider_labels[11]->set_color(highlight  && plugin->config.Aadj_val != 100 ?
@@ -1692,13 +1691,11 @@ public:
 
 class BluebananaAAActive : public BC_CheckBox {
 public:
-  int hidden;
 
   BluebananaAAActive(BluebananaMain *plugin, BluebananaWindow *gui)
   : BC_CheckBox(-1, -1, &plugin->config.Aadj_active, ""){
     this->plugin = plugin;
     this->gui = gui;
-    hidden = 0;
   }
   virtual int handle_event(){
     plugin->config.Aadj_active =
@@ -1709,11 +1706,11 @@ public:
   void update(){
     this->BC_CheckBox::update(plugin->config.Aadj_active,1);
     if( BC_CModels::has_alpha(plugin->colormodel) ) {
-      if( hidden ) { show_window();  hidden = 0; }
+      if( is_hidden() ) show_window();
     }else{
-      if( !hidden ) { hide_window();  hidden = 1; }
+      if( !is_hidden() ) hide_window();
     }
-    if( hidden ) return;
+    if( is_hidden() ) return;
     gui->Aadj_slider->update();
   }
   BluebananaMain *plugin;
@@ -1848,7 +1845,6 @@ public:
     this->y=-1;
     gui->add_subwindow(this->label);
     gui->add_subwindow(this);
-    hidden = -1;
   }
   virtual int handle_event(){
     plugin->config.capture_mask=get_value();
@@ -1887,23 +1883,21 @@ public:
       break;
     }
 
-    if(hideme && hidden!=1){
+    if(hideme && !is_hidden()){
       hide_window();
       label->hide_window();
       gui->set_color(get_resources()->get_bg_color());
       gui->draw_box(x,y,w,h);
       gui->set_color(get_resources()->default_text_color);
       gui->draw_line(x,y+h/2,x+w,y+h/2);
-      hidden=1;
       f=1;
     }
 
-    if(!hideme && hidden!=0){
+    if(!hideme && is_hidden()){
       gui->set_color(get_resources()->get_bg_color());
       gui->draw_box(x,y,w,h);
       show_window();
       label->show_window();
-      hidden=0;
       f=1;
     }
 
@@ -1915,7 +1909,7 @@ public:
   BluebananaMain *plugin;
   BluebananaWindow *gui;
   BC_Title *label;
-  int x,y,padx,hidden;
+  int x,y,padx;
 };
 
 // ------------------------------------------ Use mask ----------------------------------------
@@ -1931,7 +1925,6 @@ public:
     this->y=-1;
     gui->add_subwindow(this->label);
     gui->add_subwindow(this);
-    hidden = -1;
   }
   virtual int handle_event(){
     plugin->config.use_mask=get_value();
@@ -1969,14 +1962,13 @@ public:
     case BC_YUV888:
     case BC_RGB161616:
     case BC_YUV161616:
-      if(hidden!=1){
+      if(!is_hidden()){
         hide_window();
         label->hide_window();
         gui->set_color(get_resources()->get_bg_color());
         gui->draw_box(x,y,w,h);
         gui->set_color(get_resources()->default_text_color);
         gui->draw_line(x,y+h/2,x+w,y+h/2);
-        hidden=1;
         f=1;
       }
       break;
@@ -1985,12 +1977,11 @@ public:
     case BC_YUVA8888:
     case BC_RGBA16161616:
     case BC_YUVA16161616:
-      if(hidden!=0){
+      if(is_hidden()){
         gui->set_color(get_resources()->get_bg_color());
         gui->draw_box(x,y,w,h);
         show_window();
         label->show_window();
-        hidden=0;
         f=1;
       }
       break;
@@ -2009,7 +2000,7 @@ public:
   BluebananaMain *plugin;
   BluebananaWindow *gui;
   BC_Title *label;
-  int x,y,padx,hidden;
+  int x,y,padx;
 };
 
 // --------------------------------------- Main GUI window --------------------------------------
index 31c4dc4ec42d01f7e1ef8c7a48e02c7fb676385a..80787f40ae37e54f66449d786cd49e8c79413075 100644 (file)
@@ -743,7 +743,7 @@ static inline void cx_product(int n, int sf, double *rp, double *ip,
 static inline void cj_product(int n, int sf, double *rp, double *ip,
                double *arp, double *aip, double *brp, double *bip)
 {
-       int m = !sf ? n : n/2, i = 0;
+       int m = !sf ? n-1 : n/2, i = 0;
        while( i <= m ) {
                double ar = arp[i], ai = aip[i];
                double br = brp[i], bi = -bip[i];
diff --git a/cinelerra-5.1/plugins/motion51/Makefile b/cinelerra-5.1/plugins/motion51/Makefile
new file mode 100644 (file)
index 0000000..0990f83
--- /dev/null
@@ -0,0 +1,13 @@
+include ../../plugin_defs
+
+OBJS := \
+       $(OBJDIR)/motion51.o \
+       $(OBJDIR)/motionwindow51.o
+
+PLUGIN = motion51
+
+include ../../plugin_config
+
+$(OBJDIR)/motion51.o: motion51.C
+$(OBJDIR)/motionwindow51.o: motionwindow51.C
+
diff --git a/cinelerra-5.1/plugins/motion51/motion51.C b/cinelerra-5.1/plugins/motion51/motion51.C
new file mode 100644 (file)
index 0000000..402a5a0
--- /dev/null
@@ -0,0 +1,963 @@
+#include "affine.h"
+#include "bchash.h"
+#include "clip.h"
+#include "filexml.h"
+#include "fourier.h"
+#include "keyframe.h"
+#include "language.h"
+#include "mainerror.h"
+#include "motion51.h"
+#include "motionwindow51.h"
+#include "mutex.h"
+#include "transportque.inc"
+
+#include <errno.h>
+#include <math.h>
+#include <unistd.h>
+
+static const double passing = 0.92;
+static bool passible(double v) { return v > passing; }
+
+
+REGISTER_PLUGIN(Motion51Main)
+
+void Motion51Config::init()
+{
+       sample_steps = 1024;
+       sample_r = 50.f;
+       block_x = block_y = 50.f;
+       block_w = block_h = 50.f;
+       horiz_limit = vert_limit = twist_limit = 50.f;
+       shake_fade = twist_fade = 3.f;
+       draw_vectors = 1;
+       strcpy(tracking_file, TRACKING_FILE);
+       tracking = 0;
+}
+
+Motion51Config::Motion51Config()
+{
+       init();
+}
+
+int Motion51Config::equivalent(Motion51Config &that)
+{
+       return horiz_limit == that.horiz_limit &&
+               vert_limit == that.vert_limit &&
+               twist_limit == that.twist_limit &&
+               shake_fade == that.shake_fade &&
+               twist_fade == that.twist_fade &&
+               sample_r == that.sample_r &&
+               sample_steps == that.sample_steps &&
+               draw_vectors == that.draw_vectors &&
+               EQUIV(block_x, that.block_x) &&
+               EQUIV(block_y, that.block_y) &&
+               block_w == that.block_w &&
+               block_h == that.block_h &&
+               !strcmp(tracking_file, that.tracking_file) &&
+               tracking == that.tracking;
+}
+
+void Motion51Config::copy_from(Motion51Config &that)
+{
+       horiz_limit = that.horiz_limit;
+       vert_limit = that.vert_limit;
+       twist_limit = that.twist_limit;
+       shake_fade = that.shake_fade;
+       twist_fade = that.twist_fade;
+       sample_r = that.sample_r;
+       sample_steps = that.sample_steps;
+       draw_vectors = that.draw_vectors;
+       block_x = that.block_x;
+       block_y = that.block_y;
+       block_w = that.block_w;
+       block_h = that.block_h;
+       strcpy(tracking_file, that.tracking_file);
+       tracking = that.tracking;
+}
+
+void Motion51Config::interpolate(Motion51Config &prev, Motion51Config &next,
+       int64_t prev_frame, int64_t next_frame, int64_t current_frame)
+{
+       copy_from(prev);
+}
+
+
+Motion51Main::Motion51Main(PluginServer *server)
+ : PluginVClient(server)
+{
+       out_frame = 0;  out_position = -1;
+       ref_frame = 0;  ref_position = -1;
+       tmp_frame = 0;
+       affine = 0;
+       motion_scan = 0;
+
+       cache_file[0] = 0;
+       cache_fp = active_fp = 0;
+       cache_line[0] = 0;
+       cache_key = active_key = -1;
+       tracking_position = -1;
+
+       out_w = out_h = out_r = 0;
+       rx = ry = rw = rh = rr = 0;
+       current_dx = current_dy = 0;
+       x_steps = y_steps = 16;
+       r_steps = 4;
+       cir_sz = 0;  cir_r = 0;
+       xpts = ypts = 0;
+       total_dx = total_dy = 0;
+       total_angle = 0;
+}
+
+Motion51Main::~Motion51Main()
+{
+       update_cache_file();
+       delete out_frame;
+       delete ref_frame;
+       delete tmp_frame;
+       delete affine;
+       delete motion_scan;
+       delete [] xpts;
+       delete [] ypts;
+}
+
+const char* Motion51Main::plugin_title() { return _("Motion51"); }
+int Motion51Main::is_realtime() { return 1; }
+int Motion51Main::is_multichannel() { return 1; }
+
+
+NEW_WINDOW_MACRO(Motion51Main, Motion51Window)
+LOAD_CONFIGURATION_MACRO(Motion51Main, Motion51Config)
+
+
+void Motion51Main::update_gui()
+{
+       if( !thread ) return;
+       if( !load_configuration() ) return;
+       thread->window->lock_window("Motion51Main::update_gui");
+       Motion51Window *window = (Motion51Window*)thread->window;
+       window->update_gui();
+       thread->window->unlock_window();
+}
+
+
+
+
+void Motion51Main::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("MOTION51");
+       output.tag.set_property("HORIZ_LIMIT", config.horiz_limit);
+       output.tag.set_property("VERT_LIMIT", config.vert_limit);
+       output.tag.set_property("TWIST_LIMIT", config.twist_limit);
+       output.tag.set_property("SHAKE_FADE", config.shake_fade);
+       output.tag.set_property("TWIST_FADE", config.twist_fade);
+       output.tag.set_property("SAMPLE_R", config.sample_r);
+       output.tag.set_property("SAMPLE_STEPS", config.sample_steps);
+       output.tag.set_property("DRAW_VECTORS", config.draw_vectors);
+       output.tag.set_property("BLOCK_W", config.block_w);
+       output.tag.set_property("BLOCK_H", config.block_h);
+       output.tag.set_property("BLOCK_X", config.block_x);
+       output.tag.set_property("BLOCK_Y", config.block_y);
+       output.tag.set_property("TRACKING_FILE", config.tracking_file);
+       output.tag.set_property("TRACKING", config.tracking);
+       output.append_tag();
+       output.tag.set_title("/MOTION51");
+       output.append_tag();
+       output.terminate_string();
+}
+
+void Motion51Main::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("MOTION51") ) {
+                       config.horiz_limit = input.tag.get_property("HORIZ_LIMIT", config.horiz_limit);
+                       config.vert_limit = input.tag.get_property("VERT_LIMIT", config.vert_limit);
+                       config.twist_limit = input.tag.get_property("TWIST_LIMIT", config.twist_limit);
+                       config.shake_fade = input.tag.get_property("SHAKE_FADE", config.shake_fade);
+                       config.twist_fade = input.tag.get_property("TWIST_FADE", config.twist_fade);
+                       config.sample_r = input.tag.get_property("SAMPLE_R", config.sample_r);
+                       config.sample_steps = input.tag.get_property("SAMPLE_STEPS", config.sample_steps);
+                       config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors);
+                       config.block_w = input.tag.get_property("BLOCK_W", config.block_w);
+                       config.block_h = input.tag.get_property("BLOCK_H", config.block_h);
+                       config.block_x = input.tag.get_property("BLOCK_X", config.block_x);
+                       config.block_y = input.tag.get_property("BLOCK_Y", config.block_y);
+                       input.tag.get_property("TRACKING_FILE", config.tracking_file);
+                       config.tracking = input.tag.get_property("TRACKING", config.tracking);
+               }
+       }
+}
+
+#if 0
+static void snap(const char *fn, VFrame *img, float x, float y, float r)
+{
+       VFrame vfrm(img->get_w(),img->get_h(),img->get_color_model());
+       vfrm.copy_from(img);
+       vfrm.draw_smooth(x-r,y, x-r,y+r, x,y+r);
+       vfrm.draw_smooth(x,y+r, x+r,y+r, x+r,y);
+       vfrm.draw_smooth(x+r,y, x+r,y-r, x,y-r);
+       vfrm.draw_smooth(x,y-r, x-r,y-r, x-r,y);
+       vfrm.write_png(fn);
+}
+#endif
+
+#if 0
+// nearest sample
+static void nearest_uint8(double *pix[3], double rx, double ry,
+               uint8_t **rows, int psz, int iw1, int ih1)
+{
+       int ix = (int)rx, iy = (int)ry;
+       bclamp(ix, 0, iw1);  bclamp(iy, 0, ih1);
+       uint8_t *cp = (uint8_t*)(rows[iy] + psz * ix);
+       for( int i=0; i<3; ++pix[i], ++cp, ++i ) *pix[i] = *cp;
+}
+static void nearest_float(double *pix[3], double rx, double ry,
+               uint8_t **rows, int psz, int iw1, int ih1)
+{
+       int ix = (int)rx, iy = (int)ry;
+       bclamp(ix, 0, iw1);  bclamp(iy, 0, ih1);
+       float *fp = (float*)(rows[iy] + psz * ix);
+       for( int i=0; i<3; ++pix[i], ++fp, ++i ) *pix[i] = *fp;
+}
+#endif
+
+// corner interpolation sample
+static void corner_uint8(double *pix[3], double rx, double ry,
+               uint8_t **rows, int psz, int iw1, int ih1)
+{
+       bclamp(rx, 0, iw1);  bclamp(ry, 0, ih1);
+       int iy = (int)ry;
+       double yf1 = ry - iy, yf0 = 1.0 - yf1;
+       uint8_t *row0 = rows[iy];
+       if( iy < ih1 ) ++iy;
+       uint8_t *row1 = rows[iy];
+       int ix = (int)rx;
+       double xf1 = rx - ix, xf0 = 1.0 - xf1;
+       int i0 = psz * ix;
+       if( ix < iw1 ) ++ix;
+       int i1 = psz * ix;
+       uint8_t *cp00 = (uint8_t*)&row0[i0], *cp01 = (uint8_t*)&row0[i1];
+       uint8_t *cp10 = (uint8_t*)&row1[i0], *cp11 = (uint8_t*)&row1[i1];
+       double a00 = xf0 * yf0, a01 = xf1 * yf0;
+       double a10 = xf0 * yf1, a11 = xf1 * yf1;
+       for( int i=0; i<3; ++pix[i], ++cp00, ++cp01, ++cp10, ++cp11, ++i )
+               *pix[i] = *cp00*a00 + *cp01*a01 + *cp10*a10 + *cp11*a11;
+}
+static void corner_float(double *pix[3], double rx, double ry,
+               uint8_t **rows, int psz, int iw1, int ih1)
+{
+       bclamp(rx, 0, iw1);  bclamp(ry, 0, ih1);
+       int iy = (int)ry;
+       double yf1 = ry - iy, yf0 = 1.0 - yf1;
+       uint8_t *row0 = rows[iy];
+       if( iy < ih1 ) ++iy;
+       uint8_t *row1 = rows[iy];
+       int ix = (int)rx;
+       double xf1 = rx - ix, xf0 = 1.0 - xf1;
+       int i0 = psz * ix;
+       if( ix < iw1 ) ++ix;
+       int i1 = psz * ix;
+       float *fp00 = (float*)&row0[i0], *fp01 = (float*)&row0[i1];
+       float *fp10 = (float*)&row1[i0], *fp11 = (float*)&row1[i1];
+       double a00 = xf0 * yf0, a01 = xf1 * yf0;
+       double a10 = xf0 * yf1, a11 = xf1 * yf1;
+       for( int i=0; i<3; ++pix[i], ++fp00, ++fp01, ++fp10, ++fp11, ++i )
+               *pix[i] = *fp00*a00 + *fp01*a01 + *fp10*a10 + *fp11*a11;
+}
+
+
+static inline double cor(int n, double *ap, double *bp)
+{
+       double s = 0;
+       while( --n >= 0 ) s += *ap++ * *bp++;
+       return s;
+}
+
+static inline double sqr(double v) { return v*v; }
+
+static inline void cj_product(int n, int sf, double *rp, double *ip,
+               double *arp, double *aip, double *brp, double *bip)
+{
+       int m = !sf ? n-1 : n/2, i = 0;
+       while( i <= m ) {
+               double ar = arp[i], ai = aip[i];
+               double br = brp[i], bi = -bip[i];
+               rp[i] = ar*br - ai*bi;  // complex a*ib'
+               ip[i] = ar*bi + ai*br;
+               ++i;
+       }
+       if( !sf ) return;
+       while( --m > 0 ) { rp[i] = rp[m];  ip[i] = -ip[m];  ++i; }
+}
+
+typedef struct { double x, y, d; } coord_t;
+static int coord_cmpr(const void *ap, const void *bp)
+{
+       coord_t *a = (coord_t *)ap, *b = (coord_t *)bp;
+       return a->d == b->d ? 0 : a->d < b->d ? -1 : 1;
+}
+
+
+int64_t Motion51Main::get_ref_position()
+{
+       int64_t position = out_position - 1;
+       if( position < 0 || position != ref_position ) {
+// clip to edit boundaries
+               int64_t pos = get_source_start();
+               if( position < pos )
+                       position = pos;
+               else if( position >= (pos += get_total_len()) )
+                       position = pos-1;
+// clip to keyframe boundaries
+               KeyFrame *next_keyframe = get_next_keyframe(out_position, 1);
+               int64_t keyframe_end = next_keyframe->position;
+               if( (pos=next_keyframe->position) > 0 && position > keyframe_end )
+                       position = keyframe_end;
+               KeyFrame *prev_keyframe = get_prev_keyframe(out_position, 1);
+               int64_t keyframe_start = prev_keyframe->position;
+               if( keyframe_start > 0 && position < keyframe_start )
+                       position = keyframe_start;
+       }
+       return position;
+}
+
+void Motion51Main::set_tracking_path()
+{
+       const char *sp = TRACKING_FILE;
+       char *cp = config.tracking_file, *ep = cp+sizeof(config.tracking_file)-1;
+       while( cp < ep && *sp != 0 ) *cp++ = *sp++;
+       if( cp < ep && (sp=get_source_path()) ) {
+               *cp++ = '-';
+               const char *bp = strrchr(sp,'/');
+               if( bp ) sp = bp+1;
+               while( cp < ep && *sp != 0 ) {
+                       *cp++ = (*sp>='a' && *sp<='z') ||
+                               (*sp>='A' && *sp<='Z') ||
+                               (*sp>='0' && *sp<='9') ? *sp : '_';
+                       ++sp;
+               }
+       }
+       *cp = 0;
+}
+
+void Motion51Main::update_tracking_cache()
+{
+       if( (!config.tracking && cache_fp) || (config.tracking && !cache_fp) ||
+           (active_fp && active_key > get_source_position()) )
+               update_cache_file();
+}
+
+int Motion51Main::load_tracking_cache(int64_t position)
+{
+       if( !config.tracking ) return 1;
+       if( get_cache_line(position) ) return 1;
+       if( sscanf(cache_line, "%jd %f %f %f", &position, &dx, &dy, &dt) != 4 ) return 1;
+       return 0;
+}
+
+void Motion51Main::save_tracking_cache(int64_t position)
+{
+       if( !config.tracking ) return;
+       char line[BCSTRLEN];
+       snprintf(line, sizeof(line), "%jd %f %f %f\n", position, dx, dy, dt);
+       put_cache_line(line);
+}
+
+void Motion51Main::match(VFrame *ref, VFrame *cur)
+{
+       if( !motion_scan ) {
+               int cpus = get_project_smp()+1;
+               motion_scan = new Motion51Scan(this, cpus, x_steps, y_steps);
+       }
+
+       motion_scan->scan(ref, cur, config.sample_steps);
+
+       if( passible(motion_scan->cor_value) ) {
+               dx = motion_scan->dx_result - rx;
+               dy = motion_scan->dy_result - ry;
+               dt = motion_scan->dt_result * 180./M_PI;
+       }
+       else {
+               total_dx = total_dy = total_angle = 0;
+               dx = dy = dt = 0;
+       }
+
+}
+
+int Motion51Main::transform_target(int use_opengl)
+{
+       if( dx || dy || dt ) {
+               int cpus = get_project_smp()+1;
+               if( !affine ) affine = new AffineEngine(cpus, cpus);
+               affine->set_in_pivot(rx, ry);
+               affine->set_out_pivot(rx-dx, ry-dy);
+               if( use_opengl )
+                       return run_opengl();
+               new_temp(out_frame, out)->copy_from(out);
+               out->clear_frame();
+               affine->rotate(out, out_frame, dt);
+//printf("transform_target at %jd: rotate(%f, %f, %f)\n", out_position, dx, dy, dt);
+       }
+        return 0;
+}
+
+int Motion51Main::handle_opengl()
+{
+#ifdef HAVE_GL
+        affine->set_opengl(1);
+       affine->rotate(out, out, dt);
+       out->screen_to_ram();
+        affine->set_opengl(0);
+#endif
+        return 0;
+}
+
+
+int Motion51Main::process_buffer(VFrame **frame, int64_t position, double frame_rate)
+{
+       int need_reconfigure = load_configuration();
+
+       int target_layer = 0;
+       int reference_layer = PluginClient::total_in_buffers-1;
+       VFrame *ref_layer = frame[reference_layer];
+       out = frame[target_layer];
+       out_position = position;
+       if( !out_position ) total_dx = total_dy = total_angle = 0;
+       get_pixel = BC_CModels::is_float(out->get_color_model()) ?
+               &corner_float : &corner_uint8;
+
+       int use_opengl = get_use_opengl();
+       read_frame(out, target_layer, out_position, frame_rate, use_opengl);
+       out_w = out->get_w();
+       out_h = out->get_h();
+       out_r = 0.5 * (out_w < out_h ? out_w : out_h);
+       rw = out_w * config.block_w/100.;
+       rh = out_h * config.block_h/100.;
+       rx = out_w * config.block_x/100.;
+       ry = out_h * config.block_y/100.;
+       rr = out_r * config.sample_r/100.;
+       reset_sample(config.sample_steps, rr);
+       dx = 0;  dy = 0;  dt = 0;
+
+       update_tracking_cache();
+       if( load_tracking_cache(out_position) ) {
+               int64_t ref_pos = get_ref_position();
+               if( !ref_frame || ref_pos != ref_position || need_reconfigure ) {
+                       new_temp(ref_frame, ref_layer);
+                       read_frame(ref_frame, reference_layer, ref_pos, frame_rate, 0);
+                       total_dx = total_dy = 0;  total_angle = 0;
+                       ref_position = ref_pos;
+               }
+               VFrame *cur_frame = out;
+               if( reference_layer != target_layer ) {
+                       new_temp(tmp_frame, ref_layer);
+                       read_frame(tmp_frame, reference_layer, out_position, frame_rate, 0);
+                       cur_frame = tmp_frame;
+               }
+               match(ref_frame, cur_frame);
+               save_tracking_cache(out_position);
+       }
+       current_dx = dx;  current_dy = dy;
+       double sf = 1. - config.shake_fade/100.;
+       dx += total_dx * sf;  dy += total_dy * sf;
+       double rf = 1. - config.twist_fade/100.;
+       dt += total_angle * rf;
+       if( dt < -180. ) dt += 360.;
+       else if( dt > 180. ) dt -= 360.;
+
+       float tot_dx = out_w * config.horiz_limit/100.;
+       bclamp(dx, -tot_dx, tot_dx);
+       float tot_dy = out_h * config.vert_limit/100.;
+       bclamp(dy, -tot_dy, tot_dy);
+       float tot_dt = 180. * config.twist_limit/100.;
+       bclamp(dt, -tot_dt, +tot_dt);
+       total_dx = dx;  total_dy = dy;  total_angle = dt;
+       if( ref_frame && reference_layer == target_layer &&
+           ref_position+1 == out_position &&
+           ref_frame->get_w() == out->get_w() &&
+           ref_frame->get_h() == out->get_h() &&
+           ref_frame->get_color_model() == out->get_color_model() ) {
+               ref_frame->copy_from(out);
+               ref_position = out_position;
+       }
+       transform_target(use_opengl);
+
+       if( config.draw_vectors )
+               draw_vectors(out);
+
+       return 0;
+}
+
+VFrame* Motion51Main::new_temp(VFrame *&tmp, VFrame *ref)
+{
+       if( tmp && (tmp->get_w() != ref->get_w() || tmp->get_h() != ref->get_h() ||
+           tmp->get_color_model() != ref->get_color_model()) ) {
+               delete tmp; tmp = 0;
+       }
+       if( !tmp )
+               tmp = new VFrame(0, -1, ref->get_w(), ref->get_h(), ref->get_color_model(), -1);
+       return tmp;
+}
+
+void Motion51Main::reset_sample(int sz, double r)
+{
+       if( cir_sz == sz && cir_r == r ) return;
+       if( cir_sz != sz ) {
+               cir_sz = sz;
+               delete xpts;  xpts = new double[cir_sz];
+               delete ypts;  ypts = new double[cir_sz];
+       }
+       cir_r = r;
+       int n = cir_sz / r_steps;
+       double dt = (2*M_PI)/n;
+       double dr = r / r_steps;
+       for( int it=0; it<n; ++it ) {
+               double t = it * dt, cos_t = cos(t), sin_t = sin(t);
+               for( int i=0; i<r_steps; ) {
+                       int k = i * n + it;
+                       double r = ++i * dr;
+                       xpts[k] = r * cos_t;
+                       ypts[k] = r * sin_t;
+               }
+       }
+}
+
+void Motion51Main::get_samples(VFrame *img, double *pix[3], double x, double y)
+{
+       int iw = img->get_w(), iw1 = iw-1;
+       int ih = img->get_h(), ih1 = ih-1;
+       uint8_t **rows = img->get_rows();
+       int psz = BC_CModels::calculate_pixelsize(img->get_color_model());
+
+       double *xp = xpts, *yp = ypts;
+       for( int i=cir_sz; --i>=0; ++xp,++yp ) {
+               double px = x + *xp, py = y + *yp;
+               get_pixel(pix, px, py, rows, psz, iw1, ih1);
+       }
+}
+
+void Motion51Main::centroid(double *pix[3], double *ctr_v, double *ctr_x, double *ctr_y)
+{
+       for( int i=0; i<3; ++i )
+               ctr_v[i] = ctr_x[i] = ctr_y[i] = 0;
+       double *xp = xpts, *yp = ypts;
+       for( int k=cir_sz; --k>=0; ++xp,++yp ) {
+               double x = rx + *xp, y = ry + *yp;
+               for( int i=0; i<3; ++pix[i],++i ) {
+                       double v = *pix[i];
+                       ctr_v[i] += v;
+                       ctr_x[i] += v * x;
+                       ctr_y[i] += v * y;
+               }
+       }
+       for( int i=0; i<3; ++i ) {
+               if( !ctr_v[i] ) continue;
+               ctr_x[i] /= ctr_v[i];
+               ctr_y[i] /= ctr_v[i];
+               ctr_v[i] /= cir_sz;
+       }
+}
+
+
+void Motion51Main::draw_vectors(VFrame *img)
+{
+       img->draw_arrow(rx, ry, rx+current_dx, ry+current_dy);
+
+//     img->draw_smooth(rx-rr,ry, rx-rr,ry+rr, rx,ry+rr);
+//     img->draw_smooth(rx,ry+rr, rx+rr,ry+rr, rx+rr,ry);
+//     img->draw_smooth(rx+rr,ry, rx+rr,ry-rr, rx,ry-rr);
+//     img->draw_smooth(rx,ry-rr, rx-rr,ry-rr, rx-rr,ry);
+
+       float rx1 = rx - 0.5*rw;
+       float ry1 = ry - 0.5*rh;
+       float rx2 = rx1 + rw;
+       float ry2 = ry1 + rh;
+
+       img->draw_line(rx1, ry1, rx2, ry1);
+       img->draw_line(rx2, ry1, rx2, ry2);
+       img->draw_line(rx2, ry2, rx1, ry2);
+       img->draw_line(rx1, ry2, rx1, ry1);
+
+       float sx1 = rx1 - rr, sy1 = ry1 - rr;
+       float sx2 = rx2 + rr, sy2 = ry2 + rr;
+
+       img->draw_smooth(sx1, ry1, sx1, sy1, rx1, sy1);
+       img->draw_line(rx1, sy1, rx2, sy1);
+       img->draw_smooth(rx2, sy1, sx2, sy1, sx2, ry1);
+       img->draw_line(sx2, ry1, sx2, ry2);
+       img->draw_smooth(sx2, ry2, sx2, sy2, rx2, sy2);
+       img->draw_line(rx2, sy2, rx1, sy2);
+       img->draw_smooth(rx1, sy2, sx1, sy2, sx1, ry2);
+       img->draw_line(sx1, ry2, sx1, ry1);
+
+       double *xp = xpts, *yp = ypts;
+       for( int i=cir_sz; --i>=0; ++xp, ++yp )
+               img->draw_pixel(rx+*xp, ry+*yp);
+}
+
+int Motion51Main::open_cache_file()
+{
+       if( cache_fp ) return 0;
+       if( !cache_file[0] ) return 1;
+       if( !(cache_fp = fopen(cache_file, "r")) ) return 1;
+// match timestamp, asset path
+       char line[BCTEXTLEN], *cp = line, *ep = cp+sizeof(line);
+       if( fgets(line,sizeof(line),cache_fp) ) {
+               int64_t tm = strtoul(cp,&cp,0);
+// time 0 matches everything
+               if( !tm ) return 0;
+               const char *sp = get_source_path();
+               if( !sp ) return 0;
+               if( cp < ep && *cp == ' ' ) ++cp;
+               int n = strlen(cp);
+               if( n > 0 && cp[n-1] == '\n' ) cp[n-1] = 0;
+               struct stat st;
+               if( !strcmp(cp, sp) && !stat(cp,&st) && st.st_mtime == tm )
+                       return 0;
+       }
+       fclose(cache_fp);  cache_fp = 0;
+       return 1;
+}
+
+void Motion51Main::close_cache_file()
+{
+       if( !cache_fp ) return;
+       fclose(cache_fp);
+       cache_fp = 0; cache_key = -1;
+       tracking_position = -1;
+}
+
+int Motion51Main::load_cache_line()
+{
+       cache_key = -1;
+       if( open_cache_file() ) return 1;
+       if( !fgets(cache_line, sizeof(cache_line), cache_fp) ) return 1;
+       cache_key = strtol(cache_line, 0, 0);
+       return 0;
+}
+
+int Motion51Main::get_cache_line(int64_t key)
+{
+       if( cache_key == key ) return 0;
+       if( open_cache_file() ) return 1;
+       if( cache_key >= 0 && key > cache_key ) {
+               if( load_cache_line() ) return 1;
+               if( cache_key == key ) return 0;
+               if( cache_key > key ) return 1;
+       }
+// binary search file
+       fseek(cache_fp, 0, SEEK_END);
+       int64_t l = -1, r = ftell(cache_fp);
+       while( (r - l) > 1 ) {
+               int64_t m = (l + r) / 2;
+               fseek(cache_fp, m, SEEK_SET);
+// skip to start of next line
+               if( !fgets(cache_line, sizeof(cache_line), cache_fp) )
+                       return -1;
+               if( !load_cache_line() ) {
+                       if( cache_key == key )
+                               return 0;
+                       if( cache_key < key ) { l = m; continue; }
+               }
+               r = m;
+       }
+       return 1;
+}
+
+int Motion51Main::locate_cache_line(int64_t key)
+{
+       int ret = 1;
+       if( key < 0 || !(ret=get_cache_line(key)) ||
+           ( cache_key >= 0 && cache_key < key ) )
+               ret = load_cache_line();
+       return ret;
+}
+
+int Motion51Main::put_cache_line(const char *line)
+{
+       int64_t key = strtol(line, 0, 0);
+       if( key == active_key ) return 1;
+       if( !active_fp ) {
+               close_cache_file();
+               sprintf(cache_file, "%s.bak", config.tracking_file);
+               ::rename(config.tracking_file, cache_file);
+               if( !(active_fp = fopen(config.tracking_file, "w")) ) {
+                       perror(config.tracking_file);
+                       fprintf(stderr, "err writing key %jd\n", key);
+                       return -1;
+               }
+               const char *sp = get_source_path();
+               int64_t tm = 0;
+               if( sp ) {
+                       struct stat st;
+                       if( !stat(sp,&st) ) tm = st.st_mtime;
+               }
+               fprintf(active_fp, "%jd %s\n",tm, sp);
+               active_key = -1;
+       }
+
+       if( active_key < key ) {
+               locate_cache_line(active_key);
+               while( cache_key >= 0 && key >= cache_key ) {
+                       if( key > cache_key )
+                               fputs(cache_line, active_fp);
+                       load_cache_line();
+               }
+       }
+
+       active_key = key;
+       fputs(line, active_fp);
+       fflush(active_fp);
+       return 0;
+}
+
+void Motion51Main::update_cache_file()
+{
+       if( active_fp ) {
+               locate_cache_line(active_key);
+               while( cache_key >= 0 ) {
+                       fputs(cache_line, active_fp);
+                       load_cache_line();
+               }
+               close_cache_file();
+               ::remove(cache_file);
+               fclose(active_fp);  active_fp = 0;
+               active_key = -1;
+       }
+       else
+               close_cache_file();
+       strcpy(cache_file, config.tracking_file);
+}
+
+Motion51Scan::Motion51Scan(Motion51Main *plugin, int n_thds, int x_steps, int y_steps)
+ : LoadServer(n_thds, x_steps*y_steps)
+{
+       this->plugin = plugin;
+       this->x_steps = x_steps;
+       this->y_steps = y_steps;
+       this->fft = new FFT;
+       this->result_lock = new Mutex("Motion51Scan::result_lock");
+
+       cur = ref = 0;
+       bx = by = br = bw = bh = 0;
+       rpix_sz = 0;
+       for( int i=0; i<3; ++i ) {
+               rpix[i] = 0;  rpwr[i] = 0;
+               rctr_v[i] = rctr_x[i] = rctr_y[i] = 0;
+               ref_real[i] = ref_imag[i] = 0;
+       }
+       cor_value = value = 0;
+       dx_result = dy_result = 0;
+       dt_result = 0;
+}
+
+
+Motion51Scan::~Motion51Scan()
+{
+       for( int i=0; i<3; ++i ) {
+               delete [] rpix[i];
+               delete [] ref_real[i];
+               delete [] ref_imag[i];
+       }
+       delete fft;
+       delete result_lock;
+}
+
+// sum absolute diff of ref at (rx,ry) - (cur(cx,cy) rotated ct)
+//   downsampled using corner_sample to x_steps, y_steps
+double Motion51Scan::compare(double cx, double cy, double ct)
+{
+       int iw = ref->get_w(), iw1 = iw-1;
+       int ih = ref->get_h(), ih1 = ih-1;
+       int xsz = x_steps;// iw;
+       int ysz = y_steps;// ih;
+       int psz = BC_CModels::calculate_pixelsize(cur->get_color_model());
+       uint8_t **ref_rows = ref->get_rows();
+       uint8_t **cur_rows = cur->get_rows();
+       double cos_ct = cos(ct), sin_ct = sin(ct);
+       double rx = plugin->rx, ry = plugin->ry;
+       double sx = (double)iw/xsz, sy = (double)ih/ysz;
+       double cpix[3][xsz], rpix[3][xsz];
+       double v = 0;
+       for( int iy=0; iy<y_steps; ++iy ) {
+               double y = iy * sy;
+               double *ref_pix[3] = { rpix[0], rpix[1], rpix[2] };
+               double *cur_pix[3] = { cpix[0], cpix[1], cpix[2] };
+               for( int ix=0; ix<x_steps; ++ix ) {
+                       double x = ix * sx;
+                       plugin->get_pixel(ref_pix, x, y, ref_rows, psz, iw1, ih1);
+                       double tx = x-rx, ty = y-ry;
+                       double xt = cos_ct*tx - sin_ct*ty + cx;
+                       double yt = cos_ct*ty + sin_ct*tx + cy;
+                       plugin->get_pixel(cur_pix, xt, yt, cur_rows, psz, iw1, ih1);
+               }
+               for( int i=0; i<3; ++i ) {
+                       double *rp = rpix[i], *cp = cpix[i];
+                       for( int k=x_steps; --k>=0; ++rp,++cp ) v += fabs(*rp - *cp);
+               }
+       }
+       double mx = BC_CModels::calculate_max(ref->get_color_model());
+       v = 1.-v/(3*mx * x_steps*y_steps);
+       return v;
+}
+
+void Motion51Scan::scan(VFrame *ref, VFrame *cur, int sz)
+{
+       this->ref = ref;
+       this->cur = cur;
+       if( this->rpix_sz != sz ) {
+               this->rpix_sz = sz;
+               for( int i=0; i<3; ++i ) {
+                       delete [] rpix[i];      rpix[i] = new double[sz];
+                       delete [] ref_real[i];  ref_real[i] = new double[sz];
+                       delete [] ref_imag[i];  ref_imag[i] = new double[sz];
+               }
+       }
+
+       bw = plugin->rw;  bh = plugin->rh;
+       bx = plugin->rx;  by = plugin->ry;
+       br = plugin->rr;
+
+       double *ref_pix[3] = { rpix[0], rpix[1], rpix[2] };
+       plugin->get_samples(ref, ref_pix, bx, by);
+       double *pix[3] = { rpix[0], rpix[1], rpix[2] };
+       plugin->centroid(&pix[0], &rctr_v[0], &rctr_x[0], &rctr_y[0]);
+       for( int i=0; i<3; ++i ) {
+               fft->do_fft(sz, 0, rpix[i], 0, ref_real[i], ref_imag[i]);
+               rpwr[i] = cor(sz, rpix[i], rpix[i]);
+       }
+       double scan_limit = 0.25; // quarter pixel resolution
+//printf("frame: %jd\n", plugin->get_source_position());
+       while( bw/x_steps > scan_limit || bh/y_steps > scan_limit ) {
+               dx_result = dy_result = dt_result = 0;
+               cor_value = value = 0;
+//printf("  bx,by %6.2f,%-6.2f  bw,bh %6.2f,%-6.2f ",bx,by, bw,bh);
+               process_packages();
+               bx = dx_result;
+               by = dy_result;
+//printf(" r = %f(%f), %6.2f,%-6.2f\n",value,dt_result*180/M_PI,bx,by);
+               bw *= 0.5;  bh *= 0.5;
+       }
+}
+
+void Motion51Scan::init_packages()
+{
+// sort in increasing distance from current displacement
+       double tx = plugin->rx + plugin->total_dx;
+       double ty = plugin->ry + plugin->total_dy;
+       int npkgs = get_total_packages();
+       coord_t coords[npkgs];
+       int i = 0;
+       double x0 = bx - bw/2, y0 = by - bh/2;
+       for( int iy=0; iy<y_steps; ++iy ) {
+               double y = y0 + iy*bh/y_steps;
+               for( int ix=0; ix<x_steps; ++ix ) {
+                       double x = x0 + ix*bw/x_steps;
+                       double d = sqrt(sqr(x-tx) + sqr(y-ty));
+                       coord_t *cp = coords + i++;
+                       cp->x = x; cp->y = y; cp->d = d;
+               }
+       }
+       qsort(&coords,npkgs,sizeof(coords[0]),coord_cmpr);
+
+       for( i=0; i<npkgs; ++i ) {
+               coord_t *cp = coords + i;
+               Motion51ScanPackage *pkg = (Motion51ScanPackage*)get_package(i);
+               pkg->x = cp->x;  pkg->y = cp->y;
+       }
+}
+
+Motion51ScanUnit::Motion51ScanUnit(Motion51Scan *server, Motion51Main *plugin)
+ : LoadClient(server)
+{
+       this->server = server;
+       this->plugin = plugin;
+       fft = new FFT;
+}
+Motion51ScanUnit::~Motion51ScanUnit()
+{
+       delete fft;
+}
+
+void Motion51ScanUnit::process_package(LoadPackage *package)
+{
+       Motion51ScanPackage *pkg = (Motion51ScanPackage *)package;
+       int sz = server->rpix_sz;
+       double cur_real[3][sz], cur_imag[3][sz];
+       double cpwr[3], cpix[3][sz], *cur_pix[3] = { cpix[0], cpix[1], cpix[2] };
+       plugin->get_samples(server->cur, cur_pix, pkg->x, pkg->y);
+
+       double *pix[3] = { cpix[0], cpix[1], cpix[2] };
+       double cctr_v[3], cctr_x[3], cctr_y[3];
+       plugin->centroid(&pix[0], &cctr_v[0], &cctr_x[0], &cctr_y[0]);
+       double mx = BC_CModels::calculate_max(server->ref->get_color_model());
+       for( int i=0; i<3; ++i ) {
+               double v = 1. - fabs(server->rctr_v[i]-cctr_v[i]) / mx;
+               if( !passible(v) ) return;
+               double *rctr_x = server->rctr_x, *rctr_y = server->rctr_y;
+               double d = sqrt(sqr(rctr_x[i]-cctr_x[i]) + sqr(rctr_y[i]-cctr_y[i]));
+               v = 1 - d / plugin->cir_r;
+               if( !passible(v) ) return;
+       }
+       for( int i=0; i<3; ++i ) {
+               double cs = cor(sz, cpix[i], cpix[i]);
+               double rs = server->rpwr[i];
+               double ss = rs + cs;
+               if( ss == 0 ) ss = 1;
+               double v = 1. - fabs(rs - cs) / ss;
+               if( ! passible(v) ) return;
+               cpwr[i] = 1. / ss;
+       }
+
+       double cor_real[3][sz], cor_imag[3][sz];
+       for( int i=0; i<3; ++i ) {
+               fft->do_fft(sz, 0, cpix[i], 0, cur_real[i], cur_imag[i]);
+               cj_product(sz, 0, cur_real[i], cur_imag[i],
+                       server->ref_real[i], server->ref_imag[i],
+                       cur_real[i], cur_imag[i]);
+               fft->do_fft(sz, 1, cur_real[i], cur_imag[i], cor_real[i], cor_imag[i]);
+       }
+       double sv = 0;
+       int st = 0;
+       for( int t=0; t<sz; ++t ) {
+               double v = 0;
+               for( int i=0; i<3; ++i ) v += cor_real[i][t] * cpwr[i];
+               v = ((2*v) / 3);
+               if( sv >= v ) continue;
+               sv = v;  st = t;
+       }
+       if( server->cor_value > sv ) return;
+       server->cor_value = sv;
+       if( st > sz/2 ) st -= sz;
+       int steps = plugin->r_steps;
+       double tt = steps*(2*M_PI);
+       double th = st*tt/sz, dt = th;
+       double value = 0;
+       double dth = (2*M_PI)/sz;
+       for( int i=-steps; i<=steps; ++i ) {
+               double t = th + i*dth;
+               double v = server->compare(pkg->x, pkg->y, -t);
+               if( value >= v ) continue;
+               value = v;  dt = t;
+       }
+//static int dbg = 0;
+//if( dbg )
+//printf("  %d. %.3f,%.3f %f = %f / %f + %f\n",
+// package_number,pkg->x,pkg->y,dt*180./M_PI,value,sv);
+       server->result_lock->lock("Motion51Scan::process_package");
+       if( value > server->value ) {
+               server->value = value;
+               server->dt_result = dt;
+               server->dx_result = pkg->x;
+               server->dy_result = pkg->y;
+       }
+       server->result_lock->unlock();
+}
+
diff --git a/cinelerra-5.1/plugins/motion51/motion51.h b/cinelerra-5.1/plugins/motion51/motion51.h
new file mode 100644 (file)
index 0000000..bb93ef1
--- /dev/null
@@ -0,0 +1,181 @@
+
+/*
+ * 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
+ *
+ */
+
+#ifndef __MOTION_51_H__
+#define __MOTION_51_H__
+
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "affine.inc"
+#include "bchash.inc"
+#include "filexml.inc"
+#include "keyframe.inc"
+#include "loadbalance.h"
+#include "motion51.inc"
+#include "overlayframe.inc"
+#include "pluginvclient.h"
+#include "vframe.inc"
+
+class FFT;
+class Motion51Config;
+class Motion51Main;
+class Motion51ScanUnit;
+class Motion51ScanPackage;
+class Motion51Scan;
+
+class Motion51Config
+{
+public:
+       Motion51Config();
+
+       void init();
+       int equivalent(Motion51Config &that);
+       void copy_from(Motion51Config &that);
+       void interpolate(Motion51Config &prev, Motion51Config &next,
+               int64_t prev_frame, int64_t next_frame, int64_t current_frame);
+
+       float block_x, block_y, block_w, block_h;
+       float sample_r;
+       int sample_steps;
+       float horiz_limit, vert_limit, twist_limit;
+       float shake_fade, twist_fade;
+       int draw_vectors;
+
+       char tracking_file[BCTEXTLEN];
+       int tracking;
+};
+
+
+class Motion51Main : public PluginVClient
+{
+public:
+       Motion51Main(PluginServer *server);
+       ~Motion51Main();
+
+       int process_buffer(VFrame **frame, int64_t position, double frame_rate);
+       int is_multichannel();
+       int is_realtime();
+       void save_data(KeyFrame *keyframe);
+       void read_data(KeyFrame *keyframe);
+       void update_gui();
+       void reset_sample(int sz, double r);
+       void get_samples(VFrame *img, double *pix[3], double x, double y);
+       void centroid(double *pix[3], double *ctr_v, double *ctr_x, double *ctr_y);
+       void draw_vectors(VFrame *img);
+
+       int64_t get_ref_position();
+       void update_tracking_cache();
+       int load_tracking_cache(int64_t position);
+       void save_tracking_cache(int64_t position);
+
+       void match(VFrame *ref, VFrame *cur);
+       int transform_target(int use_opengl);
+       int handle_opengl();
+       VFrame* new_temp(VFrame *&tmp, VFrame *ref);
+
+       PLUGIN_CLASS_MEMBERS2(Motion51Config)
+
+       char cache_file[BCTEXTLEN];
+       FILE *cache_fp, *active_fp;
+       void set_tracking_path();
+       int open_cache_file();
+       void close_cache_file();
+       int load_cache_line();
+       int locate_cache_line(int64_t key);
+       int get_cache_line(int64_t key);
+       int put_cache_line(const char *line);
+       void update_cache_file();
+       void (*get_pixel)(double *pix[3], double rx, double ry,
+               uint8_t **rows, int psz, int iw1, int ih1);
+
+       VFrame *out;
+       VFrame *out_frame;
+       int64_t out_position;
+       VFrame *ref_frame;
+       int64_t ref_position;
+       VFrame *tmp_frame;
+       AffineEngine *affine;
+       Motion51Scan *motion_scan;
+
+       char cache_line[BCSTRLEN];
+       int64_t cache_key, active_key;
+       int64_t tracking_position;
+       float out_w, out_h, out_r;
+       float dx, dy, dt;
+       double rx, ry, rr, rw, rh;
+       float current_dx, current_dy;
+       int x_steps, y_steps, r_steps;
+       double cir_r;  int cir_sz;
+       double *xpts, *ypts;
+       float total_dx, total_dy;
+       float total_angle;
+};
+
+class Motion51ScanPackage : public LoadPackage
+{
+public:
+       Motion51ScanPackage() {}
+       ~Motion51ScanPackage() {}
+       double x, y;
+};
+class Motion51ScanUnit : public LoadClient
+{
+public:
+       Motion51ScanUnit(Motion51Scan *server, Motion51Main *plugin);
+       ~Motion51ScanUnit();
+       void process_package(LoadPackage *package);
+
+       Motion51Scan *server;
+       Motion51Main *plugin;
+       FFT *fft;
+};
+
+class Motion51Scan : public LoadServer
+{
+public:
+       Motion51Scan(Motion51Main *plugin, int total_clients, int x_steps, int y_steps);
+       ~Motion51Scan();
+       friend class Motion51ScanUnit;
+
+       Motion51Main *plugin;
+       Mutex *result_lock;
+       FFT *fft;
+       VFrame *cur, *ref;
+
+       int x_steps, y_steps;
+       double bx, by, br, bw, bh;
+       int rpix_sz;
+       double *rpix[3], rpwr[3];
+       double rctr_v[3], rctr_x[3], rctr_y[3];
+       double *ref_real[3], *ref_imag[3];
+       double cor_value, value;
+       double dx_result, dy_result, dt_result;
+
+       void init_packages();
+       LoadClient *new_client() { return new Motion51ScanUnit(this, plugin); }
+       LoadPackage *new_package() { return new Motion51ScanPackage(); }
+       void scan(VFrame *ref, VFrame *cur, int sz);
+       double compare(double cx, double cy, double ct);
+};
+
+#endif
diff --git a/cinelerra-5.1/plugins/motion51/motion51.inc b/cinelerra-5.1/plugins/motion51/motion51.inc
new file mode 100644 (file)
index 0000000..da4794e
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef __MOTION_51_INC__
+#define __MOTION_51_INC__
+
+class Motion51Config;
+class Motion51Main;
+class Motion51ScanPackage;
+class Motion51ScanUnit;
+class Motion51Scan;
+
+class Motion51SampleSteps;
+class Motion51DrawVectors;
+class Motion51TrackingFile;
+class Motion51Limits;
+class Motion51ResetConfig;
+class Motion51ResetTracking;
+class Motion51EnableTracking;
+class Motion51Window;
+
+#define TRACKING_FILE "/tmp/motion51"
+
+#endif
diff --git a/cinelerra-5.1/plugins/motion51/motionwindow51.C b/cinelerra-5.1/plugins/motion51/motionwindow51.C
new file mode 100644 (file)
index 0000000..b8aa73c
--- /dev/null
@@ -0,0 +1,282 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2012 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 "cstrdup.h"
+#include "edl.h"
+#include "fonts.h"
+#include "edlsession.h"
+#include "language.h"
+#include "motion51.h"
+#include "motionwindow51.h"
+#include "mwindow.h"
+#include "pluginserver.h"
+
+Motion51Window::Motion51Window(Motion51Main *plugin)
+ : PluginClientWindow(plugin, 600, 400, 600, 400, 0)
+{
+       this->plugin = plugin;
+}
+
+Motion51Window::~Motion51Window()
+{
+}
+
+void Motion51Window::create_objects()
+{
+       int x = 10, y = 20;
+       int x0 = x, x1 = get_w()/2;
+       add_subwindow(sample_steps = new Motion51SampleSteps(plugin, x0=x, y, 72));
+       BC_Title *title = new BC_Title(x0+=sample_steps->get_w()+10, y, _("Samples"));
+       add_subwindow(title);
+       sample_steps->create_objects();
+       sample_r = new Motion51Limits(plugin, this, x1,y, _("Sample Radius%"),
+               &plugin->config.sample_r, 0.f,100.f, 72);
+       sample_r->create_objects();
+       y += sample_r->get_h() + 20;
+
+       block_x = new Motion51Limits(plugin, this, x0=x,y, _("Center X%"),
+               &plugin->config.block_x, 0.f, 100.f, 72);
+       block_x->create_objects();
+       block_y = new Motion51Limits(plugin, this, x1,y, _("Center Y%"),
+               &plugin->config.block_y, 0.f, 100.f, 72);
+       block_y->create_objects();
+       y += block_x->get_h() + 10;
+       block_w = new Motion51Limits(plugin, this, x0,y, _("Search W%"),
+               &plugin->config.block_w, 0.f,100.f, 72);
+       block_w->create_objects();
+       block_h = new Motion51Limits(plugin, this, x1,y, _("Search H%"),
+               &plugin->config.block_h, 0.f,100.f, 72);
+       block_h->create_objects();
+       y += block_w->get_h() + 10;
+
+       horiz_limit = new Motion51Limits(plugin, this, x0=x,y, _("Horiz shake limit%"),
+               &plugin->config.horiz_limit, 0.f, 75.f, 72);
+       horiz_limit->create_objects();
+       shake_fade = new Motion51Limits(plugin, this, x1,y, _("Shake fade%"),
+               &plugin->config.shake_fade, 0.f, 75.f, 72);
+       shake_fade->create_objects();
+       y += horiz_limit->get_h() + 10;
+       vert_limit = new Motion51Limits(plugin, this, x0,y, _("Vert shake limit%"),
+               &plugin->config.vert_limit, 0.f, 75.f, 72);
+       vert_limit->create_objects();
+       y += vert_limit->get_h() + 10;
+       twist_limit = new Motion51Limits(plugin, this, x0,y, _("Twist limit%"),
+               &plugin->config.twist_limit, 0.f, 75.f, 72);
+       twist_limit->create_objects();
+       twist_fade = new Motion51Limits(plugin, this, x1,y, _("Twist fade%"),
+               &plugin->config.twist_fade, 0.f, 75.f, 72);
+       twist_fade->create_objects();
+       y += twist_fade->get_h() + 20;
+
+       add_subwindow(draw_vectors = new Motion51DrawVectors(plugin, this, x, y));
+       add_subwindow(title = new BC_Title(x1, y, _("Tracking file:")));
+       y += draw_vectors->get_h() + 5;
+       add_subwindow(enable_tracking = new Motion51EnableTracking(plugin, this, x, y));
+       add_subwindow(tracking_file = new Motion51TrackingFile(plugin,
+               plugin->config.tracking_file, this, x1, y, get_w()-30-x1));
+       y += enable_tracking->get_h() + 20;
+
+       add_subwindow(reset_config = new Motion51ResetConfig(plugin, this, x0=x, y));
+       add_subwindow(reset_tracking = new Motion51ResetTracking(plugin, this, x1, y));
+       y += reset_config->get_h()+20;
+
+       int pef = client->server->mwindow->edl->session->video_every_frame;
+       add_subwindow(pef_title = new BC_Title(x, y,
+               !pef ?  _("For best results\n"
+                               " Set: Play every frame\n"
+                               " Preferences-> Playback-> Video Out") :
+                       _("Currently using: Play every frame"), MEDIUMFONT,
+               !pef ? RED : GREEN));
+
+       show_window(1);
+}
+
+void Motion51Window::update_gui()
+{
+       Motion51Config &config = plugin->config;
+       horiz_limit->update(config.horiz_limit);
+       vert_limit->update(config.vert_limit);
+       twist_limit->update(config.twist_limit);
+       shake_fade->update(config.shake_fade);
+       twist_fade->update(config.twist_fade);
+
+       sample_r->update(config.sample_r);
+       char string[BCTEXTLEN];
+       sprintf(string, "%d", config.sample_steps);
+       sample_steps->set_text(string);
+
+       block_w->update(config.block_w);
+       block_h->update(config.block_h);
+       block_x->update(config.block_x);
+       block_y->update(config.block_y);
+
+       draw_vectors->update(config.draw_vectors);
+       tracking_file->update(config.tracking_file);
+       enable_tracking->update(config.tracking);
+}
+
+Motion51Limits::Motion51Limits(Motion51Main *plugin, Motion51Window *gui, int x, int y,
+       const char *ttext, float *value, float min, float max, int ttbox_w)
+ : BC_TumbleTextBox(gui, *value, min, max, x, y, ttbox_w)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+       this->ttext = cstrdup(ttext);
+       this->value = value;
+       title = 0;
+}
+
+Motion51Limits::~Motion51Limits()
+{
+       delete [] ttext;
+}
+
+void Motion51Limits::create_objects()
+{
+       BC_TumbleTextBox::create_objects();
+       int tx = BC_TumbleTextBox::get_x() + BC_TumbleTextBox::get_w() + 5;
+       int ty = BC_TumbleTextBox::get_y();
+       gui->add_subwindow(title = new BC_Title(tx,ty,ttext));
+}
+
+int Motion51Limits::handle_event()
+{
+       *value = atof(get_text());
+       plugin->send_configure_change();
+       return 1;
+}
+
+Motion51TrackingFile::Motion51TrackingFile(Motion51Main *plugin,
+       const char *filename, Motion51Window *gui, int x, int y, int w)
+ : BC_TextBox(x, y, w, 1, filename)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+};
+
+int Motion51TrackingFile::handle_event()
+{
+       strcpy(plugin->config.tracking_file, get_text());
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+Motion51SampleSteps::Motion51SampleSteps(Motion51Main *plugin,
+       int x, int y, int w)
+ : BC_PopupMenu(x, y, w, "", 1)
+{
+       this->plugin = plugin;
+}
+
+void Motion51SampleSteps::create_objects()
+{
+       add_item(new BC_MenuItem("16"));
+       add_item(new BC_MenuItem("32"));
+       add_item(new BC_MenuItem("64"));
+       add_item(new BC_MenuItem("128"));
+       add_item(new BC_MenuItem("256"));
+       add_item(new BC_MenuItem("512"));
+       add_item(new BC_MenuItem("1024"));
+       add_item(new BC_MenuItem("2048"));
+       add_item(new BC_MenuItem("4096"));
+       add_item(new BC_MenuItem("8192"));
+       add_item(new BC_MenuItem("16384"));
+       add_item(new BC_MenuItem("32768"));
+       char string[BCTEXTLEN];
+       sprintf(string, "%d", plugin->config.sample_steps);
+       set_text(string);
+}
+
+int Motion51SampleSteps::handle_event()
+{
+       plugin->config.sample_steps = atoi(get_text());
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+
+Motion51DrawVectors::Motion51DrawVectors(Motion51Main *plugin,
+       Motion51Window *gui, int x, int y)
+ : BC_CheckBox(x, y, plugin->config.draw_vectors, _("Draw vectors"))
+{
+       this->gui = gui;
+       this->plugin = plugin;
+}
+
+int Motion51DrawVectors::handle_event()
+{
+       plugin->config.draw_vectors = get_value();
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+Motion51ResetConfig::Motion51ResetConfig(Motion51Main *plugin, Motion51Window *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Reset defaults"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int Motion51ResetConfig::handle_event()
+{
+       plugin->config.init();
+       gui->update_gui();
+       plugin->send_configure_change();
+       return 1;
+}
+
+Motion51ResetTracking::Motion51ResetTracking(Motion51Main *plugin, Motion51Window *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Reset Tracking"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int Motion51ResetTracking::handle_event()
+{
+       plugin->set_tracking_path();
+       gui->tracking_file->update(plugin->config.tracking_file);
+       plugin->config.tracking = 0;
+       plugin->send_configure_change();
+       gui->enable_tracking->update(0);
+       ::remove(plugin->config.tracking_file);
+       return 1;
+}
+
+Motion51EnableTracking::Motion51EnableTracking(Motion51Main *plugin, Motion51Window *gui, int x, int y)
+ : BC_CheckBox(x, y, plugin->config.tracking,_("Enable Tracking"))
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int Motion51EnableTracking::handle_event()
+{
+       plugin->config.tracking = get_value();
+       plugin->send_configure_change();
+       return 1;
+}
+
diff --git a/cinelerra-5.1/plugins/motion51/motionwindow51.h b/cinelerra-5.1/plugins/motion51/motionwindow51.h
new file mode 100644 (file)
index 0000000..faf9405
--- /dev/null
@@ -0,0 +1,125 @@
+
+/*
+ * 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 "guicast.h"
+#include "motion51.inc"
+
+
+class Motion51SampleSteps : public BC_PopupMenu
+{
+public:
+       Motion51SampleSteps(Motion51Main *plugin, int x, int y, int w);
+       void create_objects();
+       int handle_event();
+       Motion51Main *plugin;
+};
+
+
+class Motion51DrawVectors : public BC_CheckBox
+{
+public:
+       Motion51DrawVectors(Motion51Main *plugin, Motion51Window *gui, int x, int y);
+       int handle_event();
+       Motion51Main *plugin;
+       Motion51Window *gui;
+};
+
+class Motion51TrackingFile : public BC_TextBox
+{
+public:
+       Motion51TrackingFile(Motion51Main *plugin, const char *filename,
+               Motion51Window *gui, int x, int y, int w);
+       int handle_event();
+       Motion51Main *plugin;
+       Motion51Window *gui;
+};
+
+class Motion51Limits : public BC_TumbleTextBox {
+public:
+       Motion51Limits(Motion51Main *plugin, Motion51Window *gui, int x, int y,
+               const char *ttext, float *value, float min, float max, int ttbox_w);
+       ~Motion51Limits();
+
+       void create_objects();
+       int handle_event();
+
+       Motion51Main *plugin;
+       Motion51Window *gui;
+       float *value;
+       const char *ttext;
+       BC_Title *title;
+
+       int get_w() { return BC_TumbleTextBox::get_w()+5+title->get_w(); }
+       int get_h() { return BC_TumbleTextBox::get_h(); }
+};
+
+class Motion51ResetConfig : public BC_GenericButton
+{
+public:
+       Motion51ResetConfig(Motion51Main *plugin, Motion51Window *gui, int x, int y);
+       int handle_event();
+       Motion51Main *plugin;
+       Motion51Window *gui;
+};
+
+class Motion51ResetTracking : public BC_GenericButton
+{
+public:
+       Motion51ResetTracking(Motion51Main *plugin, Motion51Window *gui, int x, int y);
+       int handle_event();
+       Motion51Main *plugin;
+       Motion51Window *gui;
+};
+
+class Motion51EnableTracking : public BC_CheckBox
+{
+public:
+       Motion51EnableTracking(Motion51Main *plugin, Motion51Window *gui, int x, int y);
+       int handle_event();
+       Motion51Main *plugin;
+       Motion51Window *gui;
+};
+
+class Motion51Window : public PluginClientWindow
+{
+public:
+       Motion51Window(Motion51Main *plugin);
+       ~Motion51Window();
+
+       void create_objects();
+       void update_gui();
+
+       Motion51Main *plugin;
+       Motion51Limits *horiz_limit, *vert_limit, *twist_limit;
+       Motion51Limits *shake_fade, *twist_fade;
+       Motion51Limits *sample_r;
+       Motion51SampleSteps *sample_steps;
+       Motion51Limits *block_x, *block_y, *block_w, *block_h;
+       Motion51DrawVectors *draw_vectors;
+       Motion51TrackingFile *tracking_file;
+       Motion51ResetConfig *reset_config;
+       Motion51ResetTracking *reset_tracking;
+       Motion51EnableTracking *enable_tracking;
+
+       BC_Title *pef_title;
+};
+
+
diff --git a/cinelerra-5.1/plugins/motion51/picon.png b/cinelerra-5.1/plugins/motion51/picon.png
new file mode 100644 (file)
index 0000000..128c5d5
Binary files /dev/null and b/cinelerra-5.1/plugins/motion51/picon.png differ