8b5a2a4222d35a501cc9c2cb2cb457227da9cde8
[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  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "clip.h"
23 #include "filexml.h"
24 #include "maskauto.h"
25 #include "maskautos.h"
26
27 #include <stdlib.h>
28 #include <string.h>
29
30
31 MaskPoint::MaskPoint()
32 {
33         x = 0;
34         y = 0;
35         control_x1 = 0;
36         control_y1 = 0;
37         control_x2 = 0;
38         control_y2 = 0;
39 }
40
41 void MaskPoint::copy_from(MaskPoint &ptr)
42 {
43         this->x = ptr.x;
44         this->y = ptr.y;
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;
49 }
50
51 MaskPoint& MaskPoint::operator=(MaskPoint& ptr)
52 {
53         copy_from(ptr);
54         return *this;
55 }
56
57 int MaskPoint::operator==(MaskPoint& ptr)
58 {
59         return EQUIV(x, ptr.x) &&
60                 EQUIV(y, ptr.y) &&
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);
65 }
66
67 SubMask::SubMask(MaskAuto *keyframe, int no)
68 {
69         this->keyframe = keyframe;
70         memset(name, 0, sizeof(name));
71         sprintf(name, "%d", no);
72         this->fader = 100;
73         this->feather = 0;
74 }
75
76 SubMask::~SubMask()
77 {
78         points.remove_all_objects();
79 }
80
81 int SubMask::equivalent(SubMask& ptr)
82 {
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)))
89                         return 0;
90         }
91         return 1;
92 }
93
94 int SubMask::operator==(SubMask& ptr)
95 {
96         return equivalent(ptr);
97 }
98
99 void SubMask::copy_from(SubMask& ptr)
100 {
101         memset(name, 0, sizeof(name));
102         strncpy(name, ptr.name, sizeof(name-1));
103         fader = ptr.fader;
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++)
108         {
109                 MaskPoint *point = new MaskPoint;
110                 *point = *ptr.points.values[i];
111                 points.append(point);
112         }
113 }
114
115 void SubMask::load(FileXML *file)
116 {
117         points.remove_all_objects();
118
119         int result = 0;
120         while( !(result = file->read_tag()) ) {
121                 if( file->tag.title_is("/MASK") ) break;
122                 if( file->tag.title_is("POINT") ) {
123                         XMLBuffer data;
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);
134                 }
135         }
136 }
137
138 void SubMask::copy(FileXML *file)
139 {
140         if(points.total)
141         {
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);
147                 file->append_tag();
148                 file->append_newline();
149
150                 for(int i = 0; i < points.total; i++)
151                 {
152                         file->append_newline();
153                         file->tag.set_title("POINT");
154                         file->append_tag();
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",
158                                 points.values[i]->x,
159                                 points.values[i]->y,
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");
167                         file->append_tag();
168                 }
169                 file->append_newline();
170
171                 file->tag.set_title("/MASK");
172                 file->append_tag();
173                 file->append_newline();
174         }
175 }
176
177 void SubMask::dump()
178 {
179         for( int i=0; i<points.size(); ++i ) {
180                 printf("        mask: %d, name: %s, fader: %f, feather %f\n", i,
181                         name, fader, feather);
182                 printf("          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);
186         }
187 }
188
189
190 MaskAuto::MaskAuto(EDL *edl, MaskAutos *autos)
191  : Auto(edl, autos)
192 {
193         apply_before_plugins = 0;
194         disable_opengl_masking = 0;
195
196 // We define a fixed number of submasks so that interpolation for each
197 // submask matches.
198
199         for(int i = 0; i < SUBMASKS; i++)
200                 masks.append(new SubMask(this, i));
201 }
202
203 MaskAuto::~MaskAuto()
204 {
205         masks.remove_all_objects();
206 }
207
208 int MaskAuto::operator==(Auto &that)
209 {
210         return identical((MaskAuto*)&that);
211 }
212
213
214
215 int MaskAuto::operator==(MaskAuto &that)
216 {
217         return identical((MaskAuto*)&that);
218 }
219
220
221 int MaskAuto::identical(MaskAuto *src)
222 {
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;
226
227         for( int i=0; i<masks.size(); ++i ) {
228                 if(!(*masks.values[i] == *src->masks.values[i])) return 0;
229         }
230         return 1;
231 }
232
233 void MaskAuto::update_parameter(MaskAuto *ref, MaskAuto *src)
234 {
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;
239
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));
243         }
244 }
245
246 void MaskAuto::copy_from(Auto *src)
247 {
248         copy_from((MaskAuto*)src);
249 }
250
251 void MaskAuto::copy_from(MaskAuto *src)
252 {
253         Auto::copy_from(src);
254         copy_data(src);
255 }
256
257 void MaskAuto::copy_data(MaskAuto *src)
258 {
259         apply_before_plugins = src->apply_before_plugins;
260         disable_opengl_masking = src->disable_opengl_masking;
261
262         masks.remove_all_objects();
263         for(int i = 0; i < src->masks.size(); i++)
264         {
265                 masks.append(new SubMask(this, i));
266                 masks.values[i]->copy_from(*src->masks.values[i]);
267         }
268 }
269
270 int MaskAuto::interpolate_from(Auto *a1, Auto *a2, int64_t position, Auto *templ) {
271         if(!a1) a1 = previous;
272         if(!a2) a2 = next;
273         MaskAuto  *mask_auto1 = (MaskAuto *)a1;
274         MaskAuto  *mask_auto2 = (MaskAuto *)a2;
275
276         if (!mask_auto2 || !mask_auto1 || mask_auto2->masks.total == 0)
277         // can't interpolate, fall back to copying (using template if possible)
278         {
279                 return Auto::interpolate_from(a1, a2, position, templ);
280         }
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();
285
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;
295
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);
304                 }
305         }
306         return 1;
307
308
309 }
310
311
312 SubMask* MaskAuto::get_submask(int number)
313 {
314         CLAMP(number, 0, masks.size() - 1);
315         return masks.values[number];
316 }
317
318 void MaskAuto::get_points(ArrayList<MaskPoint*> *points,
319         int submask)
320 {
321         points->remove_all_objects();
322         SubMask *submask_ptr = get_submask(submask);
323         for(int i = 0; i < submask_ptr->points.size(); i++)
324         {
325                 MaskPoint *point = new MaskPoint;
326                 point->copy_from(*submask_ptr->points.get(i));
327                 points->append(point);
328         }
329 }
330
331 void MaskAuto::set_points(ArrayList<MaskPoint*> *points,
332         int submask)
333 {
334         SubMask *submask_ptr = get_submask(submask);
335         submask_ptr->points.remove_all_objects();
336         for(int i = 0; i < points->size(); i++)
337         {
338                 MaskPoint *point = new MaskPoint;
339                 point->copy_from(*points->get(i));
340                 submask_ptr->points.append(point);
341         }
342 }
343
344
345 void MaskAuto::load(FileXML *file)
346 {
347 // legacy, moved to SubMask
348         int old_mode = file->tag.get_property("MODE", MASK_MULTIPLY_ALPHA);
349         int old_feather = file->tag.get_property("FEATHER", 0);
350         int old_value = file->tag.get_property("VALUE", 100);
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);
356         }
357
358         int result = 0;
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_SUBTRACT_ALPHA )
372                                 mask->fader = -mask->fader;
373                         mask->load(file);
374                 }
375         }
376 //      dump();
377 }
378
379 void MaskAuto::copy(int64_t start, int64_t end, FileXML *file, int default_auto)
380 {
381         file->tag.set_title("AUTO");
382         file->tag.set_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
383         file->tag.set_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
384         file->tag.set_property("POSITION", default_auto ? 0 : position - start);
385         file->append_tag();
386         file->append_newline();
387
388         for( int i=0; i<masks.size(); ++i )
389                 masks.values[i]->copy(file);
390
391         file->tag.set_title("/AUTO");
392         file->append_tag();
393         file->append_newline();
394 }
395
396 void MaskAuto::dump()
397 {
398         for( int i=0; i<masks.size(); ++i ) {
399                 printf("         submask %d\n", i);
400                 masks.values[i]->dump();
401         }
402 }
403
404 void MaskAuto::translate_submasks(float translate_x, float translate_y)
405 {
406         for( int i=0; i<masks.size(); ++i ) {
407                 SubMask *mask = get_submask(i);
408                 for( int j=0; j<mask->points.total; ++j ) {
409                         mask->points.values[j]->x += translate_x;
410                         mask->points.values[j]->y += translate_y;
411                 }
412         }
413 }
414
415 void MaskAuto::scale_submasks(int orig_scale, int new_scale)
416 {
417         for( int i=0; i<masks.size(); ++i ) {
418                 SubMask *mask = get_submask(i);
419                 for( int j=0; j<mask->points.total; ++j ) {
420                         float orig_x = mask->points.values[j]->x * orig_scale;
421                         float orig_y = mask->points.values[j]->y * orig_scale;
422                         mask->points.values[j]->x = orig_x / new_scale;
423                         mask->points.values[j]->y = orig_y / new_scale;
424
425                         orig_x = mask->points.values[j]->control_x1 * orig_scale;
426                         orig_y = mask->points.values[j]->control_y1 * orig_scale;
427                         mask->points.values[j]->control_x1 = orig_x / new_scale;
428                         mask->points.values[j]->control_y1 = orig_y / new_scale;
429
430                         orig_x = mask->points.values[j]->control_x2 * orig_scale;
431                         orig_y = mask->points.values[j]->control_y2 * orig_scale;
432                         mask->points.values[j]->control_x2 = orig_x / new_scale;
433                         mask->points.values[j]->control_y2 = orig_y / new_scale;
434                 }
435         }
436 }
437
438