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 COLOR_CORRECTION_HH
31 #define COLOR_CORRECTION_HH
32 
33 #include <string>
34 
35 #include "../base/color.hh"
36 #include "../base/processor.hh"
37 
38 namespace PF
39 {
40 
41   class ColorCorrectionPar: public OpParBase
42   {
43     Property<float> offs, r_offs, g_offs, b_offs;
44     Property<float> slope, r_slope, g_slope, b_slope;
45     Property<float> pow, r_pow, g_pow, b_pow;
46     Property<float> saturation;
47     Property<bool> is_log;
48 
49     ICCProfile* icc_data;
50 
51     cmsHPROFILE lab_profile;
52     cmsHTRANSFORM transform, transform_inv;
53 
54   public:
55 
56     ColorCorrectionPar();
57     ~ColorCorrectionPar();
58 
get_transform()59     cmsHTRANSFORM get_transform() { return transform; }
get_transform_inv()60     cmsHTRANSFORM get_transform_inv() { return transform_inv; }
61 
get_icc_data()62     ICCProfile* get_icc_data() { return icc_data; }
63 
set_offset(float r,float g,float b)64     void set_offset(float r, float g, float b) {offs.set(0); r_offs.set(r); g_offs.set(g); b_offs.set(b);}
get_r_offset()65     float get_r_offset() { return (offs.get() + r_offs.get()); }
get_g_offset()66     float get_g_offset() { return (offs.get() + g_offs.get()); }
get_b_offset()67     float get_b_offset() { return (offs.get() + b_offs.get()); }
68     //float get_r_slope() { return ((slope.get() + 1.0f) * (r_slope.get() + 1.0f)); }
69     //float get_g_slope() { return ((slope.get() + 1.0f) * (g_slope.get() + 1.0f)); }
70     //float get_b_slope() { return ((slope.get() + 1.0f) * (b_slope.get() + 1.0f)); }
71     //float get_r_power() { return ((1.0f - pow.get()) * (1.0f - r_pow.get())); }
72     //float get_g_power() { return ((1.0f - pow.get()) * (1.0f - g_pow.get())); }
73     //float get_b_power() { return ((1.0f - pow.get()) * (1.0f - b_pow.get())); }
set_slope(float r,float g,float b)74     void set_slope(float r, float g, float b) {slope.set(1); r_slope.set(r); g_slope.set(g); b_slope.set(b);}
get_r_slope()75     float get_r_slope() { return ((slope.get()) * (r_slope.get())); }
get_g_slope()76     float get_g_slope() { return ((slope.get()) * (g_slope.get())); }
get_b_slope()77     float get_b_slope() { return ((slope.get()) * (b_slope.get())); }
set_power(float r,float g,float b)78     void set_power(float r, float g, float b) {pow.set(1); r_pow.set(r); g_pow.set(g); b_pow.set(b);}
get_r_power()79     float get_r_power() { return ((pow.get()) * (r_pow.get())); }
get_g_power()80     float get_g_power() { return ((pow.get()) * (g_pow.get())); }
get_b_power()81     float get_b_power() { return ((pow.get()) * (b_pow.get())); }
82 
set_saturation(float s)83     void set_saturation(float s) { saturation.set(s); }
get_saturation()84     float get_saturation() { return saturation.get(); }
85 
get_log_encoding()86     bool get_log_encoding() { return is_log.get(); }
87 
has_intensity()88     bool has_intensity() { return false; }
has_opacity()89     bool has_opacity() { return true; }
needs_input()90     bool needs_input() { return true; }
91 
92     VipsImage* build(std::vector<VipsImage*>& in, int first,
93                      VipsImage* imap, VipsImage* omap,
94                      unsigned int& level);
95   };
96 
97 
98 
99   template < OP_TEMPLATE_DEF >
100   class ColorCorrection
101   {
102   public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)103     void render(VipsRegion** ireg, int n, int in_first,
104                 VipsRegion* imap, VipsRegion* omap,
105                 VipsRegion* oreg, OpParBase* par)
106     {
107     }
108   };
109 
110 
111 
112 
113   template < class BLENDER, int CHMIN, int CHMAX, bool has_imap, bool has_omap, bool PREVIEW >
114   class ColorCorrection< float, BLENDER, PF_COLORSPACE_RGB, CHMIN, CHMAX, has_imap, has_omap, PREVIEW >
115   {
116   public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)117     void render(VipsRegion** ireg, int n, int in_first,
118                 VipsRegion* imap, VipsRegion* omap,
119                 VipsRegion* oreg, OpParBase* par)
120     {
121       ColorCorrectionPar* opar = dynamic_cast<ColorCorrectionPar*>(par);
122       if( !opar ) return;
123       VipsRect *r = &oreg->valid;
124       int line_size = r->width * oreg->im->Bands;
125       //int width = r->width;
126       int height = r->height;
127 
128       float* pin;
129       float* pout;
130       float* p;
131       float* q;
132       float RGB[3], L;
133       int x, y, k, i;
134 
135       const float minus = -1.f;
136 
137       /* Do the actual processing
138        */
139 
140       float r_offs = opar->get_r_offset();
141       float g_offs = opar->get_g_offset();
142       float b_offs = opar->get_b_offset();
143       float r_slope = opar->get_r_slope();
144       float g_slope = opar->get_g_slope();
145       float b_slope = opar->get_b_slope();
146       float r_pow = opar->get_r_power();
147       float g_pow = opar->get_g_power();
148       float b_pow = opar->get_b_power();
149       float saturation = opar->get_saturation();
150       bool is_log = opar->get_log_encoding();
151 
152       ICCProfile* profile = opar->get_icc_data();
153 
154       //for(int ti=0;ti<100;ti++) {
155       //if((ti%10)==0) std::cout<<"basic_adjustments: ti="<<ti<<" corner="<<r->left<<","<<r->top<<std::endl;
156       for( y = 0; y < height; y++ ) {
157         pin = (float*)VIPS_REGION_ADDR( ireg[0], r->left, r->top + y );
158         pout = (float*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
159 
160         for( x = 0; x < line_size; x+=3 ) {
161 
162           RGB[0] = pin[x];
163           RGB[1] = pin[x+1];
164           RGB[2] = pin[x+2];
165 
166           if( is_log ) {
167             RGB[0] = lin_to_ACEScc(RGB[0]);
168             RGB[1] = lin_to_ACEScc(RGB[1]);
169             RGB[2] = lin_to_ACEScc(RGB[2]);
170           }
171 
172           RGB[0] *= r_slope;
173           RGB[1] *= g_slope;
174           RGB[2] *= b_slope;
175 
176           RGB[0] += r_offs;
177           RGB[1] += g_offs;
178           RGB[2] += b_offs;
179 
180           pout[x] = (RGB[0]<0) ? RGB[0] : powf(RGB[0], r_pow);
181           pout[x+1] = (RGB[1]<0) ? RGB[1] : powf(RGB[1], g_pow);
182           pout[x+2] = (RGB[2]<0) ? RGB[2] : powf(RGB[2], b_pow);
183 
184           if( is_log ) {
185             pout[x] = ACEScc_to_lin(pout[x]);
186             pout[x+1] = ACEScc_to_lin(pout[x+1]);
187             pout[x+2] = ACEScc_to_lin(pout[x+2]);
188           }
189 
190           if(profile && saturation != 1) {
191             L = profile->get_lightness(pout[x], pout[x+1], pout[x+2]);
192             pout[x] = L + saturation * (pout[x] - L);
193             pout[x+1] = L + saturation * (pout[x+1] - L);
194             pout[x+2] = L + saturation * (pout[x+2] - L);
195           }
196         }
197       }
198     }
199   };
200 
201 
202 
203 
204   ProcessorBase* new_color_correction();
205 }
206 
207 #endif
208 
209 
210