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)
101 memset(name, 0, sizeof(name));
102 strncpy(name, ptr.name, sizeof(name-1));
104 feather = ptr.feather;
105 points.remove_all_objects();
106 //printf("SubMask::copy_from 1 %p %d\n", this, ptr.points.total);
107 for(int i = 0; i < ptr.points.total; i++)
109 MaskPoint *point = new MaskPoint;
110 *point = *ptr.points.values[i];
111 points.append(point);
115 void SubMask::load(FileXML *file)
117 points.remove_all_objects();
120 while( !(result = file->read_tag()) ) {
121 if( file->tag.title_is("/MASK") ) break;
122 if( file->tag.title_is("POINT") ) {
124 file->read_text_until("/POINT", &data);
125 MaskPoint *point = new MaskPoint;
126 char *cp = data.cstr();
127 if( cp ) point->x = strtof(cp, &cp);
128 if( cp && *cp==',' ) point->y = strtof(cp+1, &cp);
129 if( cp && *cp==',' ) point->control_x1 = strtof(cp+1, &cp);
130 if( cp && *cp==',' ) point->control_y1 = strtof(cp+1, &cp);
131 if( cp && *cp==',' ) point->control_x2 = strtof(cp+1, &cp);
132 if( cp && *cp==',' ) point->control_y2 = strtof(cp+1, &cp);
133 points.append(point);
138 void SubMask::copy(FileXML *file)
142 file->tag.set_title("MASK");
143 file->tag.set_property("NUMBER", keyframe->masks.number_of(this));
144 file->tag.set_property("NAME", name);
145 file->tag.set_property("FADER", fader);
146 file->tag.set_property("FEATHER", feather);
148 file->append_newline();
150 for(int i = 0; i < points.total; i++)
152 file->append_newline();
153 file->tag.set_title("POINT");
155 char string[BCTEXTLEN];
156 //printf("SubMask::copy 1 %p %d %p\n", this, i, points.values[i]);
157 sprintf(string, "%.7g, %.7g, %.7g, %.7g, %.7g, %.7g",
160 points.values[i]->control_x1,
161 points.values[i]->control_y1,
162 points.values[i]->control_x2,
163 points.values[i]->control_y2);
164 //printf("SubMask::copy 2\n");
165 file->append_text(string);
166 file->tag.set_title("/POINT");
169 file->append_newline();
171 file->tag.set_title("/MASK");
173 file->append_newline();
177 void SubMask::dump(FILE *fp)
179 for( int i=0; i<points.size(); ++i ) {
180 fprintf(fp, " mask: %d, name: %s, fader: %f, feather %f\n", i,
181 name, fader, feather);
182 fprintf(fp, " point=%d x=%.2f y=%.2f in_x=%.2f in_y=%.2f out_x=%.2f out_y=%.2f\n",
183 i, points.values[i]->x, points.values[i]->y,
184 points.values[i]->control_x1, points.values[i]->control_y1,
185 points.values[i]->control_x2, points.values[i]->control_y2);
190 MaskAuto::MaskAuto(EDL *edl, MaskAutos *autos)
193 apply_before_plugins = 0;
194 disable_opengl_masking = 0;
196 // We define a fixed number of submasks so that interpolation for each
199 for(int i = 0; i < SUBMASKS; i++)
200 masks.append(new SubMask(this, i));
203 MaskAuto::~MaskAuto()
205 masks.remove_all_objects();
208 int MaskAuto::operator==(Auto &that)
210 return identical((MaskAuto*)&that);
215 int MaskAuto::operator==(MaskAuto &that)
217 return identical((MaskAuto*)&that);
221 int MaskAuto::identical(MaskAuto *src)
223 if( masks.size() != src->masks.size() ||
224 apply_before_plugins != src->apply_before_plugins ||
225 disable_opengl_masking != src->disable_opengl_masking ) return 0;
227 for( int i=0; i<masks.size(); ++i ) {
228 if(!(*masks.values[i] == *src->masks.values[i])) return 0;
233 void MaskAuto::update_parameter(MaskAuto *ref, MaskAuto *src)
235 if( src->apply_before_plugins != ref->apply_before_plugins )
236 this->apply_before_plugins = src->apply_before_plugins;
237 if( src->disable_opengl_masking != ref->disable_opengl_masking )
238 this->disable_opengl_masking = src->disable_opengl_masking;
240 for( int i=0; i<masks.size(); ++i ) {
241 if( !src->get_submask(i)->equivalent(*ref->get_submask(i)) )
242 this->get_submask(i)->copy_from(*src->get_submask(i));
246 void MaskAuto::copy_from(Auto *src)
248 copy_from((MaskAuto*)src);
251 void MaskAuto::copy_from(MaskAuto *src)
253 Auto::copy_from(src);
257 void MaskAuto::copy_data(MaskAuto *src)
259 apply_before_plugins = src->apply_before_plugins;
260 disable_opengl_masking = src->disable_opengl_masking;
262 masks.remove_all_objects();
263 for(int i = 0; i < src->masks.size(); i++)
265 masks.append(new SubMask(this, i));
266 masks.values[i]->copy_from(*src->masks.values[i]);
270 int MaskAuto::interpolate_from(Auto *a1, Auto *a2, int64_t position, Auto *templ) {
271 if(!a1) a1 = previous;
273 MaskAuto *mask_auto1 = (MaskAuto *)a1;
274 MaskAuto *mask_auto2 = (MaskAuto *)a2;
276 if (!mask_auto2 || !mask_auto1 || mask_auto2->masks.total == 0)
277 // can't interpolate, fall back to copying (using template if possible)
279 return Auto::interpolate_from(a1, a2, position, templ);
281 this->apply_before_plugins = mask_auto1->apply_before_plugins;
282 this->disable_opengl_masking = mask_auto1->disable_opengl_masking;
283 this->position = position;
284 masks.remove_all_objects();
286 for( int i=0; i<mask_auto1->masks.total; ++i ) {
287 SubMask *new_submask = new SubMask(this, i);
288 masks.append(new_submask);
289 SubMask *mask1 = mask_auto1->masks.values[i];
290 SubMask *mask2 = mask_auto2->masks.values[i];
291 double len = mask_auto2->position - mask_auto1->position;
292 double weight = !len ? 0 : (position - mask_auto1->position) / len;
293 new_submask->fader = mask1->fader*(1-weight) + mask2->fader*weight + 0.5;
294 new_submask->feather = mask1->feather*(1-weight) + mask2->feather*weight + 0.5;
296 // just in case, should never happen
297 int total_points = MIN(mask1->points.total, mask2->points.total);
298 for( int j=0; j<total_points; ++j ) {
299 MaskPoint *point = new MaskPoint;
300 MaskAutos::avg_points(point,
301 mask1->points.values[j], mask2->points.values[j],
302 position, mask_auto1->position, mask_auto2->position);
303 new_submask->points.append(point);
312 SubMask* MaskAuto::get_submask(int number)
314 CLAMP(number, 0, masks.size() - 1);
315 return masks.values[number];
318 void MaskAuto::get_points(ArrayList<MaskPoint*> *points,
321 points->remove_all_objects();
322 SubMask *submask_ptr = get_submask(submask);
323 for(int i = 0; i < submask_ptr->points.size(); i++)
325 MaskPoint *point = new MaskPoint;
326 point->copy_from(*submask_ptr->points.get(i));
327 points->append(point);
331 void MaskAuto::set_points(ArrayList<MaskPoint*> *points,
334 SubMask *submask_ptr = get_submask(submask);
335 submask_ptr->points.remove_all_objects();
336 for(int i = 0; i < points->size(); i++)
338 MaskPoint *point = new MaskPoint;
339 point->copy_from(*points->get(i));
340 submask_ptr->points.append(point);
345 void MaskAuto::load(FileXML *file)
347 // legacy, moved to SubMask
348 int old_mode = file->tag.get_property("MODE", -1);
349 int old_value = file->tag.get_property("VALUE", 100);
350 float old_feather = file->tag.get_property("FEATHER", 0);
351 apply_before_plugins = file->tag.get_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
352 disable_opengl_masking = file->tag.get_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
353 for( int i=0; i<masks.size(); ++i ) {
354 delete masks.values[i];
355 masks.values[i] = new SubMask(this, i);
359 while( !(result = file->read_tag()) ) {
360 if( file->tag.title_is("/AUTO") ) break;
361 if( file->tag.title_is("MASK") ) {
362 int no = file->tag.get_property("NUMBER", 0);
363 char name[BCTEXTLEN]; name[0] = 0;
364 file->tag.get_property("NAME", name);
365 if( !name[0] ) sprintf(name, "%d", no);
366 SubMask *mask = masks.values[no];
367 memset(mask->name, 0, sizeof(mask->name));
368 strncpy(mask->name, name, sizeof(mask->name));
369 mask->feather = file->tag.get_property("FEATHER", old_feather);
370 mask->fader = file->tag.get_property("FADER", old_value);
371 if( old_mode == MASK_MULTIPLY_ALPHA )
372 mask->fader = -mask->fader;
378 void MaskAuto::copy(int64_t start, int64_t end, FileXML *file, int default_auto)
380 file->tag.set_title("AUTO");
381 file->tag.set_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
382 file->tag.set_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
383 file->tag.set_property("POSITION", default_auto ? 0 : position - start);
385 file->append_newline();
387 for( int i=0; i<masks.size(); ++i )
388 masks.values[i]->copy(file);
390 file->tag.set_title("/AUTO");
392 file->append_newline();
395 void MaskAuto::dump(FILE *fp)
397 fprintf(fp,"mask_auto: apply_before_plugins %d, disable_opengl_masking %d\n",
398 apply_before_plugins, disable_opengl_masking);
399 for( int i=0; i<masks.size(); ++i ) {
400 fprintf(fp," submask %d:\n", i);
401 masks.values[i]->dump(fp);
405 void MaskAuto::translate_submasks(float translate_x, float translate_y)
407 for( int i=0; i<masks.size(); ++i ) {
408 SubMask *mask = get_submask(i);
409 for( int j=0; j<mask->points.total; ++j ) {
410 mask->points.values[j]->x += translate_x;
411 mask->points.values[j]->y += translate_y;
416 void MaskAuto::scale_submasks(int orig_scale, int new_scale)
418 for( int i=0; i<masks.size(); ++i ) {
419 SubMask *mask = get_submask(i);
420 for( int j=0; j<mask->points.total; ++j ) {
421 float orig_x = mask->points.values[j]->x * orig_scale;
422 float orig_y = mask->points.values[j]->y * orig_scale;
423 mask->points.values[j]->x = orig_x / new_scale;
424 mask->points.values[j]->y = orig_y / new_scale;
426 orig_x = mask->points.values[j]->control_x1 * orig_scale;
427 orig_y = mask->points.values[j]->control_y1 * orig_scale;
428 mask->points.values[j]->control_x1 = orig_x / new_scale;
429 mask->points.values[j]->control_y1 = orig_y / new_scale;
431 orig_x = mask->points.values[j]->control_x2 * orig_scale;
432 orig_y = mask->points.values[j]->control_y2 * orig_scale;
433 mask->points.values[j]->control_x2 = orig_x / new_scale;
434 mask->points.values[j]->control_y2 = orig_y / new_scale;
439 int MaskAuto::has_active_mask()
441 int total_points = 0;
442 float min_fader = 100;
443 for( int i=0; i<masks.size(); ++i ) {
444 SubMask *mask = get_submask(i);
445 int submask_points = mask->points.size();
446 if( submask_points > 1 ) total_points += submask_points;
447 int fader = mask->fader;
448 if( fader < min_fader ) min_fader = fader;
450 return min_fader >= 0 && total_points < 2 ? 0 : 1;