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