no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / crikey / crikey.C
1 /*
2  * CINELERRA
3  * Copyright (C) 1997-2015 Adam Williams <broadcast at earthling dot net>
4  *
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.
9  *
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.
14  *
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
18  *
19  */
20
21 #include<stdio.h>
22 #include<stdint.h>
23 #include<math.h>
24 #include<string.h>
25
26 #include "arraylist.h"
27 #include "bccmodels.h"
28 #include "bccolors.h"
29 #include "clip.h"
30 #include "edl.h"
31 #include "edlsession.h"
32 #include "filexml.h"
33 #include "crikey.h"
34 #include "crikeywindow.h"
35 #include "keyframe.h"
36 #include "keyframes.h"
37 #include "language.h"
38 #include "transportque.inc"
39 #include "vframe.h"
40
41 // chroma interpolated key, crikey
42
43 REGISTER_PLUGIN(CriKey)
44
45 #if 0
46 void crikey_pgm(const char *fn,VFrame *vfrm)
47 {
48         FILE *fp = fopen(fn,"w");
49         int w = vfrm->get_w(), h = vfrm->get_h();
50         fprintf(fp,"P5\n%d %d\n255\n",w,h);
51         fwrite(vfrm->get_data(),w,h,fp);
52         fclose(fp);
53 }
54 #endif
55
56 CriKeyPoint::CriKeyPoint(int tag, int e, float x, float y, float t)
57 {
58         this->tag = tag;  this->e = e;
59         this->x = x;      this->y = y;
60         this->t = t;
61 }
62 CriKeyPoint::~CriKeyPoint()
63 {
64 }
65
66 CriKeyConfig::CriKeyConfig()
67 {
68         threshold = 0.5f;
69         draw_mode = DRAW_ALPHA;
70         drag = 0;
71         selected = 0;
72 }
73 CriKeyConfig::~CriKeyConfig()
74 {
75 }
76
77 int CriKeyConfig::equivalent(CriKeyConfig &that)
78 {
79         if( !EQUIV(this->threshold, that.threshold) ) return 0;
80         if( this->draw_mode != that.draw_mode ) return 0;
81         if( this->drag != that.drag ) return 0;
82         if( this->points.size() != that.points.size() ) return 0;
83         for( int i=0, n=points.size(); i<n; ++i ) {
84                 CriKeyPoint *ap = this->points[i], *bp = that.points[i];
85                 if( ap->tag != bp->tag ) return 0;
86                 if( ap->e != bp->e ) return 0;
87                 if( !EQUIV(ap->x, bp->x) ) return 0;
88                 if( !EQUIV(ap->y, bp->y) ) return 0;
89                 if( !EQUIV(ap->t, bp->t) ) return 0;
90         }
91         return 1;
92 }
93
94 void CriKeyConfig::copy_from(CriKeyConfig &that)
95 {
96         this->threshold = that.threshold;
97         this->draw_mode = that.draw_mode;
98         this->drag = that.drag;
99         this->selected = that.selected;
100
101         points.remove_all_objects();
102         for( int i=0,n=that.points.size(); i<n; ++i ) {
103                 CriKeyPoint *pt = that.points[i];
104                 add_point(pt->tag, pt->e, pt->x, pt->y, pt->t);
105         }
106 }
107
108 void CriKeyConfig::interpolate(CriKeyConfig &prev, CriKeyConfig &next,
109                 long prev_frame, long next_frame, long current_frame)
110 {
111         this->threshold = prev.threshold;
112         this->draw_mode = prev.draw_mode;
113         this->drag = prev.drag;
114         this->selected = prev.selected;
115
116         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
117         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
118
119         points.remove_all_objects();
120         int prev_sz = prev.points.size(), next_sz = next.points.size();
121         for( int i=0; i<prev_sz; ++i ) {
122                 CriKeyPoint *pt = prev.points[i], *nt = 0;
123                 float x = pt->x, y = pt->y, t = pt->t;
124                 int k = next_sz;  // associated by tag id in next
125                 while( --k >= 0 && pt->tag != (nt=next.points[k])->tag );
126                 if( k >= 0 ) {
127                         x = x * prev_scale + nt->x * next_scale;
128                         y = y * prev_scale + nt->y * next_scale;
129                         t = t * prev_scale + nt->t * next_scale;
130                 }
131                 add_point(pt->tag, pt->e, x, y, t);
132         }
133 }
134
135 void CriKeyConfig::limits()
136 {
137         bclamp(threshold, 0.0f, 1.0f);
138         bclamp(draw_mode, 0, DRAW_MODES-1);
139 }
140
141 int CriKeyConfig::add_point(int tag, int e, float x, float y, float t)
142 {
143         int k = points.size();
144         if( tag < 0 ) {
145                 tag = 0;
146                 for( int i=k; --i>=0; ) {
147                         int n = points[i]->tag;
148                         if( n >= tag ) tag = n + 1;
149                 }
150         }
151         points.append(new CriKeyPoint(tag, e, x, y, t));
152         return k;
153 }
154
155 void CriKeyConfig::del_point(int i)
156 {
157         points.remove_object_number(i);
158 }
159
160
161 class FillRegion
162 {
163         class segment { public: int y, lt, rt; };
164         ArrayList<segment> stack;
165
166         void push(int y, int lt, int rt) {
167                 segment &seg = stack.append();
168                 seg.y = y;  seg.lt = lt;  seg.rt = rt;
169         }
170         void pop(int &y, int &lt, int &rt) {
171                 segment &seg = stack.last();
172                 y = seg.y;  lt = seg.lt;  rt = seg.rt;
173                 stack.remove();
174         }
175  
176         int w, h;
177         float *edg;
178         uint8_t *msk;
179         bool edge_pixel(int i) { return edg[i] > 0; }
180
181 public:
182         void fill(int x, int y);
183         void run();
184
185         FillRegion(VFrame *edg, VFrame *msk);
186 };
187
188 FillRegion::FillRegion(VFrame *edg, VFrame *msk)
189 {
190         this->w = msk->get_w();
191         this->h = msk->get_h();
192         this->msk = (uint8_t*) msk->get_data();
193         this->edg = (float*) edg->get_data();
194 }
195
196 void FillRegion::fill(int x, int y)
197 {
198         push(y, x, x);
199 }
200
201 void FillRegion::run()
202 {
203         while( stack.size() > 0 ) {
204                 int y, ilt, irt;
205                 pop(y, ilt, irt);
206                 int ofs = y*w + ilt;
207                 for( int x=ilt; x<=irt; ++x,++ofs ) {
208                         if( !msk[ofs] ) continue;
209                         msk[ofs] = 0;
210                         if( edge_pixel(ofs) ) continue;
211                         int lt = x, rt = x;
212                         int lofs = ofs;
213                         for( int i=lt; --i>=0; ) {
214                                 if( !msk[--lofs] ) break;
215                                 msk[lofs] = 0;  lt = i;
216                                 if( edge_pixel(lofs) ) break;
217                         }
218                         int rofs = ofs;
219                         for( int i=rt; ++i< w; ) {
220                                 if( !msk[++rofs] ) break;
221                                 msk[rofs] = 0;  rt = i;
222                                 if( edge_pixel(rofs) ) break;
223                         }
224                         if( y+1 <  h ) push(y+1, lt, rt);
225                         if( y-1 >= 0 ) push(y-1, lt, rt);
226                 }
227         }
228 }
229
230
231 CriKey::CriKey(PluginServer *server)
232  : PluginVClient(server)
233 {
234         engine = 0;
235         msk = 0;
236         edg = 0;
237 }
238
239 CriKey::~CriKey()
240 {
241         delete engine;
242         delete msk;
243         delete edg;
244 }
245
246 int CriKey::set_target(float *color, int x, int y)
247 {
248         if( x < 0 || x >= w ) return 1;
249         if( y < 0 || y >= h ) return 1;
250         uint8_t **src_rows = src->get_rows();
251         if( is_float ) {
252                 float *fp = (float*)(src_rows[y] + x*bpp);
253                 for( int i=0; i<comp; ++i,++fp ) color[i] = *fp;
254         }
255         else {
256                 uint8_t *sp = src_rows[y] + x*bpp;
257                 for( int i=0; i<comp; ++i,++sp ) color[i] = *sp;
258         }
259         return 0;
260 }
261
262 const char* CriKey::plugin_title() { return N_("CriKey"); }
263 int CriKey::is_realtime() { return 1; }
264
265 NEW_WINDOW_MACRO(CriKey, CriKeyWindow);
266 LOAD_CONFIGURATION_MACRO(CriKey, CriKeyConfig)
267
268 int CriKey::new_point()
269 {
270         EDLSession *session = get_edl()->session;
271         float x = !session ? 0.f : session->output_w / 2.f;
272         float y = !session ? 0.f : session->output_h / 2.f;
273         return config.add_point(-1, 0, x, y, 0.5f);
274 }
275
276 void CriKeyConfig::save_data(KeyFrame *keyframe)
277 {
278         FileXML output;
279
280 // cause data to be stored directly in text
281         output.set_shared_output(keyframe->xbuf);
282
283         output.tag.set_title("CRIKEY");
284         output.tag.set_property("THRESHOLD", threshold);
285         output.tag.set_property("DRAW_MODE", draw_mode);
286         output.tag.set_property("DRAG", drag);
287         output.tag.set_property("SELECTED", selected);
288         output.append_tag();
289         output.append_newline();
290         output.tag.set_title("/CRIKEY");
291         output.append_tag();
292         output.append_newline();
293         for( int i=0, n = points.size(); i<n; ++i ) {
294                 CriKeyPoint *pt = points[i];
295                 char point[BCSTRLEN];
296                 sprintf(point,"/POINT_%d",pt->tag);
297                 output.tag.set_title(point+1);
298                 output.tag.set_property("E", pt->e);
299                 output.tag.set_property("X", pt->x);
300                 output.tag.set_property("Y", pt->y);
301                 output.tag.set_property("T", pt->t);
302                 output.append_tag();
303                 output.tag.set_title(point+0);
304                 output.append_tag();
305                 output.append_newline();
306         }
307         output.terminate_string();
308 }
309
310 void CriKey::save_data(KeyFrame *keyframe)
311 {
312         config.save_data(keyframe);
313 }
314
315 void CriKeyConfig::read_data(KeyFrame *keyframe)
316 {
317         FileXML input;
318         input.set_shared_input(keyframe->xbuf);
319         points.remove_all_objects();
320         int result = 0;
321
322         while( !(result=input.read_tag()) ) {
323                 if( input.tag.title_is("CRIKEY") ) {
324                         threshold = input.tag.get_property("THRESHOLD", threshold);
325                         draw_mode = input.tag.get_property("DRAW_MODE", draw_mode);
326                         drag = input.tag.get_property("DRAG", drag);
327                         selected = input.tag.get_property("SELECTED", 0);
328                         limits();
329                 }
330                 else if( !strncmp(input.tag.get_title(),"POINT_",6) ) {
331                         int tag = atoi(input.tag.get_title() + 6);
332                         int e = input.tag.get_property("E", 0);
333                         float x = input.tag.get_property("X", 0.f);
334                         float y = input.tag.get_property("Y", 0.f);
335                         float t = input.tag.get_property("T", .5f);
336                         add_point(tag, e, x, y, t);
337                 }
338         }
339 }
340
341 void CriKey::read_data(KeyFrame *keyframe)
342 {
343         config.read_data(keyframe);
344         if( !config.points.size() ) new_point();
345 }
346
347 void CriKey::span_keyframes(KeyFrame *src, int64_t start, int64_t end)
348 {
349         CriKeyConfig src_config;
350         src_config.read_data(src);
351         KeyFrames *keyframes = (KeyFrames *)src->autos;
352         KeyFrame *prev = keyframes->get_prev_keyframe(start, PLAY_FORWARD);
353         CriKeyConfig prev_config;
354         prev_config.read_data(prev);
355 // Always update the first one
356         update_parameter(prev_config, src_config, prev);
357         KeyFrame *curr = (KeyFrame*)prev->next;
358         while( curr && curr->position < end ) {
359                 update_parameter(prev_config, src_config, curr);
360                 curr = (KeyFrame*)curr->next;
361         }
362 }
363
364 void CriKeyPoint::update_parameter(CriKeyPoint *prev, CriKeyPoint *src)
365 {
366         if( prev->e != src->e ) e = src->e;
367         if( prev->x != src->x ) x = src->x;
368         if( prev->y != src->y ) y = src->y;
369         if( prev->t != src->t ) t = src->t;
370 }
371
372 void CriKey::update_parameter(CriKeyConfig &prev_config, CriKeyConfig &src_config,
373                 KeyFrame *keyframe)
374 {
375         CriKeyConfig dst_config;
376         dst_config.read_data(keyframe);
377         if( !EQUIV(prev_config.threshold, src_config.threshold) )
378                 dst_config.threshold = src_config.threshold;
379         if( prev_config.draw_mode != src_config.draw_mode )
380                 dst_config.draw_mode = src_config.draw_mode;
381         if( prev_config.drag != src_config.drag )
382                 dst_config.drag = src_config.drag;
383         int src_points = src_config.points.size();
384         int dst_points = dst_config.points.size();
385         int prev_points = prev_config.points.size();
386         int npoints = bmin(prev_points, bmin(src_points, dst_points));
387         for( int i=0; i<npoints; ++i ) {
388                 CriKeyPoint *src_point = src_config.points[i];
389                 int tag = src_point->tag, k = prev_points;
390                 while( --k >= 0 && tag != prev_config.points[k]->tag );
391                 if( k < 0 ) continue;
392                 CriKeyPoint *prev_point = prev_config.points[k];
393                 k = dst_points;
394                 while( --k >= 0 && tag != dst_config.points[k]->tag );
395                 if( k < 0 ) continue;
396                 CriKeyPoint *dst_point = dst_config.points[k];
397                 dst_point->update_parameter(prev_point, src_point);
398         }
399         dst_config.save_data(keyframe);
400 }
401
402 void CriKey::update_gui()
403 {
404         if( !thread ) return;
405         thread->window->lock_window("CriKey::update_gui");
406         CriKeyWindow *window = (CriKeyWindow*)thread->window;
407         if( load_configuration() ) {
408                 window->update_gui();
409                 window->flush();
410         }
411         thread->window->unlock_window();
412 }
413
414 void CriKey::draw_alpha(VFrame *msk)
415 {
416         uint8_t **src_rows = src->get_rows();
417         uint8_t **msk_rows = msk->get_rows();
418         switch( color_model ) {
419         case BC_RGB_FLOAT:
420                 for( int y=0; y<h; ++y ) {
421                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
422                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
423                                 if( *mp ) continue;
424                                 float *px = (float *)sp;
425                                 px[0] = px[1] = px[2] = 0;
426                         }
427                 }
428                 break;
429         case BC_RGBA_FLOAT:
430                 for( int y=0; y<h; ++y ) {
431                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
432                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
433                                 if( *mp ) continue;
434                                 float *px = (float *)sp;
435                                 px[3] = 0;
436                         }
437                 }
438                 break;
439         case BC_RGB888:
440                 for( int y=0; y<h; ++y ) {
441                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
442                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
443                                 if( *mp ) continue;
444                                 uint8_t *px = sp;
445                                 px[0] = px[1] = px[2] = 0;
446                         }
447                 }
448                 break;
449         case BC_YUV888:
450                 for( int y=0; y<h; ++y ) {
451                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
452                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
453                                 if( *mp ) continue;
454                                 uint8_t *px = sp;
455                                 px[0] = 0;
456                                 px[1] = px[2] = 0x80;
457                         }
458                 }
459                 break;
460         case BC_RGBA8888:
461         case BC_YUVA8888:
462                 for( int y=0; y<h; ++y ) {
463                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
464                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
465                                 if( *mp ) continue;
466                                 uint8_t *px = sp;
467                                 px[3] = 0;
468                         }
469                 }
470                 break;
471         }
472 }
473
474 void CriKey::draw_edge(VFrame *edg)
475 {
476         uint8_t **src_rows = src->get_rows();
477         float **edg_rows = (float**) edg->get_rows(), gain = 10;
478         switch( color_model ) {
479         case BC_RGB_FLOAT:
480                 for( int y=0; y<h; ++y ) {
481                         uint8_t *sp = src_rows[y];
482                         float *ap = edg_rows[y];
483                         for( int x=0; x<w; ++x,++ap,sp+=bpp ) {
484                                 float *px = (float *)sp, v = *ap * gain;
485                                 px[0] = px[1] = px[2] = v<1 ? v : 1;
486                         }
487                 }
488                 break;
489         case BC_RGBA_FLOAT:
490                 for( int y=0; y<h; ++y ) {
491                         uint8_t *sp = src_rows[y];
492                         float *ap = edg_rows[y];
493                         for( int x=0; x<w; ++x,++ap,sp+=bpp ) {
494                                 float *px = (float *)sp, v = *ap * gain;
495                                 px[0] = px[1] = px[2] = v<1 ? v : 1;
496                                 px[3] = 1.0f;
497                         }
498                 }
499                 break;
500         case BC_RGB888:
501                 for( int y=0; y<h; ++y ) {
502                         uint8_t *sp = src_rows[y];
503                         float *ap = edg_rows[y];
504                         for( int x=0; x<w; ++x,++ap,sp+=bpp ) {
505                                 uint8_t *px = sp;  float v = *ap * gain;
506                                 px[0] = px[1] = px[2] = v<1 ? v*256 : 255;
507                         }
508                 }
509                 break;
510         case BC_RGBA8888:
511                 for( int y=0; y<h; ++y ) {
512                         uint8_t *sp = src_rows[y];
513                         float *ap = edg_rows[y];
514                         for( int x=0; x<w; ++x,++ap,sp+=bpp ) {
515                                 uint8_t *px = sp;  float v = *ap * gain;
516                                 px[0] = px[1] = px[2] = v<1 ? v*256 : 255;
517                                 px[3] = 0xff;
518                         }
519                 }
520                 break;
521         case BC_YUV888:
522                 for( int y=0; y<h; ++y ) {
523                         uint8_t *sp = src_rows[y];
524                         float *ap = edg_rows[y];
525                         for( int x=0; x<w; ++x,++ap,sp+=bpp ) {
526                                 uint8_t *px = sp;  float v = *ap * gain;
527                                 px[0] = *ap<1 ? v*256 : 255;
528                                 px[1] = px[2] = 0x80;
529                         }
530                 }
531                 break;
532         case BC_YUVA8888:
533                 for( int y=0; y<h; ++y ) {
534                         uint8_t *sp = src_rows[y];
535                         float *ap = edg_rows[y];
536                         for( int x=0; x<w; ++x,++ap,sp+=bpp ) {
537                                 uint8_t *px = sp;  float v = *ap * gain;
538                                 px[0] = *ap<1 ? v*256 : 255;
539                                 px[1] = px[2] = 0x80;
540                                 px[3] = 0xff;
541                         }
542                 }
543                 break;
544         }
545 }
546
547 void CriKey::draw_mask(VFrame *msk)
548 {
549         uint8_t **src_rows = src->get_rows();
550         uint8_t **msk_rows = msk->get_rows();
551         float scale = 1 / 255.0f;
552         switch( color_model ) {
553         case BC_RGB_FLOAT:
554                 for( int y=0; y<h; ++y ) {
555                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
556                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
557                                 float a = *mp * scale;
558                                 float *px = (float *)sp;
559                                 px[0] = px[1] = px[2] = a;
560                         }
561                 }
562                 break;
563         case BC_RGBA_FLOAT:
564                 for( int y=0; y<h; ++y ) {
565                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
566                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
567                                 float a = *mp * scale;
568                                 float *px = (float *)sp;
569                                 px[0] = px[1] = px[2] = a;
570                                 px[3] = 1.0f;
571                         }
572                 }
573                 break;
574         case BC_RGB888:
575                 for( int y=0; y<h; ++y ) {
576                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
577                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
578                                 float a = *mp * scale;
579                                 uint8_t *px = sp;
580                                 px[0] = px[1] = px[2] = a * 255;
581                         }
582                 }
583                 break;
584         case BC_RGBA8888:
585                 for( int y=0; y<h; ++y ) {
586                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
587                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
588                                 float a = *mp * scale;
589                                 uint8_t *px = sp;
590                                 px[0] = px[1] = px[2] = a * 255;
591                                 px[3] = 0xff;
592                         }
593                 }
594                 break;
595         case BC_YUV888:
596                 for( int y=0; y<h; ++y ) {
597                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
598                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
599                                 float a = *mp * scale;
600                                 uint8_t *px = sp;
601                                 px[0] = a * 255;
602                                 px[1] = px[2] = 0x80;
603                         }
604                 }
605                 break;
606         case BC_YUVA8888:
607                 for( int y=0; y<h; ++y ) {
608                         uint8_t *sp = src_rows[y], *mp = msk_rows[y];
609                         for( int x=0; x<w; ++x,++mp,sp+=bpp ) {
610                                 float a = *mp * scale;
611                                 uint8_t *px = sp;
612                                 px[0] = a * 255;
613                                 px[1] = px[2] = 0x80;
614                                 px[3] = 0xff;
615                         }
616                 }
617                 break;
618         }
619 }
620
621
622 void CriKey::draw_point(VFrame *src, CriKeyPoint *pt)
623 {
624         int d = bmax(w,h) / 200 + 2;
625         int r = d/2+1, x = pt->x, y = pt->y;
626         src->draw_smooth(x-r,y+0, x-r, y-r, x+0,y-r);
627         src->draw_smooth(x+0,y-r, x+r, y-r, x+r,y+0);
628         src->draw_smooth(x+r,y+0, x+r, y+r, x+0,y+r);
629         src->draw_smooth(x+0,y+r, x-r, y+r, x-r,y+0);
630         if( pt->e ) {
631                 src->set_pixel_color(RED);
632                 src->draw_x(pt->x, pt->y, d);
633         }
634         else {
635                 src->set_pixel_color(BLUE);
636                 src->draw_t(pt->x, pt->y, d);
637         }
638 }
639
640
641 static void fill_edge(VFrame *vfrm, int w, int h)
642 {
643         int w1 = w-1, h1 = h-1;
644         float *dp = (float*) vfrm->get_data();
645         if( w1 > 0 ) for( int y=0; y<h1; ++y,dp+=w ) dp[w1] = dp[w1-1];
646         if( h1 > 0 ) for( int x=0; x<w; ++x ) dp[x] = dp[x-w];
647 }
648
649 int CriKey::process_buffer(VFrame *frame, int64_t start_position, double frame_rate)
650 {
651         load_configuration();
652         src = frame;
653         w = src->get_w(), h = src->get_h();
654         color_model = src->get_color_model();
655         bpp = BC_CModels::calculate_pixelsize(color_model);
656         is_float = BC_CModels::is_float(color_model);
657         is_yuv = BC_CModels::is_yuv(color_model);
658         comp = BC_CModels::components(color_model);
659         if( comp > 3 ) comp = 3;
660
661         read_frame(src, 0, start_position, frame_rate, 0);
662         VFrame::get_temp(edg, w, h, BC_A_FLOAT);
663
664         if( !engine )
665                 engine = new CriKeyEngine(this,
666                         PluginClient::get_project_smp() + 1,
667                         PluginClient::get_project_smp() + 1);
668
669         VFrame::get_temp(msk, w, h, BC_A8);
670         memset(msk->get_data(), 0xff, msk->get_data_size());
671
672         for( int i=0, n=config.points.size(); i<n; ++i ) {
673                 CriKeyPoint *pt = config.points[i];
674                 if( !pt->e ) continue;
675                 if( set_target(engine->color, pt->x, pt->y) ) continue;
676                 engine->threshold = pt->t;
677                 edg->black_frame();
678                 engine->process_packages();
679                 fill_edge(edg, w, h);
680                 FillRegion fill_region(edg, msk);
681                 fill_region.fill(pt->x, pt->y);
682                 fill_region.run();
683         }
684
685 //crikey_pgm("/tmp/msk.pgm",msk);
686         switch( config.draw_mode ) {
687         case DRAW_ALPHA: draw_alpha(msk);  break;
688         case DRAW_EDGE:  draw_edge(edg);   break;
689         case DRAW_MASK:  draw_mask(msk);   break;
690         }
691
692         if( config.drag ) {
693                 for( int i=0, n=config.points.size(); i<n; ++i ) {
694                         CriKeyPoint *pt = config.points[i];
695                         src->set_pixel_color(config.selected == i ? GREEN : WHITE);
696                         draw_point(src, pt);
697                 }
698         }
699         return 0;
700 }
701
702
703 void CriKeyEngine::init_packages()
704 {
705         int y = 0, h1 = plugin->h-1;
706         for(int i = 0; i < get_total_packages(); ) {
707                 CriKeyPackage *pkg = (CriKeyPackage*)get_package(i++);
708                 pkg->y1 = y;
709                 y = h1 * i / LoadServer::get_total_packages();
710                 pkg->y2 = y;
711         }
712 }
713
714 LoadPackage* CriKeyEngine::new_package()
715 {
716         return new CriKeyPackage();
717 }
718
719 LoadClient* CriKeyEngine::new_client()
720 {
721         return new CriKeyUnit(this);
722 }
723
724 #define EDGE_MACRO(type, components, is_yuv) \
725 { \
726         uint8_t **src_rows = src->get_rows(); \
727         int comps = MIN(components, 3); \
728         for( int y=y1; y<y2; ++y ) { \
729                 uint8_t *row0 = src_rows[y], *row1 = src_rows[y+1]; \
730                 float *edgp = edg_rows[y]; \
731                 for( int x=x1; x<x2; ++edgp,++x,row0+=bpp,row1+=bpp ) { \
732                         type *r0 = (type*)row0, *r1 = (type*)row1; \
733                         float a00 = 0, a01 = 0, a10 = 0, a11 = 0; \
734                         for( int c=0; c<comps; ++c,++r0,++r1 ) { \
735                                 float t = target_color[c]; \
736                                 a00 += fabs(t - r0[0]); \
737                                 a01 += fabs(t - r0[components]); \
738                                 a10 += fabs(t - r1[0]); \
739                                 a11 += fabs(t - r1[components]); \
740                         } \
741                         float mx = scale * bmax(bmax(a00, a01), bmax(a10, a11)); \
742                         if( mx < threshold ) continue; \
743                         float mn = scale * bmin(bmin(a00, a01), bmin(a10, a11)); \
744                         if( mn >= threshold ) continue; \
745                         *edgp += (mx - mn); \
746                 } \
747         } \
748 } break
749
750
751 void CriKeyUnit::process_package(LoadPackage *package)
752 {
753         int color_model = server->plugin->color_model;
754         int bpp = server->plugin->bpp;
755         VFrame *src = server->plugin->src;
756         VFrame *edg = server->plugin->edg;
757         float **edg_rows = (float**)edg->get_rows();
758         float *target_color = server->color;
759         float threshold = 2.f * server->threshold*server->threshold;
760         float scale = 1./BC_CModels::calculate_max(color_model);
761         CriKeyPackage *pkg = (CriKeyPackage*)package;
762         int x1 = 0, x2 = server->plugin->w-1;
763         int y1 = pkg->y1, y2 = pkg->y2;
764
765         switch( color_model ) {
766         case BC_RGB_FLOAT:  EDGE_MACRO(float, 3, 0);
767         case BC_RGBA_FLOAT: EDGE_MACRO(float, 4, 0);
768         case BC_RGB888:     EDGE_MACRO(unsigned char, 3, 0);
769         case BC_YUV888:     EDGE_MACRO(unsigned char, 3, 1);
770         case BC_RGBA8888:   EDGE_MACRO(unsigned char, 4, 0);
771         case BC_YUVA8888:   EDGE_MACRO(unsigned char, 4, 1);
772         }
773 }
774