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