4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5 * Copyright (C) 2003-2016 Cinelerra CV contributors
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.
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.
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
26 #include "maskautos.h"
32 MaskPoint::MaskPoint()
42 void MaskPoint::copy_from(MaskPoint &ptr)
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;
52 MaskPoint& MaskPoint::operator=(MaskPoint& ptr)
58 int MaskPoint::operator==(MaskPoint& ptr)
60 return EQUIV(x, ptr.x) &&
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);
68 SubMask::SubMask(MaskAuto *keyframe, int no)
70 this->keyframe = keyframe;
71 memset(name, 0, sizeof(name));
72 sprintf(name, "%d", no);
79 points.remove_all_objects();
82 int SubMask::equivalent(SubMask& ptr)
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)))
95 int SubMask::operator==(SubMask& ptr)
97 return equivalent(ptr);
100 void SubMask::copy_from(SubMask& ptr, int do_name)
103 memset(name, 0, sizeof(name));
104 strncpy(name, ptr.name, sizeof(name)-1);
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++)
112 MaskPoint *point = new MaskPoint;
113 *point = *ptr.points.values[i];
114 points.append(point);
118 void SubMask::load(FileXML *file)
120 points.remove_all_objects();
123 while( !(result = file->read_tag()) ) {
124 if( file->tag.title_is("/MASK") ) break;
125 if( file->tag.title_is("POINT") ) {
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);
141 void SubMask::copy(FileXML *file)
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);
152 file->append_newline();
154 for(int i = 0; i < points.total; i++)
156 file->append_newline();
157 file->tag.set_title("POINT");
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",
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");
173 file->append_newline();
175 file->tag.set_title("/MASK");
177 file->append_newline();
181 void SubMask::dump(FILE *fp)
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);
194 MaskAuto::MaskAuto(EDL *edl, MaskAutos *autos)
197 apply_before_plugins = 0;
198 disable_opengl_masking = 0;
200 // We define a fixed number of submasks so that interpolation for each
203 for(int i = 0; i < SUBMASKS; i++)
204 masks.append(new SubMask(this, i));
207 MaskAuto::~MaskAuto()
209 masks.remove_all_objects();
212 int MaskAuto::operator==(Auto &that)
214 return identical((MaskAuto*)&that);
219 int MaskAuto::operator==(MaskAuto &that)
221 return identical((MaskAuto*)&that);
225 int MaskAuto::identical(MaskAuto *src)
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;
231 for( int i=0; i<masks.size(); ++i ) {
232 if(!(*masks.values[i] == *src->masks.values[i])) return 0;
237 void MaskAuto::update_parameter(MaskAuto *ref, MaskAuto *src)
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;
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));
250 void MaskAuto::copy_from(Auto *src)
252 copy_from((MaskAuto*)src);
255 void MaskAuto::copy_from(MaskAuto *src)
257 Auto::copy_from(src);
261 void MaskAuto::copy_data(MaskAuto *src)
263 apply_before_plugins = src->apply_before_plugins;
264 disable_opengl_masking = src->disable_opengl_masking;
266 masks.remove_all_objects();
267 for(int i = 0; i < src->masks.size(); i++)
269 masks.append(new SubMask(this, i));
270 masks.values[i]->copy_from(*src->masks.values[i]);
274 int MaskAuto::interpolate_from(Auto *a1, Auto *a2, int64_t position, Auto *templ) {
275 if(!a1) a1 = previous;
277 MaskAuto *mask_auto1 = (MaskAuto *)a1;
278 MaskAuto *mask_auto2 = (MaskAuto *)a2;
280 if (!mask_auto2 || !mask_auto1 || mask_auto2->masks.total == 0)
281 // can't interpolate, fall back to copying (using template if possible)
283 return Auto::interpolate_from(a1, a2, position, templ);
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();
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;
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);
316 SubMask* MaskAuto::get_submask(int number)
318 CLAMP(number, 0, masks.size() - 1);
319 return masks.values[number];
322 void MaskAuto::get_points(MaskPoints *points,
325 points->remove_all_objects();
326 SubMask *submask_ptr = get_submask(submask);
327 for(int i = 0; i < submask_ptr->points.size(); i++)
329 MaskPoint *point = new MaskPoint;
330 point->copy_from(*submask_ptr->points.get(i));
331 points->append(point);
335 void MaskAuto::set_points(MaskPoints *points,
338 SubMask *submask_ptr = get_submask(submask);
339 submask_ptr->points.remove_all_objects();
340 for(int i = 0; i < points->size(); i++)
342 MaskPoint *point = new MaskPoint;
343 point->copy_from(*points->get(i));
344 submask_ptr->points.append(point);
349 void MaskAuto::load(FileXML *file)
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);
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;
382 void MaskAuto::copy(int64_t start, int64_t end, FileXML *file, int default_auto)
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);
389 file->append_newline();
391 for( int i=0; i<masks.size(); ++i )
392 masks.values[i]->copy(file);
394 file->tag.set_title("/AUTO");
396 file->append_newline();
399 void MaskAuto::dump(FILE *fp)
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);
409 void MaskAuto::translate_submasks(float translate_x, float translate_y)
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;
420 void MaskAuto::scale_submasks(int orig_scale, int new_scale)
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;
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;
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;
443 int MaskAuto::has_active_mask()
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;
454 return min_fader >= 0 && total_points < 2 ? 0 : 1;
457 static inline double line_dist(float cx,float cy, float tx,float ty)
459 double dx = tx-cx, dy = ty-cy;
460 return sqrt(dx*dx + dy*dy);
463 void MaskEdge::load(MaskPoints &points, float ofs)
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];
475 if( !point1->control_x2 && !point1->control_y2 &&
476 !point2->control_x1 && !point2->control_y1 )
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;
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
489 float cx3, cx2, cx1, cx0;
490 float cy3, cy2, cy1, cy0;
497 cx3 = - x0 + 3*x1 - 3*x2 + x3;
498 cx2 = 3*x0 - 6*x1 + 3*x2;
502 cy3 = - y0 + 3*y1 - 3*y2 + y3;
503 cy2 = 3*y0 - 6*y1 + 3*y2;
507 // This equation is from Graphics Gems I.
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.
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);
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));
529 if( j > 0 || first_point ) {