3 * Copyright (C) 1997-2015 Adam Williams <broadcast at earthling dot net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "arraylist.h"
28 #include "bccmodels.h"
32 #include "edlsession.h"
35 #include "keyframes.h"
37 #include "transportque.inc"
38 #include "tracerwindow.h"
42 REGISTER_PLUGIN(Tracer)
44 void tracer_pgm(const char *fn,VFrame *vfrm)
46 FILE *fp = fopen(fn,"w");
47 int w = vfrm->get_w(), h = vfrm->get_h();
48 fprintf(fp,"P5\n%d %d\n255\n",w,h);
49 fwrite(vfrm->get_data(),w,h,fp);
53 TracerPoint::TracerPoint(float x, float y)
55 this->x = x; this->y = y;
57 TracerPoint::~TracerPoint()
61 TracerConfig::TracerConfig()
63 drag = draw = 1; fill = 0;
64 feather = 0; radius = 1;
65 invert = 0; selected = -1;
67 TracerConfig::~TracerConfig()
71 int TracerConfig::equivalent(TracerConfig &that)
73 if( this->drag != that.drag ) return 0;
74 if( this->draw != that.draw ) return 0;
75 if( this->fill != that.fill ) return 0;
76 if( this->feather != that.feather ) return 0;
77 if( this->invert != that.invert ) return 0;
78 if( this->radius != that.radius ) return 0;
79 if( this->points.size() != that.points.size() ) return 0;
80 for( int i=0, n=points.size(); i<n; ++i ) {
81 TracerPoint *ap = this->points[i], *bp = that.points[i];
82 if( !EQUIV(ap->x, bp->x) ) return 0;
83 if( !EQUIV(ap->y, bp->y) ) return 0;
88 void TracerConfig::copy_from(TracerConfig &that)
90 this->drag = that.drag;
91 this->draw = that.draw;
92 this->fill = that.fill;
93 this->selected = that.selected;
94 this->feather = that.feather;
95 this->invert = that.invert;
96 this->radius = that.radius;
97 points.remove_all_objects();
98 for( int i=0,n=that.points.size(); i<n; ++i ) {
99 TracerPoint *pt = that.points[i];
100 add_point(pt->x, pt->y);
104 void TracerConfig::interpolate(TracerConfig &prev, TracerConfig &next,
105 long prev_frame, long next_frame, long current_frame)
110 void TracerConfig::limits()
114 int TracerConfig::add_point(float x, float y)
116 int i = points.size();
117 points.append(new TracerPoint(x, y));
121 void TracerConfig::del_point(int i)
123 points.remove_object_number(i);
127 Tracer::Tracer(PluginServer *server)
128 : PluginVClient(server)
130 frm = 0; frm_rows = 0;
131 msk = 0; msk_rows = 0;
132 edg = 0; edg_rows = 0;
135 color_model = bpp = 0;
136 is_float = is_yuv = 0;
150 const char* Tracer::plugin_title() { return N_("Tracer"); }
151 int Tracer::is_realtime() { return 1; }
153 NEW_WINDOW_MACRO(Tracer, TracerWindow);
154 int Tracer::load_configuration1()
156 KeyFrame *prev_keyframe = get_prev_keyframe(get_source_position());
157 if( prev_keyframe->position == get_source_position() ) {
158 read_data(prev_keyframe);
161 return load_configuration();
163 LOAD_CONFIGURATION_MACRO(Tracer, TracerConfig);
165 int Tracer::new_point()
167 EDLSession *session = get_edl()->session;
168 float x = !session ? 0.f : session->output_w / 2.f;
169 float y = !session ? 0.f : session->output_h / 2.f;
170 return config.add_point(x, y);
173 void TracerConfig::save_data(KeyFrame *keyframe)
177 // cause data to be stored directly in text
178 output.set_shared_output(keyframe->xbuf);
180 output.tag.set_title("TRACER");
181 output.tag.set_property("DRAG", drag);
182 output.tag.set_property("DRAW", draw);
183 output.tag.set_property("FILL", fill);
184 output.tag.set_property("FEATHER", feather);
185 output.tag.set_property("RADIUS", radius);
186 output.tag.set_property("INVERT", invert);
187 output.tag.set_property("SELECTED", selected);
189 output.append_newline();
190 output.tag.set_title("/TRACER");
192 output.append_newline();
193 for( int i=0, n=points.size(); i<n; ++i ) {
194 TracerPoint *pt = points[i];
195 char point[BCSTRLEN];
196 sprintf(point,"/POINT_%d",i+1);
197 output.tag.set_title(point+1);
198 output.tag.set_property("X", pt->x);
199 output.tag.set_property("Y", pt->y);
201 output.tag.set_title(point+0);
203 output.append_newline();
205 output.terminate_string();
208 void Tracer::save_data(KeyFrame *keyframe)
210 config.save_data(keyframe);
213 void TracerConfig::read_data(KeyFrame *keyframe)
216 input.set_shared_input(keyframe->xbuf);
217 points.remove_all_objects();
220 while( !(result=input.read_tag()) ) {
221 if( input.tag.title_is("TRACER") ) {
222 drag = input.tag.get_property("DRAG", drag);
223 draw = input.tag.get_property("DRAW", draw);
224 fill = input.tag.get_property("FILL", fill);
225 feather = input.tag.get_property("FEATHER", feather);
226 radius = input.tag.get_property("RADIUS", radius);
227 invert = input.tag.get_property("INVERT", invert);
228 selected = input.tag.get_property("SELECTED", 0);
231 else if( !strncmp(input.tag.get_title(),"POINT_",6) ) {
232 float x = input.tag.get_property("X", 0.f);
233 float y = input.tag.get_property("Y", 0.f);
239 void Tracer::read_data(KeyFrame *keyframe)
241 config.read_data(keyframe);
244 void Tracer::span_keyframes(KeyFrame *src, int64_t start, int64_t end)
246 TracerConfig src_config;
247 src_config.read_data(src);
248 KeyFrames *keyframes = (KeyFrames *)src->autos;
249 KeyFrame *prev = keyframes->get_prev_keyframe(start, PLAY_FORWARD);
250 TracerConfig prev_config;
251 prev_config.read_data(prev);
252 // Always update the first one
253 update_parameter(prev_config, src_config, prev);
254 KeyFrame *curr = (KeyFrame*)prev->next;
255 while( curr && curr->position < end ) {
256 update_parameter(prev_config, src_config, curr);
257 curr = (KeyFrame*)curr->next;
261 void TracerPoint::update_parameter(TracerPoint *prev, TracerPoint *src)
263 if( prev->x != src->x ) x = src->x;
264 if( prev->y != src->y ) y = src->y;
267 void Tracer::update_parameter(TracerConfig &prev_config, TracerConfig &src_config,
270 TracerConfig dst_config;
271 dst_config.read_data(keyframe);
272 if( prev_config.drag != src_config.drag )
273 dst_config.drag = src_config.drag;
274 if( prev_config.draw != src_config.draw )
275 dst_config.draw = src_config.draw;
276 if( prev_config.fill != src_config.fill )
277 dst_config.fill = src_config.fill;
278 if( prev_config.feather != src_config.feather )
279 dst_config.feather = src_config.feather;
280 if( prev_config.invert != src_config.invert )
281 dst_config.invert = src_config.invert;
282 if( prev_config.radius != src_config.radius )
283 dst_config.radius = src_config.radius;
284 int src_points = src_config.points.size();
285 int dst_points = dst_config.points.size();
286 int prev_points = prev_config.points.size();
287 int npoints = bmin(prev_points, bmin(src_points, dst_points));
288 for( int i=0; i<npoints; ++i ) {
289 TracerPoint *prev_point = prev_config.points[i];
290 TracerPoint *src_point = src_config.points[i];
291 TracerPoint *dst_point = dst_config.points[i];
292 dst_point->update_parameter(prev_point, src_point);
294 dst_config.save_data(keyframe);
297 void Tracer::update_gui()
299 if( !thread ) return;
300 thread->window->lock_window("Tracer::update_gui");
301 TracerWindow *window = (TracerWindow*)thread->window;
302 if( load_configuration1() ) {
303 window->update_gui();
306 thread->window->unlock_window();
309 void Tracer::draw_point(TracerPoint *pt)
311 int d = bmax(w,h) / 200 + 2;
312 int r = d/2+1, x = pt->x, y = pt->y;
313 frm->draw_smooth(x-r,y+0, x-r, y-r, x+0,y-r);
314 frm->draw_smooth(x+0,y-r, x+r, y-r, x+r,y+0);
315 frm->draw_smooth(x+r,y+0, x+r, y+r, x+0,y+r);
316 frm->draw_smooth(x+0,y+r, x-r, y+r, x-r,y+0);
319 void Tracer::draw_points()
321 for( int i=0, n=config.points.size(); i<n; ++i ) {
322 TracerPoint *pt = config.points[i];
323 frm->set_pixel_color(config.selected == i ? GREEN : WHITE);
327 void Tracer::draw_edge()
329 float scale = 1 / 255.0f;
330 int color_model = frm->get_color_model();
331 int bpp = BC_CModels::calculate_pixelsize(color_model);
332 switch( color_model ) {
334 for( int y=0; y<h; ++y ) {
335 uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
336 for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
338 float a = *ep * scale;
339 float *px = (float *)sp;
340 px[0] = px[1] = px[2] = a;
345 for( int y=0; y<h; ++y ) {
346 uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
347 for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
349 float a = *ep * scale;
350 float *px = (float *)sp;
351 px[0] = px[1] = px[2] = a;
357 for( int y=0; y<h; ++y ) {
358 uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
359 for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
361 float a = *ep * scale;
363 px[0] = px[1] = px[2] = a * 255;
368 for( int y=0; y<h; ++y ) {
369 uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
370 for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
372 float a = *ep * scale;
374 px[0] = px[1] = px[2] = a * 255;
380 for( int y=0; y<h; ++y ) {
381 uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
382 for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
384 float a = *ep * scale;
387 px[1] = px[2] = 0x80;
392 for( int y=0; y<h; ++y ) {
393 uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
394 for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
396 float a = *ep * scale;
399 px[1] = px[2] = 0x80;
408 #define PIX_GRADIENT(type, ix, iy) do { \
409 int xi = vx+ix, yi = vy+iy; \
410 if( edg_rows[yi][xi] ) break; \
411 type *px = (type *)(frm_rows[yi] + xi*bpp); \
412 float dv = px[0]-xp[0], v = dv*dv; \
413 for( int c=1; c<comp; ++c ) { \
414 dv = px[c]-xp[c]; v += dv*dv; \
417 if( vmax < v ) vmax = v; \
419 #define ROW_GRADIENT(type, iy) do { \
420 if( vx > 0 ) PIX_GRADIENT(type,-1, iy); \
421 if( iy != 0) PIX_GRADIENT(type, 0, iy); \
422 if( vx < w1) PIX_GRADIENT(type, 1, iy); \
424 #define MAX_GRADIENT(type) do { \
425 type *xp = (type *)(frm_rows[vy] + vx*bpp); \
426 if( vy > 0 ) ROW_GRADIENT(type,-1); \
427 ROW_GRADIENT(type, 0); \
428 if( vy < h1 ) ROW_GRADIENT(type, 1); \
431 #define MAX_PIXEL(type, ix, iy) do { \
432 int vx = cx + ix, vy = cy + iy; \
433 if( edg_rows[vy][vx] ) break; \
434 float vv = FLT_MAX; \
435 int dx = ex-vx, dy = ey-vy; \
436 int rr = dx*dx + dy*dy; \
437 if( rr > dd ) break; \
439 float r = (float)(ix*dx + iy*dy) / rr; \
441 MAX_GRADIENT(type); \
449 #define ROW_MAX(type, iy) do { \
450 if( cx > 0 ) MAX_PIXEL(type,-1, iy); \
451 if( iy != 0 ) MAX_PIXEL(type, 0, iy); \
452 if( cx < w1 ) MAX_PIXEL(type, 1, iy); \
458 if( !edg_rows[cy][cx] ) {
460 edg_rows[cy][cx] = 0xff;
462 int dx = ex-cx, dy = ey-cy;
463 int dd = dx*dx + dy*dy;
464 if( !dd ) return ret;
465 int nx = cx, ny = cy;
466 double maxv = -FLT_MAX;
467 if( cy > 0 ) ROW_MAX(uint8_t,-1);
469 if( cy < h1 ) ROW_MAX(uint8_t, 1);
471 return maxv > 0 ? 1 : 0;
474 void Tracer::trace(int i0, int i1)
476 TracerPoint *pt0 = config.points[i0];
477 TracerPoint *pt1 = config.points[i1];
478 cx = pt0->x; bclamp(cx, 0, w1);
479 cy = pt0->y; bclamp(cy, 0, h1);
480 ex = pt1->x; bclamp(ex, 0, w1);
481 ey = pt1->y; bclamp(ey, 0, h1);
487 int &n = points.total, m = 0;
488 if( n < 3 ) return m;
490 TracePoint *bp = &points[0];
491 TracePoint *cp = &points[1];
494 if( abs(ap->x-cp->x)<2 && abs(ap->y-cp->y)<2 ) {
495 edg_rows[bp->y][bp->x] = 0;
501 if( n < 3 ) return m;
504 for( int i=2; i<n; ++i ) {
506 if( abs(ap->x-cp->x)<2 && abs(ap->y-cp->y)<2 &&
507 ( (bp->x==ap->x || bp->x==cp->x) &&
508 (bp->y==ap->y || bp->y==cp->y) ) ) {
509 edg_rows[bp->y][bp->x] = 0;
515 bp->x = cp->x; bp->y = cp->y;
522 int winding2(int x, int y, TracePoints &pts, int n)
525 int x0 = pts[0].x-x, y0 = pts[0].y-y;
526 for( int x1,y1,i=1; i<n; x0=x1,y0=y1,++i ) {
527 x1 = pts[i].x-x; y1 = pts[i].y-y;
528 if( y0*y1 < 0 ) { // crosses x axis
529 int xi = x0 - y0*(x1-x0)/(y1-y0); // x-intercept
530 if( xi > 0 ) w += y0<0 ? 2 : -2; // crosses x on plus side
532 else if( !y0 && x0 > 0 ) w += y1>0 ? 1 : -1;
533 else if( !y1 && x1 > 0 ) w += y0>0 ? 1 : -1;
541 class segment { public: int y, lt, rt; };
542 ArrayList<segment> stack;
544 void push(int y, int lt, int rt) {
545 segment &seg = stack.append();
546 seg.y = y; seg.lt = lt; seg.rt = rt;
548 void pop(int &y, int <, int &rt) {
549 segment &seg = stack.last();
550 y = seg.y; lt = seg.lt; rt = seg.rt;
557 bool edge_pixel(int i) { return edg[i] > 0; }
560 void fill(int x, int y);
563 FillRegion(VFrame *edg, VFrame *msk);
566 FillRegion::FillRegion(VFrame *edg, VFrame *msk)
568 this->w = msk->get_w();
569 this->h = msk->get_h();
570 this->msk = (uint8_t*) msk->get_data();
571 this->edg = (uint8_t*) edg->get_data();
574 void FillRegion::fill(int x, int y)
579 void FillRegion::run()
581 while( stack.size() > 0 ) {
585 for( int x=ilt; x<=irt; ++x,++ofs ) {
586 if( msk[ofs] ) continue;
588 if( edge_pixel(ofs) ) continue;
591 for( int i=lt; --i>=0; lt=i ) {
592 if( msk[--lofs] ) break;
594 if( edge_pixel(lofs) ) break;
597 for( int i=rt; ++i< w; rt=i ) {
598 if( msk[++rofs] ) break;
600 if( edge_pixel(rofs) ) break;
602 if( y+1 < h ) push(y+1, lt, rt);
603 if( y-1 >= 0 ) push(y-1, lt, rt);
608 void Tracer::feather(int r, double s)
611 int dir = r < 0 ? (r=-r, -1) : 1;
613 int psf[rr]; // pt spot fn
614 float p = powf(10.f, s/2);
615 for( int i=0; i<rr; ++i ) {
616 float v = powf((float)i/rr,p);
617 if( dir < 0 ) v = 1-v;
619 if( vv > 255 ) vv = 255;
622 for( int i=0,n=points.size(); i<n; ++i ) {
623 TracePoint *pt = &points[i];
624 int xs = pt->x-r, xn=pt->x+r;
627 int ys = pt->y-r, yn=pt->y+r;
630 for( int y=ys ; y<yn; ++y ) {
631 for( int x=xs; x<xn; ++x ) {
632 int dx = x-pt->x, dy = y-pt->y;
633 int dd = dx*dx + dy*dy;
634 if( dd >= rr ) continue;
635 int v = psf[dd], px = msk_rows[y][x];
636 if( dir < 0 ? px<v : px>v ) msk_rows[y][x] = v;
642 void Tracer::draw_mask()
644 switch( color_model ) {
646 for( int y=0; y<h; ++y ) {
647 uint8_t *mp = msk_rows[y];
648 uint8_t *rp = frm_rows[y];
649 for( int x=0; x<w; rp+=bpp,++x ) {
650 int a = !config.invert ? 0xff-mp[x] : mp[x];
651 rp[0] = a*rp[0] / 0xff;
652 rp[1] = a*rp[1] / 0xff;
653 rp[2] = a*rp[2] / 0xff;
658 for( int y=0; y<h; ++y ) {
659 uint8_t *mp = msk_rows[y];
660 uint8_t *rp = frm_rows[y];
661 for( int x=0; x<w; rp+=bpp,++x ) {
662 int a = !config.invert ? 0xff-mp[x] : mp[x];
663 rp[0] = a*rp[0] / 0xff;
664 rp[1] = a*(rp[1]-0x80)/0xff + 0x80;
665 rp[2] = a*(rp[2]-0x80)/0xff + 0x80;
670 for( int y=0; y<h; ++y ) {
671 uint8_t *mp = msk_rows[y];
672 uint8_t *rp = frm_rows[y];
673 for( int x=0; x<w; rp+=bpp,++x ) {
674 float a = !config.invert ? 1-mp[x]/255.f : mp[x]/255.f;
675 float *fp = (float*)rp;
676 fp[0] *= a; fp[1] *= a; fp[2] *= a;
682 for( int y=0; y<h; ++y ) {
683 uint8_t *mp = msk_rows[y];
684 uint8_t *rp = frm_rows[y];
685 for( int x=0; x<w; rp+=bpp,++x ) {
686 rp[3] = !config.invert ? 0xff-mp[x] : mp[x];
691 for( int y=0; y<h; ++y ) {
692 uint8_t *mp = msk_rows[y];
693 uint8_t *rp = frm_rows[y];
694 for( int x=0; x<w; rp+=bpp,++x ) {
695 float *fp = (float*)rp;
696 fp[3] = !config.invert ? 1-mp[x]/255.f : mp[x]/255.f;
703 int Tracer::process_buffer(VFrame *frame, int64_t start_position, double frame_rate)
705 int redraw = load_configuration1();
707 frm_rows = frm->get_rows();
708 w = frm->get_w(); w1 = w-1;
709 h = frm->get_h(); h1 = h-1;
710 color_model = frm->get_color_model();
711 bpp = BC_CModels::calculate_pixelsize(color_model);
712 is_float = BC_CModels::is_float(color_model);
713 is_yuv = BC_CModels::is_yuv(color_model);
714 has_alpha = BC_CModels::has_alpha(color_model);
715 comps = BC_CModels::components(color_model);
716 comp = bmin(comps, 3);
717 read_frame(frm, 0, start_position, frame_rate, 0);
718 if( !edg ) redraw = 1;
719 VFrame::get_temp(edg, w, h, BC_GREY8);
722 edg_rows = edg->get_rows();
724 int n = config.points.size()-1;
726 for( int i=0; i<n; ++i )
730 while( smooth() > 0 );
732 if( config.fill && points.size() > 2 ) {
733 int l = points.size(), l2 = l/2;
734 TracePoint *pt0 = &points[0], *pt1 = &points[l2];
735 int cx = (pt0->x+pt1->x)/2, cy = (pt0->y+pt1->y)/2;
736 VFrame::get_temp(msk, w, h, BC_GREY8);
738 msk_rows = msk->get_rows();
740 FillRegion fill_region(edg, msk);
741 fill_region.fill(cx, cy);
744 feather(config.feather, config.radius);
747 if( config.fill && msk )
749 if( config.draw && edg )