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
25 #include "bluebanana.h"
26 #include "bluebananaconfig.h"
29 static float halfsin2(float x){
30 if(x<-M_PI/2)return 0;
35 static float scale_gamma (float hdel, float lo, float hi, float gamma){
41 /* this is contrived, so do something that looks good */
42 /* poor man's tangent */
43 hdel *= pow(.001,1/gamma)*1000;
45 hdel = pow(hdel,1/gamma);
48 // apply range scaling
49 return hdel*(hi-lo)+lo;
53 static int select_setup(char *pattern, int change){
56 float gsq = change*change+1.1;
61 while( x*x+y*y < gsq){
64 fprintf(stderr,_("Internal error; pattern array overflow\n"));
67 pattern[n++]=(change>0 ? 'H' : 'h');
72 fprintf(stderr,_("Internal error; pattern array overflow\n"));
75 pattern[n++]=(change>0 ? 'V' : 'v');
82 void BluebananaMain::update_lookups(int serverside){
83 // compare config contents with current lookup table state. Update
88 // Alpha lookup table for hue selection
89 if(lookup_cache.Hsel_lo != config.Hsel_lo ||
90 lookup_cache.Hsel_hi != config.Hsel_hi ||
91 lookup_cache.Hsel_over != config.Hsel_over){
93 float scale = M_PI/(config.Hsel_over+.0000001);
94 for(i=0; i<SELECT_LOOKUP_SIZE+2; i++){
95 /* this is a circular scale; the overlap wraps around to the opposite side */
96 float hue = (float)i/SELECT_LOOKUP_SIZE*360.;
97 float l0 = (hue-config.Hsel_lo-360.f)*scale;
98 float l1 = (hue-config.Hsel_lo)*scale;
99 float l2 = (hue-config.Hsel_lo+360.f)*scale;
100 float l3 = (hue-config.Hsel_lo+720.f)*scale;
101 float h0 = (hue-config.Hsel_hi-360.f)*scale;
102 float h1 = (hue-config.Hsel_hi)*scale;
103 float h2 = (hue-config.Hsel_hi+360.f)*scale;
104 float h3 = (hue-config.Hsel_hi+720.f)*scale;
106 float a0 = halfsin2(l0) * (1.-halfsin2(h0));
107 float a1 = halfsin2(l1) * (1.-halfsin2(h1));
108 float a2 = halfsin2(l2) * (1.-halfsin2(h2));
109 float a3 = halfsin2(l3) * (1.-halfsin2(h3));
111 hue_select_alpha_lookup[i]=a0+a1+a2+a3;
115 // Alpha lookup table for saturation selection
116 if(lookup_cache.Ssel_lo != config.Ssel_lo ||
117 lookup_cache.Ssel_hi != config.Ssel_hi ||
118 lookup_cache.Ssel_over != config.Ssel_over){
120 float scale = M_PI/(config.Ssel_over+.0000001);
121 for(i=0; i<SELECT_LOOKUP_SIZE+2; i++){
122 /* this is a bounded scale; the overlap reflects off the boundary */
123 float sat = (float)i/SELECT_LOOKUP_SIZE*100.;
124 float l0 = (-sat-config.Ssel_lo)*scale;
125 float h0 = (-sat-config.Ssel_hi)*scale;
126 float l1 = (sat-config.Ssel_lo)*scale;
127 float h1 = (sat-config.Ssel_hi)*scale;
128 float l2 = ((200.-sat)-config.Ssel_lo)*scale;
129 float h2 = ((200.-sat)-config.Ssel_hi)*scale;
130 sat_select_alpha_lookup[i] =
131 halfsin2(l0) * (1.-halfsin2(h0))+
132 halfsin2(l1) * (1.-halfsin2(h1))+
133 halfsin2(l2) * (1.-halfsin2(h2));
137 // Alpha lookup table for value selection
138 if(lookup_cache.Vsel_lo != config.Vsel_lo ||
139 lookup_cache.Vsel_hi != config.Vsel_hi ||
140 lookup_cache.Vsel_over != config.Vsel_over){
142 float scale = M_PI/(config.Vsel_over+.0000001);
143 for(i=0; i<SELECT_LOOKUP_SIZE+2; i++){
144 /* this is a bounded scale; the overlap reflects off the boundary */
145 float val = (float)i/SELECT_LOOKUP_SIZE*100.;
146 float l0 = (-val-config.Vsel_lo)*scale;
147 float h0 = (-val-config.Vsel_hi)*scale;
148 float l1 = (val-config.Vsel_lo)*scale;
149 float h1 = (val-config.Vsel_hi)*scale;
150 float l2 = ((200.-val)-config.Vsel_lo)*scale;
151 float h2 = ((200.-val)-config.Vsel_hi)*scale;
152 val_select_alpha_lookup[i] =
153 halfsin2(l0) * (1.-halfsin2(h0)) +
154 halfsin2(l1) * (1.-halfsin2(h1)) +
155 halfsin2(l2) * (1.-halfsin2(h2));
161 // lookup table for saturation adjustment
162 if(lookup_cache.Sadj_lo != config.Sadj_lo ||
163 lookup_cache.Sadj_hi != config.Sadj_hi ||
164 lookup_cache.Sadj_gamma != config.Sadj_gamma){
166 for(i=0; i<ADJUST_LOOKUP_SIZE+2; i++){
167 sat_adj_lookup[i] = scale_gamma ((float)i/ADJUST_LOOKUP_SIZE*MAX_ADJ_HEADROOM,
168 config.Sadj_lo,config.Sadj_hi,
171 sat_adj_toe_slope = -scale_gamma (-1,config.Sadj_lo,config.Sadj_hi,config.Sadj_gamma);
174 // lookup table for value adjustment
175 if(lookup_cache.Vadj_lo != config.Vadj_lo ||
176 lookup_cache.Vadj_hi != config.Vadj_hi ||
177 lookup_cache.Vadj_gamma != config.Vadj_gamma){
179 if(config.Vadj_gamma<1.0){
180 /* scaling mode for 'darkening' gamma*/
181 for(i=0; i<ADJUST_LOOKUP_SIZE+2; i++){
182 float V0 = (float)i/ADJUST_LOOKUP_SIZE*MAX_ADJ_HEADROOM;
183 float V1 = scale_gamma ((float)i/ADJUST_LOOKUP_SIZE*MAX_ADJ_HEADROOM,
184 0,config.Vadj_hi-config.Vadj_lo,
186 val_adj_lookup[i] = V0?V1/V0:0;
188 val_adj_toe_slope = -scale_gamma (-1,0,config.Vadj_hi-config.Vadj_lo,config.Vadj_gamma);
190 /* shifting mode for 'brightening' gamma */
191 for(i=0; i<ADJUST_LOOKUP_SIZE+2; i++){
192 float V0 = (float)i/ADJUST_LOOKUP_SIZE*MAX_ADJ_HEADROOM;
193 float V1 = scale_gamma ((float)i/ADJUST_LOOKUP_SIZE*MAX_ADJ_HEADROOM,
194 config.Vadj_lo,config.Vadj_lo+100,
196 val_adj_lookup[i] = V1-V0;
198 val_adj_toe_slope = -scale_gamma (-1,config.Vadj_lo,config.Vadj_lo+100.,config.Vadj_gamma);
202 // lookup table for red adjustment
203 if(lookup_cache.Radj_lo != config.Radj_lo ||
204 lookup_cache.Radj_hi != config.Radj_hi ||
205 lookup_cache.Radj_gamma != config.Radj_gamma){
207 for(i=0; i<ADJUST_LOOKUP_SIZE+2; i++)
208 red_adj_lookup[i] = scale_gamma ((float)i/ADJUST_LOOKUP_SIZE*MAX_ADJ_HEADROOM,
209 config.Radj_lo,config.Radj_hi,
211 red_adj_toe_slope = -scale_gamma (-1,config.Radj_lo,config.Radj_hi,config.Radj_gamma);
214 // lookup table for green adjustment
215 if(lookup_cache.Gadj_lo != config.Gadj_lo ||
216 lookup_cache.Gadj_hi != config.Gadj_hi ||
217 lookup_cache.Gadj_gamma != config.Gadj_gamma){
219 for(i=0; i<ADJUST_LOOKUP_SIZE+2; i++)
220 green_adj_lookup[i] = scale_gamma ((float)i/ADJUST_LOOKUP_SIZE*MAX_ADJ_HEADROOM,
221 config.Gadj_lo,config.Gadj_hi,
223 green_adj_toe_slope = -scale_gamma (-1,config.Gadj_lo,config.Gadj_hi,config.Gadj_gamma);
226 // lookup table for blue adjustment
227 if(lookup_cache.Badj_lo != config.Badj_lo ||
228 lookup_cache.Badj_hi != config.Badj_hi ||
229 lookup_cache.Badj_gamma != config.Badj_gamma){
231 for(i=0; i<ADJUST_LOOKUP_SIZE+2; i++)
232 blue_adj_lookup[i] = scale_gamma ((float)i/ADJUST_LOOKUP_SIZE*MAX_ADJ_HEADROOM,
233 config.Badj_lo,config.Badj_hi,
235 blue_adj_toe_slope = -scale_gamma (-1,config.Badj_lo,config.Badj_hi,config.Badj_gamma);
240 if(lookup_cache.Fsel_erode != config.Fsel_erode ||
241 lookup_cache.Fsel_lo != config.Fsel_lo ||
242 lookup_cache.Fsel_mid != config.Fsel_mid ||
243 lookup_cache.Fsel_hi != config.Fsel_hi){
249 if(config.Fsel_erode){
250 /* shrink-first mode */
251 select_one_n = select_setup(select_one,config.Fsel_lo);
253 select_two_n = select_setup(select_two,config.Fsel_hi-config.Fsel_lo);
254 select_three_n = select_setup(select_three,config.Fsel_mid-config.Fsel_hi);
256 select_two_n = select_setup(select_two,config.Fsel_mid-config.Fsel_lo);
259 /* grow-first mode */
260 select_one_n = select_setup(select_one,config.Fsel_hi);
262 select_two_n = select_setup(select_two,config.Fsel_lo-config.Fsel_hi);
263 select_three_n = select_setup(select_three,config.Fsel_mid-config.Fsel_lo);
265 select_two_n = select_setup(select_two,config.Fsel_mid-config.Fsel_hi);
270 lookup_cache.copy_from(config);
274 float BluebananaMain::hue_select_alpha(float hue){
275 if(config.Hsel_active){
276 float huewarp = hue*(SELECT_LOOKUP_SIZE*.002777778f);
277 float hueint = floor(huewarp);
278 float del = huewarp-hueint;
279 return hue_select_alpha_lookup[(int)hueint]*(1.f-del)+hue_select_alpha_lookup[(int)hueint+1]*del;
285 float BluebananaMain::sat_select_alpha(float sat){
286 if(config.Ssel_active){
287 float satwarp = sat*SELECT_LOOKUP_SIZE;
288 float satint = floor(satwarp);
289 float del = satwarp-satint;
290 return sat_select_alpha_lookup[(int)satint]*(1.f-del)+sat_select_alpha_lookup[(int)satint+1]*del;
296 float BluebananaMain::val_select_alpha(float val){
297 if(config.Vsel_active){
298 float valwarp = val*SELECT_LOOKUP_SIZE;
299 float valint = floor(valwarp);
300 float del = valwarp-valint;
301 return val_select_alpha_lookup[(int)valint]*(1.f-del)+val_select_alpha_lookup[(int)valint+1]*del;
306 // [0..1] HSV selection lookup must be sanitized as S and V values can
307 // exceed [0..1]. Clamping is OK, as out of range values will still
308 // be selected at range limits.
309 float sel_lookup(float val, float *lookup){
311 float del = modff(CLAMP(val,0.f,1.f)*SELECT_LOOKUP_SCALE,&bin);
312 return lookup[(int)bin]*(1.f-del)+lookup[(int)bin+1]*del;
315 // [0..4] == lookup range
316 // below 0, lookups are linear
318 float adj_lookup(float val, float *lookup, float toe_slope){
320 return val*toe_slope+lookup[0];
324 float del = modff(val*ADJUST_LOOKUP_SCALE,&bin);
325 return lookup[(int)bin]*(1.f-del)+lookup[(int)bin+1]*del;