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 op = plugin->config.op;
57 int use_mask = plugin->config.use_mask;
58 int capture_mask = plugin->config.capture_mask;
59 int invert_selection = plugin->config.invert_selection;
61 float *Hl = (plugin->config.Hsel_active &&
62 (plugin->config.Hsel_lo!=0 ||
63 plugin->config.Hsel_hi!=360)) ? plugin->hue_select_alpha_lookup : NULL;
64 float *Sl = (plugin->config.Ssel_active &&
65 (plugin->config.Ssel_lo!=0 ||
66 plugin->config.Ssel_hi!=100)) ? plugin->sat_select_alpha_lookup : NULL;
67 float *Vl = (plugin->config.Vsel_active &&
68 (plugin->config.Vsel_lo!=0 ||
69 plugin->config.Vsel_hi!=100)) ? plugin->val_select_alpha_lookup : NULL;
71 float Hal = plugin->config.Hadj_active ? plugin->config.Hadj_val/60.f : 0.f;
73 float *Sal = (plugin->config.Sadj_active &&
74 (plugin->config.Sadj_lo!=0 ||
75 plugin->config.Sadj_hi!=100 ||
76 plugin->config.Sadj_gamma!=1)) ? plugin->sat_adj_lookup : NULL;
77 float *Val = (plugin->config.Vadj_active &&
78 (plugin->config.Vadj_lo!=0 ||
79 plugin->config.Vadj_hi!=100 ||
80 plugin->config.Vadj_gamma!=1)) ? plugin->val_adj_lookup : NULL;
81 float *Ral = (plugin->config.Radj_active &&
82 (plugin->config.Radj_lo!=0 ||
83 plugin->config.Radj_hi!=100 ||
84 plugin->config.Radj_gamma!=1)) ? plugin->red_adj_lookup : NULL;
85 float *Gal = (plugin->config.Gadj_active &&
86 (plugin->config.Gadj_lo!=0 ||
87 plugin->config.Gadj_hi!=100 ||
88 plugin->config.Gadj_gamma!=1)) ? plugin->green_adj_lookup : NULL;
89 float *Bal = (plugin->config.Badj_active &&
90 (plugin->config.Badj_lo!=0 ||
91 plugin->config.Badj_hi!=100 ||
92 plugin->config.Badj_gamma!=1)) ? plugin->blue_adj_lookup : NULL;
94 float Sas = plugin->sat_adj_toe_slope;
95 float Vas = plugin->val_adj_toe_slope;
96 float Ras = plugin->red_adj_toe_slope;
97 float Gas = plugin->green_adj_toe_slope;
98 float Bas = plugin->blue_adj_toe_slope;
100 float Oal = plugin->config.Oadj_active ? plugin->config.Oadj_val*.01 : 1.f;
101 float Aal = plugin->config.Aadj_active ? plugin->config.Aadj_val*.01 : 1.f;
103 float Vscale = (plugin->config.Vadj_hi-plugin->config.Vadj_lo) / 100.f;
104 float Vshift = plugin->config.Vadj_lo / 100.f;
105 float Vgamma = plugin->config.Vadj_gamma;
107 int doRGB = Ral || Gal || Bal;
109 int doHSV = (Hal!=0) || Sal || Val;
111 int doSEL = ( Hl || Sl || Vl ) && (active || show_ants);
113 int shaping = plugin->config.Fsel_active && doSEL &&
114 (plugin->config.Fsel_lo || plugin->config.Fsel_hi || plugin->config.Fsel_over);
121 int tasks = engine->get_total_packages()*16;
124 /* do as much work entirely local to thread memory as possible */
125 unsigned char row_fragment[SPLIT*16];
126 float *selection_fullframe=NULL;
127 float selection[SPLIT];
138 float *Rhist = pkg->Rhist;
139 float *Ghist = pkg->Ghist;
140 float *Bhist = pkg->Bhist;
141 float *Hhist = pkg->Hhist;
142 float *Shist = pkg->Shist;
143 float *Vhist = pkg->Vhist;
147 float *Hhr = pkg->Hhr;
148 float *Hhg = pkg->Hhg;
149 float *Hhb = pkg->Hhb;
150 float *Shr = pkg->Shr;
151 float *Shg = pkg->Shg;
152 float *Shb = pkg->Shb;
153 float *Vhr = pkg->Vhr;
154 float *Vhg = pkg->Vhg;
155 float *Vhb = pkg->Vhb;
157 memset(Rhist,0,sizeof(pkg->Rhist));
158 memset(Ghist,0,sizeof(pkg->Ghist));
159 memset(Bhist,0,sizeof(pkg->Bhist));
160 memset(Hhist,0,sizeof(pkg->Hhist));
161 memset(Shist,0,sizeof(pkg->Shist));
162 memset(Vhist,0,sizeof(pkg->Vhist));
164 memset(Hhr,0,sizeof(pkg->Hhr));
165 memset(Hhg,0,sizeof(pkg->Hhg));
166 memset(Hhb,0,sizeof(pkg->Hhb));
167 memset(Shr,0,sizeof(pkg->Shr));
168 memset(Shg,0,sizeof(pkg->Shg));
169 memset(Shb,0,sizeof(pkg->Shb));
170 memset(Vhr,0,sizeof(pkg->Vhr));
171 memset(Vhg,0,sizeof(pkg->Vhg));
172 memset(Vhb,0,sizeof(pkg->Vhb));
174 /* If we're doing fill shaping, we need to compute base selection
175 for the entire frame before shaping. */
178 engine->set_task(tasks*2,"shaping_even");
179 while ( (taski = engine->next_task()) >= 0){
181 /* operate on discontinuous, interleaved sections of the source
182 buffer in two passes. Although we could take extra steps to
183 make cache contention across cores completely impossible, the
184 extra locking and central join required isn't worth it. It's
185 preferable to make contention merely highly unlikely. */
187 int start_row, end_row;
189 start_row = (taski*2)*h/(tasks*2);
190 end_row = (taski*2+1)*h/(tasks*2);
192 start_row = ((taski-tasks)*2+1)*h/(tasks*2);
193 end_row = ((taski-tasks)*2+2)*h/(tasks*2);
196 for(rowi = start_row; rowi<end_row; rowi++){
197 unsigned char *row = frame->get_rows()[rowi];
199 for(coli=0;coli<w;coli+=SPLIT){
201 int todo = (w-coli>SPLIT)?SPLIT:(w-coli);
202 float *A = engine->selection_workA+w*rowi+coli;
204 switch(frame->get_color_model()) {
206 rgb8_to_RGB((unsigned char *)row,Rvec,Gvec,Bvec,todo);
210 rgba8_to_RGBA((unsigned char *)row,Rvec,Gvec,Bvec,Avec,todo);
214 rgbF_to_RGB((float *)row,Rvec,Gvec,Bvec,todo);
218 rgbaF_to_RGBA((float *)row,Rvec,Gvec,Bvec,Avec,todo);
222 yuv8_to_RGB((unsigned char *)row,Rvec,Gvec,Bvec,todo);
226 yuva8_to_RGBA((unsigned char *)row,Rvec,Gvec,Bvec,Avec,todo);
231 for(j = 0; j < todo; j++)
232 RGB_to_HSpV(Rvec[j],Gvec[j],Bvec[j],Hvec[j],Svec[j],Vvec[j]);
235 for(j = 0; j < todo; j++)
236 selection[j]=sel_lookup(Hvec[j]*.166666667f,Hl);
238 for(j = 0; j < todo; j++)
242 for(j = 0; j < todo; j++)
243 selection[j]*=sel_lookup(Svec[j],Sl);
246 for(j = 0; j < todo; j++)
247 selection[j]*=sel_lookup(Vvec[j],Vl);
249 /* lock the memcpy to prevent pessimal cache coherency
250 interleave across cores. */
251 pthread_mutex_lock(&engine->copylock);
252 memcpy(A,selection,sizeof(*selection)*todo);
253 pthread_mutex_unlock(&engine->copylock);
258 /* Perform fill shaping on the selection */
259 selection_fullframe = plugin->fill_selection(engine->selection_workA,
260 engine->selection_workB, w, h, engine);
263 /* row-by-row color modification and histogram feedback */
264 engine->set_task(tasks*2,"modification_even");
265 while((taski = engine->next_task())>=0){
267 /* operate on discontinuous, interleaved sections of the source
268 buffer in two passes. Although we could take extra steps to
269 make cache contention across cores completely impossible, the
270 extra locking and central join required isn't worth it. It's
271 preferable to make contention merely highly unlikely. */
273 int start_row, end_row;
275 start_row = (taski*2)*h/(tasks*2);
276 end_row = (taski*2+1)*h/(tasks*2);
278 start_row = ((taski-tasks)*2+1)*h/(tasks*2);
279 end_row = ((taski-tasks)*2+2)*h/(tasks*2);
282 for(rowi = start_row; rowi<end_row; rowi++){
283 unsigned char *row = frame->get_rows()[rowi];
285 for(int coli=0;coli<w;coli+=SPLIT){
286 int todo = (w-coli>SPLIT)?SPLIT:(w-coli);
287 int have_selection = 0;
289 /* convert from pipeline color format */
290 if(active || show_ants || (use_mask && capture_mask && have_alpha)){
292 switch(frame->get_color_model()) {
294 pthread_mutex_lock(&engine->copylock);
295 memcpy(row_fragment,row,todo*3);
296 pthread_mutex_unlock(&engine->copylock);
297 rgb8_to_RGB(row_fragment,Rvec,Gvec,Bvec,todo);
298 byte_advance = todo*3;
303 pthread_mutex_lock(&engine->copylock);
304 memcpy(row_fragment,row,todo*4);
305 pthread_mutex_unlock(&engine->copylock);
306 rgba8_to_RGBA(row_fragment,Rvec,Gvec,Bvec,Avec,todo);
307 byte_advance = todo*4;
312 pthread_mutex_lock(&engine->copylock);
313 memcpy(row_fragment,row,todo*12);
314 pthread_mutex_unlock(&engine->copylock);
315 rgbF_to_RGB((float *)row_fragment,Rvec,Gvec,Bvec,todo);
316 byte_advance = todo*12;
321 pthread_mutex_lock(&engine->copylock);
322 memcpy(row_fragment,row,todo*16);
323 pthread_mutex_unlock(&engine->copylock);
324 rgbaF_to_RGBA((float *)row_fragment,Rvec,Gvec,Bvec,Avec,todo);
325 byte_advance = todo*16;
330 pthread_mutex_lock(&engine->copylock);
331 memcpy(row_fragment,row,todo*3);
332 pthread_mutex_unlock(&engine->copylock);
333 yuv8_to_RGB(row_fragment,Rvec,Gvec,Bvec,todo);
334 byte_advance = todo*3;
339 pthread_mutex_lock(&engine->copylock);
340 memcpy(row_fragment,row,todo*4);
341 pthread_mutex_unlock(&engine->copylock);
342 yuva8_to_RGBA(row,Rvec,Gvec,Bvec,Avec,todo);
343 byte_advance = todo*4;
349 /* generate initial HSV values [if selection active] */
350 for(j = 0; j < todo; j++)
351 RGB_to_HSpV(Rvec[j],Gvec[j],Bvec[j],Hvec[j],Svec[j],Vvec[j]);
354 float selection_test=todo;
356 if(selection_fullframe){
358 /* get the full-frame selection data we need into thread-local storage */
359 /* the full-frame data is read-only at this point, no need to lock */
360 float *sf = selection_fullframe + rowi*w + coli;
362 for(j = 0; j < todo; j++)
363 selection_test += selection[j] = sf[j];
368 /* selection computation when no full-frame shaping */
371 for(j = 0; j < todo; j++)
372 selection_test += selection[j] = sel_lookup(Hvec[j]*.166666667f,Hl);
378 if(selection_test>SELECT_THRESH){
380 for(j = 0; j < todo; j++)
381 selection_test += selection[j] *= sel_lookup(Svec[j],Sl);
385 for(j = 0; j < todo; j++)
386 selection_test += selection[j] = sel_lookup(Svec[j],Sl);
393 if(selection_test>SELECT_THRESH){
395 for(j = 0; j < todo; j++)
396 selection_test += selection[j] *= sel_lookup(Vvec[j],Vl);
400 for(j = 0; j < todo; j++)
401 selection_test += selection[j] = sel_lookup(Vvec[j],Vl);
407 /* selection modification according to config */
408 if(use_mask && have_alpha){
411 /* selection consists only of mask */
413 for(j = 0; j < todo; j++)
414 selection_test += selection[j] = Avec[j];
417 if(invert_selection){
418 if(selection_test < SELECT_THRESH){
419 /* fully selected after invert, clip to mask */
421 for(j = 0; j < todo; j++)
422 selection_test += selection[j] = Avec[j];
424 /* partial selection after invert, clip to mask */
426 for(j = 0; j < todo; j++)
427 selection_test += selection[j] = Avec[j]*(1.f-selection[j]);
430 if(selection_test < SELECT_THRESH){
431 /* fully deselected */
432 }else if(selection_test >= todo-SELECT_THRESH){
433 /* fully selected, clip to mask */
435 for(j = 0; j < todo; j++)
436 selection_test += selection[j] = Avec[j];
438 /* partial selection, clip to mask */
440 for(j = 0; j < todo; j++)
441 selection_test += selection[j] *= Avec[j];
447 for(j = 0; j < todo; j++) selection[j] = 1;
449 if(invert_selection){
450 if(selection_test < SELECT_THRESH){
451 /* fully selected after invert */
452 for(j = 0; j < todo; j++) selection[j] = 1;
454 /* partial selection after invert, clip to mask */
456 for(j = 0; j < todo; j++)
457 selection_test += selection[j] =
458 Avec[j]+(1.f-selection[j])-Avec[j]*(1.f-selection[j]);
461 if(selection_test < SELECT_THRESH){
462 /* fully deselected, clip to mask */
464 for(j = 0; j < todo; j++)
465 selection_test += selection[j] = Avec[j];
466 }else if(selection_test >= todo-SELECT_THRESH){
469 /* partial selection, clip to mask */
471 for(j = 0; j < todo; j++)
472 selection_test += selection[j] =
473 selection[j]+Avec[j]-selection[j]*Avec[j];
479 if(!use_mask && !invert_selection && selection_test < SELECT_THRESH){
480 /* skip processing this fragment */
481 /* we're using a mask; if the mask is set to capture, we
482 need to restore alpha before skipping */
484 switch(frame->get_color_model()) {
486 unmask_rgba8(row_fragment,todo);
489 unmask_rgbaF((float *)row_fragment,todo);
492 unmask_yuva8(row_fragment,todo);
495 pthread_mutex_lock(&engine->copylock);
496 memcpy(row,row_fragment,byte_advance);
497 pthread_mutex_unlock(&engine->copylock);
503 if(selection_test > todo-SELECT_THRESH)
504 have_selection=0; // fully selected
507 if(invert_selection){
508 if(selection_test < SELECT_THRESH){
509 /* fully selected after inversion */
511 }else if (selection_test >= todo-SELECT_THRESH){
512 /* fully deselected after invert, skip fragment */
516 /* partial selection */
518 for(j = 0; j < todo; j++)
519 selection_test += selection[j] = (1.f-selection[j]);
522 if(selection_test < SELECT_THRESH){
523 /* fully deselected, skip fragment */
526 }else if (selection_test >= todo-SELECT_THRESH){
530 /* partial selection; already calculated */
533 if(selection_test < SELECT_THRESH){
535 continue; // inactive fragment
537 if(selection_test > todo-SELECT_THRESH)
538 have_selection=0; // fully selected
540 if(invert_selection){
553 for(j = 0; j < todo; j++)
554 if(selection[j]>SELECT_THRESH) Rvec[j]=adj_lookup(Rvec[j],Ral,Ras);
556 for(j = 0; j < todo; j++)
557 Rvec[j] = adj_lookup(Rvec[j],Ral,Ras);
563 for(j = 0; j < todo; j++)
564 if(selection[j]>SELECT_THRESH) Rhist[(int)CLAMP((Rvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
566 for(j = 0; j < todo; j++)
567 Rhist[(int)CLAMP((Rvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
574 for(j = 0; j < todo; j++)
575 if(selection[j]>SELECT_THRESH) Gvec[j]=adj_lookup(Gvec[j],Gal,Gas);
577 for(j = 0; j < todo; j++)
578 Gvec[j] = adj_lookup(Gvec[j],Gal,Gas);
581 /* green histogram */
584 for(j = 0; j < todo; j++)
585 if(selection[j]>SELECT_THRESH) Ghist[(int)CLAMP((Gvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
587 for(j = 0; j < todo; j++)
588 Ghist[(int)CLAMP((Gvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
595 for(j = 0; j < todo; j++)
596 if(selection[j]>SELECT_THRESH) Bvec[j]=adj_lookup(Bvec[j],Bal,Bas);
598 for(j = 0; j < todo; j++)
599 Bvec[j] = adj_lookup(Bvec[j],Bal,Bas);
605 for(j = 0; j < todo; j++)
606 if(selection[j]>SELECT_THRESH) Bhist[(int)CLAMP((Bvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
608 for(j = 0; j < todo; j++)
609 Bhist[(int)CLAMP((Bvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
613 /* compute HSV values or update values from earlier selection work if
615 if( (!doSEL || doRGB) && // not yet computed, or since modified
616 (doHSV || gui_open) ) // needed for HSV mod and/or histograms
617 for(j = 0; j < todo; j++)
618 RGB_to_HSpV(Rvec[j],Gvec[j],Bvec[j],Hvec[j],Svec[j],Vvec[j]);
622 /* don't bother checking selection, H adj is lightweight */
624 for(j = 0; j < todo; j++){
626 if(Hvec[j]<0.f)Hvec[j]+=6.f;
627 if(Hvec[j]>=6.f)Hvec[j]-=6.f;
631 /* this is pre-shift RGB data; shift hue later to save an HSV->RGB transform */
634 for(j = 0; j < todo; j++){
635 if(selection[j]>SELECT_THRESH){
636 float weight = selection[j]*Svec[j];
637 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
638 Htotal += selection[j];
640 Hhist[bin] += weight;
641 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
642 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
643 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
647 for(j = 0; j < todo; j++){
648 float weight = Svec[j];
649 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
652 Hhist[bin] += weight;
653 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
654 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
655 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
663 for(j = 0; j < todo; j++)
664 if(selection[j]>SELECT_THRESH) Svec[j] = adj_lookup(Svec[j],Sal,Sas);
666 for(j = 0; j < todo; j++)
667 Svec[j] = adj_lookup(Svec[j],Sal,Sas);
671 /* This is unrolled a few times below...
672 Although we're using HSV, we don't want hue/saturation
673 changes to have a strong effect on apparent brightness.
674 Apply a correction to V (not Y!) based on luma change. */
675 /* Calculate new RGB values at same time */
678 for(j = 0; j < todo; j++){
679 if(selection[j]>SELECT_THRESH){
680 HSpV_correct_RGB(Hvec[j],Svec[j],Vvec[j],Rvec[j],Gvec[j],Bvec[j]);
681 /* run S histogram at the same time as we've got
684 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
685 Shist[bin] += selection[j];
686 Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
687 Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
688 Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
693 for(j = 0; j < todo; j++){
694 HSpV_correct_RGB(Hvec[j],Svec[j],Vvec[j],Rvec[j],Gvec[j],Bvec[j]);
696 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
698 Shr[bin>>HRGBSHIFT] += Rvec[j];
699 Shg[bin>>HRGBSHIFT] += Gvec[j];
700 Shb[bin>>HRGBSHIFT] += Bvec[j];
707 for(j = 0; j < todo; j++){
708 if(selection[j]>SELECT_THRESH){
709 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
710 Shist[bin] += selection[j];
711 Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
712 Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
713 Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
717 for(j = 0; j < todo; j++){
718 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
720 Shr[bin>>HRGBSHIFT] += Rvec[j];
721 Shg[bin>>HRGBSHIFT] += Gvec[j];
722 Shb[bin>>HRGBSHIFT] += Bvec[j];
729 /* This one is a bit unlike the others; Value gamma
730 behavior is conditional. When < 1.0 (darkening the
731 picture) gamma directly operates on Value as normally
732 expected (thus scaling R G and B about zero). When
733 gamma > 1.0 (lightening the picture) it operates on the
734 Value scale's zero point rather than the Value. Both
735 are done to get the most natural behavior out of
736 saturation as gamma changes. The only drawback: One
737 can no longer reverse a gamma operation by applying the
738 inverse gamma in a subsequent step. */
743 for(j = 0; j < todo; j++)
744 if(selection[j]>SELECT_THRESH){
745 float scale = adj_lookup(Vvec[j],Val,Vas);
746 Vvec[j] = Vvec[j] * scale + Vshift;
747 Rvec[j] = Rvec[j] * scale + Vshift;
748 Gvec[j] = Gvec[j] * scale + Vshift;
749 Bvec[j] = Bvec[j] * scale + Vshift;
753 for(j = 0; j < todo; j++)
754 if(selection[j]>SELECT_THRESH){
755 float shift = adj_lookup(Vvec[j],Val,Vas);
756 Vvec[j] = Vvec[j] * Vscale + shift;
757 Rvec[j] = Rvec[j] * Vscale + shift;
758 Gvec[j] = Gvec[j] * Vscale + shift;
759 Bvec[j] = Bvec[j] * Vscale + shift;
765 for(j = 0; j < todo; j++){
766 float scale = adj_lookup(Vvec[j],Val,Vas);
767 Vvec[j] = Vvec[j] * scale + Vshift;
768 Rvec[j] = Rvec[j] * scale + Vshift;
769 Gvec[j] = Gvec[j] * scale + Vshift;
770 Bvec[j] = Bvec[j] * scale + Vshift;
774 for(j = 0; j < todo; j++){
775 float shift = adj_lookup(Vvec[j],Val,Vas);
776 Vvec[j] = Vvec[j] * Vscale + shift;
777 Rvec[j] = Rvec[j] * Vscale + shift;
778 Gvec[j] = Gvec[j] * Vscale + shift;
779 Bvec[j] = Bvec[j] * Vscale + shift;
788 for(j = 0; j < todo; j++){
789 if(selection[j]>SELECT_THRESH){
790 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
791 Vhist[bin] += selection[j];
792 Vhr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
793 Vhg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
794 Vhb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
798 for(j = 0; j < todo; j++){
799 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
801 Vhr[bin>>HRGBSHIFT] += Rvec[j];
802 Vhg[bin>>HRGBSHIFT] += Gvec[j];
803 Vhb[bin>>HRGBSHIFT] += Bvec[j];
812 for(j = 0; j < todo; j++){
813 if(selection[j]>SELECT_THRESH){
814 float weight = selection[j]*Svec[j];
815 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
816 Htotal += selection[j];
818 Hhist[bin] += weight;
819 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
820 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
821 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
825 for(j = 0; j < todo; j++){
826 if(selection[j]>SELECT_THRESH){
827 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
828 Shist[bin] += selection[j];
829 Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
830 Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
831 Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
835 for(j = 0; j < todo; j++){
836 if(selection[j]>SELECT_THRESH){
837 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
838 Vhist[bin] += selection[j];
839 Vhr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
840 Vhg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
841 Vhb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
847 for(j = 0; j < todo; j++){
848 float weight = Svec[j];
849 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
852 Hhist[bin] += weight;
853 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
854 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
855 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
858 for(j = 0; j < todo; j++){
859 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
861 Shr[bin>>HRGBSHIFT] += Rvec[j];
862 Shg[bin>>HRGBSHIFT] += Gvec[j];
863 Shb[bin>>HRGBSHIFT] += Bvec[j];
866 for(j = 0; j < todo; j++){
867 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
869 Vhr[bin>>HRGBSHIFT] += Rvec[j];
870 Vhg[bin>>HRGBSHIFT] += Gvec[j];
871 Vhb[bin>>HRGBSHIFT] += Bvec[j];
876 /* layer back into pipeline color format; master fader applies here */
877 switch(frame->get_color_model()) {
879 RGB_to_rgb8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,3);
882 RGB_to_rgb8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,4);
885 RGB_to_rgbF(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,(float *)row_fragment,todo,3);
888 RGB_to_rgbF(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,(float *)row_fragment,todo,4);
891 RGB_to_yuv8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,3);
894 RGB_to_yuv8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,4);
901 for(j = 0; j < todo; j++){
902 float A = have_selection ? selection[j] : 1.f;
904 if( (ant+rowi-j)&0x4 ){
916 if( (ant+rowi+j)&0x4 ){
929 float *s = have_selection?selection:0;
930 /* re-layer back into pipeline color format */
931 switch(frame->get_color_model()) {
933 RGB_to_rgb8(Rvec,Gvec,Bvec,s,1.f,row_fragment,todo,3);
936 RGB_to_rgb8(Rvec,Gvec,Bvec,s,1.f,row_fragment,todo,4);
939 RGB_to_rgbF(Rvec,Gvec,Bvec,s,1.f,(float *)row_fragment,todo,3);
942 RGB_to_rgbF(Rvec,Gvec,Bvec,s,1.f,(float *)row_fragment,todo,4);
945 RGB_to_yuv8(Rvec,Gvec,Bvec,s,1.f,row_fragment,todo,3);
948 RGB_to_yuv8(Rvec,Gvec,Bvec,s,1.f,row_fragment,todo,4);
953 if(active || show_ants || use_mask || capture_mask){
955 switch(frame->get_color_model()) {
957 unmask_rgba8(row_fragment,todo);
960 unmask_rgbaF((float *)row_fragment,todo);
963 unmask_yuva8(row_fragment,todo);
968 float *s = !show_ants&&have_selection?selection:0;
969 switch(frame->get_color_model()) {
972 Aal_to_alp8(s,Aal,row_fragment,todo,4);
975 Aal_to_alpF(s,Aal,(float *)row_fragment,todo,4);
980 /* lock to prevent interleaved cache coherency collisions
981 across cores. Two memcpys touching the same cache line
982 will cause the wings to fall off the processor. */
984 pthread_mutex_lock(&engine->copylock);
985 memcpy(row,row_fragment,byte_advance);
986 pthread_mutex_unlock(&engine->copylock);
994 pthread_mutex_lock(&engine->copylock);
996 plugin->hue_total+=Htotal;
997 plugin->hue_weight+=Hweight;
999 for(int i=0;i<HISTSIZE;i++){
1000 plugin->red_histogram[i] += Rhist[i];
1001 plugin->green_histogram[i] += Ghist[i];
1002 plugin->blue_histogram[i] += Bhist[i];
1004 plugin->hue_histogram[i] += Hhist[i];
1005 plugin->sat_histogram[i] += Shist[i];
1007 plugin->value_histogram[i] += Vhist[i];
1010 for(int i=0;i<(HISTSIZE>>HRGBSHIFT);i++){
1011 plugin->hue_histogram_red[i] += Hhr[i];
1012 plugin->hue_histogram_green[i] += Hhg[i];
1013 plugin->hue_histogram_blue[i] += Hhb[i];
1015 plugin->sat_histogram_red[i] += Shr[i];
1016 plugin->sat_histogram_green[i] += Shg[i];
1017 plugin->sat_histogram_blue[i] += Shb[i];
1019 plugin->value_histogram_red[i] += Vhr[i];
1020 plugin->value_histogram_green[i] += Vhg[i];
1021 plugin->value_histogram_blue[i] += Vhb[i];
1023 pthread_mutex_unlock(&engine->copylock);
1027 BluebananaEngine::BluebananaEngine(BluebananaMain *plugin, int total_clients,
1028 int total_packages) : LoadServer(total_clients, total_packages){
1029 this->plugin = plugin;
1033 pthread_mutex_init(©lock,NULL);
1034 pthread_mutex_init(&tasklock,NULL);
1035 pthread_cond_init(&taskcond,NULL);
1037 BluebananaEngine::~BluebananaEngine(){
1038 pthread_cond_destroy(&taskcond);
1039 pthread_mutex_destroy(&tasklock);
1040 pthread_mutex_destroy(©lock);
1041 if(selection_workA) delete[] selection_workA;
1042 if(selection_workB) delete[] selection_workB;
1045 void BluebananaEngine::init_packages(){}
1047 LoadClient* BluebananaEngine::new_client(){
1048 return new BluebananaUnit(this, plugin);
1051 LoadPackage* BluebananaEngine::new_package(){
1052 return new BluebananaPackage(this);
1055 static float lt(float *data,float pos, int n){
1060 return data[i]*(1.f-del) + data[i+1]*del;
1063 static float lt_shift(float *data, float pos, int n, int over){
1071 for(int j=0; j<over; j++)
1073 for(int j=0; j<over; j++)
1076 float temp=(s0*(1.f-del) + s1*del);
1083 void BluebananaEngine::process_packages(VFrame *data){
1084 int w = data->get_w();
1085 int h = data->get_h();
1089 task_finish_count=0;
1091 /* If we're doing any spatial modification of the selection, we'll
1092 need to operate on a temporary selection array that covers the
1094 if(plugin->config.Fsel_active){
1095 if(plugin->config.Fsel_lo ||
1096 plugin->config.Fsel_mid ||
1097 plugin->config.Fsel_hi ||
1098 plugin->config.Fsel_over){
1099 selection_workA = new float[w*h];
1100 selection_workB = new float[w*h];
1104 memset(plugin->red_histogram,0,sizeof(plugin->red_histogram));
1105 memset(plugin->green_histogram,0,sizeof(plugin->green_histogram));
1106 memset(plugin->blue_histogram,0,sizeof(plugin->blue_histogram));
1107 memset(plugin->hue_histogram,0,sizeof(plugin->hue_histogram));
1108 plugin->hue_total=0.f;
1109 plugin->hue_weight=0.f;
1110 memset(plugin->sat_histogram,0,sizeof(plugin->sat_histogram));
1111 memset(plugin->value_histogram,0,sizeof(plugin->value_histogram));
1113 memset(plugin->hue_histogram_red,0,sizeof(plugin->hue_histogram_red));
1114 memset(plugin->hue_histogram_green,0,sizeof(plugin->hue_histogram_green));
1115 memset(plugin->hue_histogram_blue,0,sizeof(plugin->hue_histogram_blue));
1117 memset(plugin->sat_histogram_red,0,sizeof(plugin->sat_histogram_red));
1118 memset(plugin->sat_histogram_green,0,sizeof(plugin->sat_histogram_green));
1119 memset(plugin->sat_histogram_blue,0,sizeof(plugin->sat_histogram_blue));
1121 memset(plugin->value_histogram_red,0,sizeof(plugin->value_histogram_red));
1122 memset(plugin->value_histogram_green,0,sizeof(plugin->value_histogram_green));
1123 memset(plugin->value_histogram_blue,0,sizeof(plugin->value_histogram_blue));
1125 LoadServer::process_packages();
1127 /* The Hue histogram needs to be adjusted/shifted */
1128 if(plugin->config.active && plugin->hue_weight){
1130 float scale = plugin->hue_total/plugin->hue_weight;
1131 float Hshift = plugin->config.Hsel_active ? (plugin->config.Hsel_lo + plugin->config.Hsel_hi)/720.f-.5f : 0.f;
1132 float Hal = plugin->config.Hadj_active ? plugin->config.Hadj_val/60.f : 0.f;
1133 float hist[HISTSIZE + (1<<HRGBSHIFT)];
1134 float red[(HISTSIZE>>HRGBSHIFT)+1];
1135 float green[(HISTSIZE>>HRGBSHIFT)+1];
1136 float blue[(HISTSIZE>>HRGBSHIFT)+1];
1138 for(i=0;i<(1<<HRGBSHIFT);i++){
1139 plugin->hue_histogram[i]+=plugin->hue_histogram[HISTSIZE+i];
1140 plugin->hue_histogram[HISTSIZE+i]=plugin->hue_histogram[i];
1142 plugin->hue_histogram_red[0]+=plugin->hue_histogram_red[HISTSIZE>>HRGBSHIFT];
1143 plugin->hue_histogram_red[HISTSIZE>>HRGBSHIFT]=plugin->hue_histogram_red[0];
1144 plugin->hue_histogram_green[0]+=plugin->hue_histogram_green[HISTSIZE>>HRGBSHIFT];
1145 plugin->hue_histogram_green[HISTSIZE>>HRGBSHIFT]=plugin->hue_histogram_green[0];
1146 plugin->hue_histogram_blue[0]+=plugin->hue_histogram_blue[HISTSIZE>>HRGBSHIFT];
1147 plugin->hue_histogram_blue[HISTSIZE>>HRGBSHIFT]=plugin->hue_histogram_blue[0];
1149 for(i=0; i<(HISTSIZE>>HRGBSHIFT); i++){
1150 float pos = i+Hshift*(HISTSIZE>>HRGBSHIFT);
1151 if(pos<0)pos+=(HISTSIZE>>HRGBSHIFT);
1152 if(pos>=(HISTSIZE>>HRGBSHIFT))pos-=(HISTSIZE>>HRGBSHIFT);
1154 float div = lt_shift(plugin->hue_histogram,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1155 (HISTSIZE>>HRGBSHIFT), (1<<HRGBSHIFT));
1156 red[i] = lt(plugin->hue_histogram_red,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1157 (HISTSIZE>>HRGBSHIFT))*div;
1158 green[i] = lt(plugin->hue_histogram_green,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1159 (HISTSIZE>>HRGBSHIFT))*div;
1160 blue[i] = lt(plugin->hue_histogram_blue,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1161 (HISTSIZE>>HRGBSHIFT))*div;
1164 for(int i=0; i<HISTSIZE; i++)
1165 hist[i] = lt(plugin->hue_histogram,i+Hshift*HISTSIZE,HISTSIZE)*scale;
1167 memcpy(hist+HISTSIZE,hist,sizeof(*hist)*(1<<HRGBSHIFT));
1168 memcpy(plugin->hue_histogram,hist,sizeof(hist));
1170 red[HISTSIZE>>HRGBSHIFT]=red[0];
1171 green[HISTSIZE>>HRGBSHIFT]=green[0];
1172 blue[HISTSIZE>>HRGBSHIFT]=blue[0];
1174 for(i=0,j=0; i<=(HISTSIZE>>HRGBSHIFT); i++){
1175 float sum=0.f,H,S,V;
1176 for(int k=0; k<(1<<HRGBSHIFT); j++,k++)
1179 RGB_to_HSpV(red[i],green[i],blue[i],H,S,V);
1183 HSpV_to_RGB(H,S,V,red[i],green[i],blue[i]);
1185 plugin->hue_histogram_red[i] = red[i] * sum;
1186 plugin->hue_histogram_green[i] = green[i] * sum;
1187 plugin->hue_histogram_blue[i] = blue[i] * sum;
1191 if(selection_workA){
1192 delete[] selection_workA;
1195 if(selection_workB){
1196 delete[] selection_workB;
1201 /* create a coordinated work-division list and join point */
1202 void BluebananaEngine::set_task(int n, const char *task){
1203 pthread_mutex_lock(&this->tasklock);
1204 if(task_init_state==0){
1205 //fprintf(stderr,"New task!: %s",task);
1207 task_finish_count=get_total_packages();
1211 pthread_mutex_unlock(&this->tasklock);
1214 /* fetch the next task ticket. If the task is complete, wait here for
1215 all package threads to complete before returning <0 */
1216 int BluebananaEngine::next_task(){
1217 pthread_mutex_lock(&tasklock);
1220 //fprintf(stderr,".");
1221 pthread_mutex_unlock(&tasklock);
1224 task_finish_count--;
1225 if(task_finish_count==0){
1227 //fprintf(stderr,"done\n");
1228 pthread_cond_broadcast(&taskcond);
1230 int serial = task_init_serial;
1231 while(task_finish_count && serial == task_init_serial){
1232 //fprintf(stderr,"+");
1233 pthread_cond_wait(&taskcond,&tasklock);
1236 pthread_mutex_unlock(&tasklock);
1237 //fprintf(stderr,"-");
1242 /* same as above, but waits for join without fetching a task slot */
1243 void BluebananaEngine::wait_task(){
1244 pthread_mutex_lock(&tasklock);
1245 task_finish_count--;
1246 if(task_finish_count==0){
1248 //fprintf(stderr,"done\n");
1249 pthread_cond_broadcast(&taskcond);
1251 int serial = task_init_serial;
1252 while(task_finish_count && serial == task_init_serial){
1253 //fprintf(stderr,"+");
1254 pthread_cond_wait(&taskcond,&tasklock);
1257 pthread_mutex_unlock(&tasklock);
1258 //fprintf(stderr,"-");