1 /*
2  *  This file is part of RawTherapee.
3  *
4  *  Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
5  *
6  *  RawTherapee is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  RawTherapee is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with RawTherapee.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include <cmath>
20 #include <glib.h>
21 #include <glibmm.h>
22 #ifdef _OPENMP
23 #include <omp.h>
24 #endif
25 
26 #include "alignedbuffer.h"
27 #include "rtengine.h"
28 #include "improcfun.h"
29 #include "curves.h"
30 #include "mytime.h"
31 #include "iccstore.h"
32 #include "imagesource.h"
33 #include "rtthumbnail.h"
34 #include "utils.h"
35 #include "iccmatrices.h"
36 #include "color.h"
37 #include "calc_distort.h"
38 #include "rt_math.h"
39 #include "improccoordinator.h"
40 #include "clutstore.h"
41 #include "StopWatch.h"
42 #include "../rtgui/ppversion.h"
43 #include "../rtgui/guiutils.h"
44 #include "refreshmap.h"
45 
46 namespace rtengine {
47 
48 using namespace procparams;
49 
50 extern const Settings* settings;
51 
ImProcFunctions(const ProcParams * iparams,bool imultiThread)52 ImProcFunctions::ImProcFunctions(const ProcParams* iparams, bool imultiThread):
53     monitor(nullptr),
54     monitorTransform(nullptr),
55     params(iparams),
56     scale(1),
57     multiThread(imultiThread),
58     cur_pipeline(Pipeline::OUTPUT),
59     dcpProf(nullptr),
60     dcpApplyState(nullptr),
61     pipetteBuffer(nullptr),
62     lumimul{},
63     offset_x(0),
64     offset_y(0),
65     full_width(-1),
66     full_height(-1),
67     histToneCurve(nullptr),
68     histCCurve(nullptr),
69     histLCurve(nullptr),
70     show_sharpening_mask(false),
71     plistener(nullptr),
72     progress_step(0),
73     progress_end(1)
74 {
75 }
76 
77 
~ImProcFunctions()78 ImProcFunctions::~ImProcFunctions ()
79 {
80     if (monitorTransform) {
81         cmsDeleteTransform (monitorTransform);
82     }
83 }
84 
setScale(double iscale)85 void ImProcFunctions::setScale (double iscale)
86 {
87     scale = iscale;
88 }
89 
90 
updateColorProfiles(const Glib::ustring & monitorProfile,RenderingIntent monitorIntent,bool softProof,bool gamutCheck)91 void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, RenderingIntent monitorIntent, bool softProof, bool gamutCheck)
92 {
93     // set up monitor transform
94     if (monitorTransform) {
95         cmsDeleteTransform (monitorTransform);
96     }
97     gamutWarning.reset(nullptr);
98 
99     monitorTransform = nullptr;
100     monitor = nullptr;
101 
102     if (!monitorProfile.empty()) {
103 #if !defined(__APPLE__) // No support for monitor profiles on OS X, all data is sRGB
104         monitor = ICCStore::getInstance()->getProfile (monitorProfile);
105 #else
106         monitor = ICCStore::getInstance()->getProfile (options.rtSettings.srgb);
107 #endif
108     }
109 
110     if (monitor) {
111         MyMutex::MyLock lcmsLock (*lcmsMutex);
112 
113         cmsUInt32Number flags;
114         cmsHPROFILE iprof  = cmsCreateLab4Profile (nullptr);
115         cmsHPROFILE gamutprof = nullptr;
116         cmsUInt32Number gamutbpc = 0;
117         RenderingIntent gamutintent = RI_RELATIVE;
118 
119         bool softProofCreated = false;
120 
121         if (softProof) {
122             cmsHPROFILE oprof = nullptr;
123             RenderingIntent outIntent;
124 
125             flags = cmsFLAGS_SOFTPROOFING | cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE;
126 
127             if (!settings->printerProfile.empty()) {
128                 oprof = ICCStore::getInstance()->getProfile (settings->printerProfile);
129                 if (settings->printerBPC) {
130                     flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
131                 }
132                 outIntent = settings->printerIntent;
133             } else {
134                 oprof = ICCStore::getInstance()->getProfile(params->icm.outputProfile);
135                 if (params->icm.outputBPC) {
136                     flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
137                 }
138                 outIntent = params->icm.outputIntent;
139             }
140 
141             if (oprof) {
142                 // NOCACHE is for thread safety, NOOPTIMIZE for precision
143 
144                 // if (gamutCheck) {
145                 //     flags |= cmsFLAGS_GAMUTCHECK;
146                 // }
147 
148                 const auto make_gamma_table =
149                     [](cmsHPROFILE prof, cmsTagSignature tag) -> void
150                     {
151                         cmsToneCurve *tc = static_cast<cmsToneCurve *>(cmsReadTag(prof, tag));
152                         if (tc) {
153                             const cmsUInt16Number *table = cmsGetToneCurveEstimatedTable(tc);
154                             cmsToneCurve *tc16 = cmsBuildTabulatedToneCurve16(nullptr, cmsGetToneCurveEstimatedTableEntries(tc), table);
155                             if (tc16) {
156                                 cmsWriteTag(prof, tag, tc16);
157                                 cmsFreeToneCurve(tc16);
158                             }
159                         }
160                     };
161 
162                 cmsHPROFILE softproof = ProfileContent(oprof).toProfile();
163                 if (softproof) {
164                     make_gamma_table(softproof, cmsSigRedTRCTag);
165                     make_gamma_table(softproof, cmsSigGreenTRCTag);
166                     make_gamma_table(softproof, cmsSigBlueTRCTag);
167                 }
168 
169                 monitorTransform = cmsCreateProofingTransform (
170                                        iprof, TYPE_Lab_FLT,
171                                        monitor, TYPE_RGB_FLT,
172                                        softproof,
173                                        monitorIntent, outIntent,
174                                        flags
175                                    );
176 
177                 if (softproof) {
178                     cmsCloseProfile(softproof);
179                 }
180 
181                 if (monitorTransform) {
182                     softProofCreated = true;
183                 }
184 
185                 if (gamutCheck) {
186                     gamutprof = oprof;
187                     if (params->icm.outputBPC) {
188                         gamutbpc = cmsFLAGS_BLACKPOINTCOMPENSATION;
189                     }
190                     gamutintent = outIntent;
191                 }
192             }
193         } else if (gamutCheck) {
194             gamutprof = monitor;
195             if (settings->monitorBPC) {
196                 gamutbpc = cmsFLAGS_BLACKPOINTCOMPENSATION;
197             }
198             gamutintent = monitorIntent;
199         }
200 
201         if (!softProofCreated) {
202             flags = cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE;
203 
204             if (settings->monitorBPC) {
205                 flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
206             }
207 
208             monitorTransform = cmsCreateTransform (iprof, TYPE_Lab_FLT, monitor, TYPE_RGB_FLT, monitorIntent, flags);
209         }
210 
211         if (gamutCheck && gamutprof) {
212             gamutWarning.reset(new GamutWarning(iprof, gamutprof, gamutintent, gamutbpc));
213         }
214 
215         cmsCloseProfile (iprof);
216     }
217 }
218 
firstAnalysis(const Imagefloat * const original,const ProcParams & params,LUTu & histogram)219 void ImProcFunctions::firstAnalysis (const Imagefloat* const original, const ProcParams &params, LUTu & histogram)
220 {
221 
222     TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params.icm.workingProfile);
223 
224     lumimul[0] = wprof[1][0];
225     lumimul[1] = wprof[1][1];
226     lumimul[2] = wprof[1][2];
227     int W = original->getWidth();
228     int H = original->getHeight();
229 
230     float lumimulf[3] = {static_cast<float> (lumimul[0]), static_cast<float> (lumimul[1]), static_cast<float> (lumimul[2])};
231 
232     // calculate histogram of the y channel needed for contrast curve calculation in exposure adjustments
233     histogram.clear();
234 
235     if (multiThread) {
236 
237 #ifdef _OPENMP
238         const int numThreads = min (max (W * H / (int)histogram.getSize(), 1), omp_get_max_threads());
239         #pragma omp parallel num_threads(numThreads) if(numThreads>1)
240 #endif
241         {
242             LUTu hist (histogram.getSize());
243             hist.clear();
244 #ifdef _OPENMP
245             #pragma omp for nowait
246 #endif
247 
248             for (int i = 0; i < H; i++) {
249                 for (int j = 0; j < W; j++) {
250 
251                     float r = original->r (i, j);
252                     float g = original->g (i, j);
253                     float b = original->b (i, j);
254 
255                     int y = (lumimulf[0] * r + lumimulf[1] * g + lumimulf[2] * b);
256                     hist[y]++;
257                 }
258             }
259 
260 #ifdef _OPENMP
261             #pragma omp critical
262 #endif
263             histogram += hist;
264 
265         }
266 #ifdef _OPENMP
267         static_cast<void> (numThreads); // to silence cppcheck warning
268 #endif
269     } else {
270         for (int i = 0; i < H; i++) {
271             for (int j = 0; j < W; j++) {
272 
273                 float r = original->r (i, j);
274                 float g = original->g (i, j);
275                 float b = original->b (i, j);
276 
277                 int y = (lumimulf[0] * r + lumimulf[1] * g + lumimulf[2] * b);
278                 histogram[y]++;
279             }
280         }
281     }
282 }
283 
284 
285 namespace {
286 
proPhotoBlue(Imagefloat * rgb,bool multiThread)287 void proPhotoBlue(Imagefloat *rgb, bool multiThread)
288 {
289     // TODO
290     const int W = rgb->getWidth();
291     const int H = rgb->getHeight();
292 #ifdef _OPENMP
293 #   pragma omp parallel for if (multiThread)
294 #endif
295     for (int y = 0; y < H; ++y) {
296         int x = 0;
297 #ifdef __SSE2__
298         for (; x < W - 3; x += 4) {
299             vfloat rv = LVF(rgb->r(y, x));
300             vfloat gv = LVF(rgb->g(y, x));
301             vmask zeromask = vorm(vmaskf_eq(rv, ZEROV), vmaskf_eq(gv, ZEROV));
302             if (_mm_movemask_ps((vfloat)zeromask)) {
303                 for (int k = 0; k < 4; ++k) {
304                     float r = rgb->r(y, x+k);
305                     float g = rgb->g(y, x+k);
306                     float b = rgb->b(y, x+k);
307 
308                     if ((r == 0.0f || g == 0.0f) && rtengine::min(r, g, b) >= 0.f) {
309                         float h, s, v;
310                         Color::rgb2hsv(r, g, b, h, s, v);
311                         s *= 0.99f;
312                         Color::hsv2rgb(h, s, v, rgb->r(y, x+k), rgb->g(y, x+k), rgb->b(y, x+k));
313                     }
314                 }
315             }
316         }
317 #endif
318         for (; x < W; ++x) {
319             float r = rgb->r(y, x);
320             float g = rgb->g(y, x);
321             float b = rgb->b(y, x);
322 
323             if ((r == 0.0f || g == 0.0f) && rtengine::min(r, g, b) >= 0.f) {
324                 float h, s, v;
325                 Color::rgb2hsv(r, g, b, h, s, v);
326                 s *= 0.99f;
327                 Color::hsv2rgb(h, s, v, rgb->r(y, x), rgb->g(y, x), rgb->b(y, x));
328             }
329         }
330     }
331 }
332 
333 
dcpProfile(Imagefloat * img,DCPProfile * dcp,const DCPProfile::ApplyState * as,bool multithread)334 void dcpProfile(Imagefloat *img, DCPProfile *dcp, const DCPProfile::ApplyState *as, bool multithread)
335 {
336     if (dcp && as) {
337         img->setMode(Imagefloat::Mode::RGB, multithread);
338 
339         const int H = img->getHeight();
340         const int W = img->getWidth();
341 #ifdef _OPENMP
342 #       pragma omp parallel for if (multithread)
343 #endif
344         for (int y = 0; y < H; ++y) {
345             float *r = img->r(y);
346             float *g = img->g(y);
347             float *b = img->b(y);
348             dcp->step2ApplyTile(r, g, b, W, 1, 1, *as);
349         }
350     }
351 }
352 
353 } // namespace
354 
355 
getAutoExp(const LUTu & histogram,int histcompr,double clip,double & expcomp,int & bright,int & contr,int & black,int & hlcompr,int & hlcomprthresh)356 void ImProcFunctions::getAutoExp  (const LUTu &histogram, int histcompr, double clip,
357                                    double& expcomp, int& bright, int& contr, int& black, int& hlcompr, int& hlcomprthresh)
358 {
359 
360     float scale = 65536.0f;
361     float midgray = 0.1842f; //middle gray in linear gamma =1 50% luminance
362 
363     int imax = 65536 >> histcompr;
364     int overex = 0;
365     float sum = 0.f, hisum = 0.f, losum = 0.f;
366     float ave = 0.f, hidev = 0.f, lodev = 0.f;
367 
368     //find average luminance
369     histogram.getSumAndAverage (sum, ave);
370 
371     //find median of luminance
372     size_t median = 0, count = histogram[0];
373 
374     while (count < sum / 2) {
375         median++;
376         count += histogram[median];
377     }
378 
379     if (median == 0 || ave < 1.f) { //probably the image is a blackframe
380         expcomp = 0.;
381         black = 0;
382         bright = 0;
383         contr = 0;
384         hlcompr = 0;
385         hlcomprthresh = 0;
386         return;
387     }
388 
389     // compute std dev on the high and low side of median
390     // and octiles of histogram
391     float octile[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, ospread = 0.f;
392     count = 0;
393 
394     int i = 0;
395 
396     for (; i < min ((int)ave, imax); i++) {
397         if (count < 8) {
398             octile[count] += histogram[i];
399 
400             if (octile[count] > sum / 8.f || (count == 7 && octile[count] > sum / 16.f)) {
401                 octile[count] = xlog (1. + (float)i) / log (2.f);
402                 count++;// = min(count+1,7);
403             }
404         }
405 
406         //lodev += SQR(ave-i)*histogram[i];
407         lodev += (xlog (ave + 1.f) - xlog ((float)i + 1.)) * histogram[i];
408         losum += histogram[i];
409     }
410 
411     for (; i < imax; i++) {
412         if (count < 8) {
413             octile[count] += histogram[i];
414 
415             if (octile[count] > sum / 8.f || (count == 7 && octile[count] > sum / 16.f)) {
416                 octile[count] = xlog (1. + (float)i) / log (2.f);
417                 count++;// = min(count+1,7);
418             }
419         }
420 
421         //hidev += SQR(i-ave)*histogram[i];
422         hidev += (xlog ((float)i + 1.) - xlog (ave + 1.f)) * histogram[i];
423         hisum += histogram[i];
424 
425     }
426 
427     if (losum == 0 || hisum == 0) { //probably the image is a blackframe
428         expcomp = 0.;
429         black = 0;
430         bright = 0;
431         contr = 0;
432         hlcompr = 0;
433         hlcomprthresh = 0;
434         return;
435     }
436 
437 //    lodev = (lodev / (log(2.f) * losum));
438 //    hidev = (hidev / (log(2.f) * hisum));
439 
440     if (octile[6] > log1p ((float)imax) / log2 (2.f)) { //if very overxposed image
441         octile[6] = 1.5f * octile[5] - 0.5f * octile[4];
442         overex = 2;
443     }
444 
445     if (octile[7] > log1p ((float)imax) / log2 (2.f)) { //if overexposed
446         octile[7] = 1.5f * octile[6] - 0.5f * octile[5];
447         overex = 1;
448     }
449 
450     // store values of octile[6] and octile[7] for calculation of exposure compensation
451     // if we don't do this and the pixture is underexposed, calculation of exposure compensation assumes
452     // that it's overexposed and calculates the wrong direction
453     float oct6, oct7;
454     oct6 = octile[6];
455     oct7 = octile[7];
456 
457 
458     for (int i = 1; i < 8; i++) {
459         if (octile[i] == 0.0f) {
460             octile[i] = octile[i - 1];
461         }
462     }
463 
464     // compute weighted average separation of octiles
465     // for future use in contrast setting
466     for (int i = 1; i < 6; i++) {
467         ospread += (octile[i + 1] - octile[i]) / max (0.5f, (i > 2 ? (octile[i + 1] - octile[3]) : (octile[3] - octile[i])));
468     }
469 
470     ospread /= 5.f;
471 
472     if (ospread <= 0.f) { //probably the image is a blackframe
473         expcomp = 0.;
474         black = 0;
475         bright = 0;
476         contr = 0;
477         hlcompr = 0;
478         hlcomprthresh = 0;
479         return;
480     }
481 
482 
483     // compute clipping points based on the original histograms (linear, without exp comp.)
484     unsigned int clipped = 0;
485     int rawmax = (imax) - 1;
486 
487     while (histogram[rawmax] + clipped <= 0 && rawmax > 1) {
488         clipped += histogram[rawmax];
489         rawmax--;
490     }
491 
492     //compute clipped white point
493     unsigned int clippable = (int) (sum * clip / 100.f );
494     clipped = 0;
495     int whiteclip = (imax) - 1;
496 
497     while (whiteclip > 1 && (histogram[whiteclip] + clipped) <= clippable) {
498         clipped += histogram[whiteclip];
499         whiteclip--;
500     }
501 
502     //compute clipped black point
503     clipped = 0;
504     int shc = 0;
505 
506     while (shc < whiteclip - 1 && histogram[shc] + clipped <= clippable) {
507         clipped += histogram[shc];
508         shc++;
509     }
510 
511     //rescale to 65535 max
512     rawmax <<= histcompr;
513     whiteclip <<= histcompr;
514     ave = ave * (1 << histcompr);
515     median <<= histcompr;
516     shc <<= histcompr;
517 
518 //    //prevent division by 0
519 //    if (lodev == 0.f) {
520 //        lodev = 1.f;
521 //    }
522 
523     //compute exposure compensation as geometric mean of the amount that
524     //sets the mean or median at middle gray, and the amount that sets the estimated top
525     //of the histogram at or near clipping.
526     //float expcomp1 = (log(/*(median/ave)*//*(hidev/lodev)*/midgray*scale/(ave-shc+midgray*shc))+log((hidev/lodev)))/log(2.f);
527     float expcomp1 = (log (/*(median/ave)*//*(hidev/lodev)*/midgray * scale / (ave - shc + midgray * shc))) / log (2.f);
528     float expcomp2;
529 
530     if (overex == 0) { // image is not overexposed
531         expcomp2 = 0.5f * ( (15.5f - histcompr - (2.f * oct7 - oct6)) + log (scale / rawmax) / log (2.f) );
532     } else {
533         expcomp2 = 0.5f * ( (15.5f - histcompr - (2.f * octile[7] - octile[6])) + log (scale / rawmax) / log (2.f) );
534     }
535 
536     if (fabs (expcomp1) - fabs (expcomp2) > 1.f) { //for great expcomp
537         expcomp = (expcomp1 * fabs (expcomp2) + expcomp2 * fabs (expcomp1)) / (fabs (expcomp1) + fabs (expcomp2));
538     } else {
539         expcomp = 0.5 * (double)expcomp1 + 0.5 * (double) expcomp2; //for small expcomp
540     }
541 
542     float gain = exp ((float)expcomp * log (2.f));
543 
544     float corr = sqrt (gain * scale / rawmax);
545     black = (int) shc * corr;
546 
547 
548     //now tune hlcompr to bring back rawmax to 65535
549     hlcomprthresh = 0;
550     //this is a series approximation of the actual formula for comp,
551     //which is a transcendental equation
552     float comp = (gain * ((float)whiteclip) / scale - 1.f) * 2.3f; // 2.3 instead of 2 to increase slightly comp
553     hlcompr = (int) (100.*comp / (max (0.0, expcomp) + 1.0));
554     hlcompr = max (0, min (100, hlcompr));
555 
556     //now find brightness if gain didn't bring ave to midgray using
557     //the envelope of the actual 'control cage' brightness curve for simplicity
558     float midtmp = gain * sqrt (median * ave) / scale;
559 
560     if (midtmp < 0.1f) {
561         bright = (midgray - midtmp) * 15.0 / (midtmp);
562     } else {
563         bright = (midgray - midtmp) * 15.0 / (0.10833 - 0.0833 * midtmp);
564     }
565 
566     bright = 0.25 */*(median/ave)*(hidev/lodev)*/max (0, bright);
567 
568     //compute contrast that spreads the average spacing of octiles
569     contr = (int) 50.0f * (1.1f - ospread);
570     contr = max (0, min (100, contr));
571     //take gamma into account
572     double whiteclipg = (int) (CurveFactory::gamma2 (whiteclip * corr / 65536.0) * 65536.0);
573 
574     float gavg = 0.;
575 
576     float val = 0.f;
577     float increment = corr * (1 << histcompr);
578 
579     for (int i = 0; i < 65536 >> histcompr; i++) {
580         gavg += histogram[i] * Color::gamma2curve[val];
581         val += increment;
582     }
583 
584     gavg /= sum;
585 
586     if (black < gavg) {
587         int maxwhiteclip = (gavg - black) * 4 / 3 + black; // don't let whiteclip be such large that the histogram average goes above 3/4
588 
589         if (whiteclipg < maxwhiteclip) {
590             whiteclipg = maxwhiteclip;
591         }
592     }
593 
594     whiteclipg = CurveFactory::igamma2 ((float) (whiteclipg / 65535.0)) * 65535.0; //need to inverse gamma transform to get correct exposure compensation parameter
595 
596     //corection with gamma
597     black = (int) ((65535 * black) / whiteclipg);
598     //expcomp = log(65535.0 / (whiteclipg)) / log(2.0);
599 
600     //diagnostics
601     //printf ("**************** AUTO LEVELS ****************\n");
602     /*
603     if (settings->verbose) {
604         printf("sum=%i clip=%f clippable=%i  clipWh=%i  clipBl=%i\n",somm, clip, clippable,clipwh, clipbl);
605         printf ("expcomp1= %f   expcomp2= %f gain= %f  expcomp=%f\n",expcomp1,expcomp2,gain,expcomp);
606         printf ("expo=%f\n",expo);
607         printf ("median: %i  average: %f    median/average: %f\n",median,ave, median/ave);
608         printf ("average: %f\n",ave);
609         printf("comp=%f hlcomp: %i\n",comp, hlcompr);
610         printf ("median/average: %f\n",median/ave);
611         printf ("lodev: %f   hidev: %f      hidev/lodev: %f\n",lodev,hidev,hidev/lodev);
612         printf ("lodev: %f\n",lodev);
613         printf ("hidev: %f\n",hidev);
614         printf ("imax=%d rawmax= %d  whiteclip= %d  gain= %f\n",imax,rawmax,whiteclip,gain);
615         printf ("octiles: %f %f %f %f %f %f %f %f\n",octile[0],octile[1],octile[2],octile[3],octile[4],octile[5],octile[6],octile[7]);
616         printf ("ospread= %f\n",ospread);
617         printf ("overexp= %i\n",overex);
618     }
619     */
620     /*
621      // %%%%%%%%%% LEGACY AUTOEXPOSURE CODE %%%%%%%%%%%%%
622      // black point selection is based on the linear result (yielding better visual results)
623      black = (int)(shc * corr);
624      // compute the white point of the exp. compensated gamma corrected image
625      double whiteclipg = (int)(CurveFactory::gamma2 (whiteclip * corr / 65536.0) * 65536.0);
626 
627      // compute average intensity of the exp compensated, gamma corrected image
628      double gavg = 0;
629      for (int i=0; i<65536>>histcompr; i++)
630      gavg += histogram[i] * CurveFactory::gamma2((int)(corr*(i<<histcompr)<65535 ? corr*(i<<histcompr) : 65535)) / sum;
631 
632      if (black < gavg) {
633      int maxwhiteclip = (gavg - black) * 4 / 3 + black; // don't let whiteclip be such large that the histogram average goes above 3/4
634      //double mavg = 65536.0 / (whiteclipg-black) * (gavg - black);
635      if (whiteclipg < maxwhiteclip)
636      whiteclipg = maxwhiteclip;
637      }
638 
639      whiteclipg = CurveFactory::igamma2 ((float)(whiteclipg/65535.0)) * 65535.0; //need to inverse gamma transform to get correct exposure compensation parameter
640 
641      black = (int)((65535*black)/whiteclipg);
642      expcomp = log(65535.0 / (whiteclipg)) / log(2.0);
643 
644      if (expcomp<0.0)   expcomp = 0.0;*/
645     if (expcomp < -5.0) {
646         expcomp = -5.0;
647     }
648 
649     if (expcomp > 12.0) {
650         expcomp = 12.0;
651     }
652 
653     bright = max (-100, min (bright, 100));
654 
655 }
656 
657 
658 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659 
getAutoDistor(const Glib::ustring & fname,int thumb_size)660 double ImProcFunctions::getAutoDistor  (const Glib::ustring &fname, int thumb_size)
661 {
662     if (fname != "") {
663         int w_raw = -1, h_raw = thumb_size;
664         int w_thumb = -1, h_thumb = thumb_size;
665 
666         eSensorType sensorType = rtengine::ST_NONE;
667         Thumbnail* thumb = rtengine::Thumbnail::loadQuickFromRaw (fname, sensorType, w_thumb, h_thumb, 1, FALSE);
668 
669         if (!thumb) {
670             return 0.0;
671         }
672 
673         Thumbnail* raw =   rtengine::Thumbnail::loadFromRaw(fname, sensorType, w_raw, h_raw, 1, 1.0, FALSE);
674 
675         if (!raw) {
676             delete thumb;
677             return 0.0;
678         }
679 
680         if (h_thumb != h_raw) {
681             delete thumb;
682             delete raw;
683             return 0.0;
684         }
685 
686         int width;
687 
688         if (w_thumb > w_raw) {
689             width = w_raw;
690         } else {
691             width = w_thumb;
692         }
693 
694         unsigned char* thumbGray;
695         unsigned char* rawGray;
696         thumbGray = thumb->getGrayscaleHistEQ (width);
697         rawGray = raw->getGrayscaleHistEQ (width);
698 
699         if (!thumbGray || !rawGray) {
700             if (thumbGray) {
701                 delete thumbGray;
702             }
703 
704             if (rawGray) {
705                 delete rawGray;
706             }
707 
708             delete thumb;
709             delete raw;
710             return 0.0;
711         }
712 
713         double dist_amount;
714         int dist_result = calcDistortion (thumbGray, rawGray, width, h_thumb, 1, dist_amount);
715 
716         if (dist_result == -1) { // not enough features found, try increasing max. number of features by factor 4
717             calcDistortion (thumbGray, rawGray, width, h_thumb, 4, dist_amount);
718         }
719 
720         delete[] thumbGray;
721         delete[] rawGray;
722         delete thumb;
723         delete raw;
724         return dist_amount;
725     } else {
726         return 0.0;
727     }
728 }
729 
rgb2lab(Imagefloat & src,LabImage & dst,const Glib::ustring & workingSpace)730 void ImProcFunctions::rgb2lab (Imagefloat &src, LabImage &dst, const Glib::ustring &workingSpace)
731 {
732     src.assignColorSpace(workingSpace);
733     src.toLab(dst, true);
734 }
735 
lab2rgb(const LabImage & src,Imagefloat & dst,const Glib::ustring & workingSpace)736 void ImProcFunctions::lab2rgb (const LabImage &src, Imagefloat &dst, const Glib::ustring &workingSpace)
737 {
738     dst.assignColorSpace(workingSpace);
739     dst.assignMode(Imagefloat::Mode::RGB);
740 
741     TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix ( workingSpace );
742     const float wip[3][3] = {
743         {static_cast<float> (wiprof[0][0]), static_cast<float> (wiprof[0][1]), static_cast<float> (wiprof[0][2])},
744         {static_cast<float> (wiprof[1][0]), static_cast<float> (wiprof[1][1]), static_cast<float> (wiprof[1][2])},
745         {static_cast<float> (wiprof[2][0]), static_cast<float> (wiprof[2][1]), static_cast<float> (wiprof[2][2])}
746     };
747 
748     const int W = dst.getWidth();
749     const int H = dst.getHeight();
750 #ifdef __SSE2__
751     vfloat wipv[3][3];
752 
753     for (int i = 0; i < 3; i++) {
754         for (int j = 0; j < 3; j++) {
755             wipv[i][j] = F2V (wiprof[i][j]);
756         }
757     }
758 
759 #endif
760 
761 #ifdef _OPENMP
762     #pragma omp parallel for schedule(dynamic,16)
763 #endif
764 
765     for (int i = 0; i < H; i++) {
766         int j = 0;
767 #ifdef __SSE2__
768 
769         for (; j < W - 3; j += 4) {
770             vfloat X, Y, Z;
771             vfloat R, G, B;
772             Color::Lab2XYZ (LVFU (src.L[i][j]), LVFU (src.a[i][j]), LVFU (src.b[i][j]), X, Y, Z);
773             Color::xyz2rgb (X, Y, Z, R, G, B, wipv);
774             STVFU (dst.r (i, j), R);
775             STVFU (dst.g (i, j), G);
776             STVFU (dst.b (i, j), B);
777         }
778 
779 #endif
780 
781         for (; j < W; j++) {
782             float X, Y, Z;
783             Color::Lab2XYZ (src.L[i][j], src.a[i][j], src.b[i][j], X, Y, Z);
784             Color::xyz2rgb (X, Y, Z, dst.r (i, j), dst.g (i, j), dst.b (i, j), wip);
785         }
786     }
787 }
788 
789 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
790 
791 
setViewport(int ox,int oy,int fw,int fh)792 void ImProcFunctions::setViewport(int ox, int oy, int fw, int fh)
793 {
794     offset_x = ox;
795     offset_y = oy;
796     full_width = fw;
797     full_height = fh;
798 }
799 
800 
setOutputHistograms(LUTu * histToneCurve,LUTu * histCCurve,LUTu * histLCurve)801 void ImProcFunctions::setOutputHistograms(LUTu *histToneCurve, LUTu *histCCurve, LUTu *histLCurve)
802 {
803     this->histToneCurve = histToneCurve;
804     this->histCCurve = histCCurve;
805     this->histLCurve = histLCurve;
806 }
807 
808 
setShowSharpeningMask(bool yes)809 void ImProcFunctions::setShowSharpeningMask(bool yes)
810 {
811     show_sharpening_mask = yes;
812 }
813 
814 
815 namespace {
816 
817 constexpr int NUM_PIPELINE_STEPS = 23;
818 
819 } // namespace
820 
setProgressListener(ProgressListener * pl,int num_previews)821 void ImProcFunctions::setProgressListener(ProgressListener *pl, int num_previews)
822 {
823     plistener = pl;
824     progress_step = 0;
825     progress_end = NUM_PIPELINE_STEPS * std::max(num_previews, 1);
826     if (plistener) {
827         plistener->setProgressStr("PROGRESSBAR_PROCESSING");
828         plistener->setProgress(0);
829     }
830 }
831 
832 
833 template <class Ret, class Method>
834 Ret ImProcFunctions::apply(Method op, Imagefloat *img)
835 {
836     if (plistener) {
837         float percent = float(++progress_step) / float(progress_end);
838         plistener->setProgress(percent);
839     }
840     return (this->*op)(img);
841 }
842 
843 
process(Pipeline pipeline,Stage stage,Imagefloat * img)844 bool ImProcFunctions::process(Pipeline pipeline, Stage stage, Imagefloat *img)
845 {
846     bool stop = false;
847     cur_pipeline = pipeline;
848 
849 #define STEP_(op) apply<void>(&ImProcFunctions::op, img)
850 #define STEP_s_(op) apply<bool>(&ImProcFunctions::op, img)
851 
852     switch (stage) {
853     case Stage::STAGE_0:
854         STEP_(dehaze);
855         STEP_(dynamicRangeCompression);
856         break;
857     case Stage::STAGE_1:
858         STEP_(channelMixer);
859         STEP_(exposure);
860         STEP_(hslEqualizer);
861         stop = STEP_s_(toneEqualizer);
862         if (params->icm.workingProfile == "ProPhoto") {
863             proPhotoBlue(img, multiThread);
864         }
865         break;
866     case Stage::STAGE_2:
867         if (pipeline == Pipeline::OUTPUT ||
868             (pipeline == Pipeline::PREVIEW /*&& scale == 1*/)) {
869             stop = STEP_s_(sharpening);
870             if (!stop) {
871                 STEP_(impulsedenoise);
872                 STEP_(defringe);
873             }
874         }
875         stop = stop || STEP_s_(colorCorrection);
876         stop = stop || STEP_s_(guidedSmoothing);
877         break;
878     case Stage::STAGE_3:
879         STEP_(creativeGradients);
880         stop = stop || STEP_s_(textureBoost);
881         if (!stop) {
882             STEP_(logEncoding);
883             STEP_(saturationVibrance);
884             dcpProfile(img, dcpProf, dcpApplyState, multiThread);
885             STEP_(toneCurve);
886             STEP_(rgbCurves);
887             STEP_(labAdjustments);
888             // stop = stop || STEP_s_(textureBoost);
889             STEP_(softLight);
890         }
891         stop = stop || STEP_s_(localContrast);
892         if (!stop) {
893             STEP_(filmSimulation);
894             STEP_(blackAndWhite);
895             STEP_(filmGrain);
896         }
897         if (pipeline == Pipeline::PREVIEW && params->prsharpening.enabled) {
898             double s = scale;
899             int fw = full_width * s, fh = full_height * s;
900             int imw, imh;
901             double s2 = resizeScale(params, fw, fh, imw, imh);
902             scale = s * s2;
903             STEP_s_(prsharpening);
904             scale = s;
905         }
906         break;
907     }
908     return stop;
909 }
910 
911 
setDeltaEData(EditUniqueID id,double x,double y)912 int ImProcFunctions::setDeltaEData(EditUniqueID id, double x, double y)
913 {
914     deltaE.ok = false;
915     deltaE.x = x;
916     deltaE.y = y;
917     deltaE.L = 0;
918     deltaE.C = 0;
919     deltaE.H = 0;
920 
921     switch (id) {
922     case EUID_LabMasks_DE1:
923         return LUMINANCECURVE | M_LUMACURVE;
924     case EUID_LabMasks_DE2:
925         return DISPLAY;
926     case EUID_LabMasks_DE3:
927         return LUMINANCECURVE | M_LUMACURVE;
928     case EUID_LabMasks_DE4:
929         return DISPLAY;
930     default:
931         return 0;
932     }
933 }
934 
935 
936 } // namespace rtengine
937