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 PF_SHAHI_H
31 #define PF_SHAHI_H
32 
33 
34 #define UNBOUND_L 1
35 #define UNBOUND_A 2
36 #define UNBOUND_B 4
37 #define UNBOUND_SHADOWS_L UNBOUND_L
38 #define UNBOUND_SHADOWS_A UNBOUND_A
39 #define UNBOUND_SHADOWS_B UNBOUND_B
40 #define UNBOUND_HIGHLIGHTS_L (UNBOUND_L << 3) /* 8 */
41 #define UNBOUND_HIGHLIGHTS_A (UNBOUND_A << 3) /* 16 */
42 #define UNBOUND_HIGHLIGHTS_B (UNBOUND_B << 3) /* 32 */
43 #define UNBOUND_GAUSSIAN 64
44 #define UNBOUND_BILATERAL 128 /* not implemented yet */
45 #define UNBOUND_DEFAULT                                                                                      \
46     (UNBOUND_SHADOWS_L | UNBOUND_SHADOWS_A | UNBOUND_SHADOWS_B | UNBOUND_HIGHLIGHTS_L | UNBOUND_HIGHLIGHTS_A   \
47         | UNBOUND_HIGHLIGHTS_B | UNBOUND_GAUSSIAN)
48 
49 #define CLAMPF(a, mn, mx) ((a) < (mn) ? (mn) : ((a) > (mx) ? (mx) : (a)))
50 #define CLAMP_RANGE(x, y, z) (CLAMP(x, y, z))
51 #define MMCLAMPPS(a, mn, mx) (_mm_min_ps((mx), _mm_max_ps((a), (mn))))
52 
53 namespace PF
54 {
55 
56 enum shahi_method_t
57 {
58   SHAHI_GAUSSIAN,
59   SHAHI_GUIDED,
60 };
61 
62 
sign(float x)63 inline float sign(float x)
64 {
65   return (x < 0 ? -1.0f : 1.0f);
66 }
67 
68 
69 
70 class ShadowsHighlightsPar: public OpParBase
71 {
72   PropertyBase method;
73   Property<float> shadows, highlights, wp_adjustment, radius, threshold, compress, sh_color_adjustment, hi_color_adjustment;
74 
75   ProcessorBase* gauss;
76   ProcessorBase* guided;
77   ProcessorBase* convert2lab;
78   ProcessorBase* convert2input;
79 
80   PF::ICCProfile* in_profile;
81 
82 public:
83   ShadowsHighlightsPar();
84 
has_intensity()85   bool has_intensity() { return false; }
86   bool needs_caching();
87 
get_shadows()88   float get_shadows() { return shadows.get(); }
get_highlights()89   float get_highlights() { return highlights.get(); }
get_wp_adjustment()90   float get_wp_adjustment() { return wp_adjustment.get(); }
get_compress()91   float get_compress() { return compress.get(); }
get_sh_color_adjustment()92   float get_sh_color_adjustment() { return sh_color_adjustment.get(); }
get_hi_color_adjustment()93   float get_hi_color_adjustment() { return hi_color_adjustment.get(); }
94 
95   void compute_padding( VipsImage* full_res, unsigned int id, unsigned int level );
96   void propagate_settings();
97 
98   VipsImage* build(std::vector<VipsImage*>& in, int first,
99       VipsImage* imap, VipsImage* omap,
100       unsigned int& level);
101 };
102 
103 
104 
105 template < OP_TEMPLATE_DEF >
106 class ShadowsHighlightsProc
107 {
108 public:
render(VipsRegion ** in,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * out,OpParBase * par)109   void render(VipsRegion** in, int n, int in_first,
110       VipsRegion* imap, VipsRegion* omap,
111       VipsRegion* out, OpParBase* par)
112   {
113   }
114 };
115 
116 
117 
118 template < OP_TEMPLATE_DEF_TYPE_SPEC >
119 class ShadowsHighlightsProc< OP_TEMPLATE_IMP_TYPE_SPEC(float) >
120 {
121 public:
render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)122   void render(VipsRegion** ireg, int n, int in_first,
123       VipsRegion* imap, VipsRegion* omap,
124       VipsRegion* oreg, OpParBase* par)
125   {
126     if( n != 2 ) return;
127     if( ireg[0] == NULL ) return;
128     if( ireg[1] == NULL ) return;
129 
130     ShadowsHighlightsPar* opar = dynamic_cast<ShadowsHighlightsPar*>(par);
131     if( !opar ) return;
132 
133     //const int order = data->order;
134     const float sigma = 100;//fmaxf(0.1f, opar->get_radius());
135     //const float sigma = radius * roi_in->scale / piece->iscale;
136     const float shadows = 2.0f * fmin(fmax(-1.0, (opar->get_shadows() / 100.0f)), 1.0f);
137     const float highlights = 2.0f * fmin(fmax(-1.0, (opar->get_highlights() / 100.0f)), 1.0f);
138     const float whitepoint = fmax(1.0f - opar->get_wp_adjustment() / 100.0f, 0.01f);
139     const float compress
140     = fmin(fmax(0, (opar->get_compress() / 100.0f)), 0.99f); // upper limit 0.99f to avoid division by zero later
141     const float shadows_ccorrect = (fmin(fmax(0.0f, (opar->get_sh_color_adjustment() / 100.0f)), 1.0f) - 0.5f)
142                                              * sign(shadows) + 0.5f;
143     const float highlights_ccorrect = (fmin(fmax(0.0f, (opar->get_hi_color_adjustment() / 100.0f)), 1.0f) - 0.5f)
144                                                 * sign(-highlights) + 0.5f;
145     const unsigned int flags = UNBOUND_DEFAULT/*data->flags*/;
146     const int unbound_mask = true;/*((data->shadhi_algo == SHADHI_ALGO_BILATERAL) && (flags & UNBOUND_BILATERAL))
147                                || ((data->shadhi_algo == SHADHI_ALGO_GAUSSIAN) && (flags & UNBOUND_GAUSSIAN));*/
148     const float low_approximation = 0.01f;//data->low_approximation;
149 
150     const float max[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
151     const float min[4] = { 0.0f, -1.0f, -1.0f, 0.0f };
152     const float lmin = 0.0f;
153     const float lmax = max[0] + fabs(min[0]);
154     const float halfmax = lmax / 2.0;
155     const float doublemax = lmax * 2.0;
156 
157     VipsRect *r = &oreg->valid;
158     int line_size = r->width * oreg->im->Bands;
159     //int width = r->width;
160     int height = r->height;
161 
162     float* pin1;
163     float* pin2;
164     float* pout;
165     //typename FormatInfo<T>::SIGNED diff;
166     float diff, out;
167     int x, y, pos;
168     //float threshold = opar->get_threshold()*FormatInfo<T>::RANGE;
169 
170     for( y = 0; y < height; y++ ) {
171       // original image
172       pin1 = (float*)VIPS_REGION_ADDR( ireg[1], r->left, r->top + y );
173       // blurred image
174       pin2 = (float*)VIPS_REGION_ADDR( ireg[0], r->left, r->top + y );
175       // output image
176       pout = (float*)VIPS_REGION_ADDR( oreg, r->left, r->top + y );
177 
178       for( x = 0; x < line_size; x+=3 ) {
179 
180         // invert and desaturate
181         pout[x] = 1.0f - pin2[x];
182         pout[x+1] = pout[x+2] = 0.0f;
183 
184         float ta[3] = {pin1[x],(pin1[x+1]-0.5f)*2,(pin1[x+2]-0.5f)*2}, tb[3] = {pout[x],0.0f,0.0f};
185         /*
186         if( y == 0 && x == 0 )
187           printf("in: %f,%f,%f    out: %f,%f,%f\n",pin1[x],pin1[x+1],pin1[x+2],tb[0],tb[1],tb[2]);
188         if( y == 0 && x == 0 )
189           printf("in2: %f,%f,%f    out: %f,%f,%f\n",ta[0],ta[1],ta[2],tb[0],tb[1],tb[2]);
190         *//*
191         pout[x] = pin1[x];
192         pout[x+1] = pin1[x+1];
193         pout[x+2] = pin1[x+2];
194         continue;
195         */
196 
197         ta[0] = ta[0] > 0.0f ? ta[0] / whitepoint : ta[0];
198         tb[0] = tb[0] > 0.0f ? tb[0] / whitepoint : tb[0];
199 
200         // overlay highlights
201         float highlights2 = highlights * highlights;
202         float highlights_xform = CLAMP_RANGE(1.0f - tb[0] / (1.0f - compress), 0.0f, 1.0f);
203 
204         while(highlights2 > 0.0f)
205         {
206           float la = (flags & UNBOUND_HIGHLIGHTS_L) ? ta[0] : CLAMP_RANGE(ta[0], lmin, lmax);
207           float lb = (tb[0] - halfmax) * sign(-highlights) * sign(lmax - la) + halfmax;
208           lb = unbound_mask ? lb : CLAMP_RANGE(lb, lmin, lmax);
209           float lref = copysignf(fabs(la) > low_approximation ? 1.0f / fabs(la) : 1.0f / low_approximation, la);
210           float href = copysignf(
211               fabs(1.0f - la) > low_approximation ? 1.0f / fabs(1.0f - la) : 1.0f / low_approximation, 1.0f - la);
212 
213           float chunk = highlights2 > 1.0f ? 1.0f : highlights2;
214           float optrans = chunk * highlights_xform;
215           highlights2 -= 1.0f;
216 
217           ta[0] = la * (1.0 - optrans)
218                           + (la > halfmax ? lmax - (lmax - doublemax * (la - halfmax)) * (lmax - lb) : doublemax * la
219                               * lb) * optrans;
220 
221           ta[0] = (flags & UNBOUND_HIGHLIGHTS_L) ? ta[0] : CLAMP_RANGE(ta[0], lmin, lmax);
222 
223           ta[1] = ta[1] * (1.0f - optrans)
224                           + (ta[1] + tb[1]) * (ta[0] * lref * (1.0f - highlights_ccorrect)
225                               + (1.0f - ta[0]) * href * highlights_ccorrect) * optrans;
226 
227           ta[1] = (flags & UNBOUND_HIGHLIGHTS_A) ? ta[1] : CLAMP_RANGE(ta[1], min[1], max[1]);
228 
229           ta[2] = ta[2] * (1.0f - optrans)
230                           + (ta[2] + tb[2]) * (ta[0] * lref * (1.0f - highlights_ccorrect)
231                               + (1.0f - ta[0]) * href * highlights_ccorrect) * optrans;
232 
233           ta[2] = (flags & UNBOUND_HIGHLIGHTS_B) ? ta[2] : CLAMP_RANGE(ta[2], min[2], max[2]);
234         }
235 
236         // overlay shadows
237         float shadows2 = shadows * shadows;
238         float shadows_xform = CLAMP_RANGE(tb[0] / (1.0f - compress) - compress / (1.0f - compress), 0.0f, 1.0f);
239 
240         while(shadows2 > 0.0f)
241         {
242           float la = (flags & UNBOUND_HIGHLIGHTS_L) ? ta[0] : CLAMP_RANGE(ta[0], lmin, lmax);
243           float lb = (tb[0] - halfmax) * sign(shadows) * sign(lmax - la) + halfmax;
244           lb = unbound_mask ? lb : CLAMP_RANGE(lb, lmin, lmax);
245           float lref = copysignf(fabs(la) > low_approximation ? 1.0f / fabs(la) : 1.0f / low_approximation, la);
246           float href = copysignf(
247               fabs(1.0f - la) > low_approximation ? 1.0f / fabs(1.0f - la) : 1.0f / low_approximation, 1.0f - la);
248 
249 
250           float chunk = shadows2 > 1.0f ? 1.0f : shadows2;
251           float optrans = chunk * shadows_xform;
252           shadows2 -= 1.0f;
253 
254           ta[0] = la * (1.0 - optrans)
255                           + (la > halfmax ? lmax - (lmax - doublemax * (la - halfmax)) * (lmax - lb) : doublemax * la
256                               * lb) * optrans;
257 
258           ta[0] = (flags & UNBOUND_SHADOWS_L) ? ta[0] : CLAMP_RANGE(ta[0], lmin, lmax);
259 
260           ta[1] = ta[1] * (1.0f - optrans)
261                           + (ta[1] + tb[1]) * (ta[0] * lref * shadows_ccorrect
262                               + (1.0f - ta[0]) * href * (1.0f - shadows_ccorrect)) * optrans;
263 
264           ta[1] = (flags & UNBOUND_SHADOWS_A) ? ta[1] : CLAMP_RANGE(ta[1], min[1], max[1]);
265 
266           ta[2] = ta[2] * (1.0f - optrans)
267                           + (ta[2] + tb[2]) * (ta[0] * lref * shadows_ccorrect
268                               + (1.0f - ta[0]) * href * (1.0f - shadows_ccorrect)) * optrans;
269 
270           ta[2] = (flags & UNBOUND_SHADOWS_B) ? ta[2] : CLAMP_RANGE(ta[2], min[2], max[2]);
271         }
272 
273         //_Lab_rescale(ta, &out[j]);
274         pout[x] = ta[0];
275         pout[x+1] = ta[1] / 2.f + 0.5f;
276         pout[x+2] = ta[2] / 2.f + 0.5f;
277       }
278     }
279   }
280 };
281 
282 
283 ProcessorBase* new_shadows_highlights();
284 
285 }
286 
287 #endif
288 
289 
290