add auto zoombar/status color, fix 3 batchrender boobies, rotate plugin tweaks, add...
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / bluebanana / bluebananalookups.C
1 /*
2  * Cinelerra :: Blue Banana - color modification plugin for Cinelerra-CV
3  * Copyright (C) 2012-2013 Monty <monty@xiph.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include <math.h>
22 #include <stdint.h>
23 #include <string.h>
24
25 #include "bluebanana.h"
26 #include "bluebananaconfig.h"
27 #include "language.h"
28
29 static float halfsin2(float x){
30   if(x<-M_PI/2)return 0;
31   if(x>M_PI/2)return 1;
32   return .5+.5*sin(x);
33 }
34
35 static float scale_gamma (float hdel, float lo, float hi, float gamma){
36   lo /= 100.;
37   hi /= 100.;
38
39   // apply gamma
40   if(hdel<0){
41     /* this is contrived, so do something that looks good */
42     /* poor man's tangent */
43     hdel *= pow(.001,1/gamma)*1000;
44   }else{
45     hdel = pow(hdel,1/gamma);
46   }
47
48   // apply range scaling
49   return hdel*(hi-lo)+lo;
50
51 }
52
53 static int select_setup(char *pattern, int change){
54   int n = 0;
55   if(change){
56     float gsq = change*change+1.1;
57     int x = -1;
58     int y = abs(change);
59
60     while(1){
61       while( x*x+y*y < gsq){
62         x--;
63         if(n==FSrange*4){
64           fprintf(stderr,_("Internal error; pattern array overflow\n"));
65           return n;
66         }
67         pattern[n++]=(change>0 ? 'H' : 'h');
68       }
69       if(!y)break;
70       y--;
71       if(n==FSrange*4){
72         fprintf(stderr,_("Internal error; pattern array overflow\n"));
73         return n;
74       }
75       pattern[n++]=(change>0 ? 'V' : 'v');
76     }
77   }
78
79   return n;
80 }
81
82 void BluebananaMain::update_lookups(int serverside){
83   // compare config contents with current lookup table state.  Update
84   // as needed.
85
86   int i;
87
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){
92
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;
105
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));
110
111       hue_select_alpha_lookup[i]=a0+a1+a2+a3;
112     }
113   }
114
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){
119
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));
134     }
135   }
136
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){
141
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));
156     }
157   }
158
159   if(serverside){
160
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){
165
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,
169                                          config.Sadj_gamma);
170       }
171       sat_adj_toe_slope = -scale_gamma (-1,config.Sadj_lo,config.Sadj_hi,config.Sadj_gamma);
172     }
173
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){
178
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,
185                                   config.Vadj_gamma);
186           val_adj_lookup[i] = V0?V1/V0:0;
187         }
188         val_adj_toe_slope = -scale_gamma (-1,0,config.Vadj_hi-config.Vadj_lo,config.Vadj_gamma);
189       }else{
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,
195                                   config.Vadj_gamma);
196           val_adj_lookup[i] = V1-V0;
197         }
198         val_adj_toe_slope = -scale_gamma (-1,config.Vadj_lo,config.Vadj_lo+100.,config.Vadj_gamma);
199       }
200     }
201
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){
206
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,
210                                          config.Radj_gamma);
211       red_adj_toe_slope = -scale_gamma (-1,config.Radj_lo,config.Radj_hi,config.Radj_gamma);
212     }
213
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){
218
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,
222                                            config.Gadj_gamma);
223       green_adj_toe_slope = -scale_gamma (-1,config.Gadj_lo,config.Gadj_hi,config.Gadj_gamma);
224     }
225
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){
230
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,
234                                           config.Badj_gamma);
235       blue_adj_toe_slope = -scale_gamma (-1,config.Badj_lo,config.Badj_hi,config.Badj_gamma);
236     }
237   }
238
239
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){
244
245     select_one_n = 0;
246     select_two_n = 0;
247     select_three_n = 0;
248
249     if(config.Fsel_erode){
250       /* shrink-first mode */
251       select_one_n = select_setup(select_one,config.Fsel_lo);
252       if(config.Fsel_hi){
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);
255       }else{
256         select_two_n = select_setup(select_two,config.Fsel_mid-config.Fsel_lo);
257       }
258     }else{
259       /* grow-first mode */
260       select_one_n = select_setup(select_one,config.Fsel_hi);
261       if(config.Fsel_lo){
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);
264       }else{
265         select_two_n = select_setup(select_two,config.Fsel_mid-config.Fsel_hi);
266       }
267     }
268   }
269
270   lookup_cache.copy_from(config);
271 }
272
273 // [0..360]
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;
280   }else
281     return 1.f;
282 }
283
284 // [0..1]
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;
291   }else
292     return 1.f;
293 }
294
295 // [0..1]
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;
302   }else
303     return 1.f;
304 }
305
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){
310   float bin;
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;
313 }
314
315 // [0..4] == lookup range
316 // below 0, lookups are linear
317 // clamp above 4
318 float adj_lookup(float val, float *lookup, float toe_slope){
319   if(val<0){
320     return val*toe_slope+lookup[0];
321   }else{
322     if(val>4.f)val=4.f;
323     float bin;
324     float del = modff(val*ADJUST_LOOKUP_SCALE,&bin);
325     return lookup[(int)bin]*(1.f-del)+lookup[(int)bin+1]*del;
326   }
327 }
328