1 /*
2  */
3 
4 /*
5 
6   Copyright (C) 2014 Ferrero Andrea
7 
8   This program is free software: you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation, either version 3 of the License, or
11   (at your option) any later version.
12 
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16   GNU General Public License for more details.
17 
18   You should have received a copy of the GNU General Public License
19   along with this program. If not, see <http://www.gnu.org/licenses/>.
20 
21 
22  */
23 
24 /*
25 
26   These files are distributed with PhotoFlow - http://aferrero2707.github.io/PhotoFlow/
27 
28  */
29 
30 #ifndef HUE_SATURATION_H
31 #define HUE_SATURATION_H
32 
33 #include <string>
34 
35 #include <glibmm.h>
36 
37 //#include <libraw/libraw.h>
38 
39 #include "../base/color.hh"
40 #include "../base/processor.hh"
41 #include "../base/splinecurve.hh"
42 
43 
44 namespace PF
45 {
46 
47 class HueSaturationPar: public OpParBase
48 {
49   Property<float> hue, hue_eq;
50   Property<float> saturation, saturation_eq;
51   Property<float> contrast, contrast_eq;
52   Property<float> brightness, brightness_eq;
53   Property<float> gamma;
54   Property<bool> brightness_is_gamma;
55   Property<SplineCurve> hue_H_equalizer;
56   Property<SplineCurve> hue_S_equalizer;
57   Property<SplineCurve> hue_L_equalizer;
58   Property<bool> hue_H_equalizer_enabled;
59   Property<bool> hue_S_equalizer_enabled;
60   Property<bool> hue_L_equalizer_enabled;
61   Property<SplineCurve> saturation_H_equalizer;
62   Property<SplineCurve> saturation_S_equalizer;
63   Property<SplineCurve> saturation_L_equalizer;
64   Property<SplineCurve> contrast_H_equalizer;
65   Property<SplineCurve> contrast_S_equalizer;
66   Property<SplineCurve> contrast_L_equalizer;
67   Property<SplineCurve> brightness_H_equalizer;
68   Property<SplineCurve> brightness_S_equalizer;
69   Property<SplineCurve> brightness_L_equalizer;
70 
71   float exponent;
72 
73   Property<bool> show_mask;
74   Property<bool> invert_mask;
75   Property<bool> feather_mask;
76   Property<float> feather_radius;
77 
78   Property<SplineCurve>* eq_vec[12];
79 
80   ProcessorBase* mask;
81   ProcessorBase* blur;
82 
83   void update_curve( Property<SplineCurve>* grey_curve, float* vec );
84 
85 public:
86 
87   float vec[12][65535];
88   bool eq_enabled[12];
89 
90   HueSaturationPar();
91 
get_hue()92   float get_hue() { return hue.get(); }
get_hue_eq()93   float get_hue_eq() { return hue_eq.get(); }
get_saturation()94   float get_saturation() { return saturation.get(); }
get_saturation_eq()95   float get_saturation_eq() { return saturation_eq.get(); }
get_contrast()96   float get_contrast() { return contrast.get(); }
get_contrast_eq()97   float get_contrast_eq() { return contrast_eq.get(); }
get_brightness()98   float get_brightness() { return brightness.get(); }
get_brightness_eq()99   float get_brightness_eq() { return brightness_eq.get(); }
get_gamma()100   float get_gamma() { return exponent; }
get_brightness_is_gamma()101   bool get_brightness_is_gamma() { return brightness_is_gamma.get(); }
get_show_mask()102   bool get_show_mask() { return show_mask.get(); }
get_invert_mask()103   bool get_invert_mask() { return invert_mask.get(); }
104 
has_intensity()105   bool has_intensity() { return false; }
has_opacity()106   bool has_opacity() { return true; }
needs_input()107   bool needs_input() { return true; }
108 
109   VipsImage* build(std::vector<VipsImage*>& in, int first,
110       VipsImage* imap, VipsImage* omap,
111       unsigned int& level);
112 };
113 
114 
115 
116 template < OP_TEMPLATE_DEF >
117 class HueSaturation
118 {
119 public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)120   void render(VipsRegion** ireg, int n, int in_first,
121       VipsRegion* imap, VipsRegion* omap,
122       VipsRegion* oreg, OpParBase* par)
123   {
124   }
125 };
126 
127 
128 
129 
130 template < OP_TEMPLATE_DEF_CS_SPEC >
131 class HueSaturation< OP_TEMPLATE_IMP_CS_SPEC(PF_COLORSPACE_RGB) >
132 {
133 public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)134   void render(VipsRegion** ireg, int n, int in_first,
135       VipsRegion* imap, VipsRegion* omap,
136       VipsRegion* oreg, OpParBase* par)
137   {
138     HueSaturationPar* opar = dynamic_cast<HueSaturationPar*>(par);
139     if( !opar ) return;
140     VipsRect *r = &oreg->valid;
141     int line_size = r->width * oreg->im->Bands;
142     //int width = r->width;
143     int height = r->height;
144 
145     float hue = opar->get_hue();
146     float saturation = opar->get_saturation();
147     float contrast = opar->get_contrast();
148     float brightness = opar->get_brightness();
149 
150     bool inv = opar->get_invert_mask();
151 
152     T* pin;
153     T* pmask;
154     T* pout;
155     T RGB[3];
156     typename FormatInfo<T>::SIGNED tempval;
157     float h_in, s_in, v_in, l_in;
158     float h, s, v, l;
159     int x, y, k;
160 
161     if( PREVIEW && opar->get_show_mask() ) {
162       for( y = 0; y < height; y++ ) {
163         pin = (T*)VIPS_REGION_ADDR( ireg[0], r->left, r->top + y );
164         pmask = (T*)VIPS_REGION_ADDR( ireg[1], r->left, r->top + y );
165         pout = (T*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
166 
167         float fmask;
168         for( x = 0; x < line_size; x+=3 ) {
169           to_float( pmask[x], fmask );
170           pout[x] = fmask*pin[x] + (1.0f-fmask)*FormatInfo<T>::MAX;
171           pout[x+1] = fmask*pin[x+1] + (1.0f-fmask)*FormatInfo<T>::MIN;
172           pout[x+2] = fmask*pin[x+2] + (1.0f-fmask)*FormatInfo<T>::MIN;
173         }
174       }
175     } else {
176       for( y = 0; y < height; y++ ) {
177         pin = (T*)VIPS_REGION_ADDR( ireg[0], r->left, r->top + y );
178         pmask = (T*)VIPS_REGION_ADDR( ireg[1], r->left, r->top + y );
179         pout = (T*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
180 
181         for( x = 0; x < line_size; x+=3 ) {
182           RGB[0] = pin[x];
183           RGB[1] = pin[x+1];
184           RGB[2] = pin[x+2];
185           //rgb2hsv( R, G, B, h, s, v );
186           rgb2hsl( RGB[0], RGB[1], RGB[2], h_in, s_in, l_in );
187 
188           //std::cout<<"in RGB: "<<RGB[0]<<" "<<RGB[1]<<" "<<RGB[2]<<"  HSL: "<<h_in<<" "<<s_in<<" "<<l_in<<std::endl;
189           /*
190             unsigned short int hid = static_cast<unsigned short int>( h_in*65535/360 );
191             unsigned short int sid = static_cast<unsigned short int>( s_in*65535 );
192             unsigned short int lid = static_cast<unsigned short int>( l_in*65535 );
193 
194 
195             float h_eq1 = opar->eq_enabled[0] ? opar->vec[0][hid] : 1;
196             float h_eq2 = opar->eq_enabled[1] ? opar->vec[1][sid] : 1;
197             float h_eq3 = opar->eq_enabled[2] ? opar->vec[2][lid] : 1;
198 
199             float h_eq = MIN3( h_eq1, h_eq2, h_eq3 );
200            */
201 
202           float h_eq;
203           to_float( pmask[x], h_eq );
204 
205           //std::cout<<"h_in="<<h_in<<"  h_eq="<<h_eq<<" ("<<h_eq1<<" "<<h_eq2<<" "<<h_eq3<<")"<<std::endl;
206 
207           float hue2 = hue;
208           float saturation2 = saturation;
209           float brightness2 = brightness;
210           float contrast2 = contrast;
211           /*
212             if( h_eq < 1 ) {
213               hue2 *= h_eq;
214               saturation2 *= h_eq;
215               brightness2 *= h_eq;
216               contrast2 *= h_eq;
217             }
218            */
219           /*
220           //h = h_in + hue + opar->get_hue_eq()*h_eq;
221           if( opar->get_hue_eq() ) {
222             hue2 += opar->get_hue_eq()*h_eq;
223           }
224            */
225           if( hue2 != 0 ) {
226             h = h_in + hue2;
227             if( h > 360 ) h -= 360;
228             else if( h < 0 ) h+= 360;
229           } else {
230             h = h_in;
231           }
232 
233           /*
234           if( opar->get_saturation_eq() ) {
235             //float s_eq1 = opar->vec[3][hid];
236             //float s_eq2 = opar->vec[4][sid];
237             //float s_eq3 = opar->vec[5][lid];
238             //float s_eq = MIN3( s_eq1, s_eq2, s_eq3 );
239             float s_eq = h_eq;
240             saturation2 += opar->get_saturation_eq()*s_eq;
241           }
242            */
243           if( saturation2 != 0 ) {
244             s = s_in*(1.0f+saturation2);
245             if( s < 0 ) s = 0;
246             else if( s > 1 ) s = 1;
247           } else {
248             s = s_in;
249           }
250 
251           l = l_in;
252 
253           //h = h_in;
254           //s = s_in;
255 
256           //hsv2rgb2( h, s, v, R, G, B );
257           if( (h != h_in) || (s != s_in) )
258             hsl2rgb( h, s, l, RGB[0], RGB[1], RGB[2] );
259           //std::cout<<"out RGB: "<<R<<" "<<G<<" "<<B<<"  HSV: "<<h<<" "<<s<<" "<<v<<std::endl;
260 
261           /*
262           if( opar->get_brightness_eq() != 0 ) {
263             //float c_eq1 = opar->vec[6][hid];
264             //float c_eq2 = opar->vec[7][sid];
265             //float c_eq3 = opar->vec[8][lid];
266             //float c_eq = MIN3( c_eq1, c_eq2, c_eq3 );
267             float b_eq = h_eq;
268             brightness2 += opar->get_brightness_eq()*b_eq;
269           }
270 
271           if( opar->get_contrast_eq() != 0 ) {
272             //float c_eq1 = opar->vec[6][hid];
273             //float c_eq2 = opar->vec[7][sid];
274             //float c_eq3 = opar->vec[8][lid];
275             //float c_eq = MIN3( c_eq1, c_eq2, c_eq3 );
276             float c_eq = h_eq;
277             contrast2 += opar->get_contrast_eq()*c_eq;
278           }
279            */
280           if( brightness2 != 0 || contrast2 != 0 ) {
281             for( k=0; k < 3; k++) {
282               tempval = (typename FormatInfo<T>::SIGNED)RGB[k] - FormatInfo<T>::HALF;
283               clip( (contrast2+1.0f)*tempval+brightness2*FormatInfo<T>::RANGE+FormatInfo<T>::HALF, RGB[k] );
284             }
285           }
286 
287           if( opar->get_gamma() != 1 ) {
288             for( k=0; k < 3; k++) {
289               RGB[k] = powf( RGB[k], opar->get_gamma() );
290             }
291           }
292 
293           pout[x] = h_eq*RGB[0] + (1.0f-h_eq)*pin[x];
294           pout[x+1] = h_eq*RGB[1] + (1.0f-h_eq)*pin[x+1];
295           pout[x+2] = h_eq*RGB[2] + (1.0f-h_eq)*pin[x+2];
296         }
297       }
298     }
299   }
300 };
301 
302 
303 
304 
305 template < OP_TEMPLATE_DEF_CS_SPEC >
306 class HueSaturation< OP_TEMPLATE_IMP_CS_SPEC(PF_COLORSPACE_LAB) >
307 {
308 public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)309   void render(VipsRegion** ireg, int n, int in_first,
310       VipsRegion* imap, VipsRegion* omap,
311       VipsRegion* oreg, OpParBase* par)
312   {
313     HueSaturationPar* opar = dynamic_cast<HueSaturationPar*>(par);
314     if( !opar ) return;
315     VipsRect *r = &oreg->valid;
316     int line_size = r->width * oreg->im->Bands;
317     //int width = r->width;
318     int height = r->height;
319 
320     T* pin;
321     T* pout;
322     typename PF::FormatInfo<T>::SIGNED a, b;
323     int x, y;
324 
325     float sat = 1.0f;
326     if( opar->get_saturation() > 0 ) sat += opar->get_saturation();
327     else {
328       sat -= opar->get_saturation();
329       sat = 1.0f / sat;
330     }
331 
332     for( y = 0; y < height; y++ ) {
333       pin = (T*)VIPS_REGION_ADDR( ireg[in_first], r->left, r->top + y );
334       pout = (T*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
335 
336       for( x = 0; x < line_size; x+=3 ) {
337         a = pin[x+1];
338         b = pin[x+2];
339 
340         a -= PF::FormatInfo<T>::HALF;
341         b -= PF::FormatInfo<T>::HALF;
342 
343         a *= sat;
344         b *= sat;
345 
346         a += PF::FormatInfo<T>::HALF;
347         b += PF::FormatInfo<T>::HALF;
348 
349         if( opar->get_gamma() != 1 ) {
350           pout[x] = powf( pin[x], opar->get_gamma() );
351           float L = 0.1;
352           if(false && r->left==0 && r->top==0 && y<5 && x < 15)
353             printf("brightness: in=%f  out=%f\n", (float)pin[x], (float)pout[x]);
354         } else {
355           pout[x] = pin[x];
356         }
357         clip( a, pout[x+1] );
358         clip( b, pout[x+2] );
359       }
360     }
361   }
362 };
363 
364 
365 
366 ProcessorBase* new_hue_saturation();
367 }
368 
369 #endif
370 
371 
372