2 * Cinelerra :: Blue Banana - color modification plugin for Cinelerra-CV
3 * Copyright (C) 2012-2013 Monty <monty@xiph.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "bluebanana.h"
27 #include "bluebananaconfig.h"
28 #include "loadbalance.h"
29 #include "bluebananacolor.c" // need the inlines
31 BluebananaPackage::BluebananaPackage(BluebananaEngine *engine) : LoadPackage(){
35 BluebananaUnit::BluebananaUnit(BluebananaEngine *server,
36 BluebananaMain *plugin) : LoadClient(server) {
37 this->plugin = plugin;
38 this->server = server;
41 BluebananaUnit::~BluebananaUnit(){}
43 void BluebananaUnit::process_package(LoadPackage *package){
44 BluebananaPackage *pkg = (BluebananaPackage*)package;
45 BluebananaEngine *engine = (BluebananaEngine*)pkg->engine;
47 VFrame *frame = engine->data;
48 int w = frame->get_w();
49 int h = frame->get_h();
50 int ant = plugin->ants_counter;
51 int gui_open = plugin->gui_open();
52 int show_ants = plugin->config.mark && gui_open;
55 int active = plugin->config.active;
56 int use_mask = plugin->config.use_mask;
57 int capture_mask = plugin->config.capture_mask;
58 int invert_selection = plugin->config.invert_selection;
60 float *Hl = (plugin->config.Hsel_active &&
61 (plugin->config.Hsel_lo!=0 ||
62 plugin->config.Hsel_hi!=360)) ? plugin->hue_select_alpha_lookup : NULL;
63 float *Sl = (plugin->config.Ssel_active &&
64 (plugin->config.Ssel_lo!=0 ||
65 plugin->config.Ssel_hi!=100)) ? plugin->sat_select_alpha_lookup : NULL;
66 float *Vl = (plugin->config.Vsel_active &&
67 (plugin->config.Vsel_lo!=0 ||
68 plugin->config.Vsel_hi!=100)) ? plugin->val_select_alpha_lookup : NULL;
70 float Hal = plugin->config.Hadj_active ? plugin->config.Hadj_val/60.f : 0.f;
72 float *Sal = (plugin->config.Sadj_active &&
73 (plugin->config.Sadj_lo!=0 ||
74 plugin->config.Sadj_hi!=100 ||
75 plugin->config.Sadj_gamma!=1)) ? plugin->sat_adj_lookup : NULL;
76 float *Val = (plugin->config.Vadj_active &&
77 (plugin->config.Vadj_lo!=0 ||
78 plugin->config.Vadj_hi!=100 ||
79 plugin->config.Vadj_gamma!=1)) ? plugin->val_adj_lookup : NULL;
80 float *Ral = (plugin->config.Radj_active &&
81 (plugin->config.Radj_lo!=0 ||
82 plugin->config.Radj_hi!=100 ||
83 plugin->config.Radj_gamma!=1)) ? plugin->red_adj_lookup : NULL;
84 float *Gal = (plugin->config.Gadj_active &&
85 (plugin->config.Gadj_lo!=0 ||
86 plugin->config.Gadj_hi!=100 ||
87 plugin->config.Gadj_gamma!=1)) ? plugin->green_adj_lookup : NULL;
88 float *Bal = (plugin->config.Badj_active &&
89 (plugin->config.Badj_lo!=0 ||
90 plugin->config.Badj_hi!=100 ||
91 plugin->config.Badj_gamma!=1)) ? plugin->blue_adj_lookup : NULL;
93 float Sas = plugin->sat_adj_toe_slope;
94 float Vas = plugin->val_adj_toe_slope;
95 float Ras = plugin->red_adj_toe_slope;
96 float Gas = plugin->green_adj_toe_slope;
97 float Bas = plugin->blue_adj_toe_slope;
99 float Oal = plugin->config.Oadj_active ? plugin->config.Oadj_val*.01 : 1.f;
100 float Aal = plugin->config.Aadj_active ? plugin->config.Aadj_val*.01 : 1.f;
102 float Vscale = (plugin->config.Vadj_hi-plugin->config.Vadj_lo) / 100.f;
103 float Vshift = plugin->config.Vadj_lo / 100.f;
104 float Vgamma = plugin->config.Vadj_gamma;
106 int doRGB = Ral || Gal || Bal;
108 int doHSV = (Hal!=0) || Sal || Val;
110 int doSEL = ( Hl || Sl || Vl ) && (active || show_ants);
112 int shaping = plugin->config.Fsel_active && doSEL &&
113 (plugin->config.Fsel_lo || plugin->config.Fsel_hi || plugin->config.Fsel_over);
120 int tasks = engine->get_total_packages()*16;
123 /* do as much work entirely local to thread memory as possible */
124 unsigned char row_fragment[SPLIT*16];
125 float *selection_fullframe=NULL;
126 float selection[SPLIT];
137 float *Rhist = pkg->Rhist;
138 float *Ghist = pkg->Ghist;
139 float *Bhist = pkg->Bhist;
140 float *Hhist = pkg->Hhist;
141 float *Shist = pkg->Shist;
142 float *Vhist = pkg->Vhist;
146 float *Hhr = pkg->Hhr;
147 float *Hhg = pkg->Hhg;
148 float *Hhb = pkg->Hhb;
149 float *Shr = pkg->Shr;
150 float *Shg = pkg->Shg;
151 float *Shb = pkg->Shb;
152 float *Vhr = pkg->Vhr;
153 float *Vhg = pkg->Vhg;
154 float *Vhb = pkg->Vhb;
156 memset(Rhist,0,sizeof(pkg->Rhist));
157 memset(Ghist,0,sizeof(pkg->Ghist));
158 memset(Bhist,0,sizeof(pkg->Bhist));
159 memset(Hhist,0,sizeof(pkg->Hhist));
160 memset(Shist,0,sizeof(pkg->Shist));
161 memset(Vhist,0,sizeof(pkg->Vhist));
163 memset(Hhr,0,sizeof(pkg->Hhr));
164 memset(Hhg,0,sizeof(pkg->Hhg));
165 memset(Hhb,0,sizeof(pkg->Hhb));
166 memset(Shr,0,sizeof(pkg->Shr));
167 memset(Shg,0,sizeof(pkg->Shg));
168 memset(Shb,0,sizeof(pkg->Shb));
169 memset(Vhr,0,sizeof(pkg->Vhr));
170 memset(Vhg,0,sizeof(pkg->Vhg));
171 memset(Vhb,0,sizeof(pkg->Vhb));
173 /* If we're doing fill shaping, we need to compute base selection
174 for the entire frame before shaping. */
177 engine->set_task(tasks*2,"shaping_even");
178 while ( (taski = engine->next_task()) >= 0){
180 /* operate on discontinuous, interleaved sections of the source
181 buffer in two passes. Although we could take extra steps to
182 make cache contention across cores completely impossible, the
183 extra locking and central join required isn't worth it. It's
184 preferable to make contention merely highly unlikely. */
186 int start_row, end_row;
188 start_row = (taski*2)*h/(tasks*2);
189 end_row = (taski*2+1)*h/(tasks*2);
191 start_row = ((taski-tasks)*2+1)*h/(tasks*2);
192 end_row = ((taski-tasks)*2+2)*h/(tasks*2);
195 for(rowi = start_row; rowi<end_row; rowi++){
196 unsigned char *row = frame->get_rows()[rowi];
198 for(coli=0;coli<w;coli+=SPLIT){
200 int todo = (w-coli>SPLIT)?SPLIT:(w-coli);
201 float *A = engine->selection_workA+w*rowi+coli;
203 switch(frame->get_color_model()) {
205 rgb8_to_RGB((unsigned char *)row,Rvec,Gvec,Bvec,todo);
209 rgba8_to_RGBA((unsigned char *)row,Rvec,Gvec,Bvec,Avec,todo);
213 rgbF_to_RGB((float *)row,Rvec,Gvec,Bvec,todo);
217 rgbaF_to_RGBA((float *)row,Rvec,Gvec,Bvec,Avec,todo);
221 yuv8_to_RGB((unsigned char *)row,Rvec,Gvec,Bvec,todo);
225 yuva8_to_RGBA((unsigned char *)row,Rvec,Gvec,Bvec,Avec,todo);
230 for(j = 0; j < todo; j++)
231 RGB_to_HSpV(Rvec[j],Gvec[j],Bvec[j],Hvec[j],Svec[j],Vvec[j]);
234 for(j = 0; j < todo; j++)
235 selection[j]=sel_lookup(Hvec[j]*.166666667f,Hl);
237 for(j = 0; j < todo; j++)
241 for(j = 0; j < todo; j++)
242 selection[j]*=sel_lookup(Svec[j],Sl);
245 for(j = 0; j < todo; j++)
246 selection[j]*=sel_lookup(Vvec[j],Vl);
248 /* lock the memcpy to prevent pessimal cache coherency
249 interleave across cores. */
250 pthread_mutex_lock(&engine->copylock);
251 memcpy(A,selection,sizeof(*selection)*todo);
252 pthread_mutex_unlock(&engine->copylock);
257 /* Perform fill shaping on the selection */
258 selection_fullframe = plugin->fill_selection(engine->selection_workA,
259 engine->selection_workB, w, h, engine);
262 /* row-by-row color modification and histogram feedback */
263 engine->set_task(tasks*2,"modification_even");
264 while((taski = engine->next_task())>=0){
266 /* operate on discontinuous, interleaved sections of the source
267 buffer in two passes. Although we could take extra steps to
268 make cache contention across cores completely impossible, the
269 extra locking and central join required isn't worth it. It's
270 preferable to make contention merely highly unlikely. */
272 int start_row, end_row;
274 start_row = (taski*2)*h/(tasks*2);
275 end_row = (taski*2+1)*h/(tasks*2);
277 start_row = ((taski-tasks)*2+1)*h/(tasks*2);
278 end_row = ((taski-tasks)*2+2)*h/(tasks*2);
281 for(rowi = start_row; rowi<end_row; rowi++){
282 unsigned char *row = frame->get_rows()[rowi];
284 for(int coli=0;coli<w;coli+=SPLIT){
285 int todo = (w-coli>SPLIT)?SPLIT:(w-coli);
286 int have_selection = 0;
288 /* convert from pipeline color format */
289 if(active || show_ants || (use_mask && capture_mask && have_alpha)){
291 switch(frame->get_color_model()) {
293 pthread_mutex_lock(&engine->copylock);
294 memcpy(row_fragment,row,todo*3);
295 pthread_mutex_unlock(&engine->copylock);
296 rgb8_to_RGB(row_fragment,Rvec,Gvec,Bvec,todo);
297 byte_advance = todo*3;
302 pthread_mutex_lock(&engine->copylock);
303 memcpy(row_fragment,row,todo*4);
304 pthread_mutex_unlock(&engine->copylock);
305 rgba8_to_RGBA(row_fragment,Rvec,Gvec,Bvec,Avec,todo);
306 byte_advance = todo*4;
311 pthread_mutex_lock(&engine->copylock);
312 memcpy(row_fragment,row,todo*12);
313 pthread_mutex_unlock(&engine->copylock);
314 rgbF_to_RGB((float *)row_fragment,Rvec,Gvec,Bvec,todo);
315 byte_advance = todo*12;
320 pthread_mutex_lock(&engine->copylock);
321 memcpy(row_fragment,row,todo*16);
322 pthread_mutex_unlock(&engine->copylock);
323 rgbaF_to_RGBA((float *)row_fragment,Rvec,Gvec,Bvec,Avec,todo);
324 byte_advance = todo*16;
329 pthread_mutex_lock(&engine->copylock);
330 memcpy(row_fragment,row,todo*3);
331 pthread_mutex_unlock(&engine->copylock);
332 yuv8_to_RGB(row_fragment,Rvec,Gvec,Bvec,todo);
333 byte_advance = todo*3;
338 pthread_mutex_lock(&engine->copylock);
339 memcpy(row_fragment,row,todo*4);
340 pthread_mutex_unlock(&engine->copylock);
341 yuva8_to_RGBA(row,Rvec,Gvec,Bvec,Avec,todo);
342 byte_advance = todo*4;
348 /* generate initial HSV values [if selection active] */
349 for(j = 0; j < todo; j++)
350 RGB_to_HSpV(Rvec[j],Gvec[j],Bvec[j],Hvec[j],Svec[j],Vvec[j]);
353 float selection_test=todo;
355 if(selection_fullframe){
357 /* get the full-frame selection data we need into thread-local storage */
358 /* the full-frame data is read-only at this point, no need to lock */
359 float *sf = selection_fullframe + rowi*w + coli;
361 for(j = 0; j < todo; j++)
362 selection_test += selection[j] = sf[j];
367 /* selection computation when no full-frame shaping */
370 for(j = 0; j < todo; j++)
371 selection_test += selection[j] = sel_lookup(Hvec[j]*.166666667f,Hl);
377 if(selection_test>SELECT_THRESH){
379 for(j = 0; j < todo; j++)
380 selection_test += selection[j] *= sel_lookup(Svec[j],Sl);
384 for(j = 0; j < todo; j++)
385 selection_test += selection[j] = sel_lookup(Svec[j],Sl);
392 if(selection_test>SELECT_THRESH){
394 for(j = 0; j < todo; j++)
395 selection_test += selection[j] *= sel_lookup(Vvec[j],Vl);
399 for(j = 0; j < todo; j++)
400 selection_test += selection[j] = sel_lookup(Vvec[j],Vl);
406 /* selection modification according to config */
407 if(use_mask && have_alpha){
409 /* selection consists only of mask */
411 for(j = 0; j < todo; j++)
412 selection_test += selection[j] = Avec[j];
415 if(invert_selection){
416 if(selection_test < SELECT_THRESH){
417 /* fully selected after invert, clip to mask */
419 for(j = 0; j < todo; j++)
420 selection_test += selection[j] = Avec[j];
421 }else if (selection_test >= todo-SELECT_THRESH){
422 /* fully deselected after invert */
425 /* partial selection after invert, clip to mask */
427 for(j = 0; j < todo; j++)
428 selection_test += selection[j] = Avec[j]*(1.f-selection[j]);
431 if(selection_test < SELECT_THRESH){
432 /* fully deselected */
433 }else if (selection_test >= todo-SELECT_THRESH){
434 /* fully selected, clip to mask */
436 for(j = 0; j < todo; j++)
437 selection_test += selection[j] = Avec[j];
439 /* partial selection, clip to mask */
441 for(j = 0; j < todo; j++)
442 selection_test += selection[j] *= Avec[j];
446 if(selection_test < SELECT_THRESH){
447 /* skip processing this fragment */
448 /* we're using a mask; if the mask is set to capture, we
449 need to restore alpha before skipping */
451 switch(frame->get_color_model()) {
453 unmask_rgba8(row_fragment,todo);
456 unmask_rgbaF((float *)row_fragment,todo);
459 unmask_yuva8(row_fragment,todo);
462 pthread_mutex_lock(&engine->copylock);
463 memcpy(row,row_fragment,byte_advance);
464 pthread_mutex_unlock(&engine->copylock);
470 if(selection_test > todo-SELECT_THRESH)
471 have_selection=0; // fully selected
474 if(invert_selection){
475 if(selection_test < SELECT_THRESH){
476 /* fully selected after inversion */
478 }else if (selection_test >= todo-SELECT_THRESH){
479 /* fully deselected after invert, skip fragment */
483 /* partial selection */
485 for(j = 0; j < todo; j++)
486 selection_test += selection[j] = (1.f-selection[j]);
489 if(selection_test < SELECT_THRESH){
490 /* fully deselected, skip fragment */
493 }else if (selection_test >= todo-SELECT_THRESH){
497 /* partial selection; already calculated */
500 if(selection_test < SELECT_THRESH){
502 continue; // inactive fragment
504 if(selection_test > todo-SELECT_THRESH)
505 have_selection=0; // fully selected
507 if(invert_selection){
520 for(j = 0; j < todo; j++)
521 if(selection[j]>SELECT_THRESH) Rvec[j]=adj_lookup(Rvec[j],Ral,Ras);
523 for(j = 0; j < todo; j++)
524 Rvec[j] = adj_lookup(Rvec[j],Ral,Ras);
530 for(j = 0; j < todo; j++)
531 if(selection[j]>SELECT_THRESH) Rhist[(int)CLAMP((Rvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
533 for(j = 0; j < todo; j++)
534 Rhist[(int)CLAMP((Rvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
541 for(j = 0; j < todo; j++)
542 if(selection[j]>SELECT_THRESH) Gvec[j]=adj_lookup(Gvec[j],Gal,Gas);
544 for(j = 0; j < todo; j++)
545 Gvec[j] = adj_lookup(Gvec[j],Gal,Gas);
548 /* green histogram */
551 for(j = 0; j < todo; j++)
552 if(selection[j]>SELECT_THRESH) Ghist[(int)CLAMP((Gvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
554 for(j = 0; j < todo; j++)
555 Ghist[(int)CLAMP((Gvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
562 for(j = 0; j < todo; j++)
563 if(selection[j]>SELECT_THRESH) Bvec[j]=adj_lookup(Bvec[j],Bal,Bas);
565 for(j = 0; j < todo; j++)
566 Bvec[j] = adj_lookup(Bvec[j],Bal,Bas);
572 for(j = 0; j < todo; j++)
573 if(selection[j]>SELECT_THRESH) Bhist[(int)CLAMP((Bvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
575 for(j = 0; j < todo; j++)
576 Bhist[(int)CLAMP((Bvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
580 /* compute HSV values or update values from earlier selection work if
582 if( (!doSEL || doRGB) && // not yet computed, or since modified
583 (doHSV || gui_open) ) // needed for HSV mod and/or histograms
584 for(j = 0; j < todo; j++)
585 RGB_to_HSpV(Rvec[j],Gvec[j],Bvec[j],Hvec[j],Svec[j],Vvec[j]);
589 /* don't bother checking selection, H adj is lightweight */
591 for(j = 0; j < todo; j++){
593 if(Hvec[j]<0.f)Hvec[j]+=6.f;
594 if(Hvec[j]>=6.f)Hvec[j]-=6.f;
598 /* this is pre-shift RGB data; shift hue later to save an HSV->RGB transform */
601 for(j = 0; j < todo; j++){
602 if(selection[j]>SELECT_THRESH){
603 float weight = selection[j]*Svec[j];
604 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
605 Htotal += selection[j];
607 Hhist[bin] += weight;
608 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
609 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
610 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
614 for(j = 0; j < todo; j++){
615 float weight = Svec[j];
616 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
619 Hhist[bin] += weight;
620 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
621 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
622 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
630 for(j = 0; j < todo; j++)
631 if(selection[j]>SELECT_THRESH) Svec[j] = adj_lookup(Svec[j],Sal,Sas);
633 for(j = 0; j < todo; j++)
634 Svec[j] = adj_lookup(Svec[j],Sal,Sas);
638 /* This is unrolled a few times below...
639 Although we're using HSV, we don't want hue/saturation
640 changes to have a strong effect on apparent brightness.
641 Apply a correction to V (not Y!) based on luma change. */
642 /* Calculate new RGB values at same time */
645 for(j = 0; j < todo; j++){
646 if(selection[j]>SELECT_THRESH){
647 HSpV_correct_RGB(Hvec[j],Svec[j],Vvec[j],Rvec[j],Gvec[j],Bvec[j]);
648 /* run S histogram at the same time as we've got
651 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
652 Shist[bin] += selection[j];
653 Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
654 Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
655 Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
660 for(j = 0; j < todo; j++){
661 HSpV_correct_RGB(Hvec[j],Svec[j],Vvec[j],Rvec[j],Gvec[j],Bvec[j]);
663 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
665 Shr[bin>>HRGBSHIFT] += Rvec[j];
666 Shg[bin>>HRGBSHIFT] += Gvec[j];
667 Shb[bin>>HRGBSHIFT] += Bvec[j];
674 for(j = 0; j < todo; j++){
675 if(selection[j]>SELECT_THRESH){
676 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
677 Shist[bin] += selection[j];
678 Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
679 Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
680 Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
684 for(j = 0; j < todo; j++){
685 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
687 Shr[bin>>HRGBSHIFT] += Rvec[j];
688 Shg[bin>>HRGBSHIFT] += Gvec[j];
689 Shb[bin>>HRGBSHIFT] += Bvec[j];
696 /* This one is a bit unlike the others; Value gamma
697 behavior is conditional. When < 1.0 (darkening the
698 picture) gamma directly operates on Value as normally
699 expected (thus scaling R G and B about zero). When
700 gamma > 1.0 (lightening the picture) it operates on the
701 Value scale's zero point rather than the Value. Both
702 are done to get the most natural behavior out of
703 saturation as gamma changes. The only drawback: One
704 can no longer reverse a gamma operation by applying the
705 inverse gamma in a subsequent step. */
710 for(j = 0; j < todo; j++)
711 if(selection[j]>SELECT_THRESH){
712 float scale = adj_lookup(Vvec[j],Val,Vas);
713 Vvec[j] = Vvec[j] * scale + Vshift;
714 Rvec[j] = Rvec[j] * scale + Vshift;
715 Gvec[j] = Gvec[j] * scale + Vshift;
716 Bvec[j] = Bvec[j] * scale + Vshift;
720 for(j = 0; j < todo; j++)
721 if(selection[j]>SELECT_THRESH){
722 float shift = adj_lookup(Vvec[j],Val,Vas);
723 Vvec[j] = Vvec[j] * Vscale + shift;
724 Rvec[j] = Rvec[j] * Vscale + shift;
725 Gvec[j] = Gvec[j] * Vscale + shift;
726 Bvec[j] = Bvec[j] * Vscale + shift;
732 for(j = 0; j < todo; j++){
733 float scale = adj_lookup(Vvec[j],Val,Vas);
734 Vvec[j] = Vvec[j] * scale + Vshift;
735 Rvec[j] = Rvec[j] * scale + Vshift;
736 Gvec[j] = Gvec[j] * scale + Vshift;
737 Bvec[j] = Bvec[j] * scale + Vshift;
741 for(j = 0; j < todo; j++){
742 float shift = adj_lookup(Vvec[j],Val,Vas);
743 Vvec[j] = Vvec[j] * Vscale + shift;
744 Rvec[j] = Rvec[j] * Vscale + shift;
745 Gvec[j] = Gvec[j] * Vscale + shift;
746 Bvec[j] = Bvec[j] * Vscale + shift;
755 for(j = 0; j < todo; j++){
756 if(selection[j]>SELECT_THRESH){
757 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
758 Vhist[bin] += selection[j];
759 Vhr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
760 Vhg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
761 Vhb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
765 for(j = 0; j < todo; j++){
766 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
768 Vhr[bin>>HRGBSHIFT] += Rvec[j];
769 Vhg[bin>>HRGBSHIFT] += Gvec[j];
770 Vhb[bin>>HRGBSHIFT] += Bvec[j];
779 for(j = 0; j < todo; j++){
780 if(selection[j]>SELECT_THRESH){
781 float weight = selection[j]*Svec[j];
782 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
783 Htotal += selection[j];
785 Hhist[bin] += weight;
786 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
787 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
788 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
792 for(j = 0; j < todo; j++){
793 if(selection[j]>SELECT_THRESH){
794 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
795 Shist[bin] += selection[j];
796 Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
797 Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
798 Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
802 for(j = 0; j < todo; j++){
803 if(selection[j]>SELECT_THRESH){
804 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
805 Vhist[bin] += selection[j];
806 Vhr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
807 Vhg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
808 Vhb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
814 for(j = 0; j < todo; j++){
815 float weight = Svec[j];
816 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
819 Hhist[bin] += weight;
820 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
821 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
822 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
825 for(j = 0; j < todo; j++){
826 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
828 Shr[bin>>HRGBSHIFT] += Rvec[j];
829 Shg[bin>>HRGBSHIFT] += Gvec[j];
830 Shb[bin>>HRGBSHIFT] += Bvec[j];
833 for(j = 0; j < todo; j++){
834 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
836 Vhr[bin>>HRGBSHIFT] += Rvec[j];
837 Vhg[bin>>HRGBSHIFT] += Gvec[j];
838 Vhb[bin>>HRGBSHIFT] += Bvec[j];
843 /* layer back into pipeline color format; master fader applies here */
844 switch(frame->get_color_model()) {
846 RGB_to_rgb8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,3);
849 RGB_to_rgb8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,4);
852 RGB_to_rgbF(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,(float *)row_fragment,todo,3);
855 RGB_to_rgbF(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,(float *)row_fragment,todo,4);
858 RGB_to_yuv8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,3);
861 RGB_to_yuv8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,4);
868 for(j = 0; j < todo; j++){
869 float A = have_selection ? selection[j] : 1.f;
871 if( (ant+rowi-j)&0x4 ){
883 if( (ant+rowi+j)&0x4 ){
897 /* re-layer back into pipeline color format */
898 switch(frame->get_color_model()) {
900 RGB_to_rgb8(Rvec,Gvec,Bvec,have_selection?selection:0,1.f,row_fragment,todo,3);
903 RGB_to_rgb8(Rvec,Gvec,Bvec,have_selection?selection:0,1.f,row_fragment,todo,4);
906 RGB_to_rgbF(Rvec,Gvec,Bvec,have_selection?selection:0,1.f,(float *)row_fragment,todo,3);
909 RGB_to_rgbF(Rvec,Gvec,Bvec,have_selection?selection:0,1.f,(float *)row_fragment,todo,4);
912 RGB_to_yuv8(Rvec,Gvec,Bvec,have_selection?selection:0,1.f,row_fragment,todo,3);
915 RGB_to_yuv8(Rvec,Gvec,Bvec,have_selection?selection:0,1.f,row_fragment,todo,4);
920 if(active || show_ants || (use_mask && capture_mask)){
922 if(use_mask && capture_mask){
923 switch(frame->get_color_model()) {
925 if( have_selection && Aal < 1.f )
926 Aal_to_alp8(selection,Aal,row_fragment,todo,4);
928 unmask_rgba8(row_fragment,todo);
931 if( have_selection && Aal < 1.f )
932 Aal_to_alpF(selection,Aal,(float *)row_fragment,todo,4);
934 unmask_rgbaF((float *)row_fragment,todo);
937 if( have_selection && Aal < 1.f )
938 Aal_to_alp8(selection,Aal,row_fragment,todo,4);
940 unmask_yuva8(row_fragment,todo);
945 /* lock to prevent interleaved cache coherency collisions
946 across cores. Two memcpys touching the same cache line
947 will cause the wings to fall off the processor. */
949 pthread_mutex_lock(&engine->copylock);
950 memcpy(row,row_fragment,byte_advance);
951 pthread_mutex_unlock(&engine->copylock);
959 pthread_mutex_lock(&engine->copylock);
961 plugin->hue_total+=Htotal;
962 plugin->hue_weight+=Hweight;
964 for(int i=0;i<HISTSIZE;i++){
965 plugin->red_histogram[i] += Rhist[i];
966 plugin->green_histogram[i] += Ghist[i];
967 plugin->blue_histogram[i] += Bhist[i];
969 plugin->hue_histogram[i] += Hhist[i];
970 plugin->sat_histogram[i] += Shist[i];
972 plugin->value_histogram[i] += Vhist[i];
975 for(int i=0;i<(HISTSIZE>>HRGBSHIFT);i++){
976 plugin->hue_histogram_red[i] += Hhr[i];
977 plugin->hue_histogram_green[i] += Hhg[i];
978 plugin->hue_histogram_blue[i] += Hhb[i];
980 plugin->sat_histogram_red[i] += Shr[i];
981 plugin->sat_histogram_green[i] += Shg[i];
982 plugin->sat_histogram_blue[i] += Shb[i];
984 plugin->value_histogram_red[i] += Vhr[i];
985 plugin->value_histogram_green[i] += Vhg[i];
986 plugin->value_histogram_blue[i] += Vhb[i];
988 pthread_mutex_unlock(&engine->copylock);
992 BluebananaEngine::BluebananaEngine(BluebananaMain *plugin, int total_clients,
993 int total_packages) : LoadServer(total_clients, total_packages){
994 this->plugin = plugin;
998 pthread_mutex_init(©lock,NULL);
999 pthread_mutex_init(&tasklock,NULL);
1000 pthread_cond_init(&taskcond,NULL);
1002 BluebananaEngine::~BluebananaEngine(){
1003 pthread_cond_destroy(&taskcond);
1004 pthread_mutex_destroy(&tasklock);
1005 pthread_mutex_destroy(©lock);
1006 if(selection_workA) delete[] selection_workA;
1007 if(selection_workB) delete[] selection_workB;
1010 void BluebananaEngine::init_packages(){}
1012 LoadClient* BluebananaEngine::new_client(){
1013 return new BluebananaUnit(this, plugin);
1016 LoadPackage* BluebananaEngine::new_package(){
1017 return new BluebananaPackage(this);
1020 static float lt(float *data,float pos, int n){
1025 return data[i]*(1.f-del) + data[i+1]*del;
1028 static float lt_shift(float *data, float pos, int n, int over){
1036 for(int j=0; j<over; j++)
1038 for(int j=0; j<over; j++)
1041 float temp=(s0*(1.f-del) + s1*del);
1048 void BluebananaEngine::process_packages(VFrame *data){
1049 int w = data->get_w();
1050 int h = data->get_h();
1054 task_finish_count=0;
1056 /* If we're doing any spatial modification of the selection, we'll
1057 need to operate on a temporary selection array that covers the
1059 if(plugin->config.Fsel_active){
1060 if(plugin->config.Fsel_lo ||
1061 plugin->config.Fsel_mid ||
1062 plugin->config.Fsel_hi ||
1063 plugin->config.Fsel_over){
1064 selection_workA = new float[w*h];
1065 selection_workB = new float[w*h];
1069 memset(plugin->red_histogram,0,sizeof(plugin->red_histogram));
1070 memset(plugin->green_histogram,0,sizeof(plugin->green_histogram));
1071 memset(plugin->blue_histogram,0,sizeof(plugin->blue_histogram));
1072 memset(plugin->hue_histogram,0,sizeof(plugin->hue_histogram));
1073 plugin->hue_total=0.f;
1074 plugin->hue_weight=0.f;
1075 memset(plugin->sat_histogram,0,sizeof(plugin->sat_histogram));
1076 memset(plugin->value_histogram,0,sizeof(plugin->value_histogram));
1078 memset(plugin->hue_histogram_red,0,sizeof(plugin->hue_histogram_red));
1079 memset(plugin->hue_histogram_green,0,sizeof(plugin->hue_histogram_green));
1080 memset(plugin->hue_histogram_blue,0,sizeof(plugin->hue_histogram_blue));
1082 memset(plugin->sat_histogram_red,0,sizeof(plugin->sat_histogram_red));
1083 memset(plugin->sat_histogram_green,0,sizeof(plugin->sat_histogram_green));
1084 memset(plugin->sat_histogram_blue,0,sizeof(plugin->sat_histogram_blue));
1086 memset(plugin->value_histogram_red,0,sizeof(plugin->value_histogram_red));
1087 memset(plugin->value_histogram_green,0,sizeof(plugin->value_histogram_green));
1088 memset(plugin->value_histogram_blue,0,sizeof(plugin->value_histogram_blue));
1090 LoadServer::process_packages();
1092 /* The Hue histogram needs to be adjusted/shifted */
1093 if(plugin->config.active && plugin->hue_weight){
1095 float scale = plugin->hue_total/plugin->hue_weight;
1096 float Hshift = plugin->config.Hsel_active ? (plugin->config.Hsel_lo + plugin->config.Hsel_hi)/720.f-.5f : 0.f;
1097 float Hal = plugin->config.Hadj_active ? plugin->config.Hadj_val/60.f : 0.f;
1098 float hist[HISTSIZE + (1<<HRGBSHIFT)];
1099 float red[(HISTSIZE>>HRGBSHIFT)+1];
1100 float green[(HISTSIZE>>HRGBSHIFT)+1];
1101 float blue[(HISTSIZE>>HRGBSHIFT)+1];
1103 for(i=0;i<(1<<HRGBSHIFT);i++){
1104 plugin->hue_histogram[i]+=plugin->hue_histogram[HISTSIZE+i];
1105 plugin->hue_histogram[HISTSIZE+i]=plugin->hue_histogram[i];
1107 plugin->hue_histogram_red[0]+=plugin->hue_histogram_red[HISTSIZE>>HRGBSHIFT];
1108 plugin->hue_histogram_red[HISTSIZE>>HRGBSHIFT]=plugin->hue_histogram_red[0];
1109 plugin->hue_histogram_green[0]+=plugin->hue_histogram_green[HISTSIZE>>HRGBSHIFT];
1110 plugin->hue_histogram_green[HISTSIZE>>HRGBSHIFT]=plugin->hue_histogram_green[0];
1111 plugin->hue_histogram_blue[0]+=plugin->hue_histogram_blue[HISTSIZE>>HRGBSHIFT];
1112 plugin->hue_histogram_blue[HISTSIZE>>HRGBSHIFT]=plugin->hue_histogram_blue[0];
1114 for(i=0; i<(HISTSIZE>>HRGBSHIFT); i++){
1115 float pos = i+Hshift*(HISTSIZE>>HRGBSHIFT);
1116 if(pos<0)pos+=(HISTSIZE>>HRGBSHIFT);
1117 if(pos>=(HISTSIZE>>HRGBSHIFT))pos-=(HISTSIZE>>HRGBSHIFT);
1119 float div = lt_shift(plugin->hue_histogram,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1120 (HISTSIZE>>HRGBSHIFT), (1<<HRGBSHIFT));
1121 red[i] = lt(plugin->hue_histogram_red,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1122 (HISTSIZE>>HRGBSHIFT))*div;
1123 green[i] = lt(plugin->hue_histogram_green,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1124 (HISTSIZE>>HRGBSHIFT))*div;
1125 blue[i] = lt(plugin->hue_histogram_blue,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1126 (HISTSIZE>>HRGBSHIFT))*div;
1129 for(int i=0; i<HISTSIZE; i++)
1130 hist[i] = lt(plugin->hue_histogram,i+Hshift*HISTSIZE,HISTSIZE)*scale;
1132 memcpy(hist+HISTSIZE,hist,sizeof(*hist)*(1<<HRGBSHIFT));
1133 memcpy(plugin->hue_histogram,hist,sizeof(hist));
1135 red[HISTSIZE>>HRGBSHIFT]=red[0];
1136 green[HISTSIZE>>HRGBSHIFT]=green[0];
1137 blue[HISTSIZE>>HRGBSHIFT]=blue[0];
1139 for(i=0,j=0; i<=(HISTSIZE>>HRGBSHIFT); i++){
1140 float sum=0.f,H,S,V;
1141 for(int k=0; k<(1<<HRGBSHIFT); j++,k++)
1144 RGB_to_HSpV(red[i],green[i],blue[i],H,S,V);
1148 HSpV_to_RGB(H,S,V,red[i],green[i],blue[i]);
1150 plugin->hue_histogram_red[i] = red[i] * sum;
1151 plugin->hue_histogram_green[i] = green[i] * sum;
1152 plugin->hue_histogram_blue[i] = blue[i] * sum;
1156 if(selection_workA){
1157 delete[] selection_workA;
1160 if(selection_workB){
1161 delete[] selection_workB;
1166 /* create a coordinated work-division list and join point */
1167 void BluebananaEngine::set_task(int n, const char *task){
1168 pthread_mutex_lock(&this->tasklock);
1169 if(task_init_state==0){
1170 //fprintf(stderr,"New task!: %s",task);
1172 task_finish_count=get_total_packages();
1176 pthread_mutex_unlock(&this->tasklock);
1179 /* fetch the next task ticket. If the task is complete, wait here for
1180 all package threads to complete before returning <0 */
1181 int BluebananaEngine::next_task(){
1182 pthread_mutex_lock(&tasklock);
1185 //fprintf(stderr,".");
1186 pthread_mutex_unlock(&tasklock);
1189 task_finish_count--;
1190 if(task_finish_count==0){
1192 //fprintf(stderr,"done\n");
1193 pthread_cond_broadcast(&taskcond);
1195 int serial = task_init_serial;
1196 while(task_finish_count && serial == task_init_serial){
1197 //fprintf(stderr,"+");
1198 pthread_cond_wait(&taskcond,&tasklock);
1201 pthread_mutex_unlock(&tasklock);
1202 //fprintf(stderr,"-");
1207 /* same as above, but waits for join without fetching a task slot */
1208 void BluebananaEngine::wait_task(){
1209 pthread_mutex_lock(&tasklock);
1210 task_finish_count--;
1211 if(task_finish_count==0){
1213 //fprintf(stderr,"done\n");
1214 pthread_cond_broadcast(&taskcond);
1216 int serial = task_init_serial;
1217 while(task_finish_count && serial == task_init_serial){
1218 //fprintf(stderr,"+");
1219 pthread_cond_wait(&taskcond,&tasklock);
1222 pthread_mutex_unlock(&tasklock);
1223 //fprintf(stderr,"-");