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