upgrade to ffmpeg 4.2, rework mask for speedup
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / playback3d.C
index 8d58deb3787880a2bd6dc3d1206163c8763b6337..503e57185b1c01d14462e25a02ea50f93c7933f4 100644 (file)
@@ -38,6 +38,7 @@
 #include "pluginclient.h"
 #include "pluginvclient.h"
 #include "edlsession.h"
+#include "track.h"
 #include "transportque.inc"
 #include "vframe.h"
 
@@ -259,36 +260,89 @@ static const char *read_texture_frag =
        "       gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
        "}\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 *feather_frag =
+       "#version 430\n"
+       "layout(location=0) out vec4 color;\n"
+       "uniform sampler2D tex;\n"
+       "const int MAX = 1024;\n"
+       "uniform float psf[MAX];\n"
+       "uniform int n;\n"
+       "uniform vec2 dxy;\n"
+       "uniform vec2 twh;\n"
+       "\n"
+       "void main() {\n"
+       "       vec2 tc = gl_FragCoord.xy/textureSize(tex,0);\n"
+       "       color = texture(tex, tc);\n"
+       "       float c = color.r, f = c*psf[0];\n"
+       "       for( int i=1; i<n; ++i ) {\n"
+       "               vec2 dd = float(i)*dxy;\n"
+       "               vec2 a = tc+dd, ac = min(max(vec2(0.),a), twh);\n"
+       "               vec2 b = tc-dd, bc = min(max(vec2(0.),b), twh);\n"
+       "               float fa = texture2D(tex, ac).r * psf[i];\n"
+       "               float fb = texture2D(tex, bc).r * psf[i];\n"
+       "               float m = max(fa, fb);\n"
+       "               if( f < m ) f = m;\n"
+       "       }\n"
+       "       if( c < f ) color = vec4(f);\n"
+       "}\n";
+
+static const char *max_frag =
+       "#version 430\n"
+       "layout(location=0) out vec4 color;\n"
+       "uniform sampler2D tex;\n"
+       "uniform sampler2D tex1;\n"
+       "uniform float r;\n"
+       "uniform float v;\n"
+       "\n"
+       "void main() {\n"
+       "       vec2 tc = gl_FragCoord.xy/textureSize(tex,0);\n"
+       "       color = texture2D(tex1, tc);\n"
+       "       float c = texture2D(tex, tc).r;\n"
+       "       float b = r<0 ? 1. : 0.;\n"
+       "       if( c == b ) return;\n"
+       "       float iv = v>=0. ? 1. : -1.;\n"
+       "       float rr = r!=0. ? r : 1.;\n"
+       "       float rv = rr*v>=0. ? 1. : -1.;\n"
+       "       float vv = v>=0. ? 1.-v : 1.+v;\n"
+       "       float fg = rv>0. ? vv : 1.;\n"
+       "       float bg = rv>0. ? 1. : vv;\n"
+       "       float a = c*fg + (1.-c)*bg;\n"
+       "       if( iv*(color.a-a) > 0. ) color = vec4(a);\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"
+       "       gl_FragColor.a *= texture2D(tex1, gl_TexCoord[0].st).r;\n"
        "}\n";
 
 static const char *multiply_mask3_frag =
        "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"
+       "       float a = texture2D(tex1, gl_TexCoord[0].st).r;\n"
        "       gl_FragColor.rgb *= vec3(a, a, a);\n"
        "}\n";
 
 static const char *multiply_yuvmask3_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"
-       "       float a = texture2D(tex1, gl_TexCoord[0].st / vec2(scale, scale)).r;\n"
+       "       float a = texture2D(tex1, gl_TexCoord[0].st).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"
@@ -357,26 +411,11 @@ 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);
-//}
-
-
 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);
 }
 
 Playback3D::~Playback3D()
@@ -402,10 +441,6 @@ void Playback3D::handle_command(BC_SynchronousCommand *command)
                        write_buffer_sync((Playback3DCommand*)command);
                        break;
 
-               case Playback3DCommand::FINISH_OUTPUT:
-                       finish_output_sync((Playback3DCommand*)command);
-                       break;
-
                case Playback3DCommand::CLEAR_OUTPUT:
                        clear_output_sync((Playback3DCommand*)command);
                        break;
@@ -469,10 +504,11 @@ 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();
+               glFinish();
                int w = command->input->get_w();
                int h = command->input->get_h();
 
@@ -641,35 +677,21 @@ void Playback3D::write_buffer_sync(Playback3DCommand *command)
        BC_WindowBase *window =
                command->canvas->lock_canvas("Playback3D::write_buffer_sync");
        if( window ) {
+               window->enable_opengl();
 // Update hidden cursor
                window->update_video_cursor();
-// Make sure OpenGL is enabled first.
-               window->enable_opengl();
-
+               command->frame->enable_opengl();
+               command->frame->init_screen();
 //printf("Playback3D::write_buffer_sync 1 %d\n", window->get_id());
-               int flip_y = 0, frame_state = command->frame->get_opengl_state();
-               switch( frame_state ) {
-// Upload texture and composite to screen
-                       case VFrame::RAM:
-                               flip_y = 1;
-                       case VFrame::SCREEN:
-                               command->frame->to_texture();
-                               window->enable_opengl();
-// Composite texture to screen and swap buffer
-                       case VFrame::TEXTURE:
-                               if( !flip_y ) {
-                                       int fh1 = command->frame->get_h()-1;
-                                       float in_y1 = fh1 - command->in_y1;
-                                       float in_y2 = fh1 - command->in_y2;
-                                       command->in_y1 = in_y2;
-                                       command->in_y2 = in_y1;
-                               }
-                               draw_output(command, flip_y);
-                               break;
-                       default:
-                               printf("Playback3D::write_buffer_sync unknown state\n");
-                               break;
+               int frame_state = command->frame->get_opengl_state();
+               if( frame_state != VFrame::TEXTURE )
+                       command->frame->to_texture();
+               if( frame_state != VFrame::RAM ) {
+                       command->in_y1 = command->frame->get_h() - command->in_y1;
+                       command->in_y2 = command->frame->get_h() - command->in_y2;
                }
+               window->enable_opengl();
+               draw_output(command, 1);
                command->frame->set_opengl_state(frame_state);
        }
        command->canvas->unlock_canvas();
@@ -704,7 +726,7 @@ void Playback3D::draw_output(Playback3DCommand *command, int flip_y)
                if(!command->is_cleared)
                {
 // If we get here, the virtual console was not used.
-                       init_frame(command, 0);
+                       color_frame(command, 0,0,0,0);
                }
 
 // Texture
@@ -734,14 +756,8 @@ void Playback3D::draw_output(Playback3DCommand *command, int flip_y)
 
 
 //printf("Playback3D::draw_output 2 %f,%f %f,%f -> %f,%f %f,%f\n",
-// command->in_x1,
-// command->in_y1,
-// command->in_x2,
-// command->in_y2,
-// command->out_x1,
-// command->out_y1,
-// command->out_x2,
-// command->out_y2);
+// command->in_x1, command->in_y1, command->in_x2, command->in_y2,
+// command->out_x1, command->out_y1, command->out_x2, command->out_y2);
 
                glUseProgram(0);
 
@@ -752,38 +768,16 @@ void Playback3D::draw_output(Playback3DCommand *command, int flip_y)
 }
 
 
-void Playback3D::init_frame(Playback3DCommand *command, int is_yuv)
+void Playback3D::color_frame(Playback3DCommand *command,
+               float r, float g, float b, float a)
 {
 #ifdef HAVE_GL
-       float gbuv = is_yuv ? 0.5 : 0.0;
-       glClearColor(0.0, gbuv, gbuv, 0.0);
+       glClearColor(r, g, b, a);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 #endif
 }
 
 
-void Playback3D::finish_output(Canvas *canvas)
-{
-       Playback3DCommand command;
-       command.canvas = canvas;
-       command.command = Playback3DCommand::FINISH_OUTPUT;
-       send_command(&command);
-}
-
-void Playback3D::finish_output_sync(Playback3DCommand *command)
-{
-#ifdef HAVE_GL
-       BC_WindowBase *window =
-               command->canvas->lock_canvas("Playback3D::finish_output_sync");
-       if( window ) {
-               command->canvas->get_canvas()->enable_opengl();
-               glFinish();
-       }
-       command->canvas->unlock_canvas();
-#endif
-}
-
-
 void Playback3D::clear_output(Canvas *canvas, VFrame *output)
 {
        Playback3DCommand command;
@@ -802,14 +796,21 @@ void Playback3D::clear_output_sync(Playback3DCommand *command)
 // If we get here, the virtual console is being used.
                command->canvas->get_canvas()->enable_opengl();
                int is_yuv = 0;
+               int color = BLACK, alpha = 0;
 // Using pbuffer for refresh frame.
                if( command->frame ) {
                        command->frame->enable_opengl();
+                       color = command->frame->get_clear_color();
+                       alpha = command->frame->get_clear_alpha();
                        int color_model = command->canvas->mwindow->edl->session->color_model;
                        is_yuv = BC_CModels::is_yuv(color_model);
                }
-
-               init_frame(command, is_yuv);
+               int a = alpha;
+               int r = (color>>16) & 0xff;
+               int g = (color>>8) & 0xff;
+               int b = (color>>0) & 0xff;
+               if( is_yuv ) YUV::yuv.rgb_to_yuv_8(r, g, b);
+               color_frame(command, r/255.f, g/255.f, b/255.f, a/255.f);
        }
        command->canvas->unlock_canvas();
 #endif
@@ -1149,33 +1150,144 @@ 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();
+       void unset_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)
+{
+       bind(1);
+       glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       glReadBuffer(GL_BACK);
+       glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, x,y, w,h);
+}
+
+void fb_texture::set_output_texture()
+{
+       GLenum at = GL_COLOR_ATTACHMENT0;
+       glFramebufferTexture(GL_FRAMEBUFFER, at, get_texture_id(), 0);
+       GLenum dbo[1] = { at, }; // 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;
+       }
+}
+void fb_texture::unset_output_texture()
+{
+       glDrawBuffers(0, 0);
+       int at = GL_COLOR_ATTACHMENT0;
+       glFramebufferTexture(GL_FRAMEBUFFER, at, 0, 0);
+       glBindFramebuffer(GL_FRAMEBUFFER, 0);
+       glDisable(GL_TEXTURE_2D);
+}
+
+
+class zglTessData : public ArrayList<double *>
+{
+public:
+       zglTessData() { set_array_delete(); }
+       ~zglTessData() { remove_all_objects(); }
+};
+
+static void combineData(GLdouble coords[3],
+               GLdouble *vertex_data[4], GLfloat weight[4],
+               GLdouble **outData, void *data)
+{
+       zglTessData *invented = (zglTessData *)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 ) {
+               GLdouble v = 0;
+               for( int k=0; k<4; ++k ) {
+                       if( !weight[k] || !vertex_data[k] ) continue;
+                       v += weight[k] * vertex_data[k][i];
+               }
+               vertex[i] = v;
+       }
+       *outData = vertex;
+}
+
+// dbug
+static void zglBegin(GLenum mode) { glBegin(mode); }
+static void zglEnd() { glEnd(); }
+static void zglVertex(const GLdouble *v) { glVertex3dv(v); }
+
+#endif
 
 void Playback3D::do_mask_sync(Playback3DCommand *command)
 {
@@ -1187,7 +1299,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,248 +1310,171 @@ 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);
-               if( command->default_auto->mode == MASK_MULTIPLY_ALPHA ) {
-                       glClearColor(0.0, 0.0, 0.0, 0.0);
-                       glColor4f((float)command->keyframe->value / 100,
-                               (float)command->keyframe->value / 100,
-                               (float)command->keyframe->value / 100,
-                               1.0);
-               }
-               else {
-                       glClearColor(1.0, 1.0, 1.0, 1.0);
-                       glColor4f((float)1.0 - (float)command->keyframe->value / 100,
-                               (float)1.0 - (float)command->keyframe->value / 100,
-                               (float)1.0 - (float)command->keyframe->value / 100,
-                               1.0);
-               }
-               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], cc = 1;
+               MaskPoints 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);
-
-                       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];
-
-                               float x, y;
-                               int segments = 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 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;
-
-                               // 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
-
-                               float cx3, cx2, cx1, cx0, cy3, cy2, cy1, cy0;
-
-                               // [-1 +3 -3 +1]
-                               // [+3 -6 +3  0]
-                               // [-3 +3  0  0]
-                               // [+1  0  0  0]
-
-                               cx3 = -  x0 + 3*x1 - 3*x2 + x3;
-                               cx2 =  3*x0 - 6*x1 + 3*x2;
-                               cx1 = -3*x0 + 3*x1;
-                               cx0 =    x0;
-
-                               cy3 = -  y0 + 3*y1 - 3*y2 + y3;
-                               cy2 =  3*y0 - 6*y1 + 3*y2;
-                               cy1 = -3*y0 + 3*y1;
-                               cy0 =    y0;
-
-                               // This equation is from Graphics Gems I.
-                               //
-                               // The idea is that since we're approximating a cubic curve with lines,
-                               // any error we incur is due to the curvature of the line, which we can
-                               // estimate by calculating the maximum acceleration of the curve.  For
-                               // a cubic, the acceleration (second derivative) is a line, meaning that
-                               // 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)
-                               {
-                                       float maxaccel1 = fabs(2*cy2) + fabs(6*cy3);
-                                       float maxaccel2 = fabs(2*cx2) + fabs(6*cx3);
-
-                                       float maxaccel = maxaccel1 > maxaccel2 ? maxaccel1 : maxaccel2;
-                                       float h = 1.0;
-
-                                       if(maxaccel > 8.0) h = sqrt((8.0) / maxaccel);
-                                       segments = int(1/h);
-                               }
-
-                               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);
-                                               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();
+                       command->start_position_project, PLAY_FORWARD);
+               int show_mask = command->keyframe_set->track->masks;
+
+               for(int k = 0; k < total_submasks; k++) {
+                       MaskPoints &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;
+                       float feather = command->keyframe_set->get_feather(
+                               command->start_position_project, k, PLAY_FORWARD);
+                       feathers[k] = feather;
+                       MaskEdge &edge = *edges.append(new MaskEdge());
+                       if( !v || !((show_mask>>k) & 1) || !points.size() ) continue;
+                       edge.load(point_set[k], h);
+                       if( v >= 0 ) continue;
+                       float vv = 1 + v;
+                       if( cc > vv ) cc = vv;
                }
-               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);
 
+               fb_texture *mask = new fb_texture(w, h, color_model);
+               mask->set_output_texture();
+               glClearColor(cc, cc, cc, cc);
+               glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+               mask->unset_output_texture();
+
+               unsigned int feather_shader =
+                       VFrame::make_shader(0, in_vertex_frag, feather_frag, 0);
+               unsigned int max_shader =
+                       VFrame::make_shader(0, in_vertex_frag, max_frag, 0);
+               if( feather_shader && max_shader ) {
+                       fb_texture *in = new fb_texture(w, h, color_model);
+                       fb_texture *out = new fb_texture(w, h, color_model);
+                       float tw = 1./out->get_texture_w(), th = 1./out->get_texture_h();
+                       float tw1 = (w-1)*tw, th1 = (h-1)*th;
+                       for(int k = 0; k < total_submasks; k++) {
+                               MaskEdge &edge = *edges[k];
+                               if( edge.size() < 3 ) continue;
+                               float r = feathers[k], v = faders[k];
+                               glBindFramebuffer(GL_FRAMEBUFFER, 0);
+                               glActiveTexture(GL_TEXTURE0);
+                               glDisable(GL_TEXTURE_2D);
+                               float b = r>=0 ? 0. : 1.;
+                               float f = r>=0 ? 1. : 0.;
+                               glClearColor(b, b, b, b);
+                               glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+                               glColor4f(f, f, f, f);
+                               glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+                               int display_list = glGenLists(1);
+#if 0
+                               glNewList(display_list, GL_COMPILE);
+                               glBegin(GL_POLYGON);
+                               MaskCoord *c = &edge[0];
+                               for( int i=edge.size(); --i>=0; ++c )
+                                       glVertex2f(c->x, c->y);
+                               glEnd();
+                               glEndList();
+                               glCallList(display_list);
+#else
+                               { zglTessData invented;
+                               GLUtesselator *tess = gluNewTess();
+                               gluTessProperty(tess, GLU_TESS_TOLERANCE, 0.5);
+                               gluTessCallback(tess, GLU_TESS_VERTEX,(GLvoid (*)()) &zglVertex);
+                               gluTessCallback(tess, GLU_TESS_BEGIN,(GLvoid (*)()) &zglBegin);
+                               gluTessCallback(tess, GLU_TESS_END,(GLvoid (*)()) &zglEnd);
+                               gluTessCallback(tess, GLU_TESS_COMBINE_DATA,(GLvoid (*)()) &combineData);
+                               glNewList(display_list, GL_COMPILE);
+                               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);
+                               glEndList();
+                               glCallList(display_list);
+                               gluDeleteTess(tess); }
+#endif
+                               glDeleteLists(1, display_list);
+                               in->read_screen(0,0, w,h);
+//in->write_tex("/tmp/in0.ppm");
+                               if( r ) {
+                                       double sig2 = -log(255.0)/(r*r);
+                                       int n = abs((int)r) + 1;
+                                       if( n > 1024 ) n = 1024; // MAX
+                                       float psf[n];  // point spot fn
+                                       for( int i=0; i<n; ++i )
+                                               psf[i] = exp(i*i * sig2);
+                                       glUseProgram(feather_shader);
+                                       glUniform1fv(glGetUniformLocation(feather_shader, "psf"), n, psf);
+                                       glUniform1i(glGetUniformLocation(feather_shader, "n"), n);
+                                       glUniform2f(glGetUniformLocation(feather_shader, "dxy"), tw, 0.);
+                                       glUniform2f(glGetUniformLocation(feather_shader, "twh"), tw1, th1);
+                                       glUniform1i(glGetUniformLocation(feather_shader, "tex"), 0);
+                                       in->bind(0);
+                                       out->set_output_texture();
+                                       out->draw_texture(0,0, w,h, 0,0, w,h);
+                                       out->unset_output_texture();
+//out->write_tex("/tmp/out1.ppm");
+                                       fb_texture *t = in;  in = out;  out = t;
+                                       glUniform2f(glGetUniformLocation(feather_shader, "dxy"), 0., th);
+                                       in->bind(0);
+                                       out->set_output_texture();
+                                       out->draw_texture(0,0, w,h, 0,0, w,h);
+                                       out->unset_output_texture();
+//out->write_tex("/tmp/out2.ppm");
+                                       glUseProgram(0);
+                                       t = in;  in = out;  out = t;
+                               }
 
-// 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;
+                               glUseProgram(max_shader);
+                               in->bind(0);
+//in->write_tex("/tmp/in1.ppm");
+//mask->write_tex("/tmp/mask1.ppm");
+                               mask->bind(1);
+                               glUniform1i(glGetUniformLocation(max_shader, "tex"), 0);
+                               glUniform1i(glGetUniformLocation(max_shader, "tex1"), 1);
+                               glUniform1f(glGetUniformLocation(max_shader, "r"), r);
+                               glUniform1f(glGetUniformLocation(max_shader, "v"), v);
+                               glViewport(0,0, w,h);
+                               out->set_output_texture();
+                               out->draw_texture(0,0, w,h, 0,0, w,h);
+                               out->unset_output_texture();
+                               glUseProgram(0);
+                               fb_texture *t = mask;  mask = out;  out = t;
+//mask->write_tex("/tmp/mask2.ppm");
+                       }
+                       delete in;
+                       delete out;
                }
 
-               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);
+               const char *alpha_shader = BC_CModels::has_alpha(color_model) ?
+                               multiply_mask4_frag :
+                       !BC_CModels::is_yuv(color_model) ?
+                               multiply_mask3_frag :
+                               multiply_yuvmask3_frag;
+               unsigned int shader = VFrame::make_shader(0, alpha_shader, 0);
+               glUseProgram(shader);
+               if( shader > 0 ) {
+                       command->frame->bind_texture(0);
+                       mask->BC_Texture::bind(1);
+                       glUniform1i(glGetUniformLocation(shader, "tex"), 0);
+                       glUniform1i(glGetUniformLocation(shader, "tex1"), 1);
                }
-
-
-
-// Write texture to PBuffer with multiply and scaling for feather.
-
-
-               command->frame->draw_texture(0, 0, w, h, 0, 0, w, h);
+               command->frame->draw_texture();
                command->frame->set_opengl_state(VFrame::SCREEN);
-
-
-// Disable temp texture
                glUseProgram(0);
-
-               glActiveTexture(GL_TEXTURE1);
-               glDisable(GL_TEXTURE_2D);
-               delete temp_texture;
-               temp_texture = 0;
-
+               delete mask;
+               glColor4f(1, 1, 1, 1);
                glActiveTexture(GL_TEXTURE0);
                glDisable(GL_TEXTURE_2D);
-
-// Default drawable
                window->enable_opengl();
        }
        command->canvas->unlock_canvas();
@@ -1447,14 +1482,6 @@ void Playback3D::do_mask_sync(Playback3DCommand *command)
 }
 
 
-
-
-
-
-
-
-
-
 void Playback3D::convert_cmodel(Canvas *canvas,
        VFrame *output,
        int dst_cmodel)
@@ -1488,7 +1515,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();
@@ -1678,7 +1705,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();