"Fossies" - the Fresh Open Source Software Archive

Member "rawtherapee-5.7/rtengine/improccoordinator.cc" (10 Sep 2019, 61370 Bytes) of package /linux/misc/rawtherapee-5.7.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "improccoordinator.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.6_vs_5.7.

    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 <https://www.gnu.org/licenses/>.
   18  */
   19 #include "improccoordinator.h"
   20 #include "curves.h"
   21 #include "mytime.h"
   22 #include "refreshmap.h"
   23 #include "../rtgui/ppversion.h"
   24 #include "colortemp.h"
   25 #include "improcfun.h"
   26 #include "iccstore.h"
   27 #include "procparams.h"
   28 #include <iostream>
   29 #include <fstream>
   30 #include <string>
   31 #include "color.h"
   32 #ifdef _OPENMP
   33 #include <omp.h>
   34 #endif
   35 namespace rtengine
   36 {
   37 
   38 extern const Settings* settings;
   39 
   40 ImProcCoordinator::ImProcCoordinator() :
   41     orig_prev(nullptr),
   42     oprevi(nullptr),
   43     oprevl(nullptr),
   44     nprevl(nullptr),
   45     fattal_11_dcrop_cache(nullptr),
   46     previmg(nullptr),
   47     workimg(nullptr),
   48     ncie (nullptr),
   49     imgsrc (nullptr),
   50     lastAwbEqual (0.),
   51     lastAwbTempBias (0.0),
   52     monitorIntent (RI_RELATIVE),
   53     softProof(false),
   54     gamutCheck(false),
   55     sharpMask(false),
   56     scale(10),
   57     highDetailPreprocessComputed(false),
   58     highDetailRawComputed(false),
   59     allocated(false),
   60     bwAutoR(-9000.f),
   61     bwAutoG(-9000.f),
   62     bwAutoB(-9000.f),
   63     CAMMean(NAN),
   64     hltonecurve(65536),
   65     shtonecurve(65536),
   66     tonecurve(65536, 0),  //,1);
   67     lumacurve(32770, 0),  // lumacurve[32768] and lumacurve[32769] will be set to 32768 and 32769 later to allow linear interpolation
   68     chroma_acurve(65536, 0),
   69     chroma_bcurve(65536, 0),
   70     satcurve(65536, 0),
   71     lhskcurve(65536, 0),
   72     clcurve(65536, 0),
   73     conversionBuffer(1, 1),
   74     wavclCurve(65536, 0),
   75     clToningcurve(65536, 0),
   76     cl2Toningcurve(65536, 0),
   77     Noisecurve(65536, 0),
   78     NoiseCCcurve(65536, 0),
   79     vhist16(65536), vhist16bw(65536),
   80     lhist16CAM(65536),
   81     lhist16CCAM(65536),
   82     lhist16RETI(),
   83     lhist16LClad(65536),
   84     histRed(256), histRedRaw(256),
   85     histGreen(256), histGreenRaw(256),
   86     histBlue(256), histBlueRaw(256),
   87     histLuma(256),
   88     histToneCurve(256),
   89     histToneCurveBW(256),
   90     histLCurve(256),
   91     histCCurve(256),
   92     histLLCurve(256),
   93 
   94     histLCAM(256),
   95     histCCAM(256),
   96     histClad(256),
   97     bcabhist(256),
   98     histChroma(256),
   99 
  100     histLRETI(256),
  101 
  102     CAMBrightCurveJ(), CAMBrightCurveQ(),
  103 
  104     rCurve(),
  105     gCurve(),
  106     bCurve(),
  107     ctColorCurve(),
  108     rcurvehist(256), rcurvehistCropped(256), rbeforehist(256),
  109     gcurvehist(256), gcurvehistCropped(256), gbeforehist(256),
  110     bcurvehist(256), bcurvehistCropped(256), bbeforehist(256),
  111     fw(0), fh(0), tr(0),
  112     fullw(1), fullh(1),
  113     pW(-1), pH(-1),
  114     plistener(nullptr),
  115     imageListener(nullptr),
  116     aeListener(nullptr),
  117     acListener(nullptr),
  118     abwListener(nullptr),
  119     awbListener(nullptr),
  120     flatFieldAutoClipListener(nullptr),
  121     bayerAutoContrastListener(nullptr),
  122     xtransAutoContrastListener(nullptr),
  123     frameCountListener(nullptr),
  124     imageTypeListener(nullptr),
  125     actListener(nullptr),
  126     adnListener(nullptr),
  127     awavListener(nullptr),
  128     dehaListener(nullptr),
  129     hListener(nullptr),
  130     resultValid(false),
  131     params(new procparams::ProcParams),
  132     lastOutputProfile("BADFOOD"),
  133     lastOutputIntent(RI__COUNT),
  134     lastOutputBPC(false),
  135     thread(nullptr),
  136     changeSinceLast(0),
  137     updaterRunning(false),
  138     nextParams(new procparams::ProcParams),
  139     destroying(false),
  140     utili(false),
  141     autili(false),
  142     butili(false),
  143     ccutili(false),
  144     cclutili(false),
  145     clcutili(false),
  146     opautili(false),
  147     wavcontlutili(false),
  148     colourToningSatLimit(0.f),
  149     colourToningSatLimitOpacity(0.f),
  150     highQualityComputed(false),
  151     customTransformIn(nullptr),
  152     customTransformOut(nullptr),
  153     ipf(params.get(), true)
  154 {
  155 }
  156 
  157 ImProcCoordinator::~ImProcCoordinator()
  158 {
  159 
  160     destroying = true;
  161     updaterThreadStart.lock();
  162 
  163     if (updaterRunning && thread) {
  164         thread->join();
  165     }
  166 
  167     mProcessing.lock();
  168     mProcessing.unlock();
  169     freeAll();
  170 
  171     if (fattal_11_dcrop_cache) {
  172         delete fattal_11_dcrop_cache;
  173         fattal_11_dcrop_cache = nullptr;
  174     }
  175 
  176     std::vector<Crop*> toDel = crops;
  177 
  178     for (size_t i = 0; i < toDel.size(); i++) {
  179         delete toDel[i];
  180     }
  181 
  182     imgsrc->decreaseRef();
  183 
  184     if(customTransformIn) {
  185         cmsDeleteTransform(customTransformIn);
  186         customTransformIn = nullptr;
  187     }
  188 
  189     if(customTransformOut) {
  190         cmsDeleteTransform(customTransformOut);
  191         customTransformOut = nullptr;
  192     }
  193 
  194     updaterThreadStart.unlock();
  195 }
  196 
  197 void ImProcCoordinator::assign(ImageSource* imgsrc)
  198 {
  199     this->imgsrc = imgsrc;
  200 }
  201 
  202 void ImProcCoordinator::getParams(procparams::ProcParams* dst)
  203 {
  204     *dst = *params;
  205 }
  206 
  207 DetailedCrop* ImProcCoordinator::createCrop(::EditDataProvider *editDataProvider, bool isDetailWindow)
  208 {
  209 
  210     return new Crop(this, editDataProvider, isDetailWindow);
  211 }
  212 
  213 
  214 // todo: bitmask containing desired actions, taken from changesSinceLast
  215 void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
  216 {
  217 
  218     MyMutex::MyLock processingLock(mProcessing);
  219 
  220     constexpr int numofphases = 14;
  221     int readyphase = 0;
  222 
  223     bool highDetailNeeded = options.prevdemo == PD_Sidecar ? true : (todo & M_HIGHQUAL);
  224 
  225     // Check if any detail crops need high detail. If not, take a fast path short cut
  226     if (!highDetailNeeded) {
  227         for (size_t i = 0; i < crops.size(); i++) {
  228             if (crops[i]->get_skip() == 1) {   // skip=1 -> full resolution
  229                 highDetailNeeded = true;
  230                 break;
  231             }
  232         }
  233     }
  234 
  235     if (((todo & ALL) == ALL) || (todo & M_MONITOR) || panningRelatedChange || (highDetailNeeded && options.prevdemo != PD_Sidecar)) {
  236         bwAutoR = bwAutoG = bwAutoB = -9000.f;
  237 
  238         if (todo == CROP && ipf.needsPCVignetting()) {
  239             todo |= TRANSFORM;    // Change about Crop does affect TRANSFORM
  240         }
  241 
  242         RAWParams rp = params->raw;
  243         ColorManagementParams cmp = params->icm;
  244         LCurveParams  lcur = params->labCurve;
  245 
  246         if (!highDetailNeeded) {
  247             // if below 100% magnification, take a fast path
  248             if (rp.bayersensor.method != RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::NONE) && rp.bayersensor.method != RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::MONO)) {
  249                 rp.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::FAST);
  250             }
  251 
  252             //bayerrp.all_enhance = false;
  253 
  254             if (rp.xtranssensor.method != RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::NONE) && rp.xtranssensor.method != RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::MONO)) {
  255                 rp.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST);
  256             }
  257 
  258             rp.bayersensor.ccSteps = 0;
  259             rp.xtranssensor.ccSteps = 0;
  260             //rp.deadPixelFilter = rp.hotPixelFilter = false;
  261         }
  262 
  263         progress("Applying white balance, color correction & sRGB conversion...", 100 * readyphase / numofphases);
  264 
  265         if (frameCountListener) {
  266             frameCountListener->FrameCountChanged(imgsrc->getFrameCount(), params->raw.bayersensor.imageNum);
  267         }
  268 
  269         // raw auto CA is bypassed if no high detail is needed, so we have to compute it when high detail is needed
  270         if ((todo & M_PREPROC) || (!highDetailPreprocessComputed && highDetailNeeded)) {
  271             imgsrc->setCurrentFrame(params->raw.bayersensor.imageNum);
  272 
  273             imgsrc->preprocess(rp, params->lensProf, params->coarse);
  274             if (flatFieldAutoClipListener && rp.ff_AutoClipControl) {
  275                 flatFieldAutoClipListener->flatFieldAutoClipValueChanged(imgsrc->getFlatFieldAutoClipValue());
  276             }
  277             imgsrc->getRAWHistogram(histRedRaw, histGreenRaw, histBlueRaw);
  278 
  279             highDetailPreprocessComputed = highDetailNeeded;
  280 
  281             // After preprocess, run film negative processing if enabled
  282             if (
  283                 (todo & M_RAW)
  284                 && (
  285                     imgsrc->getSensorType() == ST_BAYER
  286                     || imgsrc->getSensorType() == ST_FUJI_XTRANS
  287                 )
  288                 && params->filmNegative.enabled
  289             ) {
  290                 imgsrc->filmNegativeProcess(params->filmNegative);
  291             }
  292         }
  293 
  294         /*
  295         Demosaic is kicked off only when
  296         Detail considerations:
  297             accurate detail is not displayed yet needed based on preview specifics (driven via highDetailNeeded flag)
  298         OR
  299         HLR considerations:
  300             Color HLR alters rgb output of demosaic, so re-demosaic is needed when Color HLR is being turned off;
  301             if HLR is enabled and changing method *from* Color to any other method
  302             OR HLR gets disabled when Color method was selected
  303         */
  304         // If high detail (=100%) is newly selected, do a demosaic update, since the last was just with FAST
  305 
  306         if (imageTypeListener) {
  307             imageTypeListener->imageTypeChanged(imgsrc->isRAW(), imgsrc->getSensorType() == ST_BAYER, imgsrc->getSensorType() == ST_FUJI_XTRANS, imgsrc->isMono());
  308         }
  309 
  310         if ((todo & M_RAW)
  311                 || (!highDetailRawComputed && highDetailNeeded)
  312                 || (params->toneCurve.hrenabled && params->toneCurve.method != "Color" && imgsrc->isRGBSourceModified())
  313                 || (!params->toneCurve.hrenabled && params->toneCurve.method == "Color" && imgsrc->isRGBSourceModified())) {
  314 
  315             if (settings->verbose) {
  316                 if (imgsrc->getSensorType() == ST_BAYER) {
  317                     printf("Demosaic Bayer image n.%d using method: %s\n", rp.bayersensor.imageNum + 1, rp.bayersensor.method.c_str());
  318                 } else if (imgsrc->getSensorType() == ST_FUJI_XTRANS) {
  319                     printf("Demosaic X-Trans image with using method: %s\n", rp.xtranssensor.method.c_str());
  320                 }
  321             }
  322             if(imgsrc->getSensorType() == ST_BAYER) {
  323                 if(params->raw.bayersensor.method != RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::PIXELSHIFT)) {
  324                     imgsrc->setBorder(params->raw.bayersensor.border);
  325                 } else {
  326                     imgsrc->setBorder(std::max(params->raw.bayersensor.border, 2));
  327                 }
  328             } else if (imgsrc->getSensorType() == ST_FUJI_XTRANS) {
  329                 imgsrc->setBorder(params->raw.xtranssensor.border);
  330             }
  331             bool autoContrast = imgsrc->getSensorType() == ST_BAYER ? params->raw.bayersensor.dualDemosaicAutoContrast : params->raw.xtranssensor.dualDemosaicAutoContrast;
  332             double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params->raw.bayersensor.dualDemosaicContrast : params->raw.xtranssensor.dualDemosaicContrast;
  333             imgsrc->demosaic(rp, autoContrast, contrastThreshold); //enabled demosaic
  334 
  335             if (imgsrc->getSensorType() == ST_BAYER && bayerAutoContrastListener && autoContrast) {
  336                 bayerAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0);
  337             }
  338             if (imgsrc->getSensorType() == ST_FUJI_XTRANS && xtransAutoContrastListener && autoContrast) {
  339                 xtransAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0);
  340             }
  341 
  342             // if a demosaic happened we should also call getimage later, so we need to set the M_INIT flag
  343             todo |= M_INIT;
  344 
  345             if (highDetailNeeded) {
  346                 highDetailRawComputed = true;
  347             } else {
  348                 highDetailRawComputed = false;
  349             }
  350 
  351             if (params->retinex.enabled) {
  352                 lhist16RETI(32768);
  353                 lhist16RETI.clear();
  354 
  355                 imgsrc->retinexPrepareBuffers(params->icm, params->retinex, conversionBuffer, lhist16RETI);
  356             }
  357         }
  358 
  359         if ((todo & (M_RETINEX | M_INIT)) && params->retinex.enabled) {
  360             bool dehacontlutili = false;
  361             bool mapcontlutili = false;
  362             bool useHsl = false;
  363             LUTf cdcurve(65536, 0);
  364             LUTf mapcurve(65536, 0);
  365 
  366             imgsrc->retinexPrepareCurves(params->retinex, cdcurve, mapcurve, dehatransmissionCurve, dehagaintransmissionCurve, dehacontlutili, mapcontlutili, useHsl, lhist16RETI, histLRETI);
  367             float minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax;
  368             imgsrc->retinex(params->icm, params->retinex,  params->toneCurve, cdcurve, mapcurve, dehatransmissionCurve, dehagaintransmissionCurve, conversionBuffer, dehacontlutili, mapcontlutili, useHsl, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, histLRETI);   //enabled Retinex
  369 
  370             if (dehaListener) {
  371                 dehaListener->minmaxChanged(maxCD, minCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax);
  372             }
  373         }
  374 
  375         if (todo & (M_INIT | M_LINDENOISE | M_HDR)) {
  376             MyMutex::MyLock initLock(minit);  // Also used in crop window
  377 
  378             imgsrc->HLRecovery_Global(params->toneCurve);   // this handles Color HLRecovery
  379 
  380 
  381             if (settings->verbose) {
  382                 printf("Applying white balance, color correction & sRBG conversion...\n");
  383             }
  384 
  385             currWB = ColorTemp(params->wb.temperature, params->wb.green, params->wb.equal, params->wb.method);
  386 
  387             if (!params->wb.enabled) {
  388                 currWB = ColorTemp();
  389             } else if (params->wb.method == "Camera") {
  390                 currWB = imgsrc->getWB();
  391             } else if (params->wb.method == "Auto") {
  392                 if (lastAwbEqual != params->wb.equal || lastAwbTempBias != params->wb.tempBias) {
  393                     double rm, gm, bm;
  394                     imgsrc->getAutoWBMultipliers(rm, gm, bm);
  395 
  396                     if (rm != -1.) {
  397                         autoWB.update(rm, gm, bm, params->wb.equal, params->wb.tempBias);
  398                         lastAwbEqual = params->wb.equal;
  399                         lastAwbTempBias = params->wb.tempBias;
  400                     } else {
  401                         lastAwbEqual = -1.;
  402                         lastAwbTempBias = 0.0;
  403                         autoWB.useDefaults(params->wb.equal);
  404                     }
  405 
  406                     //double rr,gg,bb;
  407                     //autoWB.getMultipliers(rr,gg,bb);
  408                 }
  409 
  410                 currWB = autoWB;
  411             }
  412 
  413             if (params->wb.enabled) {
  414                 params->wb.temperature = currWB.getTemp();
  415                 params->wb.green = currWB.getGreen();
  416             }
  417 
  418             if (params->wb.method == "Auto" && awbListener && params->wb.enabled) {
  419                 awbListener->WBChanged(params->wb.temperature, params->wb.green);
  420             }
  421 
  422             /*
  423                     GammaValues g_a;
  424                     double pwr = 1.0 / params->icm.gampos;
  425                     double ts = params->icm.slpos;
  426 
  427 
  428                     int mode = 0;
  429                     Color::calcGamma(pwr, ts, mode, g_a); // call to calcGamma with selected gamma and slope
  430                         printf("ga[0]=%f ga[1]=%f ga[2]=%f ga[3]=%f ga[4]=%f\n", g_a[0],g_a[1],g_a[2],g_a[3],g_a[4]);
  431 
  432                         Glib::ustring datal;
  433                         datal = "lutsrgb.txt";
  434                                 ofstream fou(datal, ios::out | ios::trunc);
  435 
  436                     for(int i=0; i < 212; i++) {
  437                         //printf("igamma2=%i\n", (int) 65535.f*Color::igamma2(i/212.0));
  438                                 float gam = Color::igamma2(i/211.0);
  439                                 int lutga = nearbyint(65535.f* gam);
  440                               //  fou << 65535*(int)Color::igamma2(i/212.0) << endl;
  441                                 fou << i << " " << lutga << endl;
  442 
  443                     }
  444                             fou.close();
  445             */
  446             int tr = getCoarseBitMask(params->coarse);
  447 
  448             imgsrc->getFullSize(fw, fh, tr);
  449 
  450             // Will (re)allocate the preview's buffers
  451             setScale(scale);
  452             PreviewProps pp(0, 0, fw, fh, scale);
  453             // Tells to the ImProcFunctions' tools what is the preview scale, which may lead to some simplifications
  454             ipf.setScale(scale);
  455 
  456             imgsrc->getImage(currWB, tr, orig_prev, pp, params->toneCurve, params->raw);
  457             denoiseInfoStore.valid = false;
  458             //ColorTemp::CAT02 (orig_prev, &params) ;
  459             //   printf("orig_prevW=%d\n  scale=%d",orig_prev->width, scale);
  460             /* Issue 2785, disabled some 1:1 tools
  461                     if (todo & M_LINDENOISE) {
  462                         DirPyrDenoiseParams denoiseParams = params->dirpyrDenoise;
  463                         if (denoiseParams.enabled && (scale==1)) {
  464                             Imagefloat *calclum = NULL ;
  465 
  466                             denoiseParams.getCurves(noiseLCurve,noiseCCurve);
  467                             int nbw=6;//nb tile W
  468                             int nbh=4;//
  469 
  470                             float ch_M[nbw*nbh];
  471                             float max_r[nbw*nbh];
  472                             float max_b[nbw*nbh];
  473 
  474                             if(denoiseParams.Lmethod == "CUR") {
  475                                 if(noiseLCurve)
  476                                     denoiseParams.luma = 0.5f;
  477                                 else
  478                                     denoiseParams.luma = 0.0f;
  479                             } else if(denoiseParams.Lmethod == "SLI")
  480                                 noiseLCurve.Reset();
  481 
  482 
  483                             if(noiseLCurve || noiseCCurve){//only allocate memory if enabled and scale=1
  484                                 // we only need image reduced to 1/4 here
  485                                 calclum = new Imagefloat ((pW+1)/2, (pH+1)/2);//for luminance denoise curve
  486                                 for(int ii=0;ii<pH;ii+=2){
  487                                     for(int jj=0;jj<pW;jj+=2){
  488                                         calclum->r(ii>>1,jj>>1) = orig_prev->r(ii,jj);
  489                                         calclum->g(ii>>1,jj>>1) = orig_prev->g(ii,jj);
  490                                         calclum->b(ii>>1,jj>>1) = orig_prev->b(ii,jj);
  491                                     }
  492                                 }
  493                                 imgsrc->convertColorSpace(calclum, params->icm, currWB);//calculate values after colorspace conversion
  494                             }
  495 
  496                             int kall=1;
  497                             ipf.RGB_denoise(kall, orig_prev, orig_prev, calclum, ch_M, max_r, max_b, imgsrc->isRAW(), denoiseParams, imgsrc->getDirPyrDenoiseExpComp(), noiseLCurve, noiseCCurve, chaut, redaut, blueaut, maxredaut, maxblueaut, nresi, highresi);
  498                         }
  499                     }
  500             */
  501             imgsrc->convertColorSpace(orig_prev, params->icm, currWB);
  502 
  503             ipf.firstAnalysis(orig_prev, *params, vhist16);
  504         }
  505 
  506         readyphase++;
  507 
  508         if ((todo & M_HDR) && (params->fattal.enabled || params->dehaze.enabled)) {
  509             if (fattal_11_dcrop_cache) {
  510                 delete fattal_11_dcrop_cache;
  511                 fattal_11_dcrop_cache = nullptr;
  512             }
  513 
  514             ipf.dehaze(orig_prev);
  515             ipf.ToneMapFattal02(orig_prev);
  516 
  517             if (oprevi != orig_prev) {
  518                 delete oprevi;
  519             }
  520         }
  521 
  522         oprevi = orig_prev;
  523 
  524         progress("Rotate / Distortion...", 100 * readyphase / numofphases);
  525         // Remove transformation if unneeded
  526         bool needstransform = ipf.needsTransform();
  527 
  528         if ((needstransform || ((todo & (M_TRANSFORM | M_RGBCURVE))  && params->dirpyrequalizer.cbdlMethod == "bef" && params->dirpyrequalizer.enabled && !params->colorappearance.enabled))) {
  529             assert(oprevi);
  530             Imagefloat *op = oprevi;
  531             oprevi = new Imagefloat(pW, pH);
  532 
  533             if (needstransform)
  534                 ipf.transform(op, oprevi, 0, 0, 0, 0, pW, pH, fw, fh,
  535                               imgsrc->getMetaData(), imgsrc->getRotateDegree(), false);
  536             else {
  537                 op->copyData(oprevi);
  538             }
  539         }
  540 
  541         if ((todo & (M_TRANSFORM | M_RGBCURVE))  && params->dirpyrequalizer.cbdlMethod == "bef" && params->dirpyrequalizer.enabled && !params->colorappearance.enabled) {
  542             const int W = oprevi->getWidth();
  543             const int H = oprevi->getHeight();
  544             LabImage labcbdl(W, H);
  545             ipf.rgb2lab(*oprevi, labcbdl, params->icm.workingProfile);
  546             ipf.dirpyrequalizer(&labcbdl, scale);
  547             ipf.lab2rgb(labcbdl, *oprevi, params->icm.workingProfile);
  548         }
  549 
  550         readyphase++;
  551         progress("Preparing shadow/highlight map...", 100 * readyphase / numofphases);
  552 
  553         readyphase++;
  554 
  555         if (todo & M_AUTOEXP) {
  556             if (params->toneCurve.autoexp) {
  557                 LUTu aehist;
  558                 int aehistcompr;
  559                 imgsrc->getAutoExpHistogram(aehist, aehistcompr);
  560                 ipf.getAutoExp(aehist, aehistcompr, params->toneCurve.clip, params->toneCurve.expcomp,
  561                                params->toneCurve.brightness, params->toneCurve.contrast, params->toneCurve.black, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh);
  562 
  563                 if (aeListener)
  564                     aeListener->autoExpChanged(params->toneCurve.expcomp, params->toneCurve.brightness, params->toneCurve.contrast,
  565                                                params->toneCurve.black, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh, params->toneCurve.hrenabled);
  566             }
  567 
  568             if (params->toneCurve.histmatching) {
  569                 if (!params->toneCurve.fromHistMatching) {
  570                     imgsrc->getAutoMatchedToneCurve(params->icm, params->toneCurve.curve);
  571                 }
  572 
  573                 if (params->toneCurve.autoexp) {
  574                     params->toneCurve.expcomp = 0.0;
  575                 }
  576 
  577                 params->toneCurve.autoexp = false;
  578                 params->toneCurve.curveMode = ToneCurveMode::FILMLIKE;
  579                 params->toneCurve.curve2 = { 0 };
  580                 params->toneCurve.brightness = 0;
  581                 params->toneCurve.contrast = 0;
  582                 params->toneCurve.black = 0;
  583                 params->toneCurve.fromHistMatching = true;
  584 
  585                 if (aeListener) {
  586                     aeListener->autoMatchedToneCurveChanged(params->toneCurve.curveMode, params->toneCurve.curve);
  587                 }
  588             }
  589         }
  590 
  591         progress("Exposure curve & CIELAB conversion...", 100 * readyphase / numofphases);
  592 
  593         if (todo &  (M_AUTOEXP | M_RGBCURVE)) {
  594             if (params->icm.workingTRC == "Custom") { //exec TRC IN free
  595                 if (oprevi == orig_prev) {
  596                     oprevi = new Imagefloat(pW, pH);
  597                     orig_prev->copyData(oprevi);
  598                 }
  599 
  600                 const Glib::ustring profile = params->icm.workingProfile;
  601 
  602                 if (profile == "sRGB" || profile == "Adobe RGB" || profile == "ProPhoto" || profile == "WideGamut" || profile == "BruceRGB" || profile == "Beta RGB" || profile == "BestRGB" || profile == "Rec2020" || profile == "ACESp0" || profile == "ACESp1") {
  603                     const int cw = oprevi->getWidth();
  604                     const int ch = oprevi->getHeight();
  605                     // put gamma TRC to 1
  606                     if(customTransformIn) {
  607                         cmsDeleteTransform(customTransformIn);
  608                         customTransformIn = nullptr;
  609                     }
  610                     ipf.workingtrc(oprevi, oprevi, cw, ch, -5, params->icm.workingProfile, 2.4, 12.92310, customTransformIn, true, false, true);
  611                     //adjust TRC
  612                     if(customTransformOut) {
  613                         cmsDeleteTransform(customTransformOut);
  614                         customTransformOut = nullptr;
  615                     }
  616                     ipf.workingtrc(oprevi, oprevi, cw, ch, 5, params->icm.workingProfile, params->icm.workingTRCGamma, params->icm.workingTRCSlope, customTransformOut, false, true, true);
  617                 }
  618             }
  619         }
  620 
  621 
  622         if ((todo & M_RGBCURVE) || (todo & M_CROP)) {
  623     //        if (hListener) oprevi->calcCroppedHistogram(params, scale, histCropped);
  624 
  625             //complexCurve also calculated pre-curves histogram depending on crop
  626             CurveFactory::complexCurve(params->toneCurve.expcomp, params->toneCurve.black / 65535.0,
  627                                        params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh,
  628                                        params->toneCurve.shcompr, params->toneCurve.brightness, params->toneCurve.contrast,
  629                                        params->toneCurve.curve, params->toneCurve.curve2,
  630                                        vhist16, hltonecurve, shtonecurve, tonecurve, histToneCurve, customToneCurve1, customToneCurve2, 1);
  631 
  632             CurveFactory::RGBCurve(params->rgbCurves.rcurve, rCurve, 1);
  633             CurveFactory::RGBCurve(params->rgbCurves.gcurve, gCurve, 1);
  634             CurveFactory::RGBCurve(params->rgbCurves.bcurve, bCurve, 1);
  635 
  636 
  637             opautili = false;
  638 
  639             if (params->colorToning.enabled) {
  640                 TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile);
  641                 double wp[3][3] = {
  642                     {wprof[0][0], wprof[0][1], wprof[0][2]},
  643                     {wprof[1][0], wprof[1][1], wprof[1][2]},
  644                     {wprof[2][0], wprof[2][1], wprof[2][2]}
  645                 };
  646                 params->colorToning.getCurves(ctColorCurve, ctOpacityCurve, wp, opautili);
  647                 CurveFactory::curveToning(params->colorToning.clcurve, clToningcurve, scale == 1 ? 1 : 16);
  648                 CurveFactory::curveToning(params->colorToning.cl2curve, cl2Toningcurve, scale == 1 ? 1 : 16);
  649             }
  650 
  651             if (params->blackwhite.enabled) {
  652                 CurveFactory::curveBW(params->blackwhite.beforeCurve, params->blackwhite.afterCurve, vhist16bw, histToneCurveBW, beforeToneCurveBW, afterToneCurveBW, 1);
  653             }
  654 
  655             colourToningSatLimit = float (params->colorToning.satProtectionThreshold) / 100.f * 0.7f + 0.3f;
  656             colourToningSatLimitOpacity = 1.f - (float (params->colorToning.saturatedOpacity) / 100.f);
  657 
  658             int satTH = 80;
  659             int satPR = 30;
  660             int indi = 0;
  661 
  662             if (params->colorToning.enabled  && params->colorToning.autosat && params->colorToning.method != "LabGrid") { //for colortoning evaluation of saturation settings
  663                 float moyS = 0.f;
  664                 float eqty = 0.f;
  665                 ipf.moyeqt(oprevi, moyS, eqty); //return image : mean saturation and standard dev of saturation
  666                 //printf("moy=%f ET=%f\n", moyS,eqty);
  667                 float satp = ((moyS + 1.5f * eqty) - 0.3f) / 0.7f; //1.5 sigma ==> 93% pixels with high saturation -0.3 / 0.7 convert to Hombre scale
  668 
  669                 if (satp >= 0.92f) {
  670                     satp = 0.92f;    //avoid values too high (out of gamut)
  671                 }
  672 
  673                 if (satp <= 0.15f) {
  674                     satp = 0.15f;    //avoid too low values
  675                 }
  676 
  677                 //satTH=(int) 100.f*satp;
  678                 //satPR=(int) 100.f*(moyS-0.85f*eqty);//-0.85 sigma==>20% pixels with low saturation
  679                 colourToningSatLimit = 100.f * satp;
  680                 satTH = (int) 100.f * satp;
  681 
  682                 colourToningSatLimitOpacity = 100.f * (moyS - 0.85f * eqty); //-0.85 sigma==>20% pixels with low saturation
  683                 satPR = (int) 100.f * (moyS - 0.85f * eqty);
  684             }
  685 
  686             if (actListener && params->colorToning.enabled) {
  687                 if (params->blackwhite.enabled && params->colorToning.autosat) {
  688                     actListener->autoColorTonChanged(0, satTH, satPR);    //hide sliders only if autosat
  689                     indi = 0;
  690                 } else {
  691                     if (params->colorToning.autosat) {
  692                         if (params->colorToning.method == "Lab") {
  693                             indi = 1;
  694                         } else if (params->colorToning.method == "RGBCurves") {
  695                             indi = 1;
  696                         } else if (params->colorToning.method == "RGBSliders") {
  697                             indi = 1;
  698                         } else if (params->colorToning.method == "Splico") {
  699                             indi = 2;
  700                         } else if (params->colorToning.method == "Splitlr") {
  701                             indi = 2;
  702                         }
  703                     }
  704                 }
  705             }
  706 
  707             // if it's just crop we just need the histogram, no image updates
  708             if (todo & M_RGBCURVE) {
  709                 //initialize rrm bbm ggm different from zero to avoid black screen in some cases
  710                 double rrm = 33.;
  711                 double ggm = 33.;
  712                 double bbm = 33.;
  713 
  714                 DCPProfile::ApplyState as;
  715                 DCPProfile *dcpProf = imgsrc->getDCP(params->icm, as);
  716 
  717                 ipf.rgbProc (oprevi, oprevl, nullptr, hltonecurve, shtonecurve, tonecurve, params->toneCurve.saturation,
  718                             rCurve, gCurve, bCurve, colourToningSatLimit, colourToningSatLimitOpacity, ctColorCurve, ctOpacityCurve, opautili, clToningcurve, cl2Toningcurve, customToneCurve1, customToneCurve2, beforeToneCurveBW, afterToneCurveBW, rrm, ggm, bbm, bwAutoR, bwAutoG, bwAutoB, params->toneCurve.expcomp, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh, dcpProf, as, histToneCurve);
  719 
  720                 if (params->blackwhite.enabled && params->blackwhite.autoc && abwListener) {
  721                     if (settings->verbose) {
  722                         printf("ImProcCoordinator / Auto B&W coefs:   R=%.2f   G=%.2f   B=%.2f\n", bwAutoR, bwAutoG, bwAutoB);
  723                     }
  724 
  725                     abwListener->BWChanged((float) rrm, (float) ggm, (float) bbm);
  726                 }
  727 
  728                 if (params->colorToning.enabled && params->colorToning.autosat && actListener) {
  729                     actListener->autoColorTonChanged(indi, (int) colourToningSatLimit, (int)colourToningSatLimitOpacity);  //change sliders autosat
  730                 }
  731 
  732                 // correct GUI black and white with value
  733             }
  734 
  735             // compute L channel histogram
  736             int x1, y1, x2, y2;
  737             params->crop.mapToResized(pW, pH, scale, x1, x2,  y1, y2);
  738         }
  739 
  740         readyphase++;
  741 
  742         if (todo & (M_LUMACURVE | M_CROP)) {
  743             LUTu lhist16(32768);
  744             lhist16.clear();
  745 #ifdef _OPENMP
  746             const int numThreads = min(max(pW * pH / (int)lhist16.getSize(), 1), omp_get_max_threads());
  747             #pragma omp parallel num_threads(numThreads) if(numThreads>1)
  748 #endif
  749             {
  750                 LUTu lhist16thr(lhist16.getSize());
  751                 lhist16thr.clear();
  752 #ifdef _OPENMP
  753                 #pragma omp for nowait
  754 #endif
  755 
  756                 for (int x = 0; x < pH; x++)
  757                     for (int y = 0; y < pW; y++) {
  758                         int pos = (int)(oprevl->L[x][y]);
  759                         lhist16thr[pos]++;
  760                     }
  761 
  762 #ifdef _OPENMP
  763                 #pragma omp critical
  764 #endif
  765                 lhist16 += lhist16thr;
  766             }
  767 #ifdef _OPENMP
  768             static_cast<void>(numThreads);  // to silence cppcheck warning
  769 #endif
  770             CurveFactory::complexLCurve(params->labCurve.brightness, params->labCurve.contrast, params->labCurve.lcurve, lhist16, lumacurve, histLCurve, scale == 1 ? 1 : 16, utili);
  771         }
  772 
  773         if (todo & M_LUMACURVE) {
  774 
  775             CurveFactory::curveCL(clcutili, params->labCurve.clcurve, clcurve, scale == 1 ? 1 : 16);
  776 
  777             CurveFactory::complexsgnCurve(autili, butili, ccutili, cclutili, params->labCurve.acurve, params->labCurve.bcurve, params->labCurve.cccurve,
  778                                           params->labCurve.lccurve, chroma_acurve, chroma_bcurve, satcurve, lhskcurve, scale == 1 ? 1 : 16);
  779         }
  780 
  781         if (todo & (M_LUMINANCE + M_COLOR)) {
  782             nprevl->CopyFrom(oprevl);
  783 
  784             progress("Applying Color Boost...", 100 * readyphase / numofphases);
  785 
  786             histCCurve.clear();
  787             histLCurve.clear();
  788             ipf.chromiLuminanceCurve(nullptr, pW, nprevl, nprevl, chroma_acurve, chroma_bcurve, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, histCCurve, histLCurve);
  789             ipf.vibrance(nprevl);
  790             ipf.labColorCorrectionRegions(nprevl);
  791 
  792             if ((params->colorappearance.enabled && !params->colorappearance.tonecie) || (!params->colorappearance.enabled)) {
  793                 ipf.EPDToneMap(nprevl, 0, scale);
  794             }
  795 
  796             // for all treatments Defringe, Sharpening, Contrast detail , Microcontrast they are activated if "CIECAM" function are disabled
  797             readyphase++;
  798 
  799             /* Issue 2785, disabled some 1:1 tools
  800                     if (scale==1) {
  801                         if((params->colorappearance.enabled && !settings->autocielab) || (!params->colorappearance.enabled)){
  802                             progress ("Denoising luminance impulse...",100*readyphase/numofphases);
  803                             ipf.impulsedenoise (nprevl);
  804                             readyphase++;
  805                         }
  806                         if((params->colorappearance.enabled && !settings->autocielab) || (!params->colorappearance.enabled)){
  807                             progress ("Defringing...",100*readyphase/numofphases);
  808                             ipf.defringe (nprevl);
  809                             readyphase++;
  810                         }
  811                         if (params->sharpenEdge.enabled) {
  812                             progress ("Edge sharpening...",100*readyphase/numofphases);
  813                             ipf.MLsharpen (nprevl);
  814                             readyphase++;
  815                         }
  816                         if (params->sharpenMicro.enabled) {
  817                             if(( params->colorappearance.enabled && !settings->autocielab) || (!params->colorappearance.enabled)){
  818                                 progress ("Microcontrast...",100*readyphase/numofphases);
  819                                 ipf.MLmicrocontrast (nprevl);
  820                                 readyphase++;
  821                             }
  822                         }
  823                         if(((params->colorappearance.enabled && !settings->autocielab) || (!params->colorappearance.enabled)) && params->sharpening.enabled) {
  824                             progress ("Sharpening...",100*readyphase/numofphases);
  825 
  826                             float **buffer = new float*[pH];
  827                             for (int i=0; i<pH; i++)
  828                                 buffer[i] = new float[pW];
  829 
  830                             ipf.sharpening (nprevl, (float**)buffer);
  831 
  832                             for (int i=0; i<pH; i++)
  833                                 delete [] buffer[i];
  834                             delete [] buffer;
  835                             readyphase++;
  836                         }
  837                     }
  838             */
  839             if (params->dirpyrequalizer.cbdlMethod == "aft") {
  840                 if (((params->colorappearance.enabled && !settings->autocielab) || (!params->colorappearance.enabled))) {
  841                     progress("Pyramid wavelet...", 100 * readyphase / numofphases);
  842                     ipf.dirpyrequalizer(nprevl, scale);
  843                     //ipf.Lanczoslab (ip_wavelet(LabImage * lab, LabImage * dst, const procparams::EqualizerParams & eqparams), nprevl, 1.f/scale);
  844                     readyphase++;
  845                 }
  846             }
  847 
  848 
  849             wavcontlutili = false;
  850             //CurveFactory::curveWavContL ( wavcontlutili,params->wavelet.lcurve, wavclCurve, LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,int skip);
  851             CurveFactory::curveWavContL(wavcontlutili, params->wavelet.wavclCurve, wavclCurve, scale == 1 ? 1 : 16);
  852 
  853 
  854             if ((params->wavelet.enabled)) {
  855                 WaveletParams WaveParams = params->wavelet;
  856                 //      WaveParams.getCurves(wavCLVCurve, waOpacityCurveRG, waOpacityCurveBY);
  857                 WaveParams.getCurves(wavCLVCurve, waOpacityCurveRG, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL);
  858 
  859                 int kall = 0;
  860                 progress("Wavelet...", 100 * readyphase / numofphases);
  861                 //  ipf.ip_wavelet(nprevl, nprevl, kall, WaveParams, wavCLVCurve, waOpacityCurveRG, waOpacityCurveBY, scale);
  862                 ipf.ip_wavelet(nprevl, nprevl, kall, WaveParams, wavCLVCurve, waOpacityCurveRG, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, wavclCurve, scale);
  863 
  864             }
  865 
  866         ipf.softLight(nprevl);
  867 
  868             if (params->colorappearance.enabled) {
  869                 //L histo  and Chroma histo for ciecam
  870                 // histogram well be for Lab (Lch) values, because very difficult to do with J,Q, M, s, C
  871                 int x1, y1, x2, y2;
  872                 params->crop.mapToResized(pW, pH, scale, x1, x2,  y1, y2);
  873                 lhist16CAM.clear();
  874                 lhist16CCAM.clear();
  875 
  876                 if (!params->colorappearance.datacie) {
  877                     for (int x = 0; x < pH; x++)
  878                         for (int y = 0; y < pW; y++) {
  879                             int pos = CLIP((int)(nprevl->L[x][y]));
  880                             int posc = CLIP((int)sqrt(nprevl->a[x][y] * nprevl->a[x][y] + nprevl->b[x][y] * nprevl->b[x][y]));
  881                             lhist16CAM[pos]++;
  882                             lhist16CCAM[posc]++;
  883                         }
  884                 }
  885 
  886                 CurveFactory::curveLightBrightColor(params->colorappearance.curve, params->colorappearance.curve2, params->colorappearance.curve3,
  887                                                     lhist16CAM, histLCAM, lhist16CCAM, histCCAM,
  888                                                     customColCurve1, customColCurve2, customColCurve3, 1);
  889 
  890                 const FramesMetaData* metaData = imgsrc->getMetaData();
  891                 int imgNum = 0;
  892 
  893                 if (imgsrc->isRAW()) {
  894                     if (imgsrc->getSensorType() == ST_BAYER) {
  895                         imgNum = rtengine::LIM<unsigned int>(params->raw.bayersensor.imageNum, 0, metaData->getFrameCount() - 1);
  896                     } else if (imgsrc->getSensorType() == ST_FUJI_XTRANS) {
  897                         //imgNum = rtengine::LIM<unsigned int>(params->raw.xtranssensor.imageNum, 0, metaData->getFrameCount() - 1);
  898                     }
  899                 }
  900 
  901                 float fnum = metaData->getFNumber(imgNum);          // F number
  902                 float fiso = metaData->getISOSpeed(imgNum) ;        // ISO
  903                 float fspeed = metaData->getShutterSpeed(imgNum) ;  // Speed
  904                 double fcomp = metaData->getExpComp(imgNum);        // Compensation +/-
  905                 double adap;
  906 
  907                 if (fnum < 0.3f || fiso < 5.f || fspeed < 0.00001f) { //if no exif data or wrong
  908                     adap = 2000.;
  909                 } else {
  910                     double E_V = fcomp + log2(double ((fnum * fnum) / fspeed / (fiso / 100.f)));
  911                     E_V += params->toneCurve.expcomp;// exposure compensation in tonecurve ==> direct EV
  912                     E_V += log2(params->raw.expos);  // exposure raw white point ; log2 ==> linear to EV
  913                     adap = powf(2.f, E_V - 3.f);  // cd / m2
  914                     // end calculation adaptation scene luminosity
  915                 }
  916 
  917                 float d, dj, yb;
  918                 bool execsharp = false;
  919 
  920                 if (!ncie) {
  921                     ncie = new CieImage(pW, pH);
  922                 }
  923 
  924                 if (!CAMBrightCurveJ && (params->colorappearance.algo == "JC" || params->colorappearance.algo == "JS" || params->colorappearance.algo == "ALL")) {
  925                     CAMBrightCurveJ(32768, 0);
  926                 }
  927 
  928                 if (!CAMBrightCurveQ && (params->colorappearance.algo == "QM" || params->colorappearance.algo == "ALL")) {
  929                     CAMBrightCurveQ(32768, 0);
  930                 }
  931 
  932                 // Issue 2785, only float version of ciecam02 for navigator and pan background
  933                 CAMMean = NAN;
  934                 CAMBrightCurveJ.dirty = true;
  935                 CAMBrightCurveQ.dirty = true;
  936 
  937                 ipf.ciecam_02float(ncie, float (adap), pW, 2, nprevl, params.get(), customColCurve1, customColCurve2, customColCurve3, histLCAM, histCCAM, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 0 , scale, execsharp, d, dj, yb, 1);
  938 
  939                 if ((params->colorappearance.autodegree || params->colorappearance.autodegreeout) && acListener && params->colorappearance.enabled) {
  940                     acListener->autoCamChanged(100.* (double)d, 100.* (double)dj);
  941                 }
  942 
  943                 if (params->colorappearance.autoadapscen && acListener && params->colorappearance.enabled) {
  944                     acListener->adapCamChanged(adap);    //real value of adapt scene
  945                 }
  946 
  947                 if (params->colorappearance.autoybscen && acListener && params->colorappearance.enabled) {
  948                     acListener->ybCamChanged((int) yb);    //real value Yb scene
  949                 }
  950 
  951                 readyphase++;
  952             } else {
  953                 // CIECAM is disabled, we free up its image buffer to save some space
  954                 if (ncie) {
  955                     delete ncie;
  956                 }
  957 
  958                 ncie = nullptr;
  959 
  960                 if (CAMBrightCurveJ) {
  961                     CAMBrightCurveJ.reset();
  962                 }
  963 
  964                 if (CAMBrightCurveQ) {
  965                     CAMBrightCurveQ.reset();
  966                 }
  967             }
  968         }
  969 
  970         // Update the monitor color transform if necessary
  971         if ((todo & M_MONITOR) || (lastOutputProfile != params->icm.outputProfile) || lastOutputIntent != params->icm.outputIntent || lastOutputBPC != params->icm.outputBPC) {
  972             lastOutputProfile = params->icm.outputProfile;
  973             lastOutputIntent = params->icm.outputIntent;
  974             lastOutputBPC = params->icm.outputBPC;
  975             ipf.updateColorProfiles(monitorProfile, monitorIntent, softProof, gamutCheck);
  976         }
  977     }
  978 
  979     // process crop, if needed
  980     for (size_t i = 0; i < crops.size(); i++)
  981         if (crops[i]->hasListener() && (panningRelatedChange || (highDetailNeeded && options.prevdemo != PD_Sidecar) || (todo & (M_MONITOR | M_RGBCURVE | M_LUMACURVE)) || crops[i]->get_skip() == 1)) {
  982             crops[i]->update(todo);     // may call ourselves
  983         }
  984 
  985     if (panningRelatedChange || (todo & M_MONITOR)) {
  986         progress("Conversion to RGB...", 100 * readyphase / numofphases);
  987 
  988         if ((todo != CROP && todo != MINUPDATE) || (todo & M_MONITOR)) {
  989             MyMutex::MyLock prevImgLock(previmg->getMutex());
  990 
  991             try {
  992                 // Computing the preview image, i.e. converting from WCS->Monitor color space (soft-proofing disabled) or WCS->Printer profile->Monitor color space (soft-proofing enabled)
  993                 ipf.lab2monitorRgb(nprevl, previmg);
  994 
  995                 // Computing the internal image for analysis, i.e. conversion from WCS->Output profile
  996                 delete workimg;
  997                 workimg = ipf.lab2rgb(nprevl, 0, 0, pW, pH, params->icm);
  998             } catch (char * str) {
  999                 progress("Error converting file...", 0);
 1000                 return;
 1001             }
 1002         }
 1003 
 1004         if (!resultValid) {
 1005             resultValid = true;
 1006 
 1007             if (imageListener) {
 1008                 imageListener->setImage(previmg, scale, params->crop);
 1009             }
 1010         }
 1011 
 1012         if (imageListener)
 1013             // TODO: The WB tool should be advertised too in order to get the AutoWB's temp and green values
 1014         {
 1015             imageListener->imageReady(params->crop);
 1016         }
 1017 
 1018         readyphase++;
 1019 
 1020         if (hListener) {
 1021             updateLRGBHistograms();
 1022             hListener->histogramChanged(histRed, histGreen, histBlue, histLuma, histToneCurve, histLCurve, histCCurve, /*histCLurve, histLLCurve,*/ histLCAM, histCCAM, histRedRaw, histGreenRaw, histBlueRaw, histChroma, histLRETI);
 1023         }
 1024     }
 1025     if (orig_prev != oprevi) {
 1026         delete oprevi;
 1027         oprevi = nullptr;
 1028     }
 1029 
 1030 
 1031 }
 1032 
 1033 
 1034 void ImProcCoordinator::freeAll()
 1035 {
 1036 
 1037     if (allocated) {
 1038         if (orig_prev != oprevi) {
 1039             delete oprevi;
 1040         }
 1041 
 1042         oprevi    = nullptr;
 1043         delete orig_prev;
 1044         orig_prev = nullptr;
 1045         delete oprevl;
 1046         oprevl    = nullptr;
 1047         delete nprevl;
 1048         nprevl    = nullptr;
 1049 
 1050         if (ncie) {
 1051             delete ncie;
 1052         }
 1053 
 1054         ncie      = nullptr;
 1055 
 1056         if (imageListener) {
 1057             imageListener->delImage(previmg);
 1058         } else {
 1059             delete previmg;
 1060         }
 1061 
 1062         delete workimg;
 1063 
 1064     }
 1065 
 1066     allocated = false;
 1067 }
 1068 
 1069 /** @brief Handles image buffer (re)allocation and trigger sizeChanged of SizeListener[s]
 1070  * If the scale change, this method will free all buffers and reallocate ones of the new size.
 1071  * It will then tell to the SizeListener that size has changed (sizeChanged)
 1072  *
 1073  * @param prevscale New Preview's scale.
 1074  */
 1075 void ImProcCoordinator::setScale(int prevscale)
 1076 {
 1077 
 1078     tr = getCoarseBitMask(params->coarse);
 1079 
 1080     int nW, nH;
 1081     imgsrc->getFullSize(fw, fh, tr);
 1082 
 1083     prevscale++;
 1084 
 1085     do {
 1086         prevscale--;
 1087         PreviewProps pp(0, 0, fw, fh, prevscale);
 1088         imgsrc->getSize(pp, nW, nH);
 1089     } while (nH < 400 && prevscale > 1 && (nW * nH < 1000000));  // sctually hardcoded values, perhaps a better choice is possible
 1090 
 1091     if (nW != pW || nH != pH) {
 1092 
 1093         freeAll();
 1094 
 1095         pW = nW;
 1096         pH = nH;
 1097 
 1098         orig_prev = new Imagefloat(pW, pH);
 1099         oprevi = orig_prev;
 1100         oprevl = new LabImage(pW, pH);
 1101         nprevl = new LabImage(pW, pH);
 1102         //ncie is only used in ImProcCoordinator::updatePreviewImage, it will be allocated on first use and deleted if not used anymore
 1103         previmg = new Image8(pW, pH);
 1104         workimg = new Image8(pW, pH);
 1105 
 1106         allocated = true;
 1107     }
 1108 
 1109     scale = prevscale;
 1110     resultValid = false;
 1111     fullw = fw;
 1112     fullh = fh;
 1113 
 1114     if (!sizeListeners.empty())
 1115         for (size_t i = 0; i < sizeListeners.size(); i++) {
 1116             sizeListeners[i]->sizeChanged(fullw, fullh, fw, fh);
 1117         }
 1118 }
 1119 
 1120 
 1121 void ImProcCoordinator::updateLRGBHistograms()
 1122 {
 1123 
 1124     int x1, y1, x2, y2;
 1125     params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
 1126 
 1127 #ifdef _OPENMP
 1128     #pragma omp parallel sections
 1129 #endif
 1130     {
 1131 #ifdef _OPENMP
 1132         #pragma omp section
 1133 #endif
 1134         {
 1135             histChroma.clear();
 1136 
 1137             for (int i = y1; i < y2; i++)
 1138                 for (int j = x1; j < x2; j++)
 1139                 {
 1140                     histChroma[(int)(sqrtf(SQR(nprevl->a[i][j]) + SQR(nprevl->b[i][j])) / 188.f)]++;      //188 = 48000/256
 1141                 }
 1142         }
 1143 #ifdef _OPENMP
 1144         #pragma omp section
 1145 #endif
 1146         {
 1147             histLuma.clear();
 1148 
 1149             for (int i = y1; i < y2; i++)
 1150                 for (int j = x1; j < x2; j++)
 1151                 {
 1152                     histLuma[(int)(nprevl->L[i][j] / 128.f)]++;
 1153                 }
 1154         }
 1155 #ifdef _OPENMP
 1156         #pragma omp section
 1157 #endif
 1158         {
 1159             histRed.clear();
 1160             histGreen.clear();
 1161             histBlue.clear();
 1162 
 1163             for (int i = y1; i < y2; i++)
 1164             {
 1165                 int ofs = (i * pW + x1) * 3;
 1166 
 1167                 for (int j = x1; j < x2; j++) {
 1168                     int r = workimg->data[ofs++];
 1169                     int g = workimg->data[ofs++];
 1170                     int b = workimg->data[ofs++];
 1171 
 1172                     histRed[r]++;
 1173                     histGreen[g]++;
 1174                     histBlue[b]++;
 1175                 }
 1176             }
 1177         }
 1178     }
 1179 
 1180 }
 1181 
 1182 void ImProcCoordinator::progress(Glib::ustring str, int pr)
 1183 {
 1184 
 1185     /*  if (plistener) {
 1186         plistener->setProgressStr (str);
 1187         plistener->setProgress ((double)pr / 100.0);
 1188       }*/
 1189 }
 1190 
 1191 bool ImProcCoordinator::getAutoWB(double& temp, double& green, double equal, double tempBias)
 1192 {
 1193 
 1194     if (imgsrc) {
 1195         if (lastAwbEqual != equal || lastAwbTempBias != tempBias) {
 1196 // Issue 2500            MyMutex::MyLock lock(minit);  // Also used in crop window
 1197             double rm, gm, bm;
 1198             imgsrc->getAutoWBMultipliers(rm, gm, bm);
 1199 
 1200             if (rm != -1) {
 1201                 autoWB.update(rm, gm, bm, equal, tempBias);
 1202                 lastAwbEqual = equal;
 1203                 lastAwbTempBias = tempBias;
 1204             } else {
 1205                 lastAwbEqual = -1.;
 1206                 autoWB.useDefaults(equal);
 1207                 lastAwbTempBias = 0.0;
 1208             }
 1209         }
 1210 
 1211         temp = autoWB.getTemp();
 1212         green = autoWB.getGreen();
 1213         return true;
 1214     } else {
 1215         //temp = autoWB.getTemp();
 1216         temp = -1.0;
 1217         green = -1.0;
 1218         return false;
 1219     }
 1220 }
 1221 
 1222 void ImProcCoordinator::getCamWB(double& temp, double& green)
 1223 {
 1224 
 1225     if (imgsrc) {
 1226         temp = imgsrc->getWB().getTemp();
 1227         green = imgsrc->getWB().getGreen();
 1228     }
 1229 }
 1230 
 1231 void ImProcCoordinator::getSpotWB(int x, int y, int rect, double& temp, double& tgreen)
 1232 {
 1233 
 1234     ColorTemp ret;
 1235 
 1236     {
 1237         MyMutex::MyLock lock(mProcessing);
 1238         std::vector<Coord2D> points, red, green, blue;
 1239 
 1240         for (int i = y - rect; i <= y + rect; i++)
 1241             for (int j = x - rect; j <= x + rect; j++) {
 1242                 points.push_back(Coord2D(j, i));
 1243             }
 1244 
 1245         ipf.transCoord(fw, fh, points, red, green, blue);
 1246 
 1247         int tr = getCoarseBitMask(params->coarse);
 1248 
 1249         ret = imgsrc->getSpotWB(red, green, blue, tr, params->wb.equal);
 1250         currWB = ColorTemp(params->wb.temperature, params->wb.green, params->wb.equal, params->wb.method);
 1251         //double rr,gg,bb;
 1252         //currWB.getMultipliers(rr,gg,bb);
 1253 
 1254     } // end of mutex lockong
 1255 
 1256     if (ret.getTemp() > 0) {
 1257         temp = ret.getTemp();
 1258         tgreen = ret.getGreen();
 1259     } else {
 1260         temp = currWB.getTemp();
 1261         tgreen = currWB.getGreen();
 1262     }
 1263 }
 1264 
 1265 bool ImProcCoordinator::getFilmNegativeExponents(int xA, int yA, int xB, int yB, std::array<float, 3>& newExps)
 1266 {
 1267     MyMutex::MyLock lock(mProcessing);
 1268 
 1269     const auto xlate =
 1270         [this](int x, int y) -> Coord2D
 1271         {
 1272             const std::vector<Coord2D> points = {Coord2D(x, y)};
 1273 
 1274             std::vector<Coord2D> red;
 1275             std::vector<Coord2D> green;
 1276             std::vector<Coord2D> blue;
 1277             ipf.transCoord(fw, fh, points, red, green, blue);
 1278 
 1279             return green[0];
 1280         };
 1281 
 1282     const int tr = getCoarseBitMask(params->coarse);
 1283 
 1284     const Coord2D p1 = xlate(xA, yA);
 1285     const Coord2D p2 = xlate(xB, yB);
 1286 
 1287     return imgsrc->getFilmNegativeExponents(p1, p2, tr, params->filmNegative, newExps);
 1288 }
 1289 
 1290 void ImProcCoordinator::getAutoCrop(double ratio, int &x, int &y, int &w, int &h)
 1291 {
 1292 
 1293     MyMutex::MyLock lock(mProcessing);
 1294 
 1295     LensCorrection *pLCPMap = nullptr;
 1296 
 1297     if (params->lensProf.useLcp() && imgsrc->getMetaData()->getFocalLen() > 0) {
 1298         const std::shared_ptr<LCPProfile> pLCPProf = LCPStore::getInstance()->getProfile(params->lensProf.lcpFile);
 1299 
 1300         if (pLCPProf) pLCPMap = new LCPMapper(pLCPProf, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(),
 1301                                                   0, false, params->lensProf.useDist, fullw, fullh, params->coarse, imgsrc->getRotateDegree());
 1302     }
 1303 
 1304     double fillscale = ipf.getTransformAutoFill(fullw, fullh, pLCPMap);
 1305 
 1306     if (ratio > 0) {
 1307         w = fullw * fillscale;
 1308         h = w / ratio;
 1309 
 1310         if (h > fullh * fillscale) {
 1311             h = fullh * fillscale;
 1312             w = h * ratio;
 1313         }
 1314     } else {
 1315         w = fullw * fillscale;
 1316         h = fullh * fillscale;
 1317     }
 1318 
 1319     x = (fullw - w) / 2;
 1320     y = (fullh - h) / 2;
 1321 }
 1322 
 1323 void ImProcCoordinator::setMonitorProfile(const Glib::ustring& profile, RenderingIntent intent)
 1324 {
 1325     monitorProfile = profile;
 1326     monitorIntent = intent;
 1327 }
 1328 
 1329 void ImProcCoordinator::getMonitorProfile(Glib::ustring& profile, RenderingIntent& intent) const
 1330 {
 1331     profile = monitorProfile;
 1332     intent = monitorIntent;
 1333 }
 1334 
 1335 void ImProcCoordinator::setSoftProofing(bool softProof, bool gamutCheck)
 1336 {
 1337     this->softProof = softProof;
 1338     this->gamutCheck = gamutCheck;
 1339 }
 1340 
 1341 void ImProcCoordinator::getSoftProofing(bool &softProof, bool &gamutCheck)
 1342 {
 1343     softProof = this->softProof;
 1344     gamutCheck = this->gamutCheck;
 1345 }
 1346 
 1347 void ImProcCoordinator::setSharpMask (bool sharpMask)
 1348 {
 1349     this->sharpMask = sharpMask;
 1350 }
 1351 
 1352 void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool apply_wb)
 1353 {
 1354 
 1355     MyMutex::MyLock lock(mProcessing);
 1356 
 1357     int fW, fH;
 1358 
 1359     int tr = getCoarseBitMask(params->coarse);
 1360 
 1361     imgsrc->getFullSize(fW, fH, tr);
 1362     PreviewProps pp(0, 0, fW, fH, 1);
 1363     ProcParams ppar = *params;
 1364     ppar.toneCurve.hrenabled = false;
 1365     ppar.icm.inputProfile = "(none)";
 1366     Imagefloat* im = new Imagefloat(fW, fH);
 1367     imgsrc->preprocess(ppar.raw, ppar.lensProf, ppar.coarse);
 1368     double dummy = 0.0;
 1369     imgsrc->demosaic(ppar.raw, false, dummy);
 1370     ColorTemp currWB = ColorTemp(params->wb.temperature, params->wb.green, params->wb.equal, params->wb.method);
 1371 
 1372     if (params->wb.method == "Camera") {
 1373         currWB = imgsrc->getWB();
 1374     } else if (params->wb.method == "Auto") {
 1375         if (lastAwbEqual != params->wb.equal || lastAwbTempBias != params->wb.tempBias) {
 1376             double rm, gm, bm;
 1377             imgsrc->getAutoWBMultipliers(rm, gm, bm);
 1378 
 1379             if (rm != -1.) {
 1380                 autoWB.update(rm, gm, bm, params->wb.equal, params->wb.tempBias);
 1381                 lastAwbEqual = params->wb.equal;
 1382                 lastAwbTempBias = params->wb.tempBias;
 1383             } else {
 1384                 lastAwbEqual = -1.;
 1385                 lastAwbTempBias = 0.0;
 1386                 autoWB.useDefaults(params->wb.equal);
 1387             }
 1388         }
 1389 
 1390         currWB = autoWB;
 1391     }
 1392 
 1393     if (!apply_wb) {
 1394         currWB = ColorTemp(); // = no white balance
 1395     }
 1396 
 1397     imgsrc->getImage(currWB, tr, im, pp, ppar.toneCurve, ppar.raw);
 1398     ImProcFunctions ipf(&ppar, true);
 1399 
 1400     if (ipf.needsTransform()) {
 1401         Imagefloat* trImg = new Imagefloat(fW, fH);
 1402         ipf.transform(im, trImg, 0, 0, 0, 0, fW, fH, fW, fH,
 1403                       imgsrc->getMetaData(), imgsrc->getRotateDegree(), true);
 1404         delete im;
 1405         im = trImg;
 1406     }
 1407 
 1408     if (params->crop.enabled) {
 1409         Imagefloat *tmpim = new Imagefloat(params->crop.w, params->crop.h);
 1410         int cx = params->crop.x;
 1411         int cy = params->crop.y;
 1412         int cw = params->crop.w;
 1413         int ch = params->crop.h;
 1414 #ifdef _OPENMP
 1415         #pragma omp parallel for
 1416 #endif
 1417 
 1418         for (int i = cy; i < cy + ch; i++) {
 1419             for (int j = cx; j < cx + cw; j++) {
 1420                 tmpim->r(i - cy, j - cx) = im->r(i, j);
 1421                 tmpim->g(i - cy, j - cx) = im->g(i, j);
 1422                 tmpim->b(i - cy, j - cx) = im->b(i, j);
 1423             }
 1424         }
 1425 
 1426         delete im;
 1427         im = tmpim;
 1428     }
 1429 
 1430     // image may contain out of range samples, clip them to avoid wrap-arounds
 1431 #ifdef _OPENMP
 1432     #pragma omp parallel for
 1433 #endif
 1434 
 1435     for (int i = 0; i < im->getHeight(); i++) {
 1436         for (int j = 0; j < im->getWidth(); j++) {
 1437             im->r(i, j) = CLIP(im->r(i, j));
 1438             im->g(i, j) = CLIP(im->g(i, j));
 1439             im->b(i, j) = CLIP(im->b(i, j));
 1440         }
 1441     }
 1442 
 1443     int imw, imh;
 1444     double tmpScale = ipf.resizeScale(params.get(), fW, fH, imw, imh);
 1445 
 1446     if (tmpScale != 1.0) {
 1447         Imagefloat* tempImage = new Imagefloat(imw, imh);
 1448         ipf.resize(im, tempImage, tmpScale);
 1449         delete im;
 1450         im = tempImage;
 1451     }
 1452 
 1453     im->setMetadata(imgsrc->getMetaData()->getRootExifData());
 1454 
 1455     im->saveTIFF(fname, 16, false, true);
 1456     delete im;
 1457 
 1458     if (plistener) {
 1459         plistener->setProgressState(false);
 1460     }
 1461 
 1462     //im->saveJPEG (fname, 85);
 1463 }
 1464 
 1465 void ImProcCoordinator::stopProcessing()
 1466 {
 1467 
 1468     updaterThreadStart.lock();
 1469 
 1470     if (updaterRunning && thread) {
 1471         changeSinceLast = 0;
 1472         thread->join();
 1473     }
 1474 
 1475     updaterThreadStart.unlock();
 1476 }
 1477 
 1478 void ImProcCoordinator::startProcessing()
 1479 {
 1480 
 1481 #undef THREAD_PRIORITY_NORMAL
 1482 
 1483     if (!destroying) {
 1484         if (!updaterRunning) {
 1485             updaterThreadStart.lock();
 1486             thread = nullptr;
 1487             updaterRunning = true;
 1488             updaterThreadStart.unlock();
 1489 
 1490             //batchThread->yield(); //the running batch should wait other threads to avoid conflict
 1491 
 1492             thread = Glib::Thread::create(sigc::mem_fun(*this, &ImProcCoordinator::process), 0, true, true, Glib::THREAD_PRIORITY_NORMAL);
 1493 
 1494         }
 1495     }
 1496 }
 1497 
 1498 void ImProcCoordinator::startProcessing(int changeCode)
 1499 {
 1500     paramsUpdateMutex.lock();
 1501     changeSinceLast |= changeCode;
 1502     paramsUpdateMutex.unlock();
 1503 
 1504     startProcessing();
 1505 }
 1506 
 1507 void ImProcCoordinator::process()
 1508 {
 1509     if (plistener) {
 1510         plistener->setProgressState(true);
 1511     }
 1512 
 1513     paramsUpdateMutex.lock();
 1514 
 1515     while (changeSinceLast) {
 1516         const bool panningRelatedChange =
 1517                params->toneCurve.isPanningRelatedChange(nextParams->toneCurve)
 1518             || params->labCurve != nextParams->labCurve
 1519             || params->localContrast != nextParams->localContrast
 1520             || params->rgbCurves != nextParams->rgbCurves
 1521             || params->colorToning != nextParams->colorToning
 1522             || params->vibrance != nextParams->vibrance
 1523             || params->wb.isPanningRelatedChange(nextParams->wb)
 1524             || params->colorappearance != nextParams->colorappearance
 1525             || params->epd != nextParams->epd
 1526             || params->fattal != nextParams->fattal
 1527             || params->sh != nextParams->sh
 1528             || params->crop != nextParams->crop
 1529             || params->coarse != nextParams->coarse
 1530             || params->commonTrans != nextParams->commonTrans
 1531             || params->rotate != nextParams->rotate
 1532             || params->distortion != nextParams->distortion
 1533             || params->lensProf != nextParams->lensProf
 1534             || params->perspective != nextParams->perspective
 1535             || params->gradient != nextParams->gradient
 1536             || params->pcvignette != nextParams->pcvignette
 1537             || params->cacorrection != nextParams->cacorrection
 1538             || params->vignetting != nextParams->vignetting
 1539             || params->chmixer != nextParams->chmixer
 1540             || params->blackwhite != nextParams->blackwhite
 1541             || params->icm != nextParams->icm
 1542             || params->hsvequalizer != nextParams->hsvequalizer
 1543             || params->filmSimulation != nextParams->filmSimulation
 1544             || params->softlight != nextParams->softlight
 1545             || params->raw != nextParams->raw
 1546             || params->retinex != nextParams->retinex
 1547             || params->wavelet != nextParams->wavelet
 1548             || params->dirpyrequalizer != nextParams->dirpyrequalizer
 1549             || params->dehaze != nextParams->dehaze;
 1550 
 1551         *params = *nextParams;
 1552         int change = changeSinceLast;
 1553         changeSinceLast = 0;
 1554         paramsUpdateMutex.unlock();
 1555 
 1556         // M_VOID means no update, and is a bit higher that the rest
 1557         if (change & (M_VOID - 1)) {
 1558             updatePreviewImage(change, panningRelatedChange);
 1559         }
 1560 
 1561         paramsUpdateMutex.lock();
 1562     }
 1563 
 1564     paramsUpdateMutex.unlock();
 1565     updaterRunning = false;
 1566 
 1567     if (plistener) {
 1568         plistener->setProgressState(false);
 1569     }
 1570 }
 1571 
 1572 ProcParams* ImProcCoordinator::beginUpdateParams()
 1573 {
 1574     paramsUpdateMutex.lock();
 1575 
 1576     return nextParams.get();
 1577 }
 1578 
 1579 void ImProcCoordinator::endUpdateParams(ProcEvent change)
 1580 {
 1581     int action = RefreshMapper::getInstance()->getAction(change);
 1582     endUpdateParams(action);
 1583 }
 1584 
 1585 void ImProcCoordinator::endUpdateParams(int changeFlags)
 1586 {
 1587     changeSinceLast |= changeFlags;
 1588 
 1589     paramsUpdateMutex.unlock();
 1590     startProcessing();
 1591 }
 1592 
 1593 bool ImProcCoordinator::getHighQualComputed()
 1594 {
 1595     // this function may only be called from detail windows
 1596     if (!highQualityComputed) {
 1597         if (options.prevdemo == PD_Sidecar) {
 1598             // we already have high quality preview
 1599             setHighQualComputed();
 1600         } else {
 1601             for (size_t i = 0; i < crops.size() - 1; ++i) { // -1, because last entry is the freshly created detail window
 1602                 if (crops[i]->get_skip() == 1) {   // there is at least one crop with skip == 1 => we already have high quality preview
 1603                     setHighQualComputed();
 1604                     break;
 1605                 }
 1606             }
 1607         }
 1608     }
 1609 
 1610     return highQualityComputed;
 1611 }
 1612 
 1613 void ImProcCoordinator::setHighQualComputed()
 1614 {
 1615     highQualityComputed = true;
 1616 }
 1617 
 1618 }