opengl upgrade to 4.3 for masks, mask function/layout rework, make_shader rework
authorGood Guy <good1.2guy@gmail.com>
Mon, 27 May 2019 19:33:38 +0000 (13:33 -0600)
committerGood Guy <good1.2guy@gmail.com>
Mon, 27 May 2019 19:33:38 +0000 (13:33 -0600)
24 files changed:
cinelerra-5.1/cinelerra/cwindowgui.C
cinelerra-5.1/cinelerra/cwindowtool.C
cinelerra-5.1/cinelerra/cwindowtool.h
cinelerra-5.1/cinelerra/cwindowtool.inc
cinelerra-5.1/cinelerra/dcraw.C
cinelerra-5.1/cinelerra/edl.h
cinelerra-5.1/cinelerra/maskauto.C
cinelerra-5.1/cinelerra/maskauto.h
cinelerra-5.1/cinelerra/maskauto.inc
cinelerra-5.1/cinelerra/maskautos.C
cinelerra-5.1/cinelerra/maskautos.h
cinelerra-5.1/cinelerra/maskengine.C
cinelerra-5.1/cinelerra/maskengine.h
cinelerra-5.1/cinelerra/playback3d.C
cinelerra-5.1/cinelerra/playback3d.h
cinelerra-5.1/cinelerra/virtualvnode.C
cinelerra-5.1/guicast/bcsynchronous.C
cinelerra-5.1/guicast/bcsynchronous.h
cinelerra-5.1/guicast/bctexture.C
cinelerra-5.1/guicast/bctexture.h
cinelerra-5.1/guicast/bcwindow3d.C
cinelerra-5.1/guicast/bcwindowbase.C
cinelerra-5.1/guicast/bcwindowbase.h
cinelerra-5.1/guicast/vframe3d.C

index 701362e..3e9bf9e 100644 (file)
@@ -1491,11 +1491,17 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
 
        if(!track) return 0;
 //printf("CWindowCanvas::do_mask 3\n");
-
+       CWindowMaskGUI *mask_gui = (CWindowMaskGUI *)
+               (gui->tool_panel ? gui->tool_panel->tool_gui : 0);
+       int draw_markers = mask_gui ? mask_gui->markers : 0;
+       int draw_boundary = mask_gui ? mask_gui->boundary : 0;
        MaskAutos *mask_autos = (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
        int64_t position = track->to_units(
                mwindow->edl->local_session->get_selectionstart(1),
                0);
+       Auto *prev_auto = 0;
+       mask_autos->get_prev_auto(position, PLAY_FORWARD, (Auto *&)prev_auto, 1);
+       MaskAuto *prev_mask = (MaskAuto *)prev_auto;
        ArrayList<MaskPoint*> points;
 
 // Determine the points based on whether
@@ -1503,19 +1509,16 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
 // If keyframe generation occurs, use the interpolated mask.
 // If no keyframe generation occurs, use the previous mask.
        int use_interpolated = 0;
-       if(button_press || cursor_motion) {
+       if( button_press || cursor_motion ) {
 #ifdef USE_KEYFRAME_SPANNING
                double selection_start = mwindow->edl->local_session->get_selectionstart(0);
                double selection_end = mwindow->edl->local_session->get_selectionend(0);
-
-               Auto *first = 0;
-               mask_autos->get_prev_auto(track->to_units(selection_start, 0),
-                       PLAY_FORWARD, first, 1);
-               Auto *last = 0;
-               mask_autos->get_prev_auto(track->to_units(selection_end, 0),
-                       PLAY_FORWARD, last, 1);
-
-               if(last == first && (!mwindow->edl->session->auto_keyframes))
+               int64_t start_pos = track->to_units(selection_start, 0);
+               int64_t end_pos = track->to_units(selection_end, 0);
+               Auto *first = 0, *last = 0;
+               mask_autos->get_prev_auto(start_pos, PLAY_FORWARD, first, 1);
+               mask_autos->get_prev_auto(end_pos, PLAY_FORWARD, last, 1);
+               if( last == first && (!mwindow->edl->session->auto_keyframes) )
                        use_interpolated = 0;
                else
 // If keyframe spanning occurs, use the interpolated points.
@@ -1523,28 +1526,22 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
                        use_interpolated = 1;
 
 #else
-               if(mwindow->edl->session->auto_keyframes)
+               if( mwindow->edl->session->auto_keyframes )
                        use_interpolated = 1;
 #endif
        }
        else
                use_interpolated = 1;
 
-       if(use_interpolated) {
+       if( use_interpolated ) {
 // Interpolate the points to get exactly what is being rendered at this position.
                mask_autos->get_points(&points,
                        mwindow->edl->session->cwindow_mask,
-                       position,
-                       PLAY_FORWARD);
+                       position, PLAY_FORWARD);
        }
        else {
 // Use the prev mask
-               Auto *prev = 0;
-               mask_autos->get_prev_auto(position,
-                       PLAY_FORWARD,
-                       prev,
-                       1);
-               ((MaskAuto*)prev)->get_points(&points,
+               prev_mask->get_points(&points,
                        mwindow->edl->session->cwindow_mask);
        }
 
@@ -1718,15 +1715,15 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
 
                                output_to_canvas(mwindow->edl, 0, canvas_x, canvas_y);
 
-                               if(j > 0) {
+                               if( j > 0 ) {
 
-                                       if(draw) { // Draw joining line
+                                       if( draw ) { // Draw joining line
                                                x_points.append((int)canvas_x);
                                                y_points.append((int)canvas_y);
                                        }
 
-                                       if(j == segments) {
-                                               if(draw) { // Draw second anchor
+                                       if( j == segments ) {
+                                               if( draw && draw_markers ) { // Draw second anchor
                                                        if(i < points.size() - 1) {
                                                                if(i == gui->affected_point - 1)
                                                                        get_canvas()->draw_disc(
@@ -1756,23 +1753,30 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
                                }
                                else {
 // Draw first anchor
-                                       if(i == 0 && draw) {
-                                               char mask_label[BCSTRLEN];
-                                               sprintf(mask_label, "%d",
-                                                       mwindow->edl->session->cwindow_mask);
-                                               get_canvas()->draw_text(
-                                                       (int)canvas_x - FIRST_CONTROL_W,
-                                                       (int)canvas_y - FIRST_CONTROL_H,
-                                                       mask_label);
-
-                                               get_canvas()->draw_disc(
-                                                       (int)canvas_x - FIRST_CONTROL_W / 2,
-                                                       (int)canvas_y - FIRST_CONTROL_H / 2,
-                                                       FIRST_CONTROL_W, FIRST_CONTROL_H);
+                                       if( i == 0 && draw ) {
+                                               if( draw_boundary ) {
+                                                       char mask_label[BCSTRLEN];
+                                                       int k = mwindow->edl->session->cwindow_mask;
+                                                       if( !prev_mask || prev_mask->is_default ||
+                                                           k < 0 || k >= prev_mask->masks.size() )
+                                                               sprintf(mask_label, "%d", k);
+                                                       else
+                                                               sprintf(mask_label, "%s", prev_mask->masks[k]->name);
+                                                       get_canvas()->draw_text(
+                                                               (int)canvas_x - FIRST_CONTROL_W,
+                                                               (int)canvas_y - FIRST_CONTROL_H,
+                                                               mask_label);
+                                               }
+                                               if( draw_markers ) {
+                                                       get_canvas()->draw_disc(
+                                                               (int)canvas_x - FIRST_CONTROL_W / 2,
+                                                               (int)canvas_y - FIRST_CONTROL_H / 2,
+                                                               FIRST_CONTROL_W, FIRST_CONTROL_H);
+                                               }
                                        }
 
 // Draw first control point.
-                                       if(draw) {
+                                       if( draw && draw_markers ) {
                                                output_to_canvas(mwindow->edl, 0, canvas_x1, canvas_y1);
                                                get_canvas()->draw_line(
                                                        (int)canvas_x, (int)canvas_y,
@@ -1793,25 +1797,22 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
 //printf("CWindowCanvas::do_mask 1\n");
 
                BC_WindowBase *cvs_win = get_canvas();
-               if(draw) {
+               if( draw && draw_boundary ) {
                        cvs_win->draw_polygon(&x_points, &y_points);
                        cvs_win->set_opaque();
                }
-               if( draw && gui->tool_panel ) {
-                       CWindowMaskGUI *mask_gui = (CWindowMaskGUI*)gui->tool_panel->tool_gui;
-                       if( mask_gui && mask_gui->focused ) {
-                               float fx = atof(mask_gui->focus_x->get_text());
-                               float fy = atof(mask_gui->focus_y->get_text());
-                               output_to_canvas(mwindow->edl, 0, fx, fy);
-                               float r = bmax(cvs_win->get_w(), cvs_win->get_h());
-                               float d = 0.005*r;
-                               cvs_win->set_line_width((int)(0.0025*r) + 1);
-                               cvs_win->set_color(BLUE);
-                               cvs_win->draw_line(fx-d,fy-d, fx+d, fy+d);
-                               cvs_win->draw_line(fx-d,fy+d, fx+d, fy-d);
-                               cvs_win->set_line_width(0);
-                               cvs_win->set_color(WHITE);
-                       }
+               if( draw && mask_gui && mask_gui->focused ) {
+                       float fx = atof(mask_gui->focus_x->get_text());
+                       float fy = atof(mask_gui->focus_y->get_text());
+                       output_to_canvas(mwindow->edl, 0, fx, fy);
+                       float r = bmax(cvs_win->get_w(), cvs_win->get_h());
+                       float d = 0.005*r;
+                       cvs_win->set_line_width((int)(0.0025*r) + 1);
+                       cvs_win->set_color(BLUE);
+                       cvs_win->draw_line(fx-d,fy-d, fx+d, fy+d);
+                       cvs_win->draw_line(fx-d,fy+d, fx+d, fy-d);
+                       cvs_win->set_line_width(0);
+                       cvs_win->set_color(WHITE);
                }
 //printf("CWindowCanvas::do_mask 1\n");
        }
@@ -2103,12 +2104,9 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
                                float st = sin(theta), ct = cos(theta);
                                gui->x_origin = mask_cursor_x;
                                gui->y_origin = mask_cursor_y;
-                               if( gui->tool_panel ) {
-                                       CWindowMaskGUI *mask_gui = (CWindowMaskGUI*)gui->tool_panel->tool_gui;
-                                       if( mask_gui && mask_gui->focused ) {
-                                               gui->x_origin = atof(mask_gui->focus_x->get_text());
-                                               gui->y_origin = atof(mask_gui->focus_y->get_text());
-                                       }
+                               if( mask_gui && mask_gui->focused ) {
+                                       gui->x_origin = atof(mask_gui->focus_x->get_text());
+                                       gui->y_origin = atof(mask_gui->focus_y->get_text());
                                }
                                for( int i=0; i<mask_points.size(); ++i ) {
                                        MaskPoint *point = mask_points.values[i];
index fc44683..ad8aa8d 100644 (file)
@@ -1739,8 +1739,47 @@ int CWindowMaskFocus::handle_event()
        return 1;
 }
 
+CWindowMaskDrawMarkers::CWindowMaskDrawMarkers(MWindow *mwindow, CWindowToolGUI *gui, int x, int y)
+ : BC_CheckBox(x, y, ((CWindowMaskGUI*)gui)->markers, _("Markers"))
+{
+       this->mwindow = mwindow;
+       this->gui = gui;
+}
+
+CWindowMaskDrawMarkers::~CWindowMaskDrawMarkers()
+{
+}
+
+int CWindowMaskDrawMarkers::handle_event()
+{
+       ((CWindowMaskGUI*)gui)->markers = get_value();
+       gui->update();
+       gui->update_preview();
+       return 1;
+}
+
+CWindowMaskDrawBoundary::CWindowMaskDrawBoundary(MWindow *mwindow, CWindowToolGUI *gui, int x, int y)
+ : BC_CheckBox(x, y, ((CWindowMaskGUI*)gui)->boundary, _("Boundary"))
+{
+       this->mwindow = mwindow;
+       this->gui = gui;
+}
+
+CWindowMaskDrawBoundary::~CWindowMaskDrawBoundary()
+{
+}
+
+int CWindowMaskDrawBoundary::handle_event()
+{
+       ((CWindowMaskGUI*)gui)->boundary = get_value();
+       gui->update();
+       gui->update_preview();
+       return 1;
+}
+
+
 CWindowMaskFeather::CWindowMaskFeather(MWindow *mwindow, CWindowToolGUI *gui, int x, int y)
- : BC_TumbleTextBox(gui, 0, 0, 0xff, x, y, 64, 2)
+ : BC_TumbleTextBox(gui, 0, -FEATHER_MAX, FEATHER_MAX, x, y, 64, 2)
 {
        this->mwindow = mwindow;
        this->gui = gui;
@@ -1775,16 +1814,23 @@ int CWindowMaskFeather::update_value(float v)
        ((CWindowMaskGUI*)gui)->get_keyframe(track, autos, keyframe,
                        mask, point, create_it);
        if( track ) {
+               int gang = ((CWindowMaskGUI*)gui)->gang_feather->get_value();
 #ifdef USE_KEYFRAME_SPANNING
-// Create temp keyframe
                MaskAuto temp_keyframe(mwindow->edl, autos);
                temp_keyframe.copy_data(keyframe);
-// Update parameter
-               temp_keyframe.feather = v;
-// Commit change to span of keyframes
-               autos->update_parameter(&temp_keyframe);
-#else
-               keyframe->feather = v;
+               keyframe = &temp_keyframe;
+#endif
+               float change = v - mask->feather;
+               int k = mwindow->edl->session->cwindow_mask;
+               int n = gang ? keyframe->masks.size() : k+1;
+               for( int i=gang? 0 : k; i<n; ++i ) {
+                       SubMask *sub_mask = keyframe->get_submask(i);
+                       float feather = sub_mask->feather + change;
+                       bclamp(feather, -FEATHER_MAX, FEATHER_MAX);
+                       sub_mask->feather = feather;
+               }
+#ifdef USE_KEYFRAME_SPANNING
+               autos->update_parameter(keyframe);
 #endif
                gui->update_preview();
        }
@@ -1803,22 +1849,44 @@ int CWindowMaskFeather::handle_event()
 
 CWindowMaskFeatherSlider::CWindowMaskFeatherSlider(MWindow *mwindow,
                CWindowToolGUI *gui, int x, int y, int w, float v)
- : BC_FSlider(x, y, 0, w, w, 0.f, 255.f, v)
+ : BC_FSlider(x, y, 0, w, w, -FEATHER_MAX, FEATHER_MAX, v)
 {
        this->mwindow = mwindow;
        this->gui = gui;
        set_precision(0.01);
+       timer = new Timer();
+       stick = 0;
+       last_v = 0;
 }
 
 CWindowMaskFeatherSlider::~CWindowMaskFeatherSlider()
 {
+       delete timer;
 }
 
 int CWindowMaskFeatherSlider::handle_event()
 {
        float v = get_value();
+       if( stick > 0 ) {
+               int64_t ms = timer->get_difference();
+               if( ms < 250 && --stick > 0 ) {
+                       if( get_value() == 0 ) return 1;
+                       update(v = 0);
+               }
+               else {
+                       stick = 0;
+                       last_v = v;
+               }
+       }
+       else if( (last_v>=0 && v<0) || (last_v<0 && v>=0) ) {
+               stick = 16;
+               v = 0;
+       }
+       else
+               last_v = v;
+       timer->update();
        CWindowMaskGUI * mask_gui = (CWindowMaskGUI*)gui;
-       mask_gui->feather->update(v);
+       mask_gui->feather->BC_TumbleTextBox::update(v);
        return mask_gui->feather->update_value(v);
 }
 
@@ -1863,18 +1931,24 @@ int CWindowMaskFade::update_value(float v)
        ((CWindowMaskGUI*)gui)->get_keyframe(track, autos, keyframe,
                        mask, point, create_it);
        if( track ) {
+               int gang = ((CWindowMaskGUI*)gui)->gang_fader->get_value();
 #ifdef USE_KEYFRAME_SPANNING
-// Create temp keyframe
                MaskAuto temp_keyframe(mwindow->edl, autos);
                temp_keyframe.copy_data(keyframe);
-// Update parameter
-               temp_keyframe.value = v;
-// Commit change to span of keyframes
-               autos->update_parameter(&temp_keyframe);
-#else
-               keyframe->value = v;
+               keyframe = &temp_keyframe;
+#endif
+               float change = v - mask->fader;
+               int k = mwindow->edl->session->cwindow_mask;
+               int n = gang ? keyframe->masks.size() : k+1;
+               for( int i=gang? 0 : k; i<n; ++i ) {
+                       SubMask *sub_mask = keyframe->get_submask(i);
+                       float fader = sub_mask->fader + change;
+                       bclamp(fader, -100.f, 100.f);
+                       sub_mask->fader = fader;
+               }
+#ifdef USE_KEYFRAME_SPANNING
+               autos->update_parameter(keyframe);
 #endif
-
                gui->update_preview();
        }
 
@@ -1898,6 +1972,7 @@ CWindowMaskFadeSlider::CWindowMaskFadeSlider(MWindow *mwindow, CWindowToolGUI *g
        this->gui = gui;
        timer = new Timer();
        stick = 0;
+       last_v = 0;
 }
 
 CWindowMaskFadeSlider::~CWindowMaskFadeSlider()
@@ -1908,18 +1983,23 @@ CWindowMaskFadeSlider::~CWindowMaskFadeSlider()
 int CWindowMaskFadeSlider::handle_event()
 {
        float v = 100*get_value()/200;
-       if( !v && !stick )
-               stick = 16;
-       else if( stick > 0 ) {
+       if( stick > 0 ) {
                int64_t ms = timer->get_difference();
-               if( ms < 1000 ) {
-                       --stick;
+               if( ms < 250 && --stick > 0 ) {
                        if( get_value() == 0 ) return 1;
                        update(v = 0);
                }
-               else
+               else {
                        stick = 0;
+                       last_v = v;
+               }
+       }
+       else if( (last_v>=0 && v<0) || (last_v<0 && v>=0) ) {
+               stick = 16;
+               v = 0;
        }
+       else
+               last_v = v;
        timer->update();
        CWindowMaskGUI *mask_gui = (CWindowMaskGUI*)gui;
        mask_gui->fade->BC_TumbleTextBox::update(v);
@@ -1931,46 +2011,21 @@ int CWindowMaskFadeSlider::update(int64_t v)
        return BC_ISlider::update(200*v/100);
 }
 
-CWindowMaskMode::CWindowMaskMode(MWindow *mwindow,
-       CWindowToolGUI *gui, int x, int y)
- : BC_Toggle(x, y, mwindow->theme->mask_mode_toggle, 0)
+CWindowMaskGangFader::CWindowMaskGangFader(MWindow *mwindow,
+               CWindowToolGUI *gui, int x, int y)
+ : BC_Toggle(x, y, mwindow->theme->get_image_set("gangpatch_data"), 0)
 {
-       this->mwindow = mwindow;
-       this->gui = gui;
-       set_tooltip(_("Subtract/Multiply Alpha"));
+        this->mwindow = mwindow;
+        this->gui = gui;
+        set_tooltip(_("Gang fader"));
 }
 
-CWindowMaskMode::~CWindowMaskMode()
+CWindowMaskGangFader::~CWindowMaskGangFader()
 {
 }
 
-int CWindowMaskMode::handle_event()
+int CWindowMaskGangFader::handle_event()
 {
-       MaskAutos *autos;
-       MaskAuto *keyframe;
-       Track *track;
-       MaskPoint *point;
-       SubMask *mask;
-// Get existing keyframe
-       ((CWindowMaskGUI*)gui)->get_keyframe(track, autos, keyframe, mask, point, 0);
-       if( track ) {
-               mwindow->undo->update_undo_before(_("mask mode"), 0);
-#ifdef USE_KEYFRAME_SPANNING
-// Create temp keyframe
-               MaskAuto temp_keyframe(mwindow->edl, autos);
-               temp_keyframe.copy_data(keyframe);
-// Update parameter
-               temp_keyframe.mode = get_value();
-// Commit change to span of keyframes
-               autos->update_parameter(&temp_keyframe);
-#else
-               ((MaskAuto*)autos->default_auto)->mode = get_value();
-#endif
-               mwindow->undo->update_undo_after(_("mask mode"), LOAD_AUTOMATION);
-       }
-
-//printf("CWindowMaskMode::handle_event 1\n");
-       gui->update_preview();
        return 1;
 }
 
@@ -2080,28 +2135,24 @@ int CWindowMaskClrMask::handle_event()
        return 1;
 }
 
-CWindowMaskClrFeather::CWindowMaskClrFeather(MWindow *mwindow,
-               CWindowMaskGUI *gui, int x, int y)
- : BC_Button(x, y, mwindow->theme->get_image_set("reset_button"))
+CWindowMaskGangFeather::CWindowMaskGangFeather(MWindow *mwindow,
+               CWindowToolGUI *gui, int x, int y)
+ : BC_Toggle(x, y, mwindow->theme->get_image_set("gangpatch_data"), 0)
 {
-       this->mwindow = mwindow;
-       this->gui = gui;
-       set_tooltip(_("Zero Feather"));
+        this->mwindow = mwindow;
+        this->gui = gui;
+        set_tooltip(_("Gang feather"));
 }
-CWindowMaskClrFeather::~CWindowMaskClrFeather()
+
+CWindowMaskGangFeather::~CWindowMaskGangFeather()
 {
 }
 
-int CWindowMaskClrFeather::handle_event()
+int CWindowMaskGangFeather::handle_event()
 {
-       float v = 0;
-       CWindowMaskGUI * mask_gui = (CWindowMaskGUI*)gui;
-       mask_gui->feather->update(v);
-       mask_gui->feather_slider->update(v);
-       return mask_gui->feather->update_value(v);
+       return 1;
 }
 
-
 CWindowMaskGUI::CWindowMaskGUI(MWindow *mwindow, CWindowTool *thread)
  : CWindowToolGUI(mwindow, thread,
        _(PROGRAM_NAME ": Mask"), 360, 440)
@@ -2112,6 +2163,8 @@ CWindowMaskGUI::CWindowMaskGUI(MWindow *mwindow, CWindowTool *thread)
        fade = 0;
        feather = 0;
        focused = 0;
+       markers = 1;
+       boundary = 1;
 }
 CWindowMaskGUI::~CWindowMaskGUI()
 {
@@ -2147,16 +2200,15 @@ void CWindowMaskGUI::create_objects()
        int x2 = x1 + fade->get_w() + 2*margin;
        int w2 = clr_x-2*margin - x2;
        add_subwindow(fade_slider = new CWindowMaskFadeSlider(mwindow, this, x2, y, w2));
-       add_subwindow(mode = new CWindowMaskMode(mwindow, this, clr_x, y));
+       add_subwindow(gang_fader = new CWindowMaskGangFader(mwindow, this, clr_x, y));
        y += fade->get_h() + margin;
        add_subwindow(title = new BC_Title(x, y, _("Feather:")));
        feather = new CWindowMaskFeather(mwindow, this, x1, y);
        feather->create_objects();
-       x2 = x1 + feather->get_w() + margin;
        w2 = clr_x - 2*margin - x2;
        feather_slider = new CWindowMaskFeatherSlider(mwindow, this, x2, y, w2, 0);
        add_subwindow(feather_slider);
-       add_subwindow(new CWindowMaskClrFeather(mwindow, this, clr_x, y));
+       add_subwindow(gang_feather = new CWindowMaskGangFeather(mwindow, this, clr_x, y));
        y += feather->get_h() + 3*margin;
 
        add_subwindow(title = new BC_Title(x, y, _("Point:")));
@@ -2167,10 +2219,12 @@ void CWindowMaskGUI::create_objects()
        add_subwindow(title = new BC_Title(x, y, "X:"));
        this->x = new CWindowCoord(this, x1, y, (float)0.0);
        this->x->create_objects();
+       add_subwindow(draw_markers = new CWindowMaskDrawMarkers(mwindow, this, del_x, y));
        y += this->x->get_h() + margin;
        add_subwindow(title = new BC_Title(x, y, "Y:"));
        this->y = new CWindowCoord(this, x1, y, (float)0.0);
        this->y->create_objects();
+       add_subwindow(draw_boundary = new CWindowMaskDrawBoundary(mwindow, this, del_x, y));
        y += this->y->get_h() + margin;
        BC_Bar *bar;
        add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
@@ -2256,22 +2310,20 @@ void CWindowMaskGUI::update()
        {
                int64_t position_i = track->to_units(position, 0);
 
-               if(point)
-               {
+               if(point) {
                        x->update(point->x);
                        y->update(point->y);
                }
 
-               if(mask)
-               {
-                       feather->update(autos->get_feather(position_i, PLAY_FORWARD));
-                       fade->update(autos->get_value(position_i, PLAY_FORWARD));
+               if(mask) {
+                       int k = mwindow->edl->session->cwindow_mask;
+                       feather->update(autos->get_feather(position_i, k, PLAY_FORWARD));
+                       fade->update(autos->get_fader(position_i, k, PLAY_FORWARD));
                        apply_before_plugins->update(keyframe->apply_before_plugins);
                        disable_opengl_masking->update(keyframe->disable_opengl_masking);
                }
        }
 
-//printf("CWindowMaskGUI::update 1\n");
        active_point->update((int64_t)mwindow->cwindow->gui->affected_point);
        const char *text = "";
        if( keyframe ) {
@@ -2281,16 +2333,6 @@ void CWindowMaskGUI::update()
                        text = keyframe->masks[k]->name;
        }
        name->update(text);
-
-//printf("CWindowMaskGUI::update 1\n");
-       if( track ) {
-#ifdef USE_KEYFRAME_SPANNING
-               mode->update(keyframe->mode);
-#else
-               mode->set_text(((MaskAuto*)autos->default_auto)->mode);
-#endif
-       }
-//printf("CWindowMaskGUI::update 2\n");
 }
 
 void CWindowMaskGUI::handle_event()
index 4691fc0..ccdb3c2 100644 (file)
@@ -145,6 +145,26 @@ public:
        void update_items(MaskAuto *keyframe);
 };
 
+class CWindowMaskDelMask : public BC_GenericButton
+{
+public:
+       CWindowMaskDelMask(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
+       int handle_event();
+       MWindow *mwindow;
+       CWindowToolGUI *gui;
+};
+
+class CWindowMaskClrMask : public BC_Button
+{
+public:
+       CWindowMaskClrMask(MWindow *mwindow, CWindowMaskGUI *gui, int x, int y);
+       ~CWindowMaskClrMask();
+       static int calculate_w(MWindow *mwindow);
+       int handle_event();
+       MWindow *mwindow;
+       CWindowMaskGUI *gui;
+};
+
 class CWindowMaskFade : public BC_TumbleTextBox
 {
 public:
@@ -169,43 +189,55 @@ public:
        MWindow *mwindow;
        CWindowToolGUI *gui;
        int stick;
+       float last_v;
        Timer *timer;
 };
 
-class CWindowMaskMode : public BC_Toggle
+class CWindowMaskGangFader : public BC_Toggle
 {
 public:
-       CWindowMaskMode(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
-       ~CWindowMaskMode();
+       CWindowMaskGangFader(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
+       ~CWindowMaskGangFader();
        int handle_event();
        MWindow *mwindow;
        CWindowToolGUI *gui;
 };
 
-class CWindowMaskDelMask : public BC_GenericButton
+class CWindowMaskAffectedPoint : public BC_TumbleTextBox
 {
 public:
-       CWindowMaskDelMask(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
+       CWindowMaskAffectedPoint(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
+       ~CWindowMaskAffectedPoint();
        int handle_event();
        MWindow *mwindow;
        CWindowToolGUI *gui;
 };
 
-class CWindowMaskAffectedPoint : public BC_TumbleTextBox
+class CWindowMaskFocus : public BC_CheckBox
 {
 public:
-       CWindowMaskAffectedPoint(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
-       ~CWindowMaskAffectedPoint();
+       CWindowMaskFocus(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
+       ~CWindowMaskFocus();
        int handle_event();
        MWindow *mwindow;
        CWindowToolGUI *gui;
 };
 
-class CWindowMaskFocus : public BC_CheckBox
+class CWindowMaskDrawMarkers : public BC_CheckBox
 {
 public:
-       CWindowMaskFocus(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
-       ~CWindowMaskFocus();
+       CWindowMaskDrawMarkers(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
+       ~CWindowMaskDrawMarkers();
+       int handle_event();
+       MWindow *mwindow;
+       CWindowToolGUI *gui;
+};
+
+class CWindowMaskDrawBoundary : public BC_CheckBox
+{
+public:
+       CWindowMaskDrawBoundary(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
+       ~CWindowMaskDrawBoundary();
        int handle_event();
        MWindow *mwindow;
        CWindowToolGUI *gui;
@@ -244,27 +276,19 @@ public:
        char *get_caption() { return 0; }
        MWindow *mwindow;
        CWindowToolGUI *gui;
+       int stick;
+       float last_v;
+       Timer *timer;
 };
 
-class CWindowMaskClrMask : public BC_Button
-{
-public:
-       CWindowMaskClrMask(MWindow *mwindow, CWindowMaskGUI *gui, int x, int y);
-       ~CWindowMaskClrMask();
-       static int calculate_w(MWindow *mwindow);
-       int handle_event();
-       MWindow *mwindow;
-       CWindowMaskGUI *gui;
-};
-
-class CWindowMaskClrFeather : public BC_Button
+class CWindowMaskGangFeather : public BC_Toggle
 {
 public:
-       CWindowMaskClrFeather(MWindow *mwindow, CWindowMaskGUI *gui, int x, int y);
-       ~CWindowMaskClrFeather();
+       CWindowMaskGangFeather(MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
+       ~CWindowMaskGangFeather();
        int handle_event();
        MWindow *mwindow;
-       CWindowMaskGUI *gui;
+       CWindowToolGUI *gui;
 };
 
 class CWindowMaskBeforePlugins : public BC_CheckBox
@@ -297,19 +321,24 @@ public:
        void update_preview();
 
        CWindowMaskName *name;
-       CWindowMaskClrMask *clr_mask;
        CWindowMaskDelMask *del_mask;
+       CWindowMaskClrMask *clr_mask;
        CWindowMaskFade *fade;
        CWindowMaskFadeSlider *fade_slider;
-       CWindowMaskMode *mode;
+       CWindowMaskGangFader *gang_fader;
        CWindowMaskAffectedPoint *active_point;
        CWindowMaskDelPoint *del_point;
        CWindowCoord *x, *y;
        CWindowMaskFocus *focus;
        int focused;
+       CWindowMaskDrawMarkers *draw_markers;
+       int markers;
+       CWindowMaskDrawBoundary *draw_boundary;
+       int boundary;
        CWindowCoord *focus_x, *focus_y;
        CWindowMaskFeather *feather;
        CWindowMaskFeatherSlider *feather_slider;
+       CWindowMaskGangFeather *gang_feather;
        CWindowMaskBeforePlugins *apply_before_plugins;
        CWindowDisableOpenGLMasking *disable_opengl_masking;
 };
index 9fd83c5..47e02ef 100644 (file)
@@ -30,16 +30,19 @@ class CWindowCropOK;
 class CWindowCropGUI;
 class CWindowMaskItems;
 class CWindowMaskName;
+class CWindowMaskDelMask;
+class CWindowMaskClrMask;
 class CWindowMaskFade;
 class CWindowMaskFadeSlider;
-class CWindowMaskMode;
-class CWindowMaskDelMask;
+class CWindowMaskGangFader;
 class CWindowMaskAffectedPoint;
+class CWindowMaskFocus;
+class CWindowMaskDrawMarkers;
+class CWindowMaskDrawBoundary;
 class CWindowMaskDelPoint;
 class CWindowMaskFeather;
 class CWindowMaskFeatherSlider;
-class CWindowMaskClrMask;
-class CWindowMaskClrFeather;
+class CWindowMaskGangFeather;
 class CWindowMaskBeforePlugins;
 class CWindowDisableOpenGLMasking;
 class CWindowMaskGUI;
index dcb561d..b9ec466 100644 (file)
@@ -1225,7 +1225,7 @@ void CLASS nikon_yuv_load_raw()
 {
   int row, col, yuv[4], rgb[3], b, c;
   UINT64 bitbuf=0;
-
+  memset(yuv,0,sizeof(yuv));
   for (row=0; row < raw_height; row++)
     for (col=0; col < raw_width; col++) {
       if (!(b = col & 1)) {
index 0798b40..e8dadce 100644 (file)
@@ -93,7 +93,7 @@ public:
                int direction,
                PlayableTracks *playable_tracks);
 
-// Convert position to frame boundry times
+// Convert position to frame boundary times
        double frame_align(double position, int round);
 // frame align if cursor alignment is enabled
        double align_to_frame(double position, int round);
index 831b899..8b5a2a4 100644 (file)
@@ -69,6 +69,8 @@ SubMask::SubMask(MaskAuto *keyframe, int no)
        this->keyframe = keyframe;
        memset(name, 0, sizeof(name));
        sprintf(name, "%d", no);
+       this->fader = 100;
+       this->feather = 0;
 }
 
 SubMask::~SubMask()
@@ -78,18 +80,17 @@ SubMask::~SubMask()
 
 int SubMask::equivalent(SubMask& ptr)
 {
-       if(points.size() != ptr.points.size()) return 0;
-
-       for(int i = 0; i < points.size(); i++)
-       {
+       if( fader != ptr.fader ) return 0;
+       if( feather != ptr.feather ) return 0;
+       int n = points.size();
+       if( n != ptr.points.size() ) return 0;
+       for( int i=0; i<n; ++i ) {
                if(!(*points.get(i) == *ptr.points.get(i)))
                        return 0;
        }
-
        return 1;
 }
 
-
 int SubMask::operator==(SubMask& ptr)
 {
        return equivalent(ptr);
@@ -99,6 +100,8 @@ void SubMask::copy_from(SubMask& ptr)
 {
        memset(name, 0, sizeof(name));
        strncpy(name, ptr.name, sizeof(name-1));
+       fader = ptr.fader;
+       feather = ptr.feather;
        points.remove_all_objects();
 //printf("SubMask::copy_from 1 %p %d\n", this, ptr.points.total);
        for(int i = 0; i < ptr.points.total; i++)
@@ -114,57 +117,20 @@ void SubMask::load(FileXML *file)
        points.remove_all_objects();
 
        int result = 0;
-       while(!result)
-       {
-               result = file->read_tag();
-
-               if(!result)
-               {
-                       if(file->tag.title_is("/MASK"))
-                       {
-                               result = 1;
-                       }
-                       else
-                       if(file->tag.title_is("POINT"))
-                       {
-                               XMLBuffer data;
-                               file->read_text_until("/POINT", &data);
-
-                               MaskPoint *point = new MaskPoint;
-                               char *ptr = data.cstr();
-//printf("MaskAuto::load 1 %s\n", ptr);
-
-                               point->x = atof(ptr);
-                               ptr = strchr(ptr, ',');
-//printf("MaskAuto::load 2 %s\n", ptr + 1);
-                               if(ptr)
-                               {
-                                       point->y = atof(ptr + 1);
-                                       ptr = strchr(ptr + 1, ',');
-
-                                       if(ptr)
-                                       {
-//printf("MaskAuto::load 3 %s\n", ptr + 1);
-                                               point->control_x1 = atof(ptr + 1);
-                                               ptr = strchr(ptr + 1, ',');
-                                               if(ptr)
-                                               {
-//printf("MaskAuto::load 4 %s\n", ptr + 1);
-                                                       point->control_y1 = atof(ptr + 1);
-                                                       ptr = strchr(ptr + 1, ',');
-                                                       if(ptr)
-                                                       {
-//printf("MaskAuto::load 5 %s\n", ptr + 1);
-                                                               point->control_x2 = atof(ptr + 1);
-                                                               ptr = strchr(ptr + 1, ',');
-                                                               if(ptr) point->control_y2 = atof(ptr + 1);
-                                                       }
-                                               }
-                                       }
-
-                               }
-                               points.append(point);
-                       }
+       while( !(result = file->read_tag()) ) {
+               if( file->tag.title_is("/MASK") ) break;
+               if( file->tag.title_is("POINT") ) {
+                       XMLBuffer data;
+                       file->read_text_until("/POINT", &data);
+                       MaskPoint *point = new MaskPoint;
+                       char *cp = data.cstr();
+                       if( cp ) point->x = strtof(cp, &cp);
+                       if( cp && *cp==',' ) point->y = strtof(cp+1, &cp);
+                       if( cp && *cp==',' ) point->control_x1 = strtof(cp+1, &cp);
+                       if( cp && *cp==',' ) point->control_y1 = strtof(cp+1, &cp);
+                       if( cp && *cp==',' ) point->control_x2 = strtof(cp+1, &cp);
+                       if( cp && *cp==',' ) point->control_y2 = strtof(cp+1, &cp);
+                       points.append(point);
                }
        }
 }
@@ -176,6 +142,8 @@ void SubMask::copy(FileXML *file)
                file->tag.set_title("MASK");
                file->tag.set_property("NUMBER", keyframe->masks.number_of(this));
                file->tag.set_property("NAME", name);
+               file->tag.set_property("FADER", fader);
+               file->tag.set_property("FEATHER", feather);
                file->append_tag();
                file->append_newline();
 
@@ -208,16 +176,13 @@ void SubMask::copy(FileXML *file)
 
 void SubMask::dump()
 {
-       for(int i = 0; i < points.total; i++)
-       {
+       for( int i=0; i<points.size(); ++i ) {
+               printf("        mask: %d, name: %s, fader: %f, feather %f\n", i,
+                       name, fader, feather);
                printf("          point=%d x=%.2f y=%.2f in_x=%.2f in_y=%.2f out_x=%.2f out_y=%.2f\n",
-                       i,
-                       points.values[i]->x,
-                       points.values[i]->y,
-                       points.values[i]->control_x1,
-                       points.values[i]->control_y1,
-                       points.values[i]->control_x2,
-                       points.values[i]->control_y2);
+                       i, points.values[i]->x, points.values[i]->y,
+                       points.values[i]->control_x1, points.values[i]->control_y1,
+                       points.values[i]->control_x2, points.values[i]->control_y2);
        }
 }
 
@@ -225,9 +190,6 @@ void SubMask::dump()
 MaskAuto::MaskAuto(EDL *edl, MaskAutos *autos)
  : Auto(edl, autos)
 {
-       mode = MASK_SUBTRACT_ALPHA;
-       feather = 0;
-       value = 100;
        apply_before_plugins = 0;
        disable_opengl_masking = 0;
 
@@ -258,34 +220,25 @@ int MaskAuto::operator==(MaskAuto &that)
 
 int MaskAuto::identical(MaskAuto *src)
 {
-       if(value != src->value ||
-               mode != src->mode ||
-               feather != src->feather ||
-               masks.size() != src->masks.size() ||
-               apply_before_plugins != src->apply_before_plugins ||
-               disable_opengl_masking != src->disable_opengl_masking) return 0;
-
-       for(int i = 0; i < masks.size(); i++)
-               if(!(*masks.values[i] == *src->masks.values[i])) return 0;
+       if( masks.size() != src->masks.size() ||
+           apply_before_plugins != src->apply_before_plugins ||
+           disable_opengl_masking != src->disable_opengl_masking ) return 0;
 
+       for( int i=0; i<masks.size(); ++i ) {
+               if(!(*masks.values[i] == *src->masks.values[i])) return 0;
+       }
        return 1;
 }
 
 void MaskAuto::update_parameter(MaskAuto *ref, MaskAuto *src)
 {
-       if(src->value != ref->value)
-               this->value = src->value;
-       if(src->mode != ref->mode)
-               this->mode = src->mode;
-       if(src->apply_before_plugins != ref->apply_before_plugins)
+       if( src->apply_before_plugins != ref->apply_before_plugins )
                this->apply_before_plugins = src->apply_before_plugins;
-       if(src->disable_opengl_masking != ref->disable_opengl_masking)
+       if( src->disable_opengl_masking != ref->disable_opengl_masking )
                this->disable_opengl_masking = src->disable_opengl_masking;
-       if(!EQUIV(src->feather, ref->feather))
-               this->feather = src->feather;
 
        for( int i=0; i<masks.size(); ++i ) {
-               if(!src->get_submask(i)->equivalent(*ref->get_submask(i)))
+               if( !src->get_submask(i)->equivalent(*ref->get_submask(i)) )
                        this->get_submask(i)->copy_from(*src->get_submask(i));
        }
 }
@@ -303,9 +256,6 @@ void MaskAuto::copy_from(MaskAuto *src)
 
 void MaskAuto::copy_data(MaskAuto *src)
 {
-       mode = src->mode;
-       feather = src->feather;
-       value = src->value;
        apply_before_plugins = src->apply_before_plugins;
        disable_opengl_masking = src->disable_opengl_masking;
 
@@ -328,34 +278,28 @@ int MaskAuto::interpolate_from(Auto *a1, Auto *a2, int64_t position, Auto *templ
        {
                return Auto::interpolate_from(a1, a2, position, templ);
        }
-       this->mode = mask_auto1->mode;
-       this->feather = mask_auto1->feather;
-       this->value = mask_auto1->value;
        this->apply_before_plugins = mask_auto1->apply_before_plugins;
        this->disable_opengl_masking = mask_auto1->disable_opengl_masking;
        this->position = position;
        masks.remove_all_objects();
 
-       for(int i = 0;
-               i < mask_auto1->masks.total;
-               i++)
-       {
+       for( int i=0; i<mask_auto1->masks.total; ++i ) {
                SubMask *new_submask = new SubMask(this, i);
                masks.append(new_submask);
                SubMask *mask1 = mask_auto1->masks.values[i];
                SubMask *mask2 = mask_auto2->masks.values[i];
+               double len = mask_auto2->position - mask_auto1->position;
+               double weight = !len ? 0 : (position - mask_auto1->position) / len;
+               new_submask->fader = mask1->fader*(1-weight) + mask2->fader*weight + 0.5;
+               new_submask->feather = mask1->feather*(1-weight) + mask2->feather*weight + 0.5;
 
                // just in case, should never happen
                int total_points = MIN(mask1->points.total, mask2->points.total);
-               for(int j = 0; j < total_points; j++)
-               {
+               for( int j=0; j<total_points; ++j ) {
                        MaskPoint *point = new MaskPoint;
                        MaskAutos::avg_points(point,
-                               mask1->points.values[j],
-                               mask2->points.values[j],
-                               position,
-                               mask_auto1->position,
-                               mask_auto2->position);
+                               mask1->points.values[j], mask2->points.values[j],
+                               position, mask_auto1->position, mask_auto2->position);
                        new_submask->points.append(point);
                }
        }
@@ -400,13 +344,13 @@ void MaskAuto::set_points(ArrayList<MaskPoint*> *points,
 
 void MaskAuto::load(FileXML *file)
 {
-       mode = file->tag.get_property("MODE", mode);
-       feather = file->tag.get_property("FEATHER", feather);
-       value = file->tag.get_property("VALUE", value);
+// legacy, moved to SubMask
+       int old_mode = file->tag.get_property("MODE", MASK_MULTIPLY_ALPHA);
+       int old_feather = file->tag.get_property("FEATHER", 0);
+       int old_value = file->tag.get_property("VALUE", 100);
        apply_before_plugins = file->tag.get_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
        disable_opengl_masking = file->tag.get_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
-       for(int i = 0; i < masks.size(); i++)
-       {
+       for( int i=0; i<masks.size(); ++i ) {
                delete masks.values[i];
                masks.values[i] = new SubMask(this, i);
        }
@@ -422,6 +366,10 @@ void MaskAuto::load(FileXML *file)
                        SubMask *mask = masks.values[no];
                        memset(mask->name, 0, sizeof(mask->name));
                        strncpy(mask->name, name, sizeof(mask->name));
+                       mask->feather = file->tag.get_property("FEATHER", old_feather);
+                       mask->fader = file->tag.get_property("FADER", old_value);
+                       if( old_mode == MASK_SUBTRACT_ALPHA )
+                               mask->fader = -mask->fader;
                        mask->load(file);
                }
        }
@@ -431,25 +379,14 @@ void MaskAuto::load(FileXML *file)
 void MaskAuto::copy(int64_t start, int64_t end, FileXML *file, int default_auto)
 {
        file->tag.set_title("AUTO");
-       file->tag.set_property("MODE", mode);
-       file->tag.set_property("VALUE", value);
-       file->tag.set_property("FEATHER", feather);
        file->tag.set_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
        file->tag.set_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
-
-       if(default_auto)
-               file->tag.set_property("POSITION", 0);
-       else
-               file->tag.set_property("POSITION", position - start);
+       file->tag.set_property("POSITION", default_auto ? 0 : position - start);
        file->append_tag();
        file->append_newline();
 
-       for(int i = 0; i < masks.size(); i++)
-       {
-//printf("MaskAuto::copy 1 %p %d %p\n", this, i, masks.values[i]);
+       for( int i=0; i<masks.size(); ++i )
                masks.values[i]->copy(file);
-//printf("MaskAuto::copy 10\n");
-       }
 
        file->tag.set_title("/AUTO");
        file->append_tag();
@@ -458,9 +395,7 @@ void MaskAuto::copy(int64_t start, int64_t end, FileXML *file, int default_auto)
 
 void MaskAuto::dump()
 {
-       printf("         mode=%d value=%d\n", mode, value);
-       for(int i = 0; i < masks.size(); i++)
-       {
+       for( int i=0; i<masks.size(); ++i ) {
                printf("         submask %d\n", i);
                masks.values[i]->dump();
        }
@@ -468,11 +403,9 @@ void MaskAuto::dump()
 
 void MaskAuto::translate_submasks(float translate_x, float translate_y)
 {
-       for(int i = 0; i < masks.size(); i++)
-       {
+       for( int i=0; i<masks.size(); ++i ) {
                SubMask *mask = get_submask(i);
-               for (int j = 0; j < mask->points.total; j++)
-               {
+               for( int j=0; j<mask->points.total; ++j ) {
                        mask->points.values[j]->x += translate_x;
                        mask->points.values[j]->y += translate_y;
                }
@@ -481,11 +414,9 @@ void MaskAuto::translate_submasks(float translate_x, float translate_y)
 
 void MaskAuto::scale_submasks(int orig_scale, int new_scale)
 {
-       for(int i = 0; i < masks.size(); i++)
-       {
+       for( int i=0; i<masks.size(); ++i ) {
                SubMask *mask = get_submask(i);
-               for (int j = 0; j < mask->points.total; j++)
-               {
+               for( int j=0; j<mask->points.total; ++j ) {
                        float orig_x = mask->points.values[j]->x * orig_scale;
                        float orig_y = mask->points.values[j]->y * orig_scale;
                        mask->points.values[j]->x = orig_x / new_scale;
index 1bae9ab..31a11b9 100644 (file)
@@ -44,13 +44,14 @@ public:
        float control_x2, control_y2;
 };
 
+#define FEATHER_MAX 100
+
 class SubMask
 {
 public:
        SubMask(MaskAuto *keyframe, int no);
        ~SubMask();
 
-// Don't use ==
        int operator==(SubMask& ptr);
        int equivalent(SubMask& ptr);
        void copy_from(SubMask& ptr);
@@ -59,6 +60,8 @@ public:
        void dump();
 
        char name[BCSTRLEN];
+       float fader; // 0 - 100
+       float feather; // 0 - 100
        ArrayList<MaskPoint*> points;
        MaskAuto *keyframe;
 };
@@ -98,15 +101,56 @@ public:
 
        ArrayList<SubMask*> masks;
 // These are constant for the entire track
-       int mode;
-       float feather;
-// 0 - 100
-       int value;
        int apply_before_plugins;
        int disable_opengl_masking;
 };
 
+class MaskCoord { public: double x, y, z; };
+
+class MaskEdge : public ArrayList<MaskCoord>
+{
+public:
+       MaskCoord &append() { return ArrayList<MaskCoord>::append(); }
+       MaskCoord &append(double x, double y, double z=0) {
+               MaskCoord &c = append();
+               c.x = x;  c.y = y;  c.z = z;
+               return c;
+       }
+};
+
+// shader buffer unsized array vec only seems to work for dvec (05/2019)
+class MaskSpot { public: double x, y; };
+
+class MaskSpots : public ArrayList<MaskSpot>
+{
+public:
+       MaskSpot &append() { return ArrayList<MaskSpot>::append(); }
+       MaskSpot &append(double x, double y) {
+               MaskSpot &s = append();
+               s.x = x;  s.y = y;
+               return s;
+       }
+};
 
+class MaskEdges : public ArrayList<MaskEdge*> {
+public:
+       MaskEdges() {}
+       ~MaskEdges() { remove_all_objects(); }
+};
 
+class MaskPointSet : public ArrayList<MaskPoint*>
+{
+public:
+       void clear() { remove_all_objects(); }
+       MaskPointSet() {}
+       ~MaskPointSet() { clear(); }
+};
+class MaskPointSets : public ArrayList<MaskPointSet*>
+{
+public:
+       void clear() { remove_all_objects(); }
+       MaskPointSets() {}
+       ~MaskPointSets() { clear(); }
+};
 
 #endif
index 5d14461..8c75a66 100644 (file)
@@ -28,7 +28,7 @@ class MaskAuto;
 class MaskPoint;
 class SubMask;
 
-enum {
+enum {  // no longer used, legacy value
        MASK_MULTIPLY_ALPHA,
        MASK_SUBTRACT_ALPHA,
 };
index 62b6ad7..04feb88 100644 (file)
@@ -161,37 +161,35 @@ void MaskAutos::get_points(ArrayList<MaskPoint*> *points,
 }
 
 
-float MaskAutos::get_feather(int64_t position, int direction)
+float MaskAutos::get_feather(int64_t position, int i, int direction)
 {
        Auto *begin = 0, *end = 0;
        position = (direction == PLAY_FORWARD) ? position : (position - 1);
+       MaskAuto*prev = (MaskAuto*)get_prev_auto(position, PLAY_FORWARD, begin, 1);
+       MaskAuto*next = (MaskAuto*)get_next_auto(position, PLAY_FORWARD, end, 1);
+       SubMask *prev_mask = prev->get_submask(i), *next_mask = next->get_submask(i);
 
-       get_prev_auto(position, PLAY_FORWARD, begin, 1);
-       get_next_auto(position, PLAY_FORWARD, end, 1);
+       double weight = end->position == begin->position ? 0.0 :
+               (double)(position - begin->position) / (end->position - begin->position);
 
-       double weight = 0.0;
-       if(end->position != begin->position)
-               weight = (double)(position - begin->position) / (end->position - begin->position);
-
-       return ((MaskAuto*)begin)->feather * (1.0 - weight) + ((MaskAuto*)end)->feather * weight;
+       int result = prev_mask->feather * (1-weight) + next_mask->feather*weight + 0.5;
+       return result;
 }
 
-int MaskAutos::get_value(int64_t position, int direction)
+int MaskAutos::get_fader(int64_t position, int i, int direction)
 {
        Auto *begin = 0, *end = 0;
        position = (direction == PLAY_FORWARD) ? position : (position - 1);
+       MaskAuto*prev = (MaskAuto*)get_prev_auto(position, PLAY_FORWARD, begin, 1);
+       MaskAuto*next = (MaskAuto*)get_next_auto(position, PLAY_FORWARD, end, 1);
+       SubMask *prev_mask = prev->get_submask(i), *next_mask = next->get_submask(i);
 
-       get_prev_auto(position, PLAY_FORWARD, begin, 1);
-       get_next_auto(position, PLAY_FORWARD, end, 1);
-
-       double weight = 0.0;
-       if(end->position != begin->position)
-               weight = (double)(position - begin->position) / (end->position - begin->position);
+       double weight = end->position == begin->position ? 0.0 :
+               (double)(position - begin->position) / (end->position - begin->position);
 
-       int result = (int)((double)((MaskAuto*)begin)->value * (1.0 - weight) +
-               (double)((MaskAuto*)end)->value * weight + 0.5);
-// printf("MaskAutos::get_value %d %d %f %d %f %d\n", __LINE__,
-// ((MaskAuto*)begin)->value, 1.0 - weight, ((MaskAuto*)end)->value, weight, result);
+       int result = prev_mask->fader * (1-weight) + next_mask->fader*weight + 0.5;
+// printf("MaskAutos::get_fader %d %d %d %f %d %f %d\n", __LINE__, i,
+// ((MaskAuto*)begin)->fader, 1.0 - weight, ((MaskAuto*)end)->fader, weight, result);
        return result;
 }
 
index fcac125..009069a 100644 (file)
@@ -55,8 +55,8 @@ public:
        int mask_exists(int64_t position, int direction);
 // Perform interpolation
        void get_points(ArrayList<MaskPoint*> *points, int submask, int64_t position, int direction);
-       float get_feather(int64_t position, int direction);
-       int get_value(int64_t position, int direction);
+       float get_feather(int64_t position, int i, int direction);
+       int get_fader(int64_t position, int i, int direction);
        int total_submasks(int64_t position, int direction);
 // Translates all mask points
        void translate_masks(float translate_x, float translate_y);
index fd7458e..467ffa0 100644 (file)
 #include <stdint.h>
 #include <string.h>
 
+void write_mask(VFrame *vfrm, const char *fmt, ...)
+{
+  va_list ap;    va_start(ap, fmt);
+  char fn[256];  vsnprintf(fn, sizeof(fn), fmt, ap);
+  va_end(ap);
+  FILE *fp = !strcmp(fn,"-") ? stdout : fopen(fn,"w");
+  if( fp ) {
+    int w = vfrm->get_w(), h = vfrm->get_h();
+    int m = vfrm->get_color_model();
+    fprintf(fp,"P5\n%d %d\n%d\n",w,h,m==BC_A8? 0xff : 0xffff);
+    int bpp = m==BC_A8? 1 : 2;
+    fwrite(vfrm->get_data(),bpp*w,h,fp);  fflush(fp);
+    if( fp != stdout ) fclose(fp);
+  }
+}
+
 MaskPackage::MaskPackage()
 {
 }
@@ -45,469 +61,253 @@ MaskUnit::MaskUnit(MaskEngine *engine)
  : LoadClient(engine)
 {
        this->engine = engine;
-       this->temp = 0;
+       spot = 0;
+       r = 0;
 }
 
 MaskUnit::~MaskUnit()
 {
-       if( temp ) delete temp;
 }
 
-
-#define OVERSAMPLE 8
-
-void MaskUnit::draw_line_clamped(VFrame *frame,
-       int x1, int y1, int x2, int y2, unsigned char k)
+void MaskUnit::draw_line(int v, int ix1, int iy1, int ix2, int iy2)
 {
-       int draw_x1, draw_y1;
-       int draw_x2, draw_y2;
-
-       if( y2 < y1 ) {
-               draw_x1 = x2;  draw_y1 = y2;
-               draw_x2 = x1;  draw_y2 = y1;
-       }
-       else {
-               draw_x1 = x1;  draw_y1 = y1;
-               draw_x2 = x2;  draw_y2 = y2;
-       }
-
-       unsigned char **rows = (unsigned char**)frame->get_rows();
-
-       if( draw_y2 != draw_y1 ) {
-               float slope = ((float)draw_x2 - draw_x1) / ((float)draw_y2 - draw_y1);
-               int w = frame->get_w() - 1;
-               int h = frame->get_h();
-
-               for( float y = draw_y1; y < draw_y2; y++ ) {
-                       if( y >= 0 && y < h ) {
-                               int x = (int)((y - draw_y1) * slope + draw_x1);
-                               int y_i = (int)y;
-                               int x_i = CLIP(x, 0, w);
-
-                               if( rows[y_i][x_i] == k )
-                                       rows[y_i][x_i] = 0;
-                               else
-                                       rows[y_i][x_i] = k;
-                       }
-               }
+       if( iy1 == iy2 ) return;
+       int x1 = iy1 < iy2 ? ix1 : ix2;
+       int y1 = iy1 < iy2 ? iy1 : iy2;
+       int x2 = iy1 < iy2 ? ix2 : ix1;
+       int y2 = iy1 < iy2 ? iy2 : iy1;
+       float slope = (float)(x2-x1) / (y2-y1);
+       int dy = y1 - start_y;
+       int i = dy < 0 ? (y1=start_y, -dy) : 0;
+       if( y2 > end_y ) y2 = end_y;
+       if( y2 < start_y || y1 >= end_y ) return;
+
+       VFrame *temp = engine->temp;
+       int w1 = temp->get_w()-1;
+       temp_t **rows = (temp_t **)temp->get_rows();
+       for( int y=y1; y<y2; ++i,++y ) {
+               int x = (int)(i*slope + x1);
+               bclamp(x, 0, w1);
+               rows[y][x] = rows[y][x] == v ? 0 : v;
        }
 }
 
-void MaskUnit::blur_strip(double *val_p, double *val_m,
-       double *dst, double *src, int size, int max)
+void MaskUnit::draw_fill(int v)
 {
-       double *sp_p = src;
-       double *sp_m = src + size - 1;
-       double *vp = val_p;
-       double *vm = val_m + size - 1;
-       double initial_p = sp_p[0];
-       double initial_m = sp_m[0];
-
-//printf("MaskUnit::blur_strip %d\n", size);
-       for( int k = 0; k < size; k++ ) {
-               int terms = (k < 4) ? k : 4;
-               int l;
-               for( l = 0; l <= terms; l++ ) {
-                       *vp += n_p[l] * sp_p[-l] - d_p[l] * vp[-l];
-                       *vm += n_m[l] * sp_m[l] - d_m[l] * vm[l];
-               }
-
-               for( ; l <= 4; l++) {
-                       *vp += (n_p[l] - bd_p[l]) * initial_p;
-                       *vm += (n_m[l] - bd_m[l]) * initial_m;
+       VFrame *temp = engine->temp;
+       int temp_w = temp->get_w();
+       temp_t **rows = (temp_t**)temp->get_rows();
+
+       for( int y=start_y; y<end_y; ++y ) {
+               temp_t *row = rows[y];
+               int value = 0, total = 0;
+               for( int x=0; x<temp_w; ++x )
+                       if( row[x] == v ) ++total;
+               if( total < 2 ) continue;
+               if( total & 0x1 ) --total;
+               for( int x=0; x<temp_w; ++x ) {
+                       if( row[x]==v && total>0 ) {
+                               --total;
+                               value = value ? 0 : v;
+                       }
+                       else if( value )
+                               row[x] = value;
                }
-               sp_p++;  sp_m--;
-               vp++;    vm--;
-       }
-
-       for( int i = 0; i < size; i++ ) {
-               double sum = val_p[i] + val_m[i];
-               CLAMP(sum, 0, max);
-               dst[i] = sum;
        }
 }
 
-void MaskUnit::do_feather(VFrame *output, VFrame *input, double feather,
-               int start_y, int end_y, int start_x, int end_x)
+void MaskUnit::draw_feather(int ix1,int iy1, int ix2,int iy2)
 {
-//printf("MaskUnit::do_feather %f\n", feather);
-// Get constants
-       double constants[8];
-       double div;
-       double std_dev = sqrt(-(double)(feather * feather) / (2 * log(1.0 / 255.0)));
-       div = sqrt(2 * M_PI) * std_dev;
-       constants[0] = -1.783 / std_dev;
-       constants[1] = -1.723 / std_dev;
-       constants[2] = 0.6318 / std_dev;
-       constants[3] = 1.997  / std_dev;
-       constants[4] = 1.6803 / div;
-       constants[5] = 3.735 / div;
-       constants[6] = -0.6803 / div;
-       constants[7] = -0.2598 / div;
-
-       n_p[0] = constants[4] + constants[6];
-       n_p[1] = exp(constants[1]) *
-                               (constants[7] * sin(constants[3]) -
-                               (constants[6] + 2 * constants[4]) * cos(constants[3])) +
-                               exp(constants[0]) *
-                               (constants[5] * sin(constants[2]) -
-                               (2 * constants[6] + constants[4]) * cos(constants[2]));
-
-       n_p[2] = 2 * exp(constants[0] + constants[1]) *
-                               ((constants[4] + constants[6]) * cos(constants[3]) *
-                               cos(constants[2]) - constants[5] *
-                               cos(constants[3]) * sin(constants[2]) -
-                               constants[7] * cos(constants[2]) * sin(constants[3])) +
-                               constants[6] * exp(2 * constants[0]) +
-                               constants[4] * exp(2 * constants[1]);
-
-       n_p[3] = exp(constants[1] + 2 * constants[0]) *
-                               (constants[7] * sin(constants[3]) -
-                               constants[6] * cos(constants[3])) +
-                               exp(constants[0] + 2 * constants[1]) *
-                               (constants[5] * sin(constants[2]) - constants[4] *
-                               cos(constants[2]));
-       n_p[4] = 0.0;
-
-       d_p[0] = 0.0;
-       d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
-                               2 * exp(constants[0]) * cos(constants[2]);
-
-       d_p[2] = 4 * cos(constants[3]) * cos(constants[2]) *
-                               exp(constants[0] + constants[1]) +
-                               exp(2 * constants[1]) + exp (2 * constants[0]);
-
-       d_p[3] = -2 * cos(constants[2]) * exp(constants[0] + 2 * constants[1]) -
-                               2 * cos(constants[3]) * exp(constants[1] + 2 * constants[0]);
-
-       d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
-
-       for( int i = 0; i < 5; i++ ) d_m[i] = d_p[i];
-
-       n_m[0] = 0.0;
-       for( int i = 1; i <= 4; i++ )
-               n_m[i] = n_p[i] - d_p[i] * n_p[0];
-
-       double sum_n_p, sum_n_m, sum_d;
-       double a, b;
-
-       sum_n_p = 0.0;
-       sum_n_m = 0.0;
-       sum_d = 0.0;
-       for( int i = 0; i < 5; i++ ) {
-               sum_n_p += n_p[i];
-               sum_n_m += n_m[i];
-               sum_d += d_p[i];
+       int x1 = iy1 < iy2 ? ix1 : ix2;
+       int y1 = iy1 < iy2 ? iy1 : iy2;
+       int x2 = iy1 < iy2 ? ix2 : ix1;
+       int y2 = iy1 < iy2 ? iy2 : iy1;
+       VFrame *temp = engine->temp;
+       int h = temp->get_h();
+       if( y2 < 0 || y1 >= h ) return;
+
+       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 m = dx > dy ? dx : dy, n = m;
+       if( dy >= dx ) {
+               if( dx2 >= 0 ) do {     /* +Y, +X */
+                       draw_spot(x, y++);
+                       if( (m -= dx2) < 0 ) { m += dy2;  ++x; }
+               } while( --n >= 0 );
+               else do {              /* +Y, -X */
+                       draw_spot(x, y++);
+                       if( (m += dx2) < 0 ) { m += dy2;  --x; }
+               } while( --n >= 0 );
        }
-
-       a = sum_n_p / (1 + sum_d);
-       b = sum_n_m / (1 + sum_d);
-
-       for( int i = 0; i < 5; i++ ) {
-               bd_p[i] = d_p[i] * a;
-               bd_m[i] = d_m[i] * b;
+       else {
+               if( dx2 >= 0 ) do {     /* +X, +Y */
+                       draw_spot(x++, y);
+                       if( (m -= dy2) < 0 ) { m += dx2;  ++y; }
+               } while( --n >= 0 );
+               else do {              /* -X, +Y */
+                       draw_spot(x--, y);
+                       if( (m -= dy2) < 0 ) { m -= dx2;  ++y; }
+               } while( --n >= 0 );
        }
-#define DO_FEATHER(type, max) { \
-       int frame_w = input->get_w(); \
-       int frame_h = input->get_h(); \
-       int size = MAX(frame_w, frame_h); \
-       double *src = new double[size]; \
-       double *dst = new double[size]; \
-       double *val_p = new double[size]; \
-       double *val_m = new double[size]; \
-       type **in_rows = (type**)input->get_rows(); \
-       type **out_rows = (type**)output->get_rows(); \
-       int j; \
- \
-/* printf("DO_FEATHER 1\n"); */ \
-       if( end_x > start_x ) { \
-               for( j = start_x; j < end_x; j++ ) { \
-       /* printf("DO_FEATHER 1.1 %d\n", j); */ \
-                       bzero(val_p, sizeof(double) * frame_h); \
-                       bzero(val_m, sizeof(double) * frame_h); \
-                       for( int k = 0; k < frame_h; k++ ) { \
-                               src[k] = (double)in_rows[k][j]; \
-                       } \
-                       blur_strip(val_p, val_m, dst, src, frame_h, max); \
-                       for( int k = 0; k < frame_h; k++ ) { \
-                               out_rows[k][j] = (type)dst[k]; \
-                       } \
-               } \
-       } \
-       if( end_y > start_y ) { \
-               for( j = start_y; j < end_y; j++ ) { \
-       /* printf("DO_FEATHER 2 %d\n", j); */ \
-                       bzero(val_p, sizeof(double) * frame_w); \
-                       bzero(val_m, sizeof(double) * frame_w); \
-                       for( int k = 0; k < frame_w; k++ ) { \
-                               src[k] = (double)out_rows[j][k]; \
-                       } \
-                       blur_strip(val_p, val_m, dst, src, frame_w, max); \
-                       for( int k = 0; k < frame_w; k++ ) { \
-                               out_rows[j][k] = (type)dst[k]; \
-                       } \
-               } \
-       } \
-/* printf("DO_FEATHER 3\n"); */ \
-       delete [] src; \
-       delete [] dst; \
-       delete [] val_p; \
-       delete [] val_m; \
-/* printf("DO_FEATHER 4\n"); */ \
 }
 
-//printf("do_feather %d\n", frame->get_color_model());
-       switch( input->get_color_model() ) {
-       case BC_A8:
-               DO_FEATHER(unsigned char, 0xff);
-               break;
-       case BC_A16:
-               DO_FEATHER(uint16_t, 0xffff);
-               break;
-       case BC_A_FLOAT:
-               DO_FEATHER(float, 1);
-               break;
+void MaskUnit::draw_spot(int ix, int iy)
+{
+       int rr = r * r, n = abs(r), rv = r * v;
+       if( iy < start_y-n || iy >= end_y+n ) return;
+       VFrame *temp = engine->temp;
+       int w1 = temp->get_w()-1, h1 = temp->get_h()-1;
+       int xs = ix - n;  bclamp(xs, 0, w1);
+       int xn = ix + n;  bclamp(xn, 0, w1);
+       int ys = iy - n;  bclamp(ys, 0, h1);
+       int yn = iy + n;  bclamp(yn, 0, h1);
+
+       temp_t **rows = (temp_t**)temp->get_rows();
+       for( int y=ys ; y<=yn; ++y ) {
+               temp_t *row = rows[y];
+               for( int x=xs; x<=xn; ++x ) {
+                       int dx = x-ix, dy = y-iy;
+                       int dd = dx*dx + dy*dy;
+                       if( dd >= rr ) continue;
+                       temp_t *rp = &row[x], a = spot[dd];
+                       if( rv*(*rp-a) < 0 ) *rp = a;
+               }
        }
 }
 
 void MaskUnit::process_package(LoadPackage *package)
 {
        MaskPackage *ptr = (MaskPackage*)package;
-       float engine_value = engine->value;
-       int engine_mode = engine->mode;
-
+       start_y = ptr->start_y;
+       end_y = ptr->end_y;
+       if( start_y >= end_y ) return;
+       mask_model = engine->mask->get_color_model();
+       VFrame *temp = engine->temp;
        if( engine->recalculate && engine->step == DO_MASK ) {
-               VFrame *mask = engine->feather > 0 ? engine->temp_mask : engine->mask;
-// Generated oversampling frame
-               int mask_w = mask->get_w();
-               //int mask_h = mask->get_h();
-               int oversampled_package_w = mask_w * OVERSAMPLE;
-               int oversampled_package_h = (ptr->end_y - ptr->start_y) * OVERSAMPLE;
-               if( temp &&
-                   (temp->get_w() != oversampled_package_w ||
-                    temp->get_h() != oversampled_package_h) ) {
-                       delete temp;  temp = 0;
+// Draw masked region of polygons on temp
+               for( int k=0; k<engine->edges.size(); ++k ) {
+                       if( !engine->edges[k] ) continue;
+                       MaskEdge &edge = *engine->edges[k];
+                       if( edge.size() < 3 ) continue;
+                       int v = k + 1;
+                       for( int i=0; i<edge.size(); ++i ) {
+                               MaskCoord a = edge[i];
+                               MaskCoord b = i<edge.size()-1 ? edge[i+1] : edge[0];
+                               draw_line(v, a.x,a.y, b.x,b.y);
+                       }
+                       draw_fill(v);
                }
-               if( !temp ) {
-                       temp = new VFrame(oversampled_package_w, oversampled_package_h, BC_A8, 0);
+// map temp to fader alpha
+               int temp_w = temp->get_w();
+               temp_t **rows = (temp_t**)temp->get_rows();
+               temp_t *fade = engine->fade;
+               for( int y=start_y; y<end_y; ++y ) {
+                       temp_t *tp = rows[y];
+                       for( int i=temp_w; --i>=0; ++tp ) *tp = fade[*tp];
                }
-               temp->clear_frame();
-// Draw oversampled region of polygons on temp
-               for( int k=0; k<engine->point_sets.total; ++k ) {
-                       int old_x, old_y;
-                       unsigned char max = k + 1;
-                       ArrayList<MaskPoint*> *points = engine->point_sets.values[k];
-
-                       if( points->total < 3 ) continue;
-//printf("MaskUnit::process_package 2 %d %d\n", k, points->total);
-                       for( int i=0; i<points->total; ++i ) {
-                               MaskPoint *point1 = points->values[i];
-                               MaskPoint *point2 = (i >= points->total - 1) ?
-                                       points->values[0] :
-                                       points->values[i + 1];
-
-                               float x, y;
-                               int segments = (int)(sqrt(SQR(point1->x - point2->x) + SQR(point1->y - point2->y)));
-                               if( point1->control_x2 == 0 &&
-                                       point1->control_y2 == 0 &&
-                                       point2->control_x1 == 0 &&
-                                       point2->control_y1 == 0 )
-                                       segments = 1;
-                               float x0 = point1->x;
-                               float y0 = point1->y;
-                               float x1 = point1->x + point1->control_x2;
-                               float y1 = point1->y + point1->control_y2;
-                               float x2 = point2->x + point2->control_x1;
-                               float y2 = point2->y + point2->control_y1;
-                               float x3 = point2->x;
-                               float y3 = point2->y;
-
-                               for( int j = 0; j <= segments; j++ ) {
-                                       float t = (float)j / segments;
-                                       float tpow2 = t * t;
-                                       float tpow3 = t * t * t;
-                                       float invt = 1 - t;
-                                       float invtpow2 = invt * invt;
-                                       float invtpow3 = invt * invt * invt;
-
-                                       x = (        invtpow3 * x0
-                                               + 3 * t     * invtpow2 * x1
-                                               + 3 * tpow2 * invt     * x2
-                                               +     tpow3            * x3);
-                                       y = (        invtpow3 * y0
-                                               + 3 * t     * invtpow2 * y1
-                                               + 3 * tpow2 * invt     * y2
-                                               +     tpow3            * y3);
-
-                                       y -= ptr->start_y;
-                                       x *= OVERSAMPLE;
-                                       y *= OVERSAMPLE;
-
-                                       if( j > 0 ) {
-                                               draw_line_clamped(temp, old_x, old_y, (int)x, (int)y, max);
-                                       }
-
-                                       old_x = (int)x;
-                                       old_y = (int)y;
-                               }
+       }
+       if( engine->recalculate && engine->step == DO_FEATHER ) {
+// draw feather
+               for( int k=0; k<engine->edges.size(); ++k ) {
+                       if( !(v = engine->faders[k]) ) continue;
+                       if( !(r = engine->feathers[k]) ) continue;
+                       MaskEdge &edge = *engine->edges[k];
+                       if( !edge.size() ) continue;
+                       float rv = r * v, vv = fabs(v);
+                       int fg = 0xffff * (rv >= 0 ? vv : 0);
+                       int bg = 0xffff * (rv >= 0 ? 0 : vv);
+                       int rr = r*r;  double dr = 1./rr;
+                       temp_t psf[rr+1];  spot = psf;
+                       for( int i=0; i<=rr; ++i ) {
+                               double d = i*dr;
+                               psf[i] = (1-d)*fg + d*bg;
                        }
-// Fill in the polygon in the horizontal direction
-                       for( int i=0; i<oversampled_package_h; ++i ) {
-                               unsigned char *row = (unsigned char*)temp->get_rows()[i];
-                               int value = 0, total = 0;
-
-                               for( int j=0; j<oversampled_package_w; ++j )
-                                       if( row[j] == max ) ++total;
-
-                               if( total > 1 ) {
-                                       if( total & 0x1 ) --total;
-                                       for( int j=0; j<oversampled_package_w; ++j ) {
-                                               if( row[j]==max && total>0 ) {
-                                                       --total;
-                                                       value = value ? 0 : max;
-                                               }
-                                               else if( value )
-                                                       row[j] = value;
-                                       }
-                               }
+                       int n = edge.size();
+                       for( int i=0; i<n; ++i ) {
+                               MaskCoord &a = edge[i];
+                               MaskCoord &b = i<edge.size()-1 ? edge[i+1] : edge[0];
+                               draw_feather(a.x,a.y, b.x,b.y);
                        }
                }
-#define DOWNSAMPLE(type, temp_type, value, v) do { \
-for( int y=0; y<ptr->end_y-ptr->start_y; ++y ) { \
-       type *output_row = (type*)mask->get_rows()[y + ptr->start_y]; \
-       unsigned char **input_rows = (unsigned char**)temp->get_rows() + y * OVERSAMPLE; \
-       for( int x=0; x<mask_w; ++x ) { \
-               temp_type total = 0; \
-/* Accumulate pixel */ \
-               for( int k=0; k<OVERSAMPLE; ++k ) { \
-                       unsigned char *input_vector = input_rows[k] + x * OVERSAMPLE; \
-                       for( int l=0; l<OVERSAMPLE; ++l ) { \
-                               total += (input_vector[l] ? value : 0); \
-                       } \
-               } \
-/* Divide pixel */ \
-               total /= OVERSAMPLE * OVERSAMPLE; \
-               output_row[x] = v; \
-       } \
-} } while(0)
 
-               if( engine_value < 0 ) {
-                       engine_value = -engine_value;
-                       engine_mode = 1-engine_mode;
-               }
-// Downsample polygon
-               switch( mask->get_color_model() ) {
-               case BC_A8: {
-                       unsigned char value;
-                       value = (int)(engine_value / 100 * 0xff);
-                       if( engine->value >= 0 )
-                               DOWNSAMPLE(unsigned char, int64_t, value, total);
-                       else
-                               DOWNSAMPLE(unsigned char, int64_t, value, value-total);
-                       break; }
-
-               case BC_A16: {
-                       uint16_t value;
-                       value = (int)(engine_value / 100 * 0xffff);
-                       if( engine->value >= 0 )
-                               DOWNSAMPLE(uint16_t, int64_t, value, total);
-                       else
-                               DOWNSAMPLE(uint16_t, int64_t, value, value-total);
-                       break; }
-
-               case BC_A_FLOAT: {
-                       float value;
-                       value = engine_value / 100;
-                       if( engine->value >= 0 )
-                               DOWNSAMPLE(float, double, value, total);
-                       else
-                               DOWNSAMPLE(float, double, value, value-total);
-                       break; }
+#define REMAP(cmodel, type, expr) case cmodel: { \
+type **msk_rows = (type**)engine->mask->get_rows(); \
+for( int y=start_y; y<end_y; ++y ) { \
+       temp_t *rp = rows[y]; \
+       type *mp = msk_rows[y]; \
+       for( int i=temp_w; --i>=0; ++rp,++mp ) *mp = expr; \
+} } break
+// map alpha to mask
+               const float to_flt = 1/65535.;
+               int temp_w = temp->get_w();
+               temp_t **rows = (temp_t**)temp->get_rows();
+               switch( mask_model ) {
+               REMAP(BC_A8, uint8_t, *rp >> 8);
+               REMAP(BC_A16, uint16_t, *rp);
+               REMAP(BC_A_FLOAT, float, *rp * to_flt);
                }
        }
 
-       if( engine->step == DO_X_FEATHER && engine->recalculate && // Feather polygon
-           engine->feather > 0 ) {
-               do_feather(engine->mask,
-                       engine->temp_mask, engine->feather,
-                       ptr->start_y, ptr->end_y, 0, 0);
-//printf("MaskUnit::process_package 3 %f\n", engine->feather);
-       }
-
-       if( engine->step == DO_Y_FEATHER && engine->recalculate && // Feather polygon
-           engine->feather > 0 ) {
-               do_feather(engine->mask,
-                       engine->temp_mask, engine->feather,
-                       0, 0, ptr->start_x, ptr->end_x);
-       }
-
-       if( engine->step == DO_APPLY ) { // Apply mask
-#define APPLY_MASK_ALPHA(cmodel, type, max, components, do_yuv, a, b) \
+// Apply mask
+       if( engine->step == DO_APPLY ) {
+               int mask_w = engine->mask->get_w();
+               uint8_t **out_rows = engine->output->get_rows();
+               uint8_t **msk_rows = engine->mask->get_rows();
+#define APPLY_MASK_ALPHA(cmodel, type, max, components, do_yuv) \
 case cmodel: \
 for( int y=ptr->start_y; y<ptr->end_y; ++y ) { \
-       type *output_row = (type*)engine->output->get_rows()[y]; \
-       type *mask_row = (type*)engine->mask->get_rows()[y]; \
-       int chroma_offset = (int)(max + 1) / 2; \
+       type *out_row = (type*)out_rows[y]; \
+       type *msk_row = (type*)msk_rows[y]; \
+       type chroma_offset = (int)(max + 1) / 2; \
        for( int x=0; x<mask_w; ++x ) { \
-               int m = mask_row[x], n = max-m; \
+               type a = msk_row[x], b = max-a; \
                if( components == 4 ) { \
-                       output_row[x*4 + 3] = output_row[x*4 + 3]*a / max; \
+                       out_row[x*4 + 3] = out_row[x*4 + 3]*b / max; \
                } \
                else { \
-                       output_row[x*3 + 0] = output_row[x*3 + 0]*a / max; \
-                       output_row[x*3 + 1] = output_row[x*3 + 1]*a / max; \
-                       output_row[x*3 + 2] = output_row[x*3 + 2]*a / max; \
+                       out_row[x*3 + 0] = out_row[x*3 + 0]*b / max; \
+                       out_row[x*3 + 1] = out_row[x*3 + 1]*b / max; \
+                       out_row[x*3 + 2] = out_row[x*3 + 2]*b / max; \
                        if( do_yuv ) { \
-                               output_row[x*3 + 1] += chroma_offset*b / max; \
-                               output_row[x*3 + 2] += chroma_offset*b / max; \
+                               out_row[x*3 + 1] += chroma_offset*a / max; \
+                               out_row[x*3 + 2] += chroma_offset*a / max; \
                        } \
                } \
        } \
 } break
 
-#define MASK_ALPHA(mode, a, b) \
-case mode: \
-       switch( engine->output->get_color_model() ) { \
-       APPLY_MASK_ALPHA(BC_RGB888, unsigned char, 0xff, 3, 0, a, b); \
-       APPLY_MASK_ALPHA(BC_RGB_FLOAT, float, 1.0, 3, 0, a, b); \
-       APPLY_MASK_ALPHA(BC_YUV888, unsigned char, 0xff, 3, 1, a, b); \
-       APPLY_MASK_ALPHA(BC_RGBA_FLOAT, float, 1.0, 4, 0, a, b); \
-       APPLY_MASK_ALPHA(BC_YUVA8888, unsigned char, 0xff, 4, 1, a, b); \
-       APPLY_MASK_ALPHA(BC_RGBA8888, unsigned char, 0xff, 4, 0, a, b); \
-       APPLY_MASK_ALPHA(BC_RGB161616, uint16_t, 0xffff, 3, 0, a, b); \
-       APPLY_MASK_ALPHA(BC_YUV161616, uint16_t, 0xffff, 3, 1, a, b); \
-       APPLY_MASK_ALPHA(BC_YUVA16161616, uint16_t, 0xffff, 4, 1, a, b); \
-       APPLY_MASK_ALPHA(BC_RGBA16161616, uint16_t, 0xffff, 4, 0, a, b); \
-} break
-
-//printf("MaskUnit::process_package 1 %d\n", engine->mode);
-               int mask_w = engine->mask->get_w();
-               switch( engine_mode ) {
-               MASK_ALPHA(MASK_MULTIPLY_ALPHA, m, n);
-               MASK_ALPHA(MASK_SUBTRACT_ALPHA, n, m);
+               switch( engine->output->get_color_model() ) { \
+               APPLY_MASK_ALPHA(BC_RGB888, uint8_t, 0xff, 3, 0); \
+               APPLY_MASK_ALPHA(BC_RGB_FLOAT, float, 1.0, 3, 0); \
+               APPLY_MASK_ALPHA(BC_YUV888, uint8_t, 0xff, 3, 1); \
+               APPLY_MASK_ALPHA(BC_RGBA_FLOAT, float, 1.0, 4, 0); \
+               APPLY_MASK_ALPHA(BC_YUVA8888, uint8_t, 0xff, 4, 1); \
+               APPLY_MASK_ALPHA(BC_RGBA8888, uint8_t, 0xff, 4, 0); \
+               APPLY_MASK_ALPHA(BC_RGB161616, uint16_t, 0xffff, 3, 0); \
+               APPLY_MASK_ALPHA(BC_YUV161616, uint16_t, 0xffff, 3, 1); \
+               APPLY_MASK_ALPHA(BC_YUVA16161616, uint16_t, 0xffff, 4, 1); \
+               APPLY_MASK_ALPHA(BC_RGBA16161616, uint16_t, 0xffff, 4, 0); \
                }
        }
 }
 
 
 MaskEngine::MaskEngine(int cpus)
- : LoadServer(cpus, cpus * OVERSAMPLE * 2)
-// : LoadServer(1, OVERSAMPLE * 2)
+ : LoadServer(cpus, 2*cpus)
+// : LoadServer(1, 1)
 {
        mask = 0;
+       temp = 0;
 }
 
 MaskEngine::~MaskEngine()
 {
-       if( mask ) {
-               delete mask;
-               delete temp_mask;
-       }
-
-       for( int i = 0; i < point_sets.total; i++ ) {
-               ArrayList<MaskPoint*> *points = point_sets.values[i];
-               points->remove_all_objects();
-       }
+       delete mask;
+       delete temp;
+       for( int i = 0; i < point_sets.total; i++ )
+               point_sets[i]->remove_all_objects();
        point_sets.remove_all_objects();
 }
 
@@ -518,133 +318,158 @@ int MaskEngine::points_equivalent(ArrayList<MaskPoint*> *new_points,
        if( new_points->total != points->total ) return 0;
 
        for( int i = 0; i < new_points->total; i++ ) {
-               if( !(*new_points->values[i] == *points->values[i]) ) return 0;
+               if( !(*new_points->get(i) == *points->get(i)) ) return 0;
        }
 
        return 1;
 }
 
+void MaskEngine::draw_edge(MaskEdge &edge, MaskPointSet &points)
+{
+       if( points.size() < 2 ) return;
+       edge.remove_all();
+       for( int i=0; i<points.size(); ++i ) {
+               MaskPoint *ap = points[i];
+               MaskPoint *bp = (i>=points.size()-1) ?
+                               points[0] : points[i+1];
+               int dx = ap->x - bp->x, dy = ap->y - bp->y;
+               int segments = (int)(sqrt(dx*dx + dy*dy));
+               if( !segments ) continue;
+               if( ap->control_x2 == 0 && ap->control_y2 == 0 &&
+                   bp->control_x1 == 0 && bp->control_y1 == 0 )
+                       segments = 1;
+               float x0 = ap->x, y0 = ap->y;
+               float x1 = ap->x + ap->control_x2;
+               float y1 = ap->y + ap->control_y2;
+               float x2 = bp->x + bp->control_x1;
+               float y2 = bp->y + bp->control_y1;
+               float x3 = bp->x, y3 = bp->y;
+
+               for( int j = 0; j <= segments; ++j ) {
+                       float t = (float)j / segments;
+                       float tpow2 = t * t;
+                       float tpow3 = t * t * t;
+                       float invt = 1 - t;
+                       float invtpow2 = invt * invt;
+                       float invtpow3 = invt * invt * invt;
+
+                       int x = (invtpow3 * x0
+                               + 3 * t     * invtpow2 * x1
+                               + 3 * tpow2 * invt     * x2
+                               +     tpow3         * x3);
+                       int y = (invtpow3 * y0
+                               + 3 * t     * invtpow2 * y1
+                               + 3 * tpow2 * invt     * y2
+                               +     tpow3         * y3);
+                       edge.append(x, y);
+               }
+       }
+}
+
 void MaskEngine::do_mask(VFrame *output,
        int64_t start_position_project,
        MaskAutos *keyframe_set,
        MaskAuto *keyframe,
        MaskAuto *default_auto)
 {
-       int new_color_model = 0;
+       this->output = output;
        recalculate = 0;
+       int mask_model = 0;
 
        switch( output->get_color_model() ) {
        case BC_RGB_FLOAT:
        case BC_RGBA_FLOAT:
-               new_color_model = BC_A_FLOAT;
+               mask_model = BC_A_FLOAT;
                break;
 
        case BC_RGB888:
        case BC_RGBA8888:
        case BC_YUV888:
        case BC_YUVA8888:
-               new_color_model = BC_A8;
+               mask_model = BC_A8;
                break;
 
        case BC_RGB161616:
        case BC_RGBA16161616:
        case BC_YUV161616:
        case BC_YUVA16161616:
-               new_color_model = BC_A16;
+               mask_model = BC_A16;
                break;
        }
 
 // Determine if recalculation is needed
 SET_TRACE
 
-       if( mask &&
-           (mask->get_w() != output->get_w() ||
-            mask->get_h() != output->get_h() ||
-            mask->get_color_model() != new_color_model) ) {
-               delete mask;
-               delete temp_mask;
-               mask = 0;
+       int mask_w = output->get_w(), mask_h = output->get_h();
+       if( mask && ( mask->get_color_model() != mask_model ||
+           mask->get_w() != mask_w || mask->get_h() != mask_h ) ) {
+               delete mask;  mask = 0;
                recalculate = 1;
        }
-
-       if( !recalculate ) {
-               if( point_sets.total != keyframe_set->total_submasks(start_position_project,
-                       PLAY_FORWARD) )
-                       recalculate = 1;
+       if( temp && ( temp->get_w() != mask_w || temp->get_h() != mask_h ) ) {
+               delete temp;  temp = 0;
        }
 
-       if( !recalculate ) {
-               for( int i=0,n=keyframe_set->total_submasks(start_position_project, PLAY_FORWARD);
-                               i<n && !recalculate; ++i ) {
-                       ArrayList<MaskPoint*> new_points;
-                       keyframe_set->get_points(&new_points, i,
+       total_submasks = keyframe_set->total_submasks(start_position_project, PLAY_FORWARD);
+       if( total_submasks != point_sets.size() )
+               recalculate = 1;
+
+       for( int i=0; i<total_submasks && !recalculate; ++i ) {
+               float new_fader = keyframe_set->get_fader(start_position_project, i, PLAY_FORWARD);
+               if( new_fader != faders[i] ) { recalculate = 1;  break; }
+               float new_feather = keyframe_set->get_feather(start_position_project, i, PLAY_FORWARD);
+               if( new_feather != feathers[i] ) { recalculate = 1;  break; }
+               ArrayList<MaskPoint*> new_points;
+               keyframe_set->get_points(&new_points, i,
                                start_position_project, PLAY_FORWARD);
-                       if( !points_equivalent(&new_points, point_sets.values[i]) )
-                               recalculate = 1;
-                       new_points.remove_all_objects();
-               }
+               if( !points_equivalent(&new_points, point_sets[i]) )
+                       recalculate = 1;
+               new_points.remove_all_objects();
        }
 
-       int new_value = keyframe_set->get_value(start_position_project,
-               PLAY_FORWARD);
-       float new_feather = keyframe_set->get_feather(start_position_project,
-               PLAY_FORWARD);
-
-       if( recalculate ||
-               !EQUIV(new_feather, feather) ||
-               !EQUIV(new_value, value) ) {
-               recalculate = 1;
-               if( !mask ) {
-                       mask = new VFrame(output->get_w(), output->get_h(),
-                                       new_color_model, 0);
-                       temp_mask = new VFrame(output->get_w(), output->get_h(),
-                                       new_color_model, 0);
-               }
-               if( new_feather > 0 )
-                       temp_mask->clear_frame();
-               else
-                       mask->clear_frame();
-
+       if( recalculate ) {
                for( int i = 0; i < point_sets.total; i++ ) {
-                       ArrayList<MaskPoint*> *points = point_sets.values[i];
+                       ArrayList<MaskPoint*> *points = point_sets[i];
                        points->remove_all_objects();
                }
                point_sets.remove_all_objects();
-
-               for( int i = 0;
-                       i < keyframe_set->total_submasks(start_position_project,
-                               PLAY_FORWARD);
-                       i++ ) {
-                       ArrayList<MaskPoint*> *new_points = new ArrayList<MaskPoint*>;
-                       keyframe_set->get_points(new_points,
-                               i,
-                               start_position_project,
-                               PLAY_FORWARD);
+               edges.remove_all_objects();
+               faders.remove_all();
+               feathers.remove_all();
+               fade[0] = 0;
+
+               for( int i=0; i<total_submasks; ++i ) {
+                       float fader = keyframe_set->get_fader(start_position_project, i, PLAY_FORWARD);
+                       float v = fader / 100;
+                       faders.append(v);
+                       temp_t t = fabs(v) * 0xffff;
+                       if( fader < 0 ) {
+                               if( fade[0] < t ) fade[0] = t;
+                               t = 0;
+                       }
+                       fade[i+1] = t;
+                       float feather = keyframe_set->get_feather(start_position_project, i, PLAY_FORWARD);
+                       feathers.append(feather);
+                       MaskPointSet *new_points = new MaskPointSet();
+                       keyframe_set->get_points(new_points, i, start_position_project, PLAY_FORWARD);
                        point_sets.append(new_points);
+                       draw_edge(*edges.append(new MaskEdge()), *new_points);
                }
+// draw mask
+               if( !mask ) mask = new VFrame(mask_w, mask_h, mask_model, 0);
+               if( !temp ) temp = new VFrame(mask_w, mask_h, BC_A16, 0);
+               mask->clear_frame();
+               temp->clear_frame();
+               step = DO_MASK;
+               process_packages();
+               step = DO_FEATHER;
+               process_packages();
        }
-
-
-
-       this->output = output;
-       this->mode = default_auto->mode;
-       this->feather = new_feather;
-       this->value = new_value;
-
-
 // Run units
 SET_TRACE
-       step = DO_MASK;
-       process_packages();
-       step = DO_Y_FEATHER;
-       process_packages();
-       step = DO_X_FEATHER;
-       process_packages();
        step = DO_APPLY;
        process_packages();
 SET_TRACE
-
-
 }
 
 void MaskEngine::init_packages()
index 782c3cc..8ec5171 100644 (file)
@@ -30,6 +30,7 @@
 #include "mutex.inc"
 #include "vframe.inc"
 
+typedef uint16_t temp_t; // temp is A16
 
 class MaskEngine;
 
@@ -37,11 +38,11 @@ class MaskEngine;
 enum
 {
        DO_MASK,
-       DO_X_FEATHER,
-       DO_Y_FEATHER,
+       DO_FEATHER,
        DO_APPLY
 };
 
+
 class MaskPackage : public LoadPackage
 {
 public:
@@ -57,30 +58,25 @@ public:
        MaskUnit(MaskEngine *engine);
        ~MaskUnit();
 
+       void draw_line(int v, int x1, int y1, int x2, int y2);
+       void draw_fill(int v);
+       void draw_feather(int ix1,int iy1, int ix2,int iy2);
+       void draw_spot(int ix, int iy);
        void process_package(LoadPackage *package);
-       void draw_line_clamped(VFrame *frame,
-               int x1, int y1, int x2, int y2, unsigned char value);
-       void do_feather(VFrame *output, VFrame *input,
-               double feather, int start_y, int end_y, int start_x, int end_x);
-       void blur_strip(double *val_p, double *val_m,
-               double *dst, double *src, int size, int max);
-
-       double n_p[5], n_m[5];
-       double d_p[5], d_m[5];
-       double bd_p[5], bd_m[5];
 
        MaskEngine *engine;
-       VFrame *temp;
+       int start_y, end_y;
+       int mask_model;
+       float v, r;
+       temp_t *spot;
 };
 
-
 class MaskEngine : public LoadServer
 {
 public:
        MaskEngine(int cpus);
        ~MaskEngine();
 
-
        void do_mask(VFrame *output,
 // Position relative to project, compensated for playback direction
                int64_t start_position_project,
@@ -94,18 +90,17 @@ public:
        void init_packages();
        LoadClient* new_client();
        LoadPackage* new_package();
+       void draw_edge(MaskEdge &edge, MaskPointSet &points);
 
        VFrame *output;
-// State of last mask
-       VFrame *mask;
-// Temporary for feathering
-       VFrame *temp_mask;
-       ArrayList<ArrayList<MaskPoint*>*> point_sets;
-       int mode;
-       int step;
-       double feather;
+       VFrame *mask, *temp;
+       MaskEdges edges;
+       MaskPointSets point_sets;
+       ArrayList<float> faders;
+       ArrayList<float> feathers;
+       int step, total_submasks;
        int recalculate;
-       int value;
+       temp_t fade[SUBMASKS+1];
 };
 
 
index eed42cd..aefafc8 100644 (file)
@@ -259,39 +259,50 @@ static const char *read_texture_frag =
        "       gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
        "}\n";
 
-static const char *multiply_mask4_frag =
-       "uniform sampler2D tex;\n"
-       "uniform sampler2D tex1;\n"
-       "uniform float scale;\n"
-       "void main()\n"
-       "{\n"
-       "       gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
-       "       gl_FragColor.a *= texture2D(tex1, gl_TexCoord[0].st / vec2(scale, scale)).r;\n"
+static const char *in_vertex_frag =
+       "#version 430 // vertex shader\n"
+       "in vec3 in_pos;\n"
+       "void main() {\n"
+       "       gl_Position = vec4(in_pos-vec3(0.5,0.5,0.), .5);\n"
        "}\n";
 
-static const char *multiply_mask3_frag =
+static const char *feather_frag =
+       "#version 430\n"
+       "layout(location=0) out vec4 color;\n"
        "uniform sampler2D tex;\n"
-       "uniform sampler2D tex1;\n"
-       "uniform float scale;\n"
-       "uniform bool is_yuv;\n"
-       "void main()\n"
-       "{\n"
-       "       gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
-       "       float a = texture2D(tex1, gl_TexCoord[0].st / vec2(scale, scale)).r;\n"
-       "       gl_FragColor.rgb *= vec3(a, a, a);\n"
+// apparently, only doubles index properly in shared buffers
+       "buffer buf { dvec2 points[]; };\n"
+       "uniform float r;\n"
+       "uniform float v;\n"
+       "void main() {\n"
+       "       vec2 tex_st = gl_FragCoord.xy/textureSize(tex,0);\n"
+       "       color = texture(tex, tex_st);\n"
+       "       if( r==0. ) return;\n"
+       "       float rv = r*v>0. ? 1 : -1;\n"
+       "       float rr = r*r, dr = 1./rr;\n"
+       "       float vv = v>=0 ? 1.-v : 1.+v;\n"
+       "       float fg = rv>=0 ? vv : 1.;\n"
+       "       float bg = rv>=0 ? 1. : vv;\n"
+       "       int len = points.length();\n"
+       "       for( int i=0; i<len; ++i ) {\n"
+       "               float dx = float(points[i].x) - gl_FragCoord.x;\n"
+       "               float dy = float(points[i].y) - gl_FragCoord.y;\n"
+       "               float dd = dx*dx + dy*dy;\n"
+       "               if( dd >= rr ) continue;\n"
+       "               float d = dd*dr;\n"
+       "               float a = (1.-d)*fg + d*bg;\n"
+       "               if( rv*(color.a-a) > 0 ) color = vec4(a);\n"
+       "       }\n"
        "}\n";
 
-static const char *multiply_yuvmask3_frag =
+static const char *alpha_frag =
        "uniform sampler2D tex;\n"
-       "uniform sampler2D tex1;\n"
-       "uniform float scale;\n"
-       "void main()\n"
-       "{\n"
+       "uniform sampler2D tex2;\n"
+       "uniform vec2 tex2_dimensions;\n"
+       "void main() {\n" \
        "       gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
-       "       float a = texture2D(tex1, gl_TexCoord[0].st / vec2(scale, scale)).r;\n"
-       "       gl_FragColor.gb -= vec2(0.5, 0.5);\n"
-       "       gl_FragColor.rgb *= vec3(a, a, a);\n"
-       "       gl_FragColor.gb += vec2(0.5, 0.5);\n"
+       "       vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+       "       gl_FragColor.a = canvas.a;\n"
        "}\n";
 
 static const char *fade_rgba_frag =
@@ -357,26 +368,32 @@ void Playback3DCommand::copy_from(BC_SynchronousCommand *command)
        BC_SynchronousCommand::copy_from(command);
 }
 
-
-///static void glDebugCallback(GLenum src, GLenum typ, GLuint id,
-///    GLenum svy, GLsizei len, const GLchar* msg, void* dat)
-//static void glDebugCallback(unsigned int src, unsigned int typ, unsigned int id,
-//     unsigned int svy, int len, const char* msg, const void* dat)
-//{
-//     printf("glDebug: %d:%d; %d/%d %s\n",src,typ,id,svy,msg);
-//}
-
+//#define GL_BUG 1
+#ifdef GL_BUG
+static void GLAPIENTRY glDebugCallback(GLenum source, GLenum type,
+       GLuint id, GLenum severity, GLsizei length, const GLchar* message,
+       const void* userParam)
+{
+  fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+       ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+       type, severity, message );
+}
+#endif
 
 Playback3D::Playback3D(MWindow *mwindow)
  : BC_Synchronous()
 {
        this->mwindow = mwindow;
        temp_texture = 0;
-       //Enabling OpenGL debug output on nVidia drivers
-//     glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
-//     glEnable(GL_DEBUG_OUTPUT);
-//     glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
-//     glDebugMessageCallback(glDebugCallback, 0);
+#ifdef GL_BUG
+       //Enabling OpenGL debug output
+       // this does not work
+       glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
+       glEnable(GL_DEBUG_OUTPUT);
+       glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+       glDebugMessageCallback(glDebugCallback, 0);
+       glEnable(GL_DEBUG_OUTPUT);
+#endif
 }
 
 Playback3D::~Playback3D()
@@ -469,7 +486,7 @@ void Playback3D::copy_from(Canvas *canvas,
 void Playback3D::copy_from_sync(Playback3DCommand *command)
 {
 #ifdef HAVE_GL
-       BC_WindowBase *window = 
+       BC_WindowBase *window =
                command->canvas->lock_canvas("Playback3D::copy_from_sync");
        if( window ) {
                window->enable_opengl();
@@ -1149,33 +1166,123 @@ void Playback3D::do_mask(Canvas *canvas,
 }
 
 
+void Playback3D::draw_spots(MaskSpots &spots, int ix1,int iy1, int ix2,int iy2)
+{
+       int x1 = iy1 < iy2 ? ix1 : ix2;
+       int y1 = iy1 < iy2 ? iy1 : iy2;
+       int x2 = iy1 < iy2 ? ix2 : ix1;
+       int y2 = iy1 < iy2 ? iy2 : iy1;
+
+       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 m = dx > dy ? dx : dy, n = m;
+       if( dy >= dx ) {
+               if( dx2 >= 0 ) do {     /* +Y, +X */
+                       spots.append(x, y++);
+                       if( (m -= dx2) < 0 ) { m += dy2;  ++x; }
+               } while( --n >= 0 );
+               else do {              /* +Y, -X */
+                       spots.append(x, y++);
+                       if( (m += dx2) < 0 ) { m += dy2;  --x; }
+               } while( --n >= 0 );
+       }
+       else {
+               if( dx2 >= 0 ) do {     /* +X, +Y */
+                       spots.append(x++, y);
+                       if( (m -= dy2) < 0 ) { m += dx2;  ++y; }
+               } while( --n >= 0 );
+               else do {              /* -X, +Y */
+                       spots.append(x--, y);
+                       if( (m -= dy2) < 0 ) { m -= dx2;  ++y; }
+               } while( --n >= 0 );
+       }
+}
 
 #ifdef HAVE_GL
-struct Vertex : ListItem<Vertex>
+class fb_texture : public BC_Texture
 {
-       GLdouble c[3];
+public:
+       fb_texture(int w, int h, int colormodel);
+       ~fb_texture();
+       void bind(int texture_unit);
+       void read_screen(int x, int y, int w, int h);
+       void set_output_texture();
+       GLuint fb, rb;
 };
-// this list is only used from the main thread, no locking needed
-// this must be a list so that pointers to allocated entries remain valid
-// when new entries are added
-static List<Vertex> *vertex_cache = 0;
-
-static void combine_callback(GLdouble coords[3],
-       GLdouble *vertex_data[4],
-       GLfloat weight[4],
-       GLdouble **dataOut)
+
+fb_texture::fb_texture(int w, int h, int colormodel)
+ : BC_Texture(w, h, colormodel)
 {
-// can't use malloc here; GLU doesn't delete the memory for us!
-       Vertex* vertex = vertex_cache->append();
-       vertex->c[0] = coords[0];
-       vertex->c[1] = coords[1];
-       vertex->c[2] = coords[2];
-// we don't need to interpolate anything
-
-       *dataOut = &vertex->c[0];
+       fb = 0;  rb = 0;
+//     glGenRenderbuffers(1, &rb);
+//     glBindRenderbuffer(GL_RENDERBUFFER, rb);
+//     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, get_texture_w(), get_texture_w());
+       glGenFramebuffers(1, &fb);
+       glBindFramebuffer(GL_FRAMEBUFFER, fb);
+//     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb);
 }
-#endif
 
+fb_texture::~fb_texture()
+{
+       glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       glDeleteFramebuffers(1, (GLuint *)&fb);
+//     glBindRenderbuffer(GL_RENDERBUFFER, 0);
+//     glGenRenderbuffers(1, &rb);
+}
+
+void fb_texture::bind(int texture_unit)
+{
+       glBindFramebuffer(GL_FRAMEBUFFER, fb);
+//     glBindRenderbuffer(GL_RENDERBUFFER, rb);
+       BC_Texture::bind(texture_unit);
+}
+
+void fb_texture::read_screen(int x, int y, int w, int h)
+{
+       glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       glReadBuffer(GL_BACK);
+       glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, x,y, w,h);
+}
+
+void fb_texture::set_output_texture()
+{
+       glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, get_texture_id(), 0);
+       GLenum dbo[1] = { GL_COLOR_ATTACHMENT0, }; // bind layout(location=0) out vec4 color;
+       glDrawBuffers(1, dbo);
+       int ret = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+       if( ret != GL_FRAMEBUFFER_COMPLETE ) {
+               printf("glDrawBuffer error 0x%04x\n", ret);
+               return;
+       }
+}
+
+static void combineData(GLdouble coords[3],
+               GLdouble *vertex_data[4], GLfloat weight[4],
+               GLdouble **outData, void *data)
+{
+       ArrayList<double *> *invented = (ArrayList<double *> *)data;
+       GLdouble *vertex = new double[6];
+       invented->append(vertex);
+       vertex[0] = coords[0];
+       vertex[1] = coords[1];
+       vertex[2] = coords[2];
+       for( int i=3; i<6; ++i ) {
+               vertex[i] = weight[0] * vertex_data[0][i] +
+                       weight[1] * vertex_data[1][i] +
+                       weight[2] * vertex_data[2][i] +
+                       weight[3] * vertex_data[3][i];
+       }
+       *outData = vertex;
+}
+
+// dbug
+static void zglBegin(GLenum mode) { glBegin(mode); }
+static void zglEnd() { glEnd(); }
+static void zglVertex3dv(const GLdouble *v) { glVertex3dv(v); }
+
+#endif
 
 void Playback3D::do_mask_sync(Playback3DCommand *command)
 {
@@ -1187,7 +1294,7 @@ void Playback3D::do_mask_sync(Playback3DCommand *command)
 
                switch( command->frame->get_opengl_state() ) {
                case VFrame::RAM:
-// Time to upload to the texture
+// upload frame to the texture
                        command->frame->to_texture();
                        break;
 
@@ -1198,99 +1305,61 @@ void Playback3D::do_mask_sync(Playback3DCommand *command)
                        command->frame->screen_to_texture();
                        break;
                }
-// Create PBuffer and draw the mask on it
-               command->frame->enable_opengl();
 
 // Initialize coordinate system
+               command->frame->enable_opengl();
+               command->frame->init_screen();
+               int color_model = command->frame->get_color_model();
                int w = command->frame->get_w();
                int h = command->frame->get_h();
-               command->frame->init_screen();
-
-// Clear screen
-               glDisable(GL_TEXTURE_2D);
-               float value = command->keyframe->value / 100.f;
-               if( value >= 0 ) {
-                       if( command->default_auto->mode == MASK_MULTIPLY_ALPHA ) {
-                               glClearColor(0.f, 0.f, 0.f, 0.f);
-                               glColor4f(value, value, value, 1.f);
-                       }
-                       else {
-                               glClearColor(1.f, 1.f, 1.f, 1.f);
-                               value = 1.f - value;
-                               glColor4f(value, value, value, 1.f);
-                       }
-               }
-               else {
-                       if( command->default_auto->mode == MASK_MULTIPLY_ALPHA ) {
-                               value = -value;
-                               glClearColor(value, value, value, 1.f);
-                               glColor4f(0.f, 0.f, 0.f, 0.f);
-                       }
-                       else {
-                               value = 1.f + value;
-                               glClearColor(value, value, value, 1.f);
-                               glColor4f(1.f, 1.f, 1.f, 1.f);
-                       }
-               }
-               glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
-
-// Draw mask with scaling to simulate feathering
-               GLUtesselator *tesselator = gluNewTess();
-               gluTessProperty(tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
-               gluTessCallback(tesselator, GLU_TESS_VERTEX, (GLvoid (*) ( )) &glVertex3dv);
-               gluTessCallback(tesselator, GLU_TESS_BEGIN, (GLvoid (*) ( )) &glBegin);
-               gluTessCallback(tesselator, GLU_TESS_END, (GLvoid (*) ( )) &glEnd);
-               gluTessCallback(tesselator, GLU_TESS_COMBINE, (GLvoid (*) ( ))&combine_callback);
-
-               vertex_cache = new List<Vertex>;
-
-
+               MaskEdges edges;
+               float faders[SUBMASKS], feathers[SUBMASKS], bg = 1;
+               MaskPointSet point_set[SUBMASKS];
 // Draw every submask as a new polygon
                int total_submasks = command->keyframe_set->total_submasks(
-                       command->start_position_project,
-                       PLAY_FORWARD);
-               float scale = command->keyframe->feather + 1;
-               int display_list = glGenLists(1);
-               glNewList(display_list, GL_COMPILE);
-               for(int k = 0; k < total_submasks; k++)
-               {
-                       gluTessBeginPolygon(tesselator, NULL);
-                       gluTessBeginContour(tesselator);
-                       ArrayList<MaskPoint*> *points = new ArrayList<MaskPoint*>;
-                       command->keyframe_set->get_points(points,
-                               k,
-                               command->start_position_project,
-                               PLAY_FORWARD);
+                       command->start_position_project, PLAY_FORWARD);
+
+               for(int k = 0; k < total_submasks; k++) {
+                       MaskPointSet &points = point_set[k];
+                       command->keyframe_set->get_points(&points,
+                               k, command->start_position_project, PLAY_FORWARD);
+                       float fader = command->keyframe_set->get_fader(
+                               command->start_position_project, k, PLAY_FORWARD);
+                       float v = fader/100.;
+                       faders[k] = v;
+                       if( v < 0 && (v+=1) < bg ) bg = v;
+                       float feather = command->keyframe_set->get_feather(
+                               command->start_position_project, k, PLAY_FORWARD);
+                       feathers[k] = feather;
+               }
+// clear screen
+               glDisable(GL_TEXTURE_2D);
+               glClearColor(bg, bg, bg, bg);
+               glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
+               for(int k = 0; k < total_submasks; k++) {
+                       MaskPointSet &points = point_set[k];
+                       MaskEdge &edge = *edges.append(new MaskEdge());
                        int first_point = 0;
 // Need to tabulate every vertex in persistent memory because
 // gluTessVertex doesn't copy them.
-                       ArrayList<GLdouble*> coords;
-                       coords.set_array_delete();
-                       for(int i = 0; i < points->total; i++)
-                       {
-                               MaskPoint *point1 = points->values[i];
-                               MaskPoint *point2 = (i >= points->total - 1) ?
-                                       points->values[0] :
-                                       points->values[i + 1];
+                       for(int i = 0; i < points.total; i++) {
+                               MaskPoint *point1 = points.values[i];
+                               MaskPoint *point2 = (i >= points.total - 1) ?
+                                       points.values[0] : points.values[i + 1];
 
                                float x, y;
                                int segments = 0;
-                               if(point1->control_x2 == 0 &&
-                                       point1->control_y2 == 0 &&
-                                       point2->control_x1 == 0 &&
-                                       point2->control_y1 == 0)
+                               if( point1->control_x2 == 0 && point1->control_y2 == 0 &&
+                                   point2->control_x1 == 0 && point2->control_y1 == 0 )
                                        segments = 1;
 
-                               float x0 = point1->x;
-                               float y0 = point1->y;
+                               float x0 = point1->x, y0 = point1->y;
                                float x1 = point1->x + point1->control_x2;
                                float y1 = point1->y + point1->control_y2;
                                float x2 = point2->x + point2->control_x1;
                                float y2 = point2->y + point2->control_y1;
-                               float x3 = point2->x;
-                               float y3 = point2->y;
+                               float x3 = point2->x, y3 = point2->y;
 
                                // forward differencing bezier curves implementation taken from GPL code at
                                // http://cvs.sourceforge.net/viewcvs.py/guliverkli/guliverkli/src/subtitles/Rasterizer.cpp?rev=1.3
@@ -1321,8 +1390,7 @@ void Playback3D::do_mask_sync(Playback3DCommand *command)
                                // the absolute maximum acceleration must occur at either the beginning
                                // (|c2|) or the end (|c2+c3|).  Our bounds here are a little more
                                // conservative than that, but that's okay.
-                               if (segments == 0)
-                               {
+                               if (segments == 0) {
                                        float maxaccel1 = fabs(2*cy2) + fabs(6*cy3);
                                        float maxaccel2 = fabs(2*cx2) + fabs(6*cx3);
 
@@ -1333,123 +1401,124 @@ void Playback3D::do_mask_sync(Playback3DCommand *command)
                                        segments = int(1/h);
                                }
 
-                               for(int j = 0; j <= segments; j++)
-                               {
+                               for(int j = 0; j <= segments; j++) {
                                        float t = (float)j / segments;
                                        x = cx0 + t*(cx1 + t*(cx2 + t*cx3));
                                        y = cy0 + t*(cy1 + t*(cy2 + t*cy3));
 
-                                       if(j > 0 || first_point)
-                                       {
-                                               GLdouble *coord = new GLdouble[3];
-                                               coord[0] = x / scale;
-                                               coord[1] = -h + y / scale;
-                                               coord[2] = 0;
-                                               coords.append(coord);
+                                       if(j > 0 || first_point) {
+                                               edge.append(x, y - h);
                                                first_point = 0;
                                        }
                                }
                        }
-
-// Now that we know the total vertices, send them to GLU
-                       for(int i = 0; i < coords.total; i++)
-                               gluTessVertex(tesselator, coords.values[i], coords.values[i]);
-
-                       gluTessEndContour(tesselator);
-                       gluTessEndPolygon(tesselator);
-                       points->remove_all_objects();
-                       delete points;
-                       coords.remove_all_objects();
-               }
-               glEndList();
-               glCallList(display_list);
-               glDeleteLists(display_list, 1);
-               gluDeleteTess(tesselator);
-
-               delete vertex_cache;
-               vertex_cache = 0;
-
-               glColor4f(1, 1, 1, 1);
-
-
-// Read mask into temporary texture.
-// For feathering, just read the part of the screen after the downscaling.
-
-
-               float w_scaled = w / scale;
-               float h_scaled = h / scale;
-// Don't vary the texture size according to scaling because that
-// would waste memory.
-// This enables and binds the temporary texture.
-               glActiveTexture(GL_TEXTURE1);
-               BC_Texture::new_texture(&temp_texture,
-                       w,
-                       h,
-                       command->frame->get_color_model());
-               temp_texture->bind(1);
-               glReadBuffer(GL_BACK);
-
-// Need to add extra size to fill in the bottom right
-               glCopyTexSubImage2D(GL_TEXTURE_2D,
-                       0,
-                       0,
-                       0,
-                       0,
-                       0,
-                       (int)MIN(w_scaled + 2, w),
-                       (int)MIN(h_scaled + 2, h));
-
-               command->frame->bind_texture(0);
-
-
-// For feathered masks, use a shader to multiply.
-// For unfeathered masks, we could use a stencil buffer
-// for further optimization but we also need a YUV algorithm.
-               unsigned int frag_shader = 0;
-               switch(temp_texture->get_texture_components()) {
-               case 3:
-                       frag_shader = VFrame::make_shader(0,
-                               command->frame->get_color_model() == BC_YUV888 ?
-                                       multiply_yuvmask3_frag : multiply_mask3_frag,
-                               0);
-                       break;
-               case 4:
-                       frag_shader = VFrame::make_shader(0, multiply_mask4_frag, 0);
-                       break;
+                       if( edge.size() > 0 ) {
+// draw polygon
+                               float fader = faders[k];
+                               float v = fader < 0 ? 1 : 1-fader;
+                               glColor4f(v, v, v, v);
+                               int display_list = glGenLists(1);
+                               glNewList(display_list, GL_COMPILE);
+#if 0
+                               glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+                               glBegin(GL_POLYGON);
+                               MaskCoord *c = &edge[0];
+                               for( int i=edge.size(); --i>=0; ++c )
+                                       glVertex2f(c->x, c->y);
+                               glEnd();
+#else
+                               GLUtesselator *tess = gluNewTess();
+                               gluTessCallback(tess, GLU_TESS_VERTEX,(GLvoid (*)()) &zglVertex3dv);
+                               gluTessCallback(tess, GLU_TESS_BEGIN,(GLvoid (*)()) &zglBegin);
+                               gluTessCallback(tess, GLU_TESS_END,(GLvoid (*)()) &zglEnd);
+                               gluTessCallback(tess, GLU_TESS_COMBINE_DATA,(GLvoid (*)()) &combineData);
+                               ArrayList<double *> invented;
+                               invented.set_array_delete();
+
+                               gluTessBeginPolygon(tess, &invented);
+                               gluTessBeginContour(tess);
+                               MaskCoord *c = &edge[0];
+                               for( int i=edge.size(); --i>=0; ++c )
+                                       gluTessVertex(tess, (GLdouble *)c, c);
+                               gluTessEndContour(tess);
+                               gluTessEndPolygon(tess);
+                               gluDeleteTess(tess);
+                               invented.remove_all_objects();
+#endif
+                               glEndList();
+                               glCallList(display_list);
+                               glDeleteLists(1, display_list);
+                       }
                }
 
-               if( frag_shader ) {
-                       int variable;
-                       glUseProgram(frag_shader);
-                       if((variable = glGetUniformLocation(frag_shader, "tex")) >= 0)
-                               glUniform1i(variable, 0);
-                       if((variable = glGetUniformLocation(frag_shader, "tex1")) >= 0)
-                               glUniform1i(variable, 1);
-                       if((variable = glGetUniformLocation(frag_shader, "scale")) >= 0)
-                               glUniform1f(variable, scale);
+// in/out textures
+               fb_texture *in = new fb_texture(w, h, color_model);
+               in->bind(0);
+               in->read_screen(0,0, w,h);
+               fb_texture *out = new fb_texture(w, h, color_model);
+
+               unsigned int frag_shader =
+                       VFrame::make_shader(0, in_vertex_frag, feather_frag, 0);
+               if( frag_shader > 0 ) {
+                       GLuint points[1];
+                       glGenBuffers(1, points);
+                       for(int k = 0; k < total_submasks; k++) {
+                               MaskEdge &edge = *edges[k];
+                               if( !edge.size() ) continue;
+                               if( !faders[k] ) continue;
+                               if( !feathers[k] ) continue;
+                               MaskSpots spots;
+                               for( int i=0; i<edge.size(); ++i ) {
+                                       MaskCoord &a = edge[i];
+                                       MaskCoord &b = i<edge.size()-1 ? edge[i+1] : edge[0];
+                                       draw_spots(spots, a.x,a.y+h, b.x,b.y+h);
+                               }
+                               int sz = spots.size() * sizeof(MaskSpot);
+                               glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, points[0], 0, sz);
+                               glBufferData(GL_SHADER_STORAGE_BUFFER, sz, &spots[0], GL_DYNAMIC_COPY);
+                               glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+                               glUseProgram(frag_shader);
+                               float r = feathers[k], v = faders[k];
+                               glUniform1f(glGetUniformLocation(frag_shader, "r"), r);
+                               glUniform1f(glGetUniformLocation(frag_shader, "v"), v);
+                               in->bind(0);
+                               glUniform1i(glGetUniformLocation(frag_shader, "tex"), 0);
+                               out->set_output_texture();
+                               glViewport(0,0, w,h);
+                               out->draw_texture(0,0, w,h, 0,0, w,h);
+                               glUseProgram(0);
+                               fb_texture *t = in;  in = out;  out = t;
+                       }
+                       glDeleteBuffers(1, points);
                }
 
+               glDrawBuffers(0, 0);
+               glBindFramebuffer(GL_FRAMEBUFFER, 0);
 
-
-// Write texture to PBuffer with multiply and scaling for feather.
-
-
-               command->frame->draw_texture(0, 0, w, h, 0, 0, w, h);
+               unsigned int shader = VFrame::make_shader(0, alpha_frag, 0);
+               glUseProgram(shader);
+               if( shader > 0 ) {
+                       command->frame->bind_texture(0);
+                       in->BC_Texture::bind(1);
+                       glUniform1i(glGetUniformLocation(shader, "tex"), 0);
+                       glUniform1i(glGetUniformLocation(shader, "tex2"), 1);
+                       glUniform2f(glGetUniformLocation(shader, "tex2_dimensions"),
+                                       (float)in->get_texture_w(),
+                                       (float)in->get_texture_h());
+//                     if( BC_CModels::components(color_model ) == 4) {
+//                             glEnable(GL_BLEND);
+//                             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+//                     }
+               }
+               command->frame->draw_texture();
                command->frame->set_opengl_state(VFrame::SCREEN);
-
-
-// Disable temp texture
                glUseProgram(0);
-
-               glActiveTexture(GL_TEXTURE1);
+               delete in;
+               delete out;
+// Default drawable
                glDisable(GL_TEXTURE_2D);
-               delete temp_texture;
-               temp_texture = 0;
-
+               glColor4f(1, 1, 1, 1);
                glActiveTexture(GL_TEXTURE0);
-               glDisable(GL_TEXTURE_2D);
-
-// Default drawable
                window->enable_opengl();
        }
        command->canvas->unlock_canvas();
@@ -1457,14 +1526,6 @@ void Playback3D::do_mask_sync(Playback3DCommand *command)
 }
 
 
-
-
-
-
-
-
-
-
 void Playback3D::convert_cmodel(Canvas *canvas,
        VFrame *output,
        int dst_cmodel)
@@ -1498,7 +1559,7 @@ void Playback3D::convert_cmodel(Canvas *canvas,
 void Playback3D::convert_cmodel_sync(Playback3DCommand *command)
 {
 #ifdef HAVE_GL
-       BC_WindowBase *window = 
+       BC_WindowBase *window =
                command->canvas->lock_canvas("Playback3D::convert_cmodel_sync");
        if( window ) {
                window->enable_opengl();
@@ -1688,7 +1749,7 @@ int Playback3D::run_plugin(Canvas *canvas, PluginClient *client)
 
 void Playback3D::run_plugin_sync(Playback3DCommand *command)
 {
-       BC_WindowBase *window = 
+       BC_WindowBase *window =
                command->canvas->lock_canvas("Playback3D::run_plugin_sync");
        if( window ) {
                window->enable_opengl();
index 6a38d7a..ba38571 100644 (file)
@@ -29,7 +29,7 @@
 #include "bcwindowbase.inc"
 #include "canvas.inc"
 #include "condition.inc"
-#include "maskauto.inc"
+#include "maskauto.h"
 #include "maskautos.inc"
 #include "mutex.inc"
 #include "mwindow.inc"
@@ -285,13 +285,9 @@ public:
        void do_fade(Canvas *canvas, VFrame *frame, float fade);
        void convert_cmodel(Canvas *canvas, VFrame *output, int dst_cmodel);
 
-       void do_mask(Canvas *canvas,
-               VFrame *output,
-               int64_t start_position_project,
-               MaskAutos *keyframe_set,
-               MaskAuto *keyframe,
-               MaskAuto *default_auto);
-
+       void draw_spots(MaskSpots &spots, int ix1,int iy1, int ix2,int iy2);
+       void do_mask(Canvas *canvas, VFrame *output, int64_t start_position_project,
+               MaskAutos *keyframe_set, MaskAuto *keyframe, MaskAuto *default_auto);
 
 // Overlay a virtual node on the framebuffer
        void overlay(Canvas *canvas,
index dd7e4fe..c96b51c 100644 (file)
@@ -332,7 +332,7 @@ void VirtualVNode::render_mask(VFrame *output_temp,
                int submask_points = mask->points.total;
                if(submask_points > 1) total_points += submask_points;
        }
-
+/*
 //printf("VirtualVNode::render_mask 1 %d %d\n", total_points, keyframe->value);
 // Ignore certain masks
        if(total_points <= 2 ||
@@ -347,7 +347,7 @@ void VirtualVNode::render_mask(VFrame *output_temp,
                output_temp->clear_frame();
                return;
        }
-
+*/
        if(use_opengl) {
                if( !((VDeviceX11*)((VirtualVConsole*)vconsole)->get_vdriver())->can_mask(
                                start_position_project, keyframe_set) )
index 0bc4f10..9e49bf5 100644 (file)
@@ -46,16 +46,21 @@ TextureID::TextureID(int window_id, int id, int w, int h, int components)
        in_use = 1;
 }
 
-ShaderID::ShaderID(int window_id, unsigned int handle, char *source)
+ShaderID::ShaderID(int window_id, unsigned int handle,
+               const char *vert, const char *frag)
 {
        this->window_id = window_id;
        this->handle = handle;
-       this->source = strdup(source);
+       if( !vert ) vert = "";
+       if( !frag ) frag = "";
+       this->vert = cstrdup(vert);
+       this->frag = cstrdup(frag);
 }
 
 ShaderID::~ShaderID()
 {
-       free(source);
+       delete [] vert;
+       delete [] frag;
 }
 
 #ifdef HAVE_GL
@@ -240,37 +245,24 @@ void BC_Synchronous::handle_command(BC_SynchronousCommand *command)
 
 void BC_Synchronous::put_texture(int id, int w, int h, int components)
 {
-       if(id >= 0)
-       {
+       if( id >= 0 ) {
                table_lock->lock("BC_Resources::put_texture");
 // Search for duplicate
-               for(int i = 0; i < texture_ids.total; i++)
-               {
+               for( int i = 0; i < texture_ids.total; i++ ) {
                        TextureID *ptr = texture_ids.values[i];
-                       if(ptr->window_id == current_window->get_id() &&
-                               ptr->id == id)
-                       {
+                       if( ptr->window_id == current_window->get_id() && ptr->id == id ) {
                                printf("BC_Synchronous::push_texture: texture exists\n"
                                        "exists: window=%d id=%d w=%d h=%d\n"
                                        "new:    window=%d id=%d w=%d h=%d\n",
-                                       ptr->window_id,
-                                       ptr->id,
-                                       ptr->w,
-                                       ptr->h,
-                                       current_window->get_id(),
-                                       id,
-                                       w,
-                                       h);
+                                       ptr->window_id, ptr->id, ptr->w, ptr->h,
+                                       current_window->get_id(), id, w, h);
                                table_lock->unlock();
                                return;
                        }
                }
 
                TextureID *new_id = new TextureID(current_window->get_id(),
-                       id,
-                       w,
-                       h,
-                       components);
+                       id, w, h, components);
                texture_ids.append(new_id);
                table_lock->unlock();
        }
@@ -317,30 +309,31 @@ void BC_Synchronous::release_texture(int window_id, int id)
 
 
 
-unsigned int BC_Synchronous::get_shader(char *source, int *got_it)
+int BC_Synchronous::get_shader(unsigned int *handle,
+               const char *vert, const char *frag)
 {
+       unsigned int shader = 0, ret = 0;
+       if( !vert ) vert = "";
+       if( !frag ) frag = "";
        table_lock->lock("BC_Resources::get_shader");
-       for(int i = 0; i < shader_ids.total; i++)
-       {
-               if(shader_ids.values[i]->window_id == current_window->get_id() &&
-                       !strcmp(shader_ids.values[i]->source, source))
-               {
-                       unsigned int result = shader_ids.values[i]->handle;
-                       table_lock->unlock();
-                       *got_it = 1;
-                       return result;
+       for( int i=0; !ret && i<shader_ids.size(); ++i ) {
+               ShaderID &sp = *shader_ids[i];
+               if( sp.window_id == current_window->get_id() &&
+                   !strcmp(sp.vert, vert) && !strcmp(sp.frag, frag) ) {
+                       shader = shader_ids.values[i]->handle;
+                       ret = 1;
                }
        }
        table_lock->unlock();
-       *got_it = 0;
-       return 0;
+       *handle = shader;
+       return ret;
 }
 
 void BC_Synchronous::put_shader(unsigned int handle,
-       char *source)
+               const char *vert, const char *frag)
 {
        table_lock->lock("BC_Resources::put_shader");
-       shader_ids.append(new ShaderID(current_window->get_id(), handle, source));
+       shader_ids.append(new ShaderID(current_window->get_id(), handle, vert, frag));
        table_lock->unlock();
 }
 
@@ -348,18 +341,18 @@ void BC_Synchronous::dump_shader(unsigned int handle)
 {
        int got_it = 0;
        table_lock->lock("BC_Resources::dump_shader");
-       for(int i = 0; i < shader_ids.total; i++)
-       {
-               if(shader_ids.values[i]->handle == handle)
-               {
+       for( int i=0; i<shader_ids.size(); ++i ) {
+               if( shader_ids.values[i]->handle == handle ) {
                        printf("BC_Synchronous::dump_shader\n"
-                               "%s", shader_ids.values[i]->source);
+                               "vert: %s\nfrag: %s\n",
+                                shader_ids[i]->vert, shader_ids[i]->frag);
                        got_it = 1;
                        break;
                }
        }
        table_lock->unlock();
-       if(!got_it) printf("BC_Synchronous::dump_shader couldn't find %d\n", handle);
+       if( !got_it )
+               printf("BC_Synchronous::dump_shader couldn't find %d\n", handle);
 }
 
 void BC_Synchronous::delete_window(BC_WindowBase *window)
index 26d6e7f..db81a20 100644 (file)
@@ -72,11 +72,12 @@ public:
 class ShaderID
 {
 public:
-       ShaderID(int window_id, unsigned int handle, char *source);
+       ShaderID(int window_id, unsigned int handle,
+                const char *vert, const char *frag);
        ~ShaderID();
 
 // Should really use an MD5 to compare sources but this is easiest.
-       char *source;
+       char *vert, *frag;
        int window_id;
        unsigned int handle;
 };
@@ -182,13 +183,9 @@ public:
 // Can be called outside synchronous loop.
        void release_texture(int window_id, int id);
 
-// Get the shader by window_id and source comparison if it exists.
-// Not run in OpenGL thread because it has its own lock.
-// Sets *got_it to 1 on success.
-       unsigned int get_shader(char *source, int *got_it);
-// Add a new shader program by title if it doesn't exist.
-// Doesn't check if it already exists.
-       void put_shader(unsigned int handle, char *source);
+// Get the shader by window_id and vertex/fragment source comparison
+       int get_shader(unsigned int *handle, const char *vert, const char *frag);
+       void put_shader(unsigned int handle, const char *vert, const char *frag);
        void dump_shader(unsigned int handle);
 
 
index 7cebeef..1d0f7e8 100644 (file)
@@ -100,8 +100,8 @@ void BC_Texture::create_texture(int w, int h, int colormodel)
                BC_WindowBase::get_synchronous()->release_texture(
                        window_id,
                        texture_id);
-           texture_id = -1;
-           window_id = -1;
+               texture_id = -1;
+               window_id = -1;
        }
 
 
@@ -124,13 +124,9 @@ void BC_Texture::create_texture(int w, int h, int colormodel)
                glGenTextures(1, (GLuint*)&texture_id);
                glBindTexture(GL_TEXTURE_2D, (GLuint)texture_id);
                glEnable(GL_TEXTURE_2D);
-               if(texture_components == 4)
-                       glTexImage2D(GL_TEXTURE_2D, 0, 4, texture_w, texture_h,
+               int internal_format = texture_components == 4 ? GL_RGBA8 : GL_RGB8 ;
+               glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture_w, texture_h,
                                0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
-               else
-                       glTexImage2D(GL_TEXTURE_2D, 0, 3, texture_w, texture_h,
-                               0, GL_RGB, GL_UNSIGNED_BYTE, 0);
-
                window_id = BC_WindowBase::get_synchronous()->current_window->get_id();
                BC_WindowBase::get_synchronous()->put_texture(texture_id,
                        texture_w, texture_h, texture_components);
@@ -241,12 +237,12 @@ static void write_ppm(uint8_t *tp, int w, int h, const char *fmt, ...)
 }
 #endif
 
-void BC_Texture::write_tex(const char *fn)
+void BC_Texture::write_tex(const char *fn, int id)
 {
 #ifdef HAVE_GL
        int prev_id = -1;
        glGetIntegerv(GL_ACTIVE_TEXTURE, &prev_id);
-       glActiveTexture(GL_TEXTURE31);
+       glActiveTexture(GL_TEXTURE0+id);
        glBindTexture(GL_TEXTURE_2D, texture_id);
        glEnable(GL_TEXTURE_2D);
        int w = get_texture_w(), h = get_texture_h();
@@ -258,4 +254,10 @@ void BC_Texture::write_tex(const char *fn)
 #endif
 }
 
+void BC_Texture::write_tex(const char *fn)
+{
+#ifdef HAVE_GL
+       write_tex(fn, 31);
+#endif
+}
 
index 82fcf83..1ac9f08 100644 (file)
@@ -66,6 +66,7 @@ public:
                float in_x1, float in_y1, float in_x2, float in_y2,
                float out_x1, float out_y1, float out_x2, float out_y2);
 
+       void write_tex(const char *fn, int id);
        void write_tex(const char *fn);
 private:
        void clear_objects();
index 57118e7..902f65e 100644 (file)
@@ -237,14 +237,14 @@ void BC_WindowBase::flip_opengl()
 #endif
 }
 
-unsigned int BC_WindowBase::get_shader(char *source, int *got_it)
+int BC_WindowBase::get_shader(unsigned int *handle, const char *vert, const char *frag)
 {
-       return get_resources()->get_synchronous()->get_shader(source, got_it);
+       return get_resources()->get_synchronous()->get_shader(handle, vert, frag);
 }
 
-void BC_WindowBase::put_shader(unsigned int handle, char *source)
+void BC_WindowBase::put_shader(unsigned int handle, const char *vert, const char *frag)
 {
-       get_resources()->get_synchronous()->put_shader(handle, source);
+       get_resources()->get_synchronous()->put_shader(handle, vert, frag);
 }
 
 int BC_WindowBase::get_opengl_server_version()
index 5d177c9..21d6bd3 100644 (file)
@@ -149,7 +149,11 @@ BC_WindowBase::~BC_WindowBase()
 //printf("delete glx=%08x, win=%08x %s\n", (unsigned)glx_win, (unsigned)win, title);
 #ifdef HAVE_GL
        if( get_resources()->get_synchronous() && glx_win != 0 ) {
+               if( window_type == MAIN_WINDOW )
+                       unlock_window();
                get_resources()->get_synchronous()->delete_window(this);
+               if( window_type == MAIN_WINDOW )
+                       lock_window("BC_WindowBase::delete_window");
        }
 #endif
        XDestroyWindow(top_level->display, win);
index cd4dcd0..f231c6d 100644 (file)
@@ -237,10 +237,9 @@ public:
        void disable_opengl();
        void flip_opengl();
 
-// Calls the BC_Synchronous version of the function with the window_id.
-// Not run in OpenGL thread because it has its own lock.
-       unsigned int get_shader(char *title, int *got_it);
-       void put_shader(unsigned int handle, char *title);
+// Calls the BC_Synchronous version of the function
+       int get_shader(unsigned int *handle, const char *vert, const char *frag);
+       void put_shader(unsigned int handle, const char *vert, const char *frag);
        int get_opengl_server_version();
 
        int flash(int x, int y, int w, int h, int flush = 1);
index 649a3b1..30c1b2b 100644 (file)
@@ -307,103 +307,136 @@ void VFrame::init_screen()
        init_screen(get_w(), get_h());
 }
 
-static int print_error(char *source, unsigned int object, int is_program)
-{
+
+
 #ifdef HAVE_GL
-    char string[BCTEXTLEN];
+
+static int print_error(const char *text, unsigned int object, int is_program)
+{
+       char info[BCTEXTLEN];
        int len = 0;
-    if(is_program)
-               glGetProgramInfoLog(object, BCTEXTLEN, &len, string);
+       if( is_program )
+               glGetProgramInfoLog(object, BCTEXTLEN, &len, info);
        else
-               glGetShaderInfoLog(object, BCTEXTLEN, &len, string);
-       if(len > 0) printf("Playback3D::print_error:\n%s\n%s\n", source, string);
-       if(len > 0) return 1;
-#endif
-       return 0;
+               glGetShaderInfoLog(object, BCTEXTLEN, &len, info);
+       if( len > 0 ) printf("Playback3D::print_error:\n%s\n%s\n", text, info);
+       return !len ? 0 : 1;
 }
 
-
-
-// call as:
-//    make_shader(0, frag1, .., fragn, 0);
-// or make_shader(fragments);
-
-unsigned int VFrame::make_shader(const char **fragments, ...)
+static char *shader_segs(const char **segs, int n)
 {
-       unsigned int result = 0;
-#ifdef HAVE_GL
-// Construct single source file out of arguments
-       char *program = 0;
-       int nb_mains = 0;
-
-       int nb_frags = 1;
-       if( !fragments ) {
-               va_list list;  va_start(list, fragments);
-               while( va_arg(list, char*) != 0 ) ++nb_frags;
-               va_end(list);
-       }
-       const char *frags[nb_frags], *text = 0;
-       if( !fragments ) {
-               va_list list;  va_start(list, fragments);
-               for( int i=0; i<nb_frags; ++i ) frags[i] = va_arg(list, char*);
-               va_end(list);
-               fragments = frags;
-       }
-
-       while( (text = *fragments++) ) {
-               char src[strlen(text) + BCSTRLEN + 1];
+       if( !segs || !n ) return 0;
+// concat source segs
+       int ids = 0;
+       char *ret = 0;
+       for( int i=0; i<n; ++i ) {
+               const char *text = *segs++;
+               char src[strlen(text) + BCSTRLEN + 1], *sp = src;
                const char *tp = strstr(text, "main()");
                if( tp ) {
 // Replace main() with a mainxxx()
-                       char mainxxx[BCSTRLEN], *sp = src;
-                       sprintf(mainxxx, "main%03d()", nb_mains++);
                        int n = tp - text;
                        memcpy(sp, text, n);  sp += n;
-                       n = strlen(mainxxx);
-                       memcpy(sp, mainxxx, n);  sp += n;
-                       tp += strlen("main()");
-                       strcpy(sp, tp);
+                       sp += sprintf(sp, "main%03d()", ids++);
+                       strcpy(sp, tp+strlen("main()"));
                        text = src;
                }
-
-               char *new_program = !program ? cstrdup(text) :
-                       cstrcat(2, program, text);
-               delete [] program;  program = new_program;
+               char *cp = !ret ? cstrdup(text) : cstrcat(2, ret, text);
+               delete [] ret;  ret = cp;
        }
 
-// Add main() which calls mainxxx() in order
-       char main_program[BCTEXTLEN], *cp = main_program;
+// add main() which calls mainxxx() in order
+       char main_prog[BCTEXTLEN];
+       char *cp = main_prog;
        cp += sprintf(cp, "\nvoid main() {\n");
-       for( int i=0; i < nb_mains; ++i )
+       for( int i=0; i < ids; ++i )
                cp += sprintf(cp, "\tmain%03d();\n", i);
        cp += sprintf(cp, "}\n");
-       cp = !program ? cstrdup(main_program) :
-               cstrcat(2, program, main_program);
-       delete [] program;  program = cp;
-
-       int got_it = 0;
-       result = BC_WindowBase::get_synchronous()->get_shader(program, &got_it);
-       if( !got_it ) {
-               result = glCreateProgram();
-               unsigned int shader = glCreateShader(GL_FRAGMENT_SHADER);
-               const GLchar *text_ptr = program;
-               glShaderSource(shader, 1, &text_ptr, NULL);
-               glCompileShader(shader);
-               int error = print_error(program, shader, 0);
-               glAttachShader(result, shader);
-               glDeleteShader(shader);
-               glLinkProgram(result);
-               if( !error )
-                       error = print_error(program, result, 1);
-//printf("BC_WindowBase::make_shader: shader=%d window_id=%d\n", result,
-// BC_WindowBase::get_synchronous()->current_window->get_id());
-               BC_WindowBase::get_synchronous()->put_shader(result, program);
+       if( ret ) {
+               cp = cstrcat(2, ret, main_prog);
+               delete [] ret;  ret = cp;
+       }
+       else
+               ret = cstrdup(main_prog);
+       return ret;
+}
+
+static int compile_shader(unsigned int &shader, int type, const GLchar *text)
+{
+       shader = glCreateShader(type);
+       glShaderSource(shader, 1, &text, 0);
+       glCompileShader(shader);
+       return print_error(text, shader, 0);
+}
+
+static unsigned int build_shader(const char *vert, const char *frag)
+{
+       int error = 0;
+       unsigned int vertex_shader = 0;
+       unsigned int fragment_shader = 0;
+       unsigned int program = glCreateProgram();
+       if( !error && vert )
+               error = compile_shader(vertex_shader, GL_VERTEX_SHADER, vert);
+       if( !error && frag )
+               error = compile_shader(fragment_shader, GL_FRAGMENT_SHADER, frag);
+       if( !error && vert ) glAttachShader(program, vertex_shader);
+       if( !error && frag ) glAttachShader(program, fragment_shader);
+       if( !error ) glLinkProgram(program);
+       if( !error ) error = print_error("link", program, 1);
+       if( !error )
+               BC_WindowBase::get_synchronous()->put_shader(program, vert, frag);
+       else {
+               glDeleteProgram(program);
+               program = 0;
+       }
+       return program;
+}
+
+#endif
+
+// call as:
+//    make_shader(0, seg1, .., segn, 0);
+// or make_shader(&seg);
+// line 1: optional comment // vertex shader
+
+unsigned int VFrame::make_shader(const char **segments, ...)
+{
+       unsigned int program = 0;
+#ifdef HAVE_GL
+// Construct single source file out of arguments
+       int nb_segs = 1;
+       if( !segments ) {
+               va_list list;  va_start(list, segments);
+               while( va_arg(list, char*) != 0 ) ++nb_segs;
+               va_end(list);
+       }
+       const char *segs[nb_segs];
+       if( !segments ) {
+               va_list list;  va_start(list, segments);
+               for( int i=0; i<nb_segs; ++i )
+                       segs[i] = va_arg(list, const char *);
+               va_end(list);
+               segments = segs;
+       }
+
+       const char *vert_shaders[nb_segs];  int vert_segs = 0;
+       const char *frag_shaders[nb_segs];  int frag_segs = 0;
+       for( int i=0; segments[i]!=0; ++i ) {
+               const char *seg = segments[i];
+               if( strstr(seg, "// vertex shader") )
+                       vert_shaders[vert_segs++] = seg;
+               else
+                       frag_shaders[frag_segs++] = seg;
        }
 
-//printf("VFrame::make_shader\n%s\n", program);
-       delete [] program;
+       char *vert = shader_segs(vert_shaders, vert_segs);
+       char *frag = shader_segs(frag_shaders, frag_segs);
+       if( !BC_WindowBase::get_synchronous()->get_shader(&program, vert, frag) )
+               program = build_shader(vert, frag);
+       delete [] vert;
+       delete [] frag;
 #endif
-       return result;
+       return program;
 }
 
 void VFrame::dump_shader(int shader_id)