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