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