74eec3a857bbf549f8d51bef2db2c48d80165de2
[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 }
73
74 SubMask::~SubMask()
75 {
76         points.remove_all_objects();
77 }
78
79 int SubMask::equivalent(SubMask& ptr)
80 {
81         if(points.size() != ptr.points.size()) return 0;
82
83         for(int i = 0; i < points.size(); i++)
84         {
85                 if(!(*points.get(i) == *ptr.points.get(i)))
86                         return 0;
87         }
88
89         return 1;
90 }
91
92
93 int SubMask::operator==(SubMask& ptr)
94 {
95         return equivalent(ptr);
96 }
97
98 void SubMask::copy_from(SubMask& ptr)
99 {
100         memset(name, 0, sizeof(name));
101         strncpy(name, ptr.name, sizeof(name-1));
102         points.remove_all_objects();
103 //printf("SubMask::copy_from 1 %p %d\n", this, ptr.points.total);
104         for(int i = 0; i < ptr.points.total; i++)
105         {
106                 MaskPoint *point = new MaskPoint;
107                 *point = *ptr.points.values[i];
108                 points.append(point);
109         }
110 }
111
112 void SubMask::load(FileXML *file)
113 {
114         points.remove_all_objects();
115
116         int result = 0;
117         while(!result)
118         {
119                 result = file->read_tag();
120
121                 if(!result)
122                 {
123                         if(file->tag.title_is("/MASK"))
124                         {
125                                 result = 1;
126                         }
127                         else
128                         if(file->tag.title_is("POINT"))
129                         {
130                                 XMLBuffer data;
131                                 file->read_text_until("/POINT", &data);
132
133                                 MaskPoint *point = new MaskPoint;
134                                 char *ptr = data.cstr();
135 //printf("MaskAuto::load 1 %s\n", ptr);
136
137                                 point->x = atof(ptr);
138                                 ptr = strchr(ptr, ',');
139 //printf("MaskAuto::load 2 %s\n", ptr + 1);
140                                 if(ptr)
141                                 {
142                                         point->y = atof(ptr + 1);
143                                         ptr = strchr(ptr + 1, ',');
144
145                                         if(ptr)
146                                         {
147 //printf("MaskAuto::load 3 %s\n", ptr + 1);
148                                                 point->control_x1 = atof(ptr + 1);
149                                                 ptr = strchr(ptr + 1, ',');
150                                                 if(ptr)
151                                                 {
152 //printf("MaskAuto::load 4 %s\n", ptr + 1);
153                                                         point->control_y1 = atof(ptr + 1);
154                                                         ptr = strchr(ptr + 1, ',');
155                                                         if(ptr)
156                                                         {
157 //printf("MaskAuto::load 5 %s\n", ptr + 1);
158                                                                 point->control_x2 = atof(ptr + 1);
159                                                                 ptr = strchr(ptr + 1, ',');
160                                                                 if(ptr) point->control_y2 = atof(ptr + 1);
161                                                         }
162                                                 }
163                                         }
164
165                                 }
166                                 points.append(point);
167                         }
168                 }
169         }
170 }
171
172 void SubMask::copy(FileXML *file)
173 {
174         if(points.total)
175         {
176                 file->tag.set_title("MASK");
177                 file->tag.set_property("NUMBER", keyframe->masks.number_of(this));
178                 file->tag.set_property("NAME", name);
179                 file->append_tag();
180                 file->append_newline();
181
182                 for(int i = 0; i < points.total; i++)
183                 {
184                         file->append_newline();
185                         file->tag.set_title("POINT");
186                         file->append_tag();
187                         char string[BCTEXTLEN];
188 //printf("SubMask::copy 1 %p %d %p\n", this, i, points.values[i]);
189                         sprintf(string, "%.7g, %.7g, %.7g, %.7g, %.7g, %.7g",
190                                 points.values[i]->x,
191                                 points.values[i]->y,
192                                 points.values[i]->control_x1,
193                                 points.values[i]->control_y1,
194                                 points.values[i]->control_x2,
195                                 points.values[i]->control_y2);
196 //printf("SubMask::copy 2\n");
197                         file->append_text(string);
198                         file->tag.set_title("/POINT");
199                         file->append_tag();
200                 }
201                 file->append_newline();
202
203                 file->tag.set_title("/MASK");
204                 file->append_tag();
205                 file->append_newline();
206         }
207 }
208
209 void SubMask::dump()
210 {
211         for(int i = 0; i < points.total; i++)
212         {
213                 printf("          point=%d x=%.2f y=%.2f in_x=%.2f in_y=%.2f out_x=%.2f out_y=%.2f\n",
214                         i,
215                         points.values[i]->x,
216                         points.values[i]->y,
217                         points.values[i]->control_x1,
218                         points.values[i]->control_y1,
219                         points.values[i]->control_x2,
220                         points.values[i]->control_y2);
221         }
222 }
223
224
225 MaskAuto::MaskAuto(EDL *edl, MaskAutos *autos)
226  : Auto(edl, autos)
227 {
228         mode = MASK_SUBTRACT_ALPHA;
229         feather = 0;
230         value = 100;
231         apply_before_plugins = 0;
232         disable_opengl_masking = 0;
233
234 // We define a fixed number of submasks so that interpolation for each
235 // submask matches.
236
237         for(int i = 0; i < SUBMASKS; i++)
238                 masks.append(new SubMask(this, i));
239 }
240
241 MaskAuto::~MaskAuto()
242 {
243         masks.remove_all_objects();
244 }
245
246 int MaskAuto::operator==(Auto &that)
247 {
248         return identical((MaskAuto*)&that);
249 }
250
251
252
253 int MaskAuto::operator==(MaskAuto &that)
254 {
255         return identical((MaskAuto*)&that);
256 }
257
258
259 int MaskAuto::identical(MaskAuto *src)
260 {
261         if(value != src->value ||
262                 mode != src->mode ||
263                 feather != src->feather ||
264                 masks.size() != src->masks.size() ||
265                 apply_before_plugins != src->apply_before_plugins ||
266                 disable_opengl_masking != src->disable_opengl_masking) return 0;
267
268         for(int i = 0; i < masks.size(); i++)
269                 if(!(*masks.values[i] == *src->masks.values[i])) return 0;
270
271         return 1;
272 }
273
274 void MaskAuto::update_parameter(MaskAuto *ref, MaskAuto *src)
275 {
276         if(src->value != ref->value)
277         {
278                 this->value = src->value;
279         }
280
281         if(src->mode != ref->mode)
282         {
283                 this->mode = src->mode;
284         }
285
286         if(!EQUIV(src->feather, ref->feather))
287         {
288                 this->feather = src->feather;
289         }
290
291         for(int i = 0; i < masks.size(); i++)
292         {
293                 if(!src->get_submask(i)->equivalent(*ref->get_submask(i)))
294                         this->get_submask(i)->copy_from(*src->get_submask(i));
295         }
296 }
297
298 void MaskAuto::copy_from(Auto *src)
299 {
300         copy_from((MaskAuto*)src);
301 }
302
303 void MaskAuto::copy_from(MaskAuto *src)
304 {
305         Auto::copy_from(src);
306         copy_data(src);
307 }
308
309 void MaskAuto::copy_data(MaskAuto *src)
310 {
311         mode = src->mode;
312         feather = src->feather;
313         value = src->value;
314         apply_before_plugins = src->apply_before_plugins;
315         disable_opengl_masking = src->disable_opengl_masking;
316
317         masks.remove_all_objects();
318         for(int i = 0; i < src->masks.size(); i++)
319         {
320                 masks.append(new SubMask(this, i));
321                 masks.values[i]->copy_from(*src->masks.values[i]);
322         }
323 }
324
325 int MaskAuto::interpolate_from(Auto *a1, Auto *a2, int64_t position, Auto *templ) {
326         if(!a1) a1 = previous;
327         if(!a2) a2 = next;
328         MaskAuto  *mask_auto1 = (MaskAuto *)a1;
329         MaskAuto  *mask_auto2 = (MaskAuto *)a2;
330
331         if (!mask_auto2 || !mask_auto1 || mask_auto2->masks.total == 0)
332         // can't interpolate, fall back to copying (using template if possible)
333         {
334                 return Auto::interpolate_from(a1, a2, position, templ);
335         }
336         this->mode = mask_auto1->mode;
337         this->feather = mask_auto1->feather;
338         this->value = mask_auto1->value;
339         this->apply_before_plugins = mask_auto1->apply_before_plugins;
340         this->disable_opengl_masking = mask_auto1->disable_opengl_masking;
341         this->position = position;
342         masks.remove_all_objects();
343
344         for(int i = 0;
345                 i < mask_auto1->masks.total;
346                 i++)
347         {
348                 SubMask *new_submask = new SubMask(this, i);
349                 masks.append(new_submask);
350                 SubMask *mask1 = mask_auto1->masks.values[i];
351                 SubMask *mask2 = mask_auto2->masks.values[i];
352
353                 // just in case, should never happen
354                 int total_points = MIN(mask1->points.total, mask2->points.total);
355                 for(int j = 0; j < total_points; j++)
356                 {
357                         MaskPoint *point = new MaskPoint;
358                         MaskAutos::avg_points(point,
359                                 mask1->points.values[j],
360                                 mask2->points.values[j],
361                                 position,
362                                 mask_auto1->position,
363                                 mask_auto2->position);
364                         new_submask->points.append(point);
365                 }
366         }
367         return 1;
368
369
370 }
371
372
373 SubMask* MaskAuto::get_submask(int number)
374 {
375         CLAMP(number, 0, masks.size() - 1);
376         return masks.values[number];
377 }
378
379 void MaskAuto::get_points(ArrayList<MaskPoint*> *points,
380         int submask)
381 {
382         points->remove_all_objects();
383         SubMask *submask_ptr = get_submask(submask);
384         for(int i = 0; i < submask_ptr->points.size(); i++)
385         {
386                 MaskPoint *point = new MaskPoint;
387                 point->copy_from(*submask_ptr->points.get(i));
388                 points->append(point);
389         }
390 }
391
392 void MaskAuto::set_points(ArrayList<MaskPoint*> *points,
393         int submask)
394 {
395         SubMask *submask_ptr = get_submask(submask);
396         submask_ptr->points.remove_all_objects();
397         for(int i = 0; i < points->size(); i++)
398         {
399                 MaskPoint *point = new MaskPoint;
400                 point->copy_from(*points->get(i));
401                 submask_ptr->points.append(point);
402         }
403 }
404
405
406 void MaskAuto::load(FileXML *file)
407 {
408         mode = file->tag.get_property("MODE", mode);
409         feather = file->tag.get_property("FEATHER", feather);
410         value = file->tag.get_property("VALUE", value);
411         apply_before_plugins = file->tag.get_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
412         disable_opengl_masking = file->tag.get_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
413         for(int i = 0; i < masks.size(); i++)
414         {
415                 delete masks.values[i];
416                 masks.values[i] = new SubMask(this, i);
417         }
418
419         int result = 0;
420         while( !(result = file->read_tag()) ) {
421                 if( file->tag.title_is("/AUTO") ) break;
422                 if( file->tag.title_is("MASK") ) {
423                         int no = file->tag.get_property("NUMBER", 0);
424                         char name[BCTEXTLEN];  name[0] = 0;
425                         file->tag.get_property("NAME", name);
426                         if( !name[0] ) sprintf(name, "%d", no);
427                         SubMask *mask = masks.values[no];
428                         memset(mask->name, 0, sizeof(mask->name));
429                         strncpy(mask->name, name, sizeof(mask->name));
430                         mask->load(file);
431                 }
432         }
433 //      dump();
434 }
435
436 void MaskAuto::copy(int64_t start, int64_t end, FileXML *file, int default_auto)
437 {
438         file->tag.set_title("AUTO");
439         file->tag.set_property("MODE", mode);
440         file->tag.set_property("VALUE", value);
441         file->tag.set_property("FEATHER", feather);
442         file->tag.set_property("APPLY_BEFORE_PLUGINS", apply_before_plugins);
443         file->tag.set_property("DISABLE_OPENGL_MASKING", disable_opengl_masking);
444
445         if(default_auto)
446                 file->tag.set_property("POSITION", 0);
447         else
448                 file->tag.set_property("POSITION", position - start);
449         file->append_tag();
450         file->append_newline();
451
452         for(int i = 0; i < masks.size(); i++)
453         {
454 //printf("MaskAuto::copy 1 %p %d %p\n", this, i, masks.values[i]);
455                 masks.values[i]->copy(file);
456 //printf("MaskAuto::copy 10\n");
457         }
458
459         file->tag.set_title("/AUTO");
460         file->append_tag();
461         file->append_newline();
462 }
463
464 void MaskAuto::dump()
465 {
466         printf("         mode=%d value=%d\n", mode, value);
467         for(int i = 0; i < masks.size(); i++)
468         {
469                 printf("         submask %d\n", i);
470                 masks.values[i]->dump();
471         }
472 }
473
474 void MaskAuto::translate_submasks(float translate_x, float translate_y)
475 {
476         for(int i = 0; i < masks.size(); i++)
477         {
478                 SubMask *mask = get_submask(i);
479                 for (int j = 0; j < mask->points.total; j++)
480                 {
481                         mask->points.values[j]->x += translate_x;
482                         mask->points.values[j]->y += translate_y;
483                 }
484         }
485 }
486
487 void MaskAuto::scale_submasks(int orig_scale, int new_scale)
488 {
489         for(int i = 0; i < masks.size(); i++)
490         {
491                 SubMask *mask = get_submask(i);
492                 for (int j = 0; j < mask->points.total; j++)
493                 {
494                         float orig_x = mask->points.values[j]->x * orig_scale;
495                         float orig_y = mask->points.values[j]->y * orig_scale;
496                         mask->points.values[j]->x = orig_x / new_scale;
497                         mask->points.values[j]->y = orig_y / new_scale;
498
499                         orig_x = mask->points.values[j]->control_x1 * orig_scale;
500                         orig_y = mask->points.values[j]->control_y1 * orig_scale;
501                         mask->points.values[j]->control_x1 = orig_x / new_scale;
502                         mask->points.values[j]->control_y1 = orig_y / new_scale;
503
504                         orig_x = mask->points.values[j]->control_x2 * orig_scale;
505                         orig_y = mask->points.values[j]->control_y2 * orig_scale;
506                         mask->points.values[j]->control_x2 = orig_x / new_scale;
507                         mask->points.values[j]->control_y2 = orig_y / new_scale;
508                 }
509         }
510 }
511
512