Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / maskauto.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * Copyright (C) 2003-2016 Cinelerra CV contributors
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include "clip.h"
24 #include "filexml.h"
25 #include "maskauto.h"
26 #include "maskautos.h"
27
28 #include <stdlib.h>
29 #include <string.h>
30
31
32 MaskPoint::MaskPoint()
33 {
34         x = 0;
35         y = 0;
36         control_x1 = 0;
37         control_y1 = 0;
38         control_x2 = 0;
39         control_y2 = 0;
40 }
41
42 void MaskPoint::copy_from(MaskPoint &ptr)
43 {
44         this->x = ptr.x;
45         this->y = ptr.y;
46         this->control_x1 = ptr.control_x1;
47         this->control_y1 = ptr.control_y1;
48         this->control_x2 = ptr.control_x2;
49         this->control_y2 = ptr.control_y2;
50 }
51
52 MaskPoint& MaskPoint::operator=(MaskPoint& ptr)
53 {
54         copy_from(ptr);
55         return *this;
56 }
57
58 int MaskPoint::operator==(MaskPoint& ptr)
59 {
60         return EQUIV(x, ptr.x) &&
61                 EQUIV(y, ptr.y) &&
62                 EQUIV(control_x1, ptr.control_x1) &&
63                 EQUIV(control_y1, ptr.control_y1) &&
64                 EQUIV(control_x2, ptr.control_x2) &&
65                 EQUIV(control_y2, ptr.control_y2);
66 }
67
68 SubMask::SubMask(MaskAuto *keyframe, int no)
69 {
70         this->keyframe = keyframe;
71         memset(name, 0, sizeof(name));
72         sprintf(name, "%d", no);
73         this->fader = 100;
74         this->feather = 0;
75 }
76
77 SubMask::~SubMask()
78 {
79         points.remove_all_objects();
80 }
81
82 int SubMask::equivalent(SubMask& ptr)
83 {
84         if( fader != ptr.fader ) return 0;
85         if( feather != ptr.feather ) return 0;
86         int n = points.size();
87         if( n != ptr.points.size() ) return 0;
88         for( int i=0; i<n; ++i ) {
89                 if(!(*points.get(i) == *ptr.points.get(i)))
90                         return 0;
91         }
92         return 1;
93 }
94
95 int SubMask::operator==(SubMask& ptr)
96 {
97         return equivalent(ptr);
98 }
99
100 void SubMask::copy_from(SubMask& ptr, int do_name)
101 {
102         if( do_name ) {
103                 memset(name, 0, sizeof(name));
104                 strncpy(name, ptr.name, sizeof(name)-1);
105         }
106         fader = ptr.fader;
107         feather = ptr.feather;
108         points.remove_all_objects();
109 //printf("SubMask::copy_from 1 %p %d\n", this, ptr.points.total);
110         for(int i = 0; i < ptr.points.total; i++)
111         {
112                 MaskPoint *point = new MaskPoint;
113                 *point = *ptr.points.values[i];
114                 points.append(point);
115         }
116 }
117
118 void SubMask::load(FileXML *file)
119 {
120         points.remove_all_objects();
121
122         int result = 0;
123         while( !(result = file->read_tag()) ) {
124                 if( file->tag.title_is("/MASK") ) break;
125                 if( file->tag.title_is("POINT") ) {
126                         XMLBuffer data;
127                         file->read_text_until("/POINT", &data);
128                         MaskPoint *point = new MaskPoint;
129                         char *cp = data.cstr();
130                         if( cp ) point->x = strtof(cp, &cp);
131                         if( cp && *cp==',' ) point->y = strtof(cp+1, &cp);
132                         if( cp && *cp==',' ) point->control_x1 = strtof(cp+1, &cp);
133                         if( cp && *cp==',' ) point->control_y1 = strtof(cp+1, &cp);
134                         if( cp && *cp==',' ) point->control_x2 = strtof(cp+1, &cp);
135                         if( cp && *cp==',' ) point->control_y2 = strtof(cp+1, &cp);
136                         points.append(point);
137                 }
138         }
139 }
140
141 void SubMask::copy(FileXML *file)
142 {
143         if(points.total)
144         {
145                 file->tag.set_title("MASK");
146                 file->tag.set_property("NUMBER",
147                         !keyframe ? -1 : keyframe->masks.number_of(this));
148                 file->tag.set_property("NAME", name);
149                 file->tag.set_property("FADER", fader);
150                 file->tag.set_property("FEATHER", feather);
151                 file->append_tag();
152                 file->append_newline();
153
154                 for(int i = 0; i < points.total; i++)
155                 {
156                         file->append_newline();
157                         file->tag.set_title("POINT");
158                         file->append_tag();
159                         char string[BCTEXTLEN];
160 //printf("SubMask::copy 1 %p %d %p\n", this, i, points.values[i]);
161                         sprintf(string, "%.7g, %.7g, %.7g, %.7g, %.7g, %.7g",
162                                 points.values[i]->x,
163                                 points.values[i]->y,
164                                 points.values[i]->control_x1,
165                                 points.values[i]->control_y1,
166                                 points.values[i]->control_x2,
167                                 points.values[i]->control_y2);
168 //printf("SubMask::copy 2\n");
169                         file->append_text(string);
170                         file->tag.set_title("/POINT");
171                         file->append_tag();
172                 }
173                 file->append_newline();
174
175                 file->tag.set_title("/MASK");
176                 file->append_tag();
177                 file->append_newline();
178         }
179 }
180
181 void SubMask::dump(FILE *fp)
182 {
183         for( int i=0; i<points.size(); ++i ) {
184                 fprintf(fp, "   mask: %d, name: %s, fader: %f, feather %f\n", i,
185                         name, fader, feather);
186                 fprintf(fp, "     point=%d x=%.2f y=%.2f in_x=%.2f in_y=%.2f out_x=%.2f out_y=%.2f\n",
187                         i, points.values[i]->x, points.values[i]->y,
188                         points.values[i]->control_x1, points.values[i]->control_y1,
189                         points.values[i]->control_x2, points.values[i]->control_y2);
190         }
191 }
192
193
194 MaskAuto::MaskAuto(EDL *edl, MaskAutos *autos)
195  : Auto(edl, autos)
196 {
197         apply_before_plugins = 0;
198         disable_opengl_masking = 0;
199
200 // We define a fixed number of submasks so that interpolation for each
201 // submask matches.
202
203         for(int i = 0; i < SUBMASKS; i++)
204                 masks.append(new SubMask(this, i));
205 }
206
207 MaskAuto::~MaskAuto()
208 {
209         masks.remove_all_objects();
210 }
211
212 int MaskAuto::operator==(Auto &that)
213 {
214         return identical((MaskAuto*)&that);
215 }
216
217
218
219 int MaskAuto::operator==(MaskAuto &that)
220 {
221         return identical((MaskAuto*)&that);
222 }
223
224
225 int MaskAuto::identical(MaskAuto *src)
226 {
227         if( masks.size() != src->masks.size() ||
228             apply_before_plugins != src->apply_before_plugins ||
229             disable_opengl_masking != src->disable_opengl_masking ) return 0;
230
231         for( int i=0; i<masks.size(); ++i ) {
232                 if(!(*masks.values[i] == *src->masks.values[i])) return 0;
233         }
234         return 1;
235 }
236
237 void MaskAuto::update_parameter(MaskAuto *ref, MaskAuto *src)
238 {
239         if( src->apply_before_plugins != ref->apply_before_plugins )
240                 this->apply_before_plugins = src->apply_before_plugins;
241         if( src->disable_opengl_masking != ref->disable_opengl_masking )
242                 this->disable_opengl_masking = src->disable_opengl_masking;
243
244         for( int i=0; i<masks.size(); ++i ) {
245                 if( !src->get_submask(i)->equivalent(*ref->get_submask(i)) )
246                         this->get_submask(i)->copy_from(*src->get_submask(i));
247         }
248 }
249
250 void MaskAuto::copy_from(Auto *src)
251 {
252         copy_from((MaskAuto*)src);
253 }
254
255 void MaskAuto::copy_from(MaskAuto *src)
256 {
257         Auto::copy_from(src);
258         copy_data(src);
259 }
260
261 void MaskAuto::copy_data(MaskAuto *src)
262 {
263         apply_before_plugins = src->apply_before_plugins;
264         disable_opengl_masking = src->disable_opengl_masking;
265
266         masks.remove_all_objects();
267         for(int i = 0; i < src->masks.size(); i++)
268         {
269                 masks.append(new SubMask(this, i));
270                 masks.values[i]->copy_from(*src->masks.values[i]);
271         }
272 }
273
274 int MaskAuto::interpolate_from(Auto *a1, Auto *a2, int64_t position, Auto *templ) {
275         if(!a1) a1 = previous;
276         if(!a2) a2 = next;
277         MaskAuto  *mask_auto1 = (MaskAuto *)a1;
278         MaskAuto  *mask_auto2 = (MaskAuto *)a2;
279
280         if (!mask_auto2 || !mask_auto1 || mask_auto2->masks.total == 0)
281         // can't interpolate, fall back to copying (using template if possible)
282         {
283                 return Auto::interpolate_from(a1, a2, position, templ);
284         }
285         this->apply_before_plugins = mask_auto1->apply_before_plugins;
286         this->disable_opengl_masking = mask_auto1->disable_opengl_masking;
287         this->position = position;
288         masks.remove_all_objects();
289
290         for( int i=0; i<mask_auto1->masks.total; ++i ) {
291                 SubMask *new_submask = new SubMask(this, i);
292                 masks.append(new_submask);
293                 SubMask *mask1 = mask_auto1->masks.values[i];
294                 SubMask *mask2 = mask_auto2->masks.values[i];
295                 double len = mask_auto2->position - mask_auto1->position;
296                 double weight = !len ? 0 : (position - mask_auto1->position) / len;
297                 new_submask->fader = mask1->fader*(1-weight) + mask2->fader*weight + 0.5;
298                 new_submask->feather = mask1->feather*(1-weight) + mask2->feather*weight + 0.5;
299
300                 // just in case, should never happen
301                 int total_points = MIN(mask1->points.total, mask2->points.total);
302                 for( int j=0; j<total_points; ++j ) {
303                         MaskPoint *point = new MaskPoint;
304                         MaskAutos::avg_points(point,
305                                 mask1->points.values[j], mask2->points.values[j],
306                                 position, mask_auto1->position, mask_auto2->position);
307                         new_submask->points.append(point);
308                 }
309         }
310         return 1;
311
312
313 }
314
315
316 SubMask* MaskAuto::get_submask(int number)
317 {
318         CLAMP(number, 0, masks.size() - 1);
319         return masks.values[number];
320 }
321
322 void MaskAuto::get_points(MaskPoints *points,
323         int submask)
324 {
325         points->remove_all_objects();
326         SubMask *submask_ptr = get_submask(submask);
327         for(int i = 0; i < submask_ptr->points.size(); i++)
328         {
329                 MaskPoint *point = new MaskPoint;
330                 point->copy_from(*submask_ptr->points.get(i));
331                 points->append(point);
332         }
333 }
334
335 void MaskAuto::set_points(MaskPoints *points,
336         int submask)
337 {
338         SubMask *submask_ptr = get_submask(submask);
339         submask_ptr->points.remove_all_objects();
340         for(int i = 0; i < points->size(); i++)
341         {
342                 MaskPoint *point = new MaskPoint;
343                 point->copy_from(*points->get(i));
344                 submask_ptr->points.append(point);
345         }
346 }
347
348
349 void MaskAuto::load(FileXML *file)
350 {
351 // legacy, moved to SubMask
352         int old_mode = file->tag.get_property("MODE", -1);
353         int old_value = file->tag.get_property("VALUE", 100);
354         float old_feather = file->tag.get_property("FEATHER", 0);
355         apply_before_plugins = file->tag.get_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
356         disable_opengl_masking = file->tag.get_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
357         for( int i=0; i<masks.size(); ++i ) {
358                 delete masks.values[i];
359                 masks.values[i] = new SubMask(this, i);
360         }
361
362         int result = 0;
363         while( !(result = file->read_tag()) ) {
364                 if( file->tag.title_is("/AUTO") ) break;
365                 if( file->tag.title_is("MASK") ) {
366                         int no = file->tag.get_property("NUMBER", 0);
367                         char name[BCTEXTLEN];  name[0] = 0;
368                         file->tag.get_property("NAME", name);
369                         if( !name[0] ) sprintf(name, "%d", no);
370                         SubMask *mask = masks.values[no];
371                         memset(mask->name, 0, sizeof(mask->name));
372                         strncpy(mask->name, name, sizeof(mask->name));
373                         mask->feather = file->tag.get_property("FEATHER", old_feather);
374                         mask->fader = file->tag.get_property("FADER", old_value);
375                         if( old_mode == MASK_MULTIPLY_ALPHA )
376                                 mask->fader = -mask->fader;
377                         mask->load(file);
378                 }
379         }
380 }
381
382 void MaskAuto::copy(int64_t start, int64_t end, FileXML *file, int default_auto)
383 {
384         file->tag.set_title("AUTO");
385         file->tag.set_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
386         file->tag.set_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
387         file->tag.set_property("POSITION", default_auto ? 0 : position - start);
388         file->append_tag();
389         file->append_newline();
390
391         for( int i=0; i<masks.size(); ++i )
392                 masks.values[i]->copy(file);
393
394         file->tag.set_title("/AUTO");
395         file->append_tag();
396         file->append_newline();
397 }
398
399 void MaskAuto::dump(FILE *fp)
400 {
401         fprintf(fp,"mask_auto:  apply_before_plugins %d, disable_opengl_masking %d\n",
402                 apply_before_plugins, disable_opengl_masking);
403         for( int i=0; i<masks.size(); ++i ) {
404                 fprintf(fp,"    submask %d:\n", i);
405                 masks.values[i]->dump(fp);
406         }
407 }
408
409 void MaskAuto::translate_submasks(float translate_x, float translate_y)
410 {
411         for( int i=0; i<masks.size(); ++i ) {
412                 SubMask *mask = get_submask(i);
413                 for( int j=0; j<mask->points.total; ++j ) {
414                         mask->points.values[j]->x += translate_x;
415                         mask->points.values[j]->y += translate_y;
416                 }
417         }
418 }
419
420 void MaskAuto::scale_submasks(int orig_scale, int new_scale)
421 {
422         for( int i=0; i<masks.size(); ++i ) {
423                 SubMask *mask = get_submask(i);
424                 for( int j=0; j<mask->points.total; ++j ) {
425                         float orig_x = mask->points.values[j]->x * orig_scale;
426                         float orig_y = mask->points.values[j]->y * orig_scale;
427                         mask->points.values[j]->x = orig_x / new_scale;
428                         mask->points.values[j]->y = orig_y / new_scale;
429
430                         orig_x = mask->points.values[j]->control_x1 * orig_scale;
431                         orig_y = mask->points.values[j]->control_y1 * orig_scale;
432                         mask->points.values[j]->control_x1 = orig_x / new_scale;
433                         mask->points.values[j]->control_y1 = orig_y / new_scale;
434
435                         orig_x = mask->points.values[j]->control_x2 * orig_scale;
436                         orig_y = mask->points.values[j]->control_y2 * orig_scale;
437                         mask->points.values[j]->control_x2 = orig_x / new_scale;
438                         mask->points.values[j]->control_y2 = orig_y / new_scale;
439                 }
440         }
441 }
442
443 int MaskAuto::has_active_mask()
444 {
445         int total_points = 0;
446         float min_fader = 100;
447         for( int i=0; i<masks.size(); ++i ) {
448                 SubMask *mask = get_submask(i);
449                 int submask_points = mask->points.size();
450                 if( submask_points > 1 ) total_points += submask_points;
451                 int fader = mask->fader;
452                 if( fader < min_fader ) min_fader = fader;
453         }
454         return min_fader >= 0 && total_points < 2 ? 0 : 1;
455 }
456
457 static inline double line_dist(float cx,float cy, float tx,float ty)
458 {
459         double dx = tx-cx, dy = ty-cy;
460         return sqrt(dx*dx + dy*dy);
461 }
462
463 void MaskEdge::load(MaskPoints &points, float ofs)
464 {
465         remove_all();
466         int first_point = 1;
467 // Need to tabulate every vertex in persistent memory because
468 // gluTessVertex doesn't copy them.
469         for( int i=0; i<points.total; ++i ) {
470                 MaskPoint *point1 = points.values[i];
471                 MaskPoint *point2 = (i >= points.total-1) ?
472                         points.values[0] : points.values[i+1];
473
474                 int segments = 0;
475                 if( !point1->control_x2 && !point1->control_y2 &&
476                     !point2->control_x1 && !point2->control_y1 )
477                         segments = 1;
478
479                 float x0 = point1->x, y0 = point1->y;
480                 float x1 = point1->x + point1->control_x2;
481                 float y1 = point1->y + point1->control_y2;
482                 float x2 = point2->x + point2->control_x1;
483                 float y2 = point2->y + point2->control_y1;
484                 float x3 = point2->x, y3 = point2->y;
485
486                 // forward differencing bezier curves implementation taken from GPL code at
487                 // http://cvs.sourceforge.net/viewcvs.py/guliverkli/guliverkli/src/subtitles/Rasterizer.cpp?rev=1.3
488
489                 float cx3, cx2, cx1, cx0;
490                 float cy3, cy2, cy1, cy0;
491
492                 // [-1 +3 -3 +1]
493                 // [+3 -6 +3  0]
494                 // [-3 +3  0  0]
495                 // [+1  0  0  0]
496
497                 cx3 = -  x0 + 3*x1 - 3*x2 + x3;
498                 cx2 =  3*x0 - 6*x1 + 3*x2;
499                 cx1 = -3*x0 + 3*x1;
500                 cx0 =    x0;
501
502                 cy3 = -  y0 + 3*y1 - 3*y2 + y3;
503                 cy2 =  3*y0 - 6*y1 + 3*y2;
504                 cy1 = -3*y0 + 3*y1;
505                 cy0 =    y0;
506
507                 // This equation is from Graphics Gems I.
508                 //
509                 // The idea is that since we're approximating a cubic curve with lines,
510                 // any error we incur is due to the curvature of the line, which we can
511                 // estimate by calculating the maximum acceleration of the curve.  For
512                 // a cubic, the acceleration (second derivative) is a line, meaning that
513                 // the absolute maximum acceleration must occur at either the beginning
514                 // (|c2|) or the end (|c2+c3|).  Our bounds here are a little more
515                 // conservative than that, but that's okay.
516                 if( !segments ) {
517                         float maxaccel1 = fabs(2*cy2) + fabs(6*cy3);
518                         float maxaccel2 = fabs(2*cx2) + fabs(6*cx3);
519                         float maxaccel = maxaccel1 > maxaccel2 ? maxaccel1 : maxaccel2;
520                         segments =  maxaccel > 1.0 ? sqrt(maxaccel) :
521                                 1 + line_dist(point1->x,point1->y, point2->x,point2->y);
522                 }
523
524                 for( int j=0; j<=segments; ++j ) {
525                         float t = (float)j / segments;
526                         float x = cx0 + t*(cx1 + t*(cx2 + t*cx3));
527                         float y = cy0 + t*(cy1 + t*(cy2 + t*cy3));
528
529                         if( j > 0 || first_point ) {
530                                 append(x, y-ofs);
531                                 first_point = 0;
532                         }
533                 }
534         }
535 }
536