upgrade to ffmpeg 4.2, rework mask for speedup
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / playback3d.C
index 1c7ddc75701e8fbcbebc5779aebe4e32e3bc6028..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"
 
@@ -270,29 +271,50 @@ static const char *feather_frag =
        "#version 430\n"
        "layout(location=0) out vec4 color;\n"
        "uniform sampler2D tex;\n"
-// apparently, only doubles index properly in shared buffers
-       "buffer buf { dvec2 points[]; };\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 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"
+       "       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 =
@@ -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
@@ -746,11 +768,11 @@ 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
 }
@@ -774,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
@@ -1164,6 +1193,7 @@ public:
        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;
 };
 
@@ -1196,6 +1226,7 @@ void fb_texture::bind(int 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);
@@ -1203,8 +1234,9 @@ void fb_texture::read_screen(int x, int y, int w, int 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;
+       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 ) {
@@ -1212,12 +1244,28 @@ void fb_texture::set_output_texture()
                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)
 {
-       ArrayList<double *> *invented = (ArrayList<double *> *)data;
+       zglTessData *invented = (zglTessData *)data;
        GLdouble *vertex = new double[6];
        invented->append(vertex);
        vertex[0] = coords[0];
@@ -1237,7 +1285,7 @@ static void combineData(GLdouble coords[3],
 // dbug
 static void zglBegin(GLenum mode) { glBegin(mode); }
 static void zglEnd() { glEnd(); }
-static void zglVertex3dv(const GLdouble *v) { glVertex3dv(v); }
+static void zglVertex(const GLdouble *v) { glVertex3dv(v); }
 
 #endif
 
@@ -1270,188 +1318,143 @@ void Playback3D::do_mask_sync(Playback3DCommand *command)
                int w = command->frame->get_w();
                int h = command->frame->get_h();
                MaskEdges edges;
-               float faders[SUBMASKS], feathers[SUBMASKS], bg = 1;
-               MaskPointSet point_set[SUBMASKS];
+               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);
+               int show_mask = command->keyframe_set->track->masks;
 
                for(int k = 0; k < total_submasks; k++) {
-                       MaskPointSet &points = point_set[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;
-                       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.
-                       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, 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, 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);
-                               }
+                       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;
+               }
 
-                               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));
+               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();
 
-                                       if(j > 0 || first_point) {
-                                               edge.append(x, y - h);
-                                               first_point = 0;
-                                       }
-                               }
-                       }
-                       if( edge.size() > 0 ) {
-// draw polygon
-                               float fader = faders[k];
-                               float v = fader < 0 ? 1 : 1-fader;
-                               glColor4f(v, v, v, v);
+               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);
-                               glNewList(display_list, GL_COMPILE);
 #if 0
-                               glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+                               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();
-                               gluTessCallback(tess, GLU_TESS_VERTEX,(GLvoid (*)()) &zglVertex3dv);
+                               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);
-                               ArrayList<double *> invented;
-                               invented.set_array_delete();
-
-                               gluTessBeginPolygon(tess, &invented);
+                               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);
-                               gluDeleteTess(tess);
-                               invented.remove_all_objects();
-#endif
+                               gluTessEndPolygon(tess);
                                glEndList();
                                glCallList(display_list);
+                               gluDeleteTess(tess); }
+#endif
                                glDeleteLists(1, display_list);
-                       }
-               }
-
-// 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);
+                               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;
                                }
-                               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);
+
+                               glUseProgram(max_shader);
                                in->bind(0);
-                               glUniform1i(glGetUniformLocation(frag_shader, "tex"), 0);
-                               out->set_output_texture();
+//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 = in;  in = out;  out = t;
+                               fb_texture *t = mask;  mask = out;  out = t;
+//mask->write_tex("/tmp/mask2.ppm");
                        }
-                       glDeleteBuffers(1, points);
+                       delete in;
+                       delete out;
                }
 
-               glDrawBuffers(0, 0);
-               glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
                const char *alpha_shader = BC_CModels::has_alpha(color_model) ?
                                multiply_mask4_frag :
                        !BC_CModels::is_yuv(color_model) ?
@@ -1461,19 +1464,17 @@ void Playback3D::do_mask_sync(Playback3DCommand *command)
                glUseProgram(shader);
                if( shader > 0 ) {
                        command->frame->bind_texture(0);
-                       in->BC_Texture::bind(1);
+                       mask->BC_Texture::bind(1);
                        glUniform1i(glGetUniformLocation(shader, "tex"), 0);
                        glUniform1i(glGetUniformLocation(shader, "tex1"), 1);
                }
                command->frame->draw_texture();
                command->frame->set_opengl_state(VFrame::SCREEN);
                glUseProgram(0);
-               delete in;
-               delete out;
-// Default drawable
-               glDisable(GL_TEXTURE_2D);
+               delete mask;
                glColor4f(1, 1, 1, 1);
                glActiveTexture(GL_TEXTURE0);
+               glDisable(GL_TEXTURE_2D);
                window->enable_opengl();
        }
        command->canvas->unlock_canvas();