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