589b7938a875b9dc27a1aba95ee193d91dfa5456
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / maskengine.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 "bcsignals.h"
23 #include "condition.h"
24 #include "clip.h"
25 #include "maskauto.h"
26 #include "maskautos.h"
27 #include "maskengine.h"
28 #include "mutex.h"
29 #include "transportque.inc"
30 #include "vframe.h"
31
32 #include <math.h>
33 #include <stdint.h>
34 #include <string.h>
35
36 void write_mask(VFrame *vfrm, const char *fmt, ...)
37 {
38   va_list ap;    va_start(ap, fmt);
39   char fn[256];  vsnprintf(fn, sizeof(fn), fmt, ap);
40   va_end(ap);
41   FILE *fp = !strcmp(fn,"-") ? stdout : fopen(fn,"w");
42   if( fp ) {
43     int w = vfrm->get_w(), h = vfrm->get_h();
44     int m = vfrm->get_color_model();
45     fprintf(fp,"P5\n%d %d\n%d\n",w,h,m==BC_A8? 0xff : 0xffff);
46     int bpp = m==BC_A8? 1 : 2;
47     fwrite(vfrm->get_data(),bpp*w,h,fp);  fflush(fp);
48     if( fp != stdout ) fclose(fp);
49   }
50 }
51
52 MaskPackage::MaskPackage()
53 {
54 }
55
56 MaskPackage::~MaskPackage()
57 {
58 }
59
60 MaskUnit::MaskUnit(MaskEngine *engine)
61  : LoadClient(engine)
62 {
63         this->engine = engine;
64         spot = 0;
65         r = 0;
66 }
67
68 MaskUnit::~MaskUnit()
69 {
70 }
71
72 void MaskUnit::draw_line(int v, int ix1, int iy1, int ix2, int iy2)
73 {
74         if( iy1 == iy2 ) return;
75         int x1 = iy1 < iy2 ? ix1 : ix2;
76         int y1 = iy1 < iy2 ? iy1 : iy2;
77         int x2 = iy1 < iy2 ? ix2 : ix1;
78         int y2 = iy1 < iy2 ? iy2 : iy1;
79         float slope = (float)(x2-x1) / (y2-y1);
80         int dy = y1 - start_y;
81         int i = dy < 0 ? (y1=start_y, -dy) : 0;
82         if( y2 > end_y ) y2 = end_y;
83         if( y2 < start_y || y1 >= end_y ) return;
84
85         VFrame *temp = engine->temp;
86         int w1 = temp->get_w()-1;
87         temp_t **rows = (temp_t **)temp->get_rows();
88         for( int y=y1; y<y2; ++i,++y ) {
89                 int x = (int)(i*slope + x1);
90                 bclamp(x, 0, w1);
91                 rows[y][x] = rows[y][x] == v ? 0 : v;
92         }
93 }
94
95 void MaskUnit::draw_fill(int v)
96 {
97         VFrame *temp = engine->temp;
98         int temp_w = temp->get_w();
99         temp_t **rows = (temp_t**)temp->get_rows();
100
101         for( int y=start_y; y<end_y; ++y ) {
102                 temp_t *row = rows[y];
103                 int value = 0, total = 0;
104                 for( int x=0; x<temp_w; ++x )
105                         if( row[x] == v ) ++total;
106                 if( total < 2 ) continue;
107                 if( total & 0x1 ) --total;
108                 for( int x=0; x<temp_w; ++x ) {
109                         if( row[x]==v && total>0 ) {
110                                 --total;
111                                 value = value ? 0 : v;
112                         }
113                         else if( value )
114                                 row[x] = value;
115                 }
116         }
117 }
118
119 void MaskUnit::draw_feather(int ix1,int iy1, int ix2,int iy2)
120 {
121         int x1 = iy1 < iy2 ? ix1 : ix2;
122         int y1 = iy1 < iy2 ? iy1 : iy2;
123         int x2 = iy1 < iy2 ? ix2 : ix1;
124         int y2 = iy1 < iy2 ? iy2 : iy1;
125         VFrame *temp = engine->temp;
126         int h = temp->get_h();
127         if( y2 < 0 || y1 >= h ) return;
128
129         int x = x1, y = y1;
130         int dx = x2-x1, dy = y2-y1;
131         int dx2 = 2*dx, dy2 = 2*dy;
132         if( dx < 0 ) dx = -dx;
133         int m = dx > dy ? dx : dy, n = m;
134         if( dy >= dx ) {
135                 if( dx2 >= 0 ) do {     /* +Y, +X */
136                         draw_spot(x, y++);
137                         if( (m -= dx2) < 0 ) { m += dy2;  ++x; }
138                 } while( --n >= 0 );
139                 else do {              /* +Y, -X */
140                         draw_spot(x, y++);
141                         if( (m += dx2) < 0 ) { m += dy2;  --x; }
142                 } while( --n >= 0 );
143         }
144         else {
145                 if( dx2 >= 0 ) do {     /* +X, +Y */
146                         draw_spot(x++, y);
147                         if( (m -= dy2) < 0 ) { m += dx2;  ++y; }
148                 } while( --n >= 0 );
149                 else do {              /* -X, +Y */
150                         draw_spot(x--, y);
151                         if( (m -= dy2) < 0 ) { m -= dx2;  ++y; }
152                 } while( --n >= 0 );
153         }
154 }
155
156 void MaskUnit::draw_spot(int ix, int iy)
157 {
158         int rr = r * r, n = abs(r), rv = r * v;
159         if( iy < start_y-n || iy >= end_y+n ) return;
160         VFrame *temp = engine->temp;
161         int w1 = temp->get_w()-1, h1 = temp->get_h()-1;
162         int xs = ix - n;  bclamp(xs, 0, w1);
163         int xn = ix + n;  bclamp(xn, 0, w1);
164         int ys = iy - n;  bclamp(ys, 0, h1);
165         int yn = iy + n;  bclamp(yn, 0, h1);
166
167         temp_t **rows = (temp_t**)temp->get_rows();
168         for( int y=ys ; y<=yn; ++y ) {
169                 temp_t *row = rows[y];
170                 for( int x=xs; x<=xn; ++x ) {
171                         int dx = x-ix, dy = y-iy;
172                         int dd = dx*dx + dy*dy;
173                         if( dd >= rr ) continue;
174                         temp_t *rp = &row[x], a = spot[dd];
175                         if( rv*(*rp-a) < 0 ) *rp = a;
176                 }
177         }
178 }
179
180 void MaskUnit::process_package(LoadPackage *package)
181 {
182         MaskPackage *ptr = (MaskPackage*)package;
183         start_y = ptr->start_y;
184         end_y = ptr->end_y;
185         if( start_y >= end_y ) return;
186         mask_model = engine->mask->get_color_model();
187         VFrame *temp = engine->temp;
188         if( engine->recalculate && engine->step == DO_MASK ) {
189 // Draw masked region of polygons on temp
190                 for( int k=0; k<engine->edges.size(); ++k ) {
191                         if( !engine->edges[k] ) continue;
192                         MaskEdge &edge = *engine->edges[k];
193                         if( edge.size() < 3 ) continue;
194                         int v = k + 1;
195                         for( int i=0; i<edge.size(); ++i ) {
196                                 MaskCoord a = edge[i];
197                                 MaskCoord b = i<edge.size()-1 ? edge[i+1] : edge[0];
198                                 draw_line(v, a.x,a.y, b.x,b.y);
199                         }
200                         draw_fill(v);
201                 }
202 // map temp to fader alpha
203                 int temp_w = temp->get_w();
204                 temp_t **rows = (temp_t**)temp->get_rows();
205                 temp_t *fade = engine->fade;
206                 for( int y=start_y; y<end_y; ++y ) {
207                         temp_t *tp = rows[y];
208                         for( int i=temp_w; --i>=0; ++tp ) *tp = fade[*tp];
209                 }
210         }
211         if( engine->recalculate && engine->step == DO_FEATHER ) {
212 // draw feather
213                 for( int k=0; k<engine->edges.size(); ++k ) {
214                         if( !(v = engine->faders[k]) ) continue;
215                         if( !(r = engine->feathers[k]) ) continue;
216                         MaskEdge &edge = *engine->edges[k];
217                         if( !edge.size() ) continue;
218                         float rv = r * v, vv = fabs(v);
219                         int fg = 0xffff * (rv >= 0 ? vv : 0);
220                         int bg = 0xffff * (rv >= 0 ? 0 : vv);
221                         int rr = r*r;  double dr = 1./rr;
222                         temp_t psf[rr+1];  spot = psf;
223                         for( int i=0; i<=rr; ++i ) {
224                                 double d = i*dr;
225                                 psf[i] = (1-d)*fg + d*bg;
226                         }
227                         int n = edge.size();
228                         for( int i=0; i<n; ++i ) {
229                                 MaskCoord &a = edge[i];
230                                 MaskCoord &b = i<edge.size()-1 ? edge[i+1] : edge[0];
231                                 draw_feather(a.x,a.y, b.x,b.y);
232                         }
233                 }
234
235 #define REMAP(cmodel, type, expr) case cmodel: { \
236 type **msk_rows = (type**)engine->mask->get_rows(); \
237 for( int y=start_y; y<end_y; ++y ) { \
238         temp_t *rp = rows[y]; \
239         type *mp = msk_rows[y]; \
240         for( int i=temp_w; --i>=0; ++rp,++mp ) *mp = expr; \
241 } } break
242 // map alpha to mask
243                 const float to_flt = 1/65535.;
244                 int temp_w = temp->get_w();
245                 temp_t **rows = (temp_t**)temp->get_rows();
246                 switch( mask_model ) {
247                 REMAP(BC_A8, uint8_t, *rp >> 8);
248                 REMAP(BC_A16, uint16_t, *rp);
249                 REMAP(BC_A_FLOAT, float, *rp * to_flt);
250                 }
251         }
252
253 // Apply mask
254         if( engine->step == DO_APPLY ) {
255                 int mask_w = engine->mask->get_w();
256                 uint8_t **out_rows = engine->output->get_rows();
257                 uint8_t **msk_rows = engine->mask->get_rows();
258 #define APPLY_MASK_ALPHA(cmodel, type, max, components, do_yuv) \
259 case cmodel: \
260 for( int y=ptr->start_y; y<ptr->end_y; ++y ) { \
261         type *out_row = (type*)out_rows[y]; \
262         type *msk_row = (type*)msk_rows[y]; \
263         type chroma_offset = (int)(max + 1) / 2; \
264         for( int x=0; x<mask_w; ++x ) { \
265                 type a = msk_row[x], b = max-a; \
266                 if( components == 4 ) { \
267                         out_row[x*4 + 3] = out_row[x*4 + 3]*b / max; \
268                 } \
269                 else { \
270                         out_row[x*3 + 0] = out_row[x*3 + 0]*b / max; \
271                         out_row[x*3 + 1] = out_row[x*3 + 1]*b / max; \
272                         out_row[x*3 + 2] = out_row[x*3 + 2]*b / max; \
273                         if( do_yuv ) { \
274                                 out_row[x*3 + 1] += chroma_offset*a / max; \
275                                 out_row[x*3 + 2] += chroma_offset*a / max; \
276                         } \
277                 } \
278         } \
279 } break
280
281                 switch( engine->output->get_color_model() ) { \
282                 APPLY_MASK_ALPHA(BC_RGB888, uint8_t, 0xff, 3, 0); \
283                 APPLY_MASK_ALPHA(BC_RGB_FLOAT, float, 1.0, 3, 0); \
284                 APPLY_MASK_ALPHA(BC_YUV888, uint8_t, 0xff, 3, 1); \
285                 APPLY_MASK_ALPHA(BC_RGBA_FLOAT, float, 1.0, 4, 0); \
286                 APPLY_MASK_ALPHA(BC_YUVA8888, uint8_t, 0xff, 4, 1); \
287                 APPLY_MASK_ALPHA(BC_RGBA8888, uint8_t, 0xff, 4, 0); \
288                 APPLY_MASK_ALPHA(BC_RGB161616, uint16_t, 0xffff, 3, 0); \
289                 APPLY_MASK_ALPHA(BC_YUV161616, uint16_t, 0xffff, 3, 1); \
290                 APPLY_MASK_ALPHA(BC_YUVA16161616, uint16_t, 0xffff, 4, 1); \
291                 APPLY_MASK_ALPHA(BC_RGBA16161616, uint16_t, 0xffff, 4, 0); \
292                 }
293         }
294 }
295
296
297 MaskEngine::MaskEngine(int cpus)
298  : LoadServer(cpus, 2*cpus)
299 // : LoadServer(1, 1)
300 {
301         mask = 0;
302         temp = 0;
303 }
304
305 MaskEngine::~MaskEngine()
306 {
307         delete mask;
308         delete temp;
309         for( int i = 0; i < point_sets.total; i++ )
310                 point_sets[i]->remove_all_objects();
311         point_sets.remove_all_objects();
312 }
313
314 int MaskEngine::points_equivalent(ArrayList<MaskPoint*> *new_points,
315         ArrayList<MaskPoint*> *points)
316 {
317 //printf("MaskEngine::points_equivalent %d %d\n", new_points->total, points->total);
318         if( new_points->total != points->total ) return 0;
319
320         for( int i = 0; i < new_points->total; i++ ) {
321                 if( !(*new_points->get(i) == *points->get(i)) ) return 0;
322         }
323
324         return 1;
325 }
326
327 void MaskEngine::draw_edge(MaskEdge &edge, MaskPointSet &points)
328 {
329         if( points.size() < 2 ) return;
330         edge.remove_all();
331         for( int i=0; i<points.size(); ++i ) {
332                 MaskPoint *ap = points[i];
333                 MaskPoint *bp = (i>=points.size()-1) ?
334                                 points[0] : points[i+1];
335                 int segments = 0;
336                 if( ap->control_x2 == 0 && ap->control_y2 == 0 &&
337                     bp->control_x1 == 0 && bp->control_y1 == 0 )
338                         segments = 1;
339                 float x0 = ap->x, y0 = ap->y;
340                 float x1 = ap->x + ap->control_x2;
341                 float y1 = ap->y + ap->control_y2;
342                 float x2 = bp->x + bp->control_x1;
343                 float y2 = bp->y + bp->control_y1;
344                 float x3 = bp->x, y3 = bp->y;
345
346 // from Playback3D::do_mask_sync
347                 float cx3 = -  x0 + 3*x1 - 3*x2 + x3;
348                 float cx2 =  3*x0 - 6*x1 + 3*x2;
349                 float cx1 = -3*x0 + 3*x1;
350                 float cx0 =    x0;
351
352                 float cy3 = -  y0 + 3*y1 - 3*y2 + y3;
353                 float cy2 =  3*y0 - 6*y1 + 3*y2;
354                 float cy1 = -3*y0 + 3*y1;
355                 float cy0 =    y0;
356
357                 if( segments == 0 ) {
358                         float maxaccel1 = fabs(2*cy2) + fabs(6*cy3);
359                         float maxaccel2 = fabs(2*cx2) + fabs(6*cx3);
360                         float maxaccel = maxaccel1 > maxaccel2 ? maxaccel1 : maxaccel2;
361                         float h = 1.0;
362                         if( maxaccel > 8.0 ) h = sqrt((8.0) / maxaccel);
363                         segments = int(1/h);
364                 }
365
366                 for( int j = 0; j <= segments; ++j ) {
367                         float t = (float)j / segments;
368                         float x = cx0 + t*(cx1 + t*(cx2 + t*cx3));
369                         float y = cy0 + t*(cy1 + t*(cy2 + t*cy3));
370                         edge.append(x, y);
371                 }
372         }
373 }
374
375 void MaskEngine::do_mask(VFrame *output,
376         int64_t start_position_project,
377         MaskAutos *keyframe_set,
378         MaskAuto *keyframe,
379         MaskAuto *default_auto)
380 {
381         this->output = output;
382         recalculate = 0;
383         int mask_model = 0;
384
385         switch( output->get_color_model() ) {
386         case BC_RGB_FLOAT:
387         case BC_RGBA_FLOAT:
388                 mask_model = BC_A_FLOAT;
389                 break;
390
391         case BC_RGB888:
392         case BC_RGBA8888:
393         case BC_YUV888:
394         case BC_YUVA8888:
395                 mask_model = BC_A8;
396                 break;
397
398         case BC_RGB161616:
399         case BC_RGBA16161616:
400         case BC_YUV161616:
401         case BC_YUVA16161616:
402                 mask_model = BC_A16;
403                 break;
404         }
405
406 // Determine if recalculation is needed
407 SET_TRACE
408
409         int mask_w = output->get_w(), mask_h = output->get_h();
410         if( mask && ( mask->get_color_model() != mask_model ||
411             mask->get_w() != mask_w || mask->get_h() != mask_h ) ) {
412                 delete mask;  mask = 0;
413                 recalculate = 1;
414         }
415         if( temp && ( temp->get_w() != mask_w || temp->get_h() != mask_h ) ) {
416                 delete temp;  temp = 0;
417         }
418
419         total_submasks = keyframe_set->total_submasks(start_position_project, PLAY_FORWARD);
420         if( total_submasks != point_sets.size() )
421                 recalculate = 1;
422
423         for( int i=0; i<total_submasks && !recalculate; ++i ) {
424                 float new_fader = keyframe_set->get_fader(start_position_project, i, PLAY_FORWARD);
425                 if( new_fader != faders[i] ) { recalculate = 1;  break; }
426                 float new_feather = keyframe_set->get_feather(start_position_project, i, PLAY_FORWARD);
427                 if( new_feather != feathers[i] ) { recalculate = 1;  break; }
428                 ArrayList<MaskPoint*> new_points;
429                 keyframe_set->get_points(&new_points, i,
430                                 start_position_project, PLAY_FORWARD);
431                 if( !points_equivalent(&new_points, point_sets[i]) )
432                         recalculate = 1;
433                 new_points.remove_all_objects();
434         }
435
436         if( recalculate ) {
437                 for( int i = 0; i < point_sets.total; i++ ) {
438                         ArrayList<MaskPoint*> *points = point_sets[i];
439                         points->remove_all_objects();
440                 }
441                 point_sets.remove_all_objects();
442                 edges.remove_all_objects();
443                 faders.remove_all();
444                 feathers.remove_all();
445                 fade[0] = 0;
446
447                 for( int i=0; i<total_submasks; ++i ) {
448                         float fader = keyframe_set->get_fader(start_position_project, i, PLAY_FORWARD);
449                         float v = fader / 100;
450                         faders.append(v);
451                         temp_t t = fabs(v) * 0xffff;
452                         if( fader < 0 ) {
453                                 if( fade[0] < t ) fade[0] = t;
454                                 t = 0;
455                         }
456                         fade[i+1] = t;
457                         float feather = keyframe_set->get_feather(start_position_project, i, PLAY_FORWARD);
458                         feathers.append(feather);
459                         MaskPointSet *new_points = new MaskPointSet();
460                         keyframe_set->get_points(new_points, i, start_position_project, PLAY_FORWARD);
461                         point_sets.append(new_points);
462                         draw_edge(*edges.append(new MaskEdge()), *new_points);
463                 }
464 // draw mask
465                 if( !mask ) mask = new VFrame(mask_w, mask_h, mask_model, 0);
466                 if( !temp ) temp = new VFrame(mask_w, mask_h, BC_A16, 0);
467                 mask->clear_frame();
468                 temp->clear_frame();
469                 step = DO_MASK;
470                 process_packages();
471                 step = DO_FEATHER;
472                 process_packages();
473         }
474 // Run units
475 SET_TRACE
476         step = DO_APPLY;
477         process_packages();
478 SET_TRACE
479 }
480
481 void MaskEngine::init_packages()
482 {
483 SET_TRACE
484 //printf("MaskEngine::init_packages 1\n");
485         int x0 = 0, y0 = 0, i = 0, n = get_total_packages();
486         int out_w = output->get_w(), out_h = output->get_h();
487 SET_TRACE
488         while( i < n ) {
489                 MaskPackage *ptr = (MaskPackage*)get_package(i++);
490                 int x1 = (out_w * i) / n, y1 = (out_h * i) / n;
491                 ptr->start_x = x0;  ptr->end_x = x1;
492                 ptr->start_y = y0;  ptr->end_y = y1;
493                 x0 = x1;  y0 = y1;
494         }
495 SET_TRACE
496 //printf("MaskEngine::init_packages 2\n");
497 }
498
499 LoadClient* MaskEngine::new_client()
500 {
501         return new MaskUnit(this);
502 }
503
504 LoadPackage* MaskEngine::new_package()
505 {
506         return new MaskPackage;
507 }
508