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