4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
22 #include "bcsignals.h"
23 #include "condition.h"
26 #include "maskautos.h"
27 #include "maskengine.h"
29 #include "transportque.inc"
36 void write_mask(VFrame *vfrm, const char *fmt, ...)
38 va_list ap; va_start(ap, fmt);
39 char fn[256]; vsnprintf(fn, sizeof(fn), fmt, ap);
41 FILE *fp = !strcmp(fn,"-") ? stdout : fopen(fn,"w");
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);
52 MaskPackage::MaskPackage()
56 MaskPackage::~MaskPackage()
60 MaskUnit::MaskUnit(MaskEngine *engine)
63 this->engine = engine;
72 void MaskUnit::draw_line(int v, int ix1, int iy1, int ix2, int iy2)
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;
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);
91 rows[y][x] = rows[y][x] == v ? 0 : v;
95 void MaskUnit::draw_fill(int v)
97 VFrame *temp = engine->temp;
98 int temp_w = temp->get_w();
99 temp_t **rows = (temp_t**)temp->get_rows();
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 ) {
111 value = value ? 0 : v;
119 void MaskUnit::draw_feather(int ix1,int iy1, int ix2,int iy2)
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;
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;
135 if( dx2 >= 0 ) do { /* +Y, +X */
137 if( (m -= dx2) < 0 ) { m += dy2; ++x; }
139 else do { /* +Y, -X */
141 if( (m += dx2) < 0 ) { m += dy2; --x; }
145 if( dx2 >= 0 ) do { /* +X, +Y */
147 if( (m -= dy2) < 0 ) { m += dx2; ++y; }
149 else do { /* -X, +Y */
151 if( (m -= dy2) < 0 ) { m -= dx2; ++y; }
156 void MaskUnit::draw_spot(int ix, int iy)
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);
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;
180 void MaskUnit::process_package(LoadPackage *package)
182 MaskPackage *ptr = (MaskPackage*)package;
183 start_y = ptr->start_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;
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);
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];
211 if( engine->recalculate && engine->step == DO_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 ) {
225 psf[i] = (1-d)*fg + d*bg;
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);
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; \
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);
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) \
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; \
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; \
274 out_row[x*3 + 1] += chroma_offset*a / max; \
275 out_row[x*3 + 2] += chroma_offset*a / max; \
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); \
297 MaskEngine::MaskEngine(int cpus)
298 : LoadServer(cpus, 2*cpus)
299 // : LoadServer(1, 1)
305 MaskEngine::~MaskEngine()
309 for( int i = 0; i < point_sets.total; i++ )
310 point_sets[i]->remove_all_objects();
311 point_sets.remove_all_objects();
314 int MaskEngine::points_equivalent(ArrayList<MaskPoint*> *new_points,
315 ArrayList<MaskPoint*> *points)
317 //printf("MaskEngine::points_equivalent %d %d\n", new_points->total, points->total);
318 if( new_points->total != points->total ) return 0;
320 for( int i = 0; i < new_points->total; i++ ) {
321 if( !(*new_points->get(i) == *points->get(i)) ) return 0;
327 void MaskEngine::draw_edge(MaskEdge &edge, MaskPointSet &points)
329 if( points.size() < 2 ) return;
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 dx = ap->x - bp->x, dy = ap->y - bp->y;
336 int segments = (int)(sqrt(dx*dx + dy*dy));
337 if( !segments ) continue;
338 if( ap->control_x2 == 0 && ap->control_y2 == 0 &&
339 bp->control_x1 == 0 && bp->control_y1 == 0 )
341 float x0 = ap->x, y0 = ap->y;
342 float x1 = ap->x + ap->control_x2;
343 float y1 = ap->y + ap->control_y2;
344 float x2 = bp->x + bp->control_x1;
345 float y2 = bp->y + bp->control_y1;
346 float x3 = bp->x, y3 = bp->y;
348 for( int j = 0; j <= segments; ++j ) {
349 float t = (float)j / segments;
351 float tpow3 = t * t * t;
353 float invtpow2 = invt * invt;
354 float invtpow3 = invt * invt * invt;
356 int x = (invtpow3 * x0
357 + 3 * t * invtpow2 * x1
358 + 3 * tpow2 * invt * x2
360 int y = (invtpow3 * y0
361 + 3 * t * invtpow2 * y1
362 + 3 * tpow2 * invt * y2
369 void MaskEngine::do_mask(VFrame *output,
370 int64_t start_position_project,
371 MaskAutos *keyframe_set,
373 MaskAuto *default_auto)
375 this->output = output;
379 switch( output->get_color_model() ) {
382 mask_model = BC_A_FLOAT;
393 case BC_RGBA16161616:
395 case BC_YUVA16161616:
400 // Determine if recalculation is needed
403 int mask_w = output->get_w(), mask_h = output->get_h();
404 if( mask && ( mask->get_color_model() != mask_model ||
405 mask->get_w() != mask_w || mask->get_h() != mask_h ) ) {
406 delete mask; mask = 0;
409 if( temp && ( temp->get_w() != mask_w || temp->get_h() != mask_h ) ) {
410 delete temp; temp = 0;
413 total_submasks = keyframe_set->total_submasks(start_position_project, PLAY_FORWARD);
414 if( total_submasks != point_sets.size() )
417 for( int i=0; i<total_submasks && !recalculate; ++i ) {
418 float new_fader = keyframe_set->get_fader(start_position_project, i, PLAY_FORWARD);
419 if( new_fader != faders[i] ) { recalculate = 1; break; }
420 float new_feather = keyframe_set->get_feather(start_position_project, i, PLAY_FORWARD);
421 if( new_feather != feathers[i] ) { recalculate = 1; break; }
422 ArrayList<MaskPoint*> new_points;
423 keyframe_set->get_points(&new_points, i,
424 start_position_project, PLAY_FORWARD);
425 if( !points_equivalent(&new_points, point_sets[i]) )
427 new_points.remove_all_objects();
431 for( int i = 0; i < point_sets.total; i++ ) {
432 ArrayList<MaskPoint*> *points = point_sets[i];
433 points->remove_all_objects();
435 point_sets.remove_all_objects();
436 edges.remove_all_objects();
438 feathers.remove_all();
441 for( int i=0; i<total_submasks; ++i ) {
442 float fader = keyframe_set->get_fader(start_position_project, i, PLAY_FORWARD);
443 float v = fader / 100;
445 temp_t t = fabs(v) * 0xffff;
447 if( fade[0] < t ) fade[0] = t;
451 float feather = keyframe_set->get_feather(start_position_project, i, PLAY_FORWARD);
452 feathers.append(feather);
453 MaskPointSet *new_points = new MaskPointSet();
454 keyframe_set->get_points(new_points, i, start_position_project, PLAY_FORWARD);
455 point_sets.append(new_points);
456 draw_edge(*edges.append(new MaskEdge()), *new_points);
459 if( !mask ) mask = new VFrame(mask_w, mask_h, mask_model, 0);
460 if( !temp ) temp = new VFrame(mask_w, mask_h, BC_A16, 0);
475 void MaskEngine::init_packages()
478 //printf("MaskEngine::init_packages 1\n");
479 int x0 = 0, y0 = 0, i = 0, n = get_total_packages();
480 int out_w = output->get_w(), out_h = output->get_h();
483 MaskPackage *ptr = (MaskPackage*)get_package(i++);
484 int x1 = (out_w * i) / n, y1 = (out_h * i) / n;
485 ptr->start_x = x0; ptr->end_x = x1;
486 ptr->start_y = y0; ptr->end_y = y1;
490 //printf("MaskEngine::init_packages 2\n");
493 LoadClient* MaskEngine::new_client()
495 return new MaskUnit(this);
498 LoadPackage* MaskEngine::new_package()
500 return new MaskPackage;