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 */
484 if(selection_test > todo-SELECT_THRESH)
485 have_selection=0; // fully selected
488 if(invert_selection){
489 if(selection_test < SELECT_THRESH){
490 /* fully selected after inversion */
492 }else if (selection_test >= todo-SELECT_THRESH){
493 /* fully deselected after invert, skip fragment */
497 /* partial selection */
499 for(j = 0; j < todo; j++)
500 selection_test += selection[j] = (1.f-selection[j]);
503 if(selection_test < SELECT_THRESH){
504 /* fully deselected, skip fragment */
507 }else if (selection_test >= todo-SELECT_THRESH){
511 /* partial selection; already calculated */
514 if(selection_test < SELECT_THRESH){
516 continue; // inactive fragment
518 if(selection_test > todo-SELECT_THRESH)
519 have_selection=0; // fully selected
521 if(invert_selection){
534 for(j = 0; j < todo; j++)
535 if(selection[j]>SELECT_THRESH) Rvec[j]=adj_lookup(Rvec[j],Ral,Ras);
537 for(j = 0; j < todo; j++)
538 Rvec[j] = adj_lookup(Rvec[j],Ral,Ras);
544 for(j = 0; j < todo; j++)
545 if(selection[j]>SELECT_THRESH) Rhist[(int)CLAMP((Rvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
547 for(j = 0; j < todo; j++)
548 Rhist[(int)CLAMP((Rvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
555 for(j = 0; j < todo; j++)
556 if(selection[j]>SELECT_THRESH) Gvec[j]=adj_lookup(Gvec[j],Gal,Gas);
558 for(j = 0; j < todo; j++)
559 Gvec[j] = adj_lookup(Gvec[j],Gal,Gas);
562 /* green histogram */
565 for(j = 0; j < todo; j++)
566 if(selection[j]>SELECT_THRESH) Ghist[(int)CLAMP((Gvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
568 for(j = 0; j < todo; j++)
569 Ghist[(int)CLAMP((Gvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
576 for(j = 0; j < todo; j++)
577 if(selection[j]>SELECT_THRESH) Bvec[j]=adj_lookup(Bvec[j],Bal,Bas);
579 for(j = 0; j < todo; j++)
580 Bvec[j] = adj_lookup(Bvec[j],Bal,Bas);
586 for(j = 0; j < todo; j++)
587 if(selection[j]>SELECT_THRESH) Bhist[(int)CLAMP((Bvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += selection[j];
589 for(j = 0; j < todo; j++)
590 Bhist[(int)CLAMP((Bvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE)] += 1.f;
594 /* compute HSV values or update values from earlier selection work if
596 if( (!doSEL || doRGB) && // not yet computed, or since modified
597 (doHSV || gui_open) ) // needed for HSV mod and/or histograms
598 for(j = 0; j < todo; j++)
599 RGB_to_HSpV(Rvec[j],Gvec[j],Bvec[j],Hvec[j],Svec[j],Vvec[j]);
603 /* don't bother checking selection, H adj is lightweight */
605 for(j = 0; j < todo; j++){
607 if(Hvec[j]<0.f)Hvec[j]+=6.f;
608 if(Hvec[j]>=6.f)Hvec[j]-=6.f;
612 /* this is pre-shift RGB data; shift hue later to save an HSV->RGB transform */
615 for(j = 0; j < todo; j++){
616 if(selection[j]>SELECT_THRESH){
617 float weight = selection[j]*Svec[j];
618 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
619 Htotal += selection[j];
621 Hhist[bin] += weight;
622 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
623 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
624 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
628 for(j = 0; j < todo; j++){
629 float weight = Svec[j];
630 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
633 Hhist[bin] += weight;
634 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
635 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
636 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
644 for(j = 0; j < todo; j++)
645 if(selection[j]>SELECT_THRESH) Svec[j] = adj_lookup(Svec[j],Sal,Sas);
647 for(j = 0; j < todo; j++)
648 Svec[j] = adj_lookup(Svec[j],Sal,Sas);
652 /* This is unrolled a few times below...
653 Although we're using HSV, we don't want hue/saturation
654 changes to have a strong effect on apparent brightness.
655 Apply a correction to V (not Y!) based on luma change. */
656 /* Calculate new RGB values at same time */
659 for(j = 0; j < todo; j++){
660 if(selection[j]>SELECT_THRESH){
661 HSpV_correct_RGB(Hvec[j],Svec[j],Vvec[j],Rvec[j],Gvec[j],Bvec[j]);
662 /* run S histogram at the same time as we've got
665 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
666 Shist[bin] += selection[j];
667 Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
668 Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
669 Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
674 for(j = 0; j < todo; j++){
675 HSpV_correct_RGB(Hvec[j],Svec[j],Vvec[j],Rvec[j],Gvec[j],Bvec[j]);
677 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
679 Shr[bin>>HRGBSHIFT] += Rvec[j];
680 Shg[bin>>HRGBSHIFT] += Gvec[j];
681 Shb[bin>>HRGBSHIFT] += Bvec[j];
688 for(j = 0; j < todo; j++){
689 if(selection[j]>SELECT_THRESH){
690 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
691 Shist[bin] += selection[j];
692 Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
693 Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
694 Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
698 for(j = 0; j < todo; j++){
699 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
701 Shr[bin>>HRGBSHIFT] += Rvec[j];
702 Shg[bin>>HRGBSHIFT] += Gvec[j];
703 Shb[bin>>HRGBSHIFT] += Bvec[j];
710 /* This one is a bit unlike the others; Value gamma
711 behavior is conditional. When < 1.0 (darkening the
712 picture) gamma directly operates on Value as normally
713 expected (thus scaling R G and B about zero). When
714 gamma > 1.0 (lightening the picture) it operates on the
715 Value scale's zero point rather than the Value. Both
716 are done to get the most natural behavior out of
717 saturation as gamma changes. The only drawback: One
718 can no longer reverse a gamma operation by applying the
719 inverse gamma in a subsequent step. */
724 for(j = 0; j < todo; j++)
725 if(selection[j]>SELECT_THRESH){
726 float scale = adj_lookup(Vvec[j],Val,Vas);
727 Vvec[j] = Vvec[j] * scale + Vshift;
728 Rvec[j] = Rvec[j] * scale + Vshift;
729 Gvec[j] = Gvec[j] * scale + Vshift;
730 Bvec[j] = Bvec[j] * scale + Vshift;
734 for(j = 0; j < todo; j++)
735 if(selection[j]>SELECT_THRESH){
736 float shift = adj_lookup(Vvec[j],Val,Vas);
737 Vvec[j] = Vvec[j] * Vscale + shift;
738 Rvec[j] = Rvec[j] * Vscale + shift;
739 Gvec[j] = Gvec[j] * Vscale + shift;
740 Bvec[j] = Bvec[j] * Vscale + shift;
746 for(j = 0; j < todo; j++){
747 float scale = adj_lookup(Vvec[j],Val,Vas);
748 Vvec[j] = Vvec[j] * scale + Vshift;
749 Rvec[j] = Rvec[j] * scale + Vshift;
750 Gvec[j] = Gvec[j] * scale + Vshift;
751 Bvec[j] = Bvec[j] * scale + Vshift;
755 for(j = 0; j < todo; j++){
756 float shift = adj_lookup(Vvec[j],Val,Vas);
757 Vvec[j] = Vvec[j] * Vscale + shift;
758 Rvec[j] = Rvec[j] * Vscale + shift;
759 Gvec[j] = Gvec[j] * Vscale + shift;
760 Bvec[j] = Bvec[j] * Vscale + shift;
769 for(j = 0; j < todo; j++){
770 if(selection[j]>SELECT_THRESH){
771 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
772 Vhist[bin] += selection[j];
773 Vhr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
774 Vhg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
775 Vhb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
779 for(j = 0; j < todo; j++){
780 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
782 Vhr[bin>>HRGBSHIFT] += Rvec[j];
783 Vhg[bin>>HRGBSHIFT] += Gvec[j];
784 Vhb[bin>>HRGBSHIFT] += Bvec[j];
793 for(j = 0; j < todo; j++){
794 if(selection[j]>SELECT_THRESH){
795 float weight = selection[j]*Svec[j];
796 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
797 Htotal += selection[j];
799 Hhist[bin] += weight;
800 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
801 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
802 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
806 for(j = 0; j < todo; j++){
807 if(selection[j]>SELECT_THRESH){
808 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
809 Shist[bin] += selection[j];
810 Shr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
811 Shg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
812 Shb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
816 for(j = 0; j < todo; j++){
817 if(selection[j]>SELECT_THRESH){
818 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
819 Vhist[bin] += selection[j];
820 Vhr[bin>>HRGBSHIFT] += Rvec[j]*selection[j];
821 Vhg[bin>>HRGBSHIFT] += Gvec[j]*selection[j];
822 Vhb[bin>>HRGBSHIFT] += Bvec[j]*selection[j];
828 for(j = 0; j < todo; j++){
829 float weight = Svec[j];
830 int bin = CLAMP(Hvec[j]*HUESCALE,0,HISTSIZE);
833 Hhist[bin] += weight;
834 Hhr[bin>>HRGBSHIFT] += Rvec[j]*weight;
835 Hhg[bin>>HRGBSHIFT] += Gvec[j]*weight;
836 Hhb[bin>>HRGBSHIFT] += Bvec[j]*weight;
839 for(j = 0; j < todo; j++){
840 int bin = CLAMP((Svec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
842 Shr[bin>>HRGBSHIFT] += Rvec[j];
843 Shg[bin>>HRGBSHIFT] += Gvec[j];
844 Shb[bin>>HRGBSHIFT] += Bvec[j];
847 for(j = 0; j < todo; j++){
848 int bin = CLAMP((Vvec[j]+HISTOFF)*HISTSCALE,0,HISTSIZE);
850 Vhr[bin>>HRGBSHIFT] += Rvec[j];
851 Vhg[bin>>HRGBSHIFT] += Gvec[j];
852 Vhb[bin>>HRGBSHIFT] += Bvec[j];
857 /* layer back into pipeline color format; master fader applies here */
858 switch(frame->get_color_model()) {
860 RGB_to_rgb8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,3);
863 RGB_to_rgb8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,4);
866 RGB_to_rgbF(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,(float *)row_fragment,todo,3);
869 RGB_to_rgbF(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,(float *)row_fragment,todo,4);
872 RGB_to_yuv8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,3);
875 RGB_to_yuv8(Rvec,Gvec,Bvec,have_selection?selection:0,Oal,row_fragment,todo,4);
882 for(j = 0; j < todo; j++){
883 float A = have_selection ? selection[j] : 1.f;
885 if( (ant+rowi-j)&0x4 ){
897 if( (ant+rowi+j)&0x4 ){
910 float *s = have_selection?selection:0;
911 /* re-layer back into pipeline color format */
912 switch(frame->get_color_model()) {
914 RGB_to_rgb8(Rvec,Gvec,Bvec,s,1.f,row_fragment,todo,3);
917 RGB_to_rgb8(Rvec,Gvec,Bvec,s,1.f,row_fragment,todo,4);
920 RGB_to_rgbF(Rvec,Gvec,Bvec,s,1.f,(float *)row_fragment,todo,3);
923 RGB_to_rgbF(Rvec,Gvec,Bvec,s,1.f,(float *)row_fragment,todo,4);
926 RGB_to_yuv8(Rvec,Gvec,Bvec,s,1.f,row_fragment,todo,3);
929 RGB_to_yuv8(Rvec,Gvec,Bvec,s,1.f,row_fragment,todo,4);
934 if(active || show_ants || use_mask || capture_mask){
935 if(use_mask && capture_mask){
936 switch(frame->get_color_model()) {
938 unmask_rgba8(row_fragment,todo);
941 unmask_rgbaF((float *)row_fragment,todo);
944 unmask_yuva8(row_fragment,todo);
949 float *s = !show_ants&&have_selection?selection:0;
950 switch(frame->get_color_model()) {
953 Aal_to_alp8(s,Aal,row_fragment,todo,4);
956 Aal_to_alpF(s,Aal,(float *)row_fragment,todo,4);
961 /* lock to prevent interleaved cache coherency collisions
962 across cores. Two memcpys touching the same cache line
963 will cause the wings to fall off the processor. */
965 pthread_mutex_lock(&engine->copylock);
966 memcpy(row,row_fragment,byte_advance);
967 pthread_mutex_unlock(&engine->copylock);
975 pthread_mutex_lock(&engine->copylock);
977 plugin->hue_total+=Htotal;
978 plugin->hue_weight+=Hweight;
980 for(int i=0;i<HISTSIZE;i++){
981 plugin->red_histogram[i] += Rhist[i];
982 plugin->green_histogram[i] += Ghist[i];
983 plugin->blue_histogram[i] += Bhist[i];
985 plugin->hue_histogram[i] += Hhist[i];
986 plugin->sat_histogram[i] += Shist[i];
988 plugin->value_histogram[i] += Vhist[i];
991 for(int i=0;i<(HISTSIZE>>HRGBSHIFT);i++){
992 plugin->hue_histogram_red[i] += Hhr[i];
993 plugin->hue_histogram_green[i] += Hhg[i];
994 plugin->hue_histogram_blue[i] += Hhb[i];
996 plugin->sat_histogram_red[i] += Shr[i];
997 plugin->sat_histogram_green[i] += Shg[i];
998 plugin->sat_histogram_blue[i] += Shb[i];
1000 plugin->value_histogram_red[i] += Vhr[i];
1001 plugin->value_histogram_green[i] += Vhg[i];
1002 plugin->value_histogram_blue[i] += Vhb[i];
1004 pthread_mutex_unlock(&engine->copylock);
1008 BluebananaEngine::BluebananaEngine(BluebananaMain *plugin, int total_clients,
1009 int total_packages) : LoadServer(total_clients, total_packages){
1010 this->plugin = plugin;
1014 pthread_mutex_init(©lock,NULL);
1015 pthread_mutex_init(&tasklock,NULL);
1016 pthread_cond_init(&taskcond,NULL);
1018 BluebananaEngine::~BluebananaEngine(){
1019 pthread_cond_destroy(&taskcond);
1020 pthread_mutex_destroy(&tasklock);
1021 pthread_mutex_destroy(©lock);
1022 if(selection_workA) delete[] selection_workA;
1023 if(selection_workB) delete[] selection_workB;
1026 void BluebananaEngine::init_packages(){}
1028 LoadClient* BluebananaEngine::new_client(){
1029 return new BluebananaUnit(this, plugin);
1032 LoadPackage* BluebananaEngine::new_package(){
1033 return new BluebananaPackage(this);
1036 static float lt(float *data,float pos, int n){
1041 return data[i]*(1.f-del) + data[i+1]*del;
1044 static float lt_shift(float *data, float pos, int n, int over){
1052 for(int j=0; j<over; j++)
1054 for(int j=0; j<over; j++)
1057 float temp=(s0*(1.f-del) + s1*del);
1064 void BluebananaEngine::process_packages(VFrame *data){
1065 int w = data->get_w();
1066 int h = data->get_h();
1070 task_finish_count=0;
1072 /* If we're doing any spatial modification of the selection, we'll
1073 need to operate on a temporary selection array that covers the
1075 if(plugin->config.Fsel_active){
1076 if(plugin->config.Fsel_lo ||
1077 plugin->config.Fsel_mid ||
1078 plugin->config.Fsel_hi ||
1079 plugin->config.Fsel_over){
1080 selection_workA = new float[w*h];
1081 selection_workB = new float[w*h];
1085 memset(plugin->red_histogram,0,sizeof(plugin->red_histogram));
1086 memset(plugin->green_histogram,0,sizeof(plugin->green_histogram));
1087 memset(plugin->blue_histogram,0,sizeof(plugin->blue_histogram));
1088 memset(plugin->hue_histogram,0,sizeof(plugin->hue_histogram));
1089 plugin->hue_total=0.f;
1090 plugin->hue_weight=0.f;
1091 memset(plugin->sat_histogram,0,sizeof(plugin->sat_histogram));
1092 memset(plugin->value_histogram,0,sizeof(plugin->value_histogram));
1094 memset(plugin->hue_histogram_red,0,sizeof(plugin->hue_histogram_red));
1095 memset(plugin->hue_histogram_green,0,sizeof(plugin->hue_histogram_green));
1096 memset(plugin->hue_histogram_blue,0,sizeof(plugin->hue_histogram_blue));
1098 memset(plugin->sat_histogram_red,0,sizeof(plugin->sat_histogram_red));
1099 memset(plugin->sat_histogram_green,0,sizeof(plugin->sat_histogram_green));
1100 memset(plugin->sat_histogram_blue,0,sizeof(plugin->sat_histogram_blue));
1102 memset(plugin->value_histogram_red,0,sizeof(plugin->value_histogram_red));
1103 memset(plugin->value_histogram_green,0,sizeof(plugin->value_histogram_green));
1104 memset(plugin->value_histogram_blue,0,sizeof(plugin->value_histogram_blue));
1106 LoadServer::process_packages();
1108 /* The Hue histogram needs to be adjusted/shifted */
1109 if(plugin->config.active && plugin->hue_weight){
1111 float scale = plugin->hue_total/plugin->hue_weight;
1112 float Hshift = plugin->config.Hsel_active ? (plugin->config.Hsel_lo + plugin->config.Hsel_hi)/720.f-.5f : 0.f;
1113 float Hal = plugin->config.Hadj_active ? plugin->config.Hadj_val/60.f : 0.f;
1114 float hist[HISTSIZE + (1<<HRGBSHIFT)];
1115 float red[(HISTSIZE>>HRGBSHIFT)+1];
1116 float green[(HISTSIZE>>HRGBSHIFT)+1];
1117 float blue[(HISTSIZE>>HRGBSHIFT)+1];
1119 for(i=0;i<(1<<HRGBSHIFT);i++){
1120 plugin->hue_histogram[i]+=plugin->hue_histogram[HISTSIZE+i];
1121 plugin->hue_histogram[HISTSIZE+i]=plugin->hue_histogram[i];
1123 plugin->hue_histogram_red[0]+=plugin->hue_histogram_red[HISTSIZE>>HRGBSHIFT];
1124 plugin->hue_histogram_red[HISTSIZE>>HRGBSHIFT]=plugin->hue_histogram_red[0];
1125 plugin->hue_histogram_green[0]+=plugin->hue_histogram_green[HISTSIZE>>HRGBSHIFT];
1126 plugin->hue_histogram_green[HISTSIZE>>HRGBSHIFT]=plugin->hue_histogram_green[0];
1127 plugin->hue_histogram_blue[0]+=plugin->hue_histogram_blue[HISTSIZE>>HRGBSHIFT];
1128 plugin->hue_histogram_blue[HISTSIZE>>HRGBSHIFT]=plugin->hue_histogram_blue[0];
1130 for(i=0; i<(HISTSIZE>>HRGBSHIFT); i++){
1131 float pos = i+Hshift*(HISTSIZE>>HRGBSHIFT);
1132 if(pos<0)pos+=(HISTSIZE>>HRGBSHIFT);
1133 if(pos>=(HISTSIZE>>HRGBSHIFT))pos-=(HISTSIZE>>HRGBSHIFT);
1135 float div = lt_shift(plugin->hue_histogram,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1136 (HISTSIZE>>HRGBSHIFT), (1<<HRGBSHIFT));
1137 red[i] = lt(plugin->hue_histogram_red,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1138 (HISTSIZE>>HRGBSHIFT))*div;
1139 green[i] = lt(plugin->hue_histogram_green,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1140 (HISTSIZE>>HRGBSHIFT))*div;
1141 blue[i] = lt(plugin->hue_histogram_blue,i+Hshift*(HISTSIZE>>HRGBSHIFT),
1142 (HISTSIZE>>HRGBSHIFT))*div;
1145 for(int i=0; i<HISTSIZE; i++)
1146 hist[i] = lt(plugin->hue_histogram,i+Hshift*HISTSIZE,HISTSIZE)*scale;
1148 memcpy(hist+HISTSIZE,hist,sizeof(*hist)*(1<<HRGBSHIFT));
1149 memcpy(plugin->hue_histogram,hist,sizeof(hist));
1151 red[HISTSIZE>>HRGBSHIFT]=red[0];
1152 green[HISTSIZE>>HRGBSHIFT]=green[0];
1153 blue[HISTSIZE>>HRGBSHIFT]=blue[0];
1155 for(i=0,j=0; i<=(HISTSIZE>>HRGBSHIFT); i++){
1156 float sum=0.f,H,S,V;
1157 for(int k=0; k<(1<<HRGBSHIFT); j++,k++)
1160 RGB_to_HSpV(red[i],green[i],blue[i],H,S,V);
1164 HSpV_to_RGB(H,S,V,red[i],green[i],blue[i]);
1166 plugin->hue_histogram_red[i] = red[i] * sum;
1167 plugin->hue_histogram_green[i] = green[i] * sum;
1168 plugin->hue_histogram_blue[i] = blue[i] * sum;
1172 if(selection_workA){
1173 delete[] selection_workA;
1176 if(selection_workB){
1177 delete[] selection_workB;
1182 /* create a coordinated work-division list and join point */
1183 void BluebananaEngine::set_task(int n, const char *task){
1184 pthread_mutex_lock(&this->tasklock);
1185 if(task_init_state==0){
1186 //fprintf(stderr,"New task!: %s",task);
1188 task_finish_count=get_total_packages();
1192 pthread_mutex_unlock(&this->tasklock);
1195 /* fetch the next task ticket. If the task is complete, wait here for
1196 all package threads to complete before returning <0 */
1197 int BluebananaEngine::next_task(){
1198 pthread_mutex_lock(&tasklock);
1201 //fprintf(stderr,".");
1202 pthread_mutex_unlock(&tasklock);
1205 task_finish_count--;
1206 if(task_finish_count==0){
1208 //fprintf(stderr,"done\n");
1209 pthread_cond_broadcast(&taskcond);
1211 int serial = task_init_serial;
1212 while(task_finish_count && serial == task_init_serial){
1213 //fprintf(stderr,"+");
1214 pthread_cond_wait(&taskcond,&tasklock);
1217 pthread_mutex_unlock(&tasklock);
1218 //fprintf(stderr,"-");
1223 /* same as above, but waits for join without fetching a task slot */
1224 void BluebananaEngine::wait_task(){
1225 pthread_mutex_lock(&tasklock);
1226 task_finish_count--;
1227 if(task_finish_count==0){
1229 //fprintf(stderr,"done\n");
1230 pthread_cond_broadcast(&taskcond);
1232 int serial = task_init_serial;
1233 while(task_finish_count && serial == task_init_serial){
1234 //fprintf(stderr,"+");
1235 pthread_cond_wait(&taskcond,&tasklock);
1238 pthread_mutex_unlock(&tasklock);
1239 //fprintf(stderr,"-");