4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "maskautos.h"
31 MaskPoint::MaskPoint()
41 void MaskPoint::copy_from(MaskPoint &ptr)
45 this->control_x1 = ptr.control_x1;
46 this->control_y1 = ptr.control_y1;
47 this->control_x2 = ptr.control_x2;
48 this->control_y2 = ptr.control_y2;
51 MaskPoint& MaskPoint::operator=(MaskPoint& ptr)
57 int MaskPoint::operator==(MaskPoint& ptr)
59 return EQUIV(x, ptr.x) &&
61 EQUIV(control_x1, ptr.control_x1) &&
62 EQUIV(control_y1, ptr.control_y1) &&
63 EQUIV(control_x2, ptr.control_x2) &&
64 EQUIV(control_y2, ptr.control_y2);
67 SubMask::SubMask(MaskAuto *keyframe, int no)
69 this->keyframe = keyframe;
70 memset(name, 0, sizeof(name));
71 sprintf(name, "%d", no);
78 points.remove_all_objects();
81 int SubMask::equivalent(SubMask& ptr)
83 if( fader != ptr.fader ) return 0;
84 if( feather != ptr.feather ) return 0;
85 int n = points.size();
86 if( n != ptr.points.size() ) return 0;
87 for( int i=0; i<n; ++i ) {
88 if(!(*points.get(i) == *ptr.points.get(i)))
94 int SubMask::operator==(SubMask& ptr)
96 return equivalent(ptr);
99 void SubMask::copy_from(SubMask& ptr, int do_name)
102 memset(name, 0, sizeof(name));
103 strncpy(name, ptr.name, sizeof(name)-1);
106 feather = ptr.feather;
107 points.remove_all_objects();
108 //printf("SubMask::copy_from 1 %p %d\n", this, ptr.points.total);
109 for(int i = 0; i < ptr.points.total; i++)
111 MaskPoint *point = new MaskPoint;
112 *point = *ptr.points.values[i];
113 points.append(point);
117 void SubMask::load(FileXML *file)
119 points.remove_all_objects();
122 while( !(result = file->read_tag()) ) {
123 if( file->tag.title_is("/MASK") ) break;
124 if( file->tag.title_is("POINT") ) {
126 file->read_text_until("/POINT", &data);
127 MaskPoint *point = new MaskPoint;
128 char *cp = data.cstr();
129 if( cp ) point->x = strtof(cp, &cp);
130 if( cp && *cp==',' ) point->y = strtof(cp+1, &cp);
131 if( cp && *cp==',' ) point->control_x1 = strtof(cp+1, &cp);
132 if( cp && *cp==',' ) point->control_y1 = strtof(cp+1, &cp);
133 if( cp && *cp==',' ) point->control_x2 = strtof(cp+1, &cp);
134 if( cp && *cp==',' ) point->control_y2 = strtof(cp+1, &cp);
135 points.append(point);
140 void SubMask::copy(FileXML *file)
144 file->tag.set_title("MASK");
145 file->tag.set_property("NUMBER",
146 !keyframe ? -1 : keyframe->masks.number_of(this));
147 file->tag.set_property("NAME", name);
148 file->tag.set_property("FADER", fader);
149 file->tag.set_property("FEATHER", feather);
151 file->append_newline();
153 for(int i = 0; i < points.total; i++)
155 file->append_newline();
156 file->tag.set_title("POINT");
158 char string[BCTEXTLEN];
159 //printf("SubMask::copy 1 %p %d %p\n", this, i, points.values[i]);
160 sprintf(string, "%.7g, %.7g, %.7g, %.7g, %.7g, %.7g",
163 points.values[i]->control_x1,
164 points.values[i]->control_y1,
165 points.values[i]->control_x2,
166 points.values[i]->control_y2);
167 //printf("SubMask::copy 2\n");
168 file->append_text(string);
169 file->tag.set_title("/POINT");
172 file->append_newline();
174 file->tag.set_title("/MASK");
176 file->append_newline();
180 void SubMask::dump(FILE *fp)
182 for( int i=0; i<points.size(); ++i ) {
183 fprintf(fp, " mask: %d, name: %s, fader: %f, feather %f\n", i,
184 name, fader, feather);
185 fprintf(fp, " point=%d x=%.2f y=%.2f in_x=%.2f in_y=%.2f out_x=%.2f out_y=%.2f\n",
186 i, points.values[i]->x, points.values[i]->y,
187 points.values[i]->control_x1, points.values[i]->control_y1,
188 points.values[i]->control_x2, points.values[i]->control_y2);
193 MaskAuto::MaskAuto(EDL *edl, MaskAutos *autos)
196 apply_before_plugins = 0;
197 disable_opengl_masking = 0;
199 // We define a fixed number of submasks so that interpolation for each
202 for(int i = 0; i < SUBMASKS; i++)
203 masks.append(new SubMask(this, i));
206 MaskAuto::~MaskAuto()
208 masks.remove_all_objects();
211 int MaskAuto::operator==(Auto &that)
213 return identical((MaskAuto*)&that);
218 int MaskAuto::operator==(MaskAuto &that)
220 return identical((MaskAuto*)&that);
224 int MaskAuto::identical(MaskAuto *src)
226 if( masks.size() != src->masks.size() ||
227 apply_before_plugins != src->apply_before_plugins ||
228 disable_opengl_masking != src->disable_opengl_masking ) return 0;
230 for( int i=0; i<masks.size(); ++i ) {
231 if(!(*masks.values[i] == *src->masks.values[i])) return 0;
236 void MaskAuto::update_parameter(MaskAuto *ref, MaskAuto *src)
238 if( src->apply_before_plugins != ref->apply_before_plugins )
239 this->apply_before_plugins = src->apply_before_plugins;
240 if( src->disable_opengl_masking != ref->disable_opengl_masking )
241 this->disable_opengl_masking = src->disable_opengl_masking;
243 for( int i=0; i<masks.size(); ++i ) {
244 if( !src->get_submask(i)->equivalent(*ref->get_submask(i)) )
245 this->get_submask(i)->copy_from(*src->get_submask(i));
249 void MaskAuto::copy_from(Auto *src)
251 copy_from((MaskAuto*)src);
254 void MaskAuto::copy_from(MaskAuto *src)
256 Auto::copy_from(src);
260 void MaskAuto::copy_data(MaskAuto *src)
262 apply_before_plugins = src->apply_before_plugins;
263 disable_opengl_masking = src->disable_opengl_masking;
265 masks.remove_all_objects();
266 for(int i = 0; i < src->masks.size(); i++)
268 masks.append(new SubMask(this, i));
269 masks.values[i]->copy_from(*src->masks.values[i]);
273 int MaskAuto::interpolate_from(Auto *a1, Auto *a2, int64_t position, Auto *templ) {
274 if(!a1) a1 = previous;
276 MaskAuto *mask_auto1 = (MaskAuto *)a1;
277 MaskAuto *mask_auto2 = (MaskAuto *)a2;
279 if (!mask_auto2 || !mask_auto1 || mask_auto2->masks.total == 0)
280 // can't interpolate, fall back to copying (using template if possible)
282 return Auto::interpolate_from(a1, a2, position, templ);
284 this->apply_before_plugins = mask_auto1->apply_before_plugins;
285 this->disable_opengl_masking = mask_auto1->disable_opengl_masking;
286 this->position = position;
287 masks.remove_all_objects();
289 for( int i=0; i<mask_auto1->masks.total; ++i ) {
290 SubMask *new_submask = new SubMask(this, i);
291 masks.append(new_submask);
292 SubMask *mask1 = mask_auto1->masks.values[i];
293 SubMask *mask2 = mask_auto2->masks.values[i];
294 double len = mask_auto2->position - mask_auto1->position;
295 double weight = !len ? 0 : (position - mask_auto1->position) / len;
296 new_submask->fader = mask1->fader*(1-weight) + mask2->fader*weight + 0.5;
297 new_submask->feather = mask1->feather*(1-weight) + mask2->feather*weight + 0.5;
299 // just in case, should never happen
300 int total_points = MIN(mask1->points.total, mask2->points.total);
301 for( int j=0; j<total_points; ++j ) {
302 MaskPoint *point = new MaskPoint;
303 MaskAutos::avg_points(point,
304 mask1->points.values[j], mask2->points.values[j],
305 position, mask_auto1->position, mask_auto2->position);
306 new_submask->points.append(point);
315 SubMask* MaskAuto::get_submask(int number)
317 CLAMP(number, 0, masks.size() - 1);
318 return masks.values[number];
321 void MaskAuto::get_points(MaskPoints *points,
324 points->remove_all_objects();
325 SubMask *submask_ptr = get_submask(submask);
326 for(int i = 0; i < submask_ptr->points.size(); i++)
328 MaskPoint *point = new MaskPoint;
329 point->copy_from(*submask_ptr->points.get(i));
330 points->append(point);
334 void MaskAuto::set_points(MaskPoints *points,
337 SubMask *submask_ptr = get_submask(submask);
338 submask_ptr->points.remove_all_objects();
339 for(int i = 0; i < points->size(); i++)
341 MaskPoint *point = new MaskPoint;
342 point->copy_from(*points->get(i));
343 submask_ptr->points.append(point);
348 void MaskAuto::load(FileXML *file)
350 // legacy, moved to SubMask
351 int old_mode = file->tag.get_property("MODE", -1);
352 int old_value = file->tag.get_property("VALUE", 100);
353 float old_feather = file->tag.get_property("FEATHER", 0);
354 apply_before_plugins = file->tag.get_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
355 disable_opengl_masking = file->tag.get_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
356 for( int i=0; i<masks.size(); ++i ) {
357 delete masks.values[i];
358 masks.values[i] = new SubMask(this, i);
362 while( !(result = file->read_tag()) ) {
363 if( file->tag.title_is("/AUTO") ) break;
364 if( file->tag.title_is("MASK") ) {
365 int no = file->tag.get_property("NUMBER", 0);
366 char name[BCTEXTLEN]; name[0] = 0;
367 file->tag.get_property("NAME", name);
368 if( !name[0] ) sprintf(name, "%d", no);
369 SubMask *mask = masks.values[no];
370 memset(mask->name, 0, sizeof(mask->name));
371 strncpy(mask->name, name, sizeof(mask->name));
372 mask->feather = file->tag.get_property("FEATHER", old_feather);
373 mask->fader = file->tag.get_property("FADER", old_value);
374 if( old_mode == MASK_MULTIPLY_ALPHA )
375 mask->fader = -mask->fader;
381 void MaskAuto::copy(int64_t start, int64_t end, FileXML *file, int default_auto)
383 file->tag.set_title("AUTO");
384 file->tag.set_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
385 file->tag.set_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
386 file->tag.set_property("POSITION", default_auto ? 0 : position - start);
388 file->append_newline();
390 for( int i=0; i<masks.size(); ++i )
391 masks.values[i]->copy(file);
393 file->tag.set_title("/AUTO");
395 file->append_newline();
398 void MaskAuto::dump(FILE *fp)
400 fprintf(fp,"mask_auto: apply_before_plugins %d, disable_opengl_masking %d\n",
401 apply_before_plugins, disable_opengl_masking);
402 for( int i=0; i<masks.size(); ++i ) {
403 fprintf(fp," submask %d:\n", i);
404 masks.values[i]->dump(fp);
408 void MaskAuto::translate_submasks(float translate_x, float translate_y)
410 for( int i=0; i<masks.size(); ++i ) {
411 SubMask *mask = get_submask(i);
412 for( int j=0; j<mask->points.total; ++j ) {
413 mask->points.values[j]->x += translate_x;
414 mask->points.values[j]->y += translate_y;
419 void MaskAuto::scale_submasks(int orig_scale, int new_scale)
421 for( int i=0; i<masks.size(); ++i ) {
422 SubMask *mask = get_submask(i);
423 for( int j=0; j<mask->points.total; ++j ) {
424 float orig_x = mask->points.values[j]->x * orig_scale;
425 float orig_y = mask->points.values[j]->y * orig_scale;
426 mask->points.values[j]->x = orig_x / new_scale;
427 mask->points.values[j]->y = orig_y / new_scale;
429 orig_x = mask->points.values[j]->control_x1 * orig_scale;
430 orig_y = mask->points.values[j]->control_y1 * orig_scale;
431 mask->points.values[j]->control_x1 = orig_x / new_scale;
432 mask->points.values[j]->control_y1 = orig_y / new_scale;
434 orig_x = mask->points.values[j]->control_x2 * orig_scale;
435 orig_y = mask->points.values[j]->control_y2 * orig_scale;
436 mask->points.values[j]->control_x2 = orig_x / new_scale;
437 mask->points.values[j]->control_y2 = orig_y / new_scale;
442 int MaskAuto::has_active_mask()
444 int total_points = 0;
445 float min_fader = 100;
446 for( int i=0; i<masks.size(); ++i ) {
447 SubMask *mask = get_submask(i);
448 int submask_points = mask->points.size();
449 if( submask_points > 1 ) total_points += submask_points;
450 int fader = mask->fader;
451 if( fader < min_fader ) min_fader = fader;
453 return min_fader >= 0 && total_points < 2 ? 0 : 1;
456 static inline double line_dist(float cx,float cy, float tx,float ty)
458 double dx = tx-cx, dy = ty-cy;
459 return sqrt(dx*dx + dy*dy);
462 void MaskEdge::load(MaskPoints &points, float ofs)
466 // Need to tabulate every vertex in persistent memory because
467 // gluTessVertex doesn't copy them.
468 for( int i=0; i<points.total; ++i ) {
469 MaskPoint *point1 = points.values[i];
470 MaskPoint *point2 = (i >= points.total-1) ?
471 points.values[0] : points.values[i+1];
474 if( !point1->control_x2 && !point1->control_y2 &&
475 !point2->control_x1 && !point2->control_y1 )
478 float x0 = point1->x, y0 = point1->y;
479 float x1 = point1->x + point1->control_x2;
480 float y1 = point1->y + point1->control_y2;
481 float x2 = point2->x + point2->control_x1;
482 float y2 = point2->y + point2->control_y1;
483 float x3 = point2->x, y3 = point2->y;
485 // forward differencing bezier curves implementation taken from GPL code at
486 // http://cvs.sourceforge.net/viewcvs.py/guliverkli/guliverkli/src/subtitles/Rasterizer.cpp?rev=1.3
488 float cx3, cx2, cx1, cx0;
489 float cy3, cy2, cy1, cy0;
496 cx3 = - x0 + 3*x1 - 3*x2 + x3;
497 cx2 = 3*x0 - 6*x1 + 3*x2;
501 cy3 = - y0 + 3*y1 - 3*y2 + y3;
502 cy2 = 3*y0 - 6*y1 + 3*y2;
506 // This equation is from Graphics Gems I.
508 // The idea is that since we're approximating a cubic curve with lines,
509 // any error we incur is due to the curvature of the line, which we can
510 // estimate by calculating the maximum acceleration of the curve. For
511 // a cubic, the acceleration (second derivative) is a line, meaning that
512 // the absolute maximum acceleration must occur at either the beginning
513 // (|c2|) or the end (|c2+c3|). Our bounds here are a little more
514 // conservative than that, but that's okay.
516 float maxaccel1 = fabs(2*cy2) + fabs(6*cy3);
517 float maxaccel2 = fabs(2*cx2) + fabs(6*cx3);
518 float maxaccel = maxaccel1 > maxaccel2 ? maxaccel1 : maxaccel2;
519 segments = maxaccel > 1.0 ? sqrt(maxaccel) :
520 1 + line_dist(point1->x,point1->y, point2->x,point2->y);
523 for( int j=0; j<=segments; ++j ) {
524 float t = (float)j / segments;
525 float x = cx0 + t*(cx1 + t*(cx2 + t*cx3));
526 float y = cy0 + t*(cy1 + t*(cy2 + t*cy3));
528 if( j > 0 || first_point ) {