/* * This file is part of RawTherapee. * * Copyright (c) 2004-2010 Gabor Horvath * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RawTherapee is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #undef THREAD_PRIORITY_NORMAL #define CLIP(a) ((a)>0?((a)<65535?(a):65535):0) namespace rtengine { IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* pl, bool tunnelMetaData) { errorCode = 0; ProcessingJobImpl* job = (ProcessingJobImpl*) pjob; if (pl) { pl->setProgressStr ("PROGRESSBAR_PROCESSING"); pl->setProgress (0.0); } InitialImage* ii = job->initialImage; if (!ii) { ii = InitialImage::load (job->fname, job->isRaw, &errorCode); if (errorCode) { delete job; return NULL; } } procparams::ProcParams& params = job->pparams; // aquire image from imagesource ImageSource* imgsrc = ii->getImageSource (); int tr = TR_NONE; if (params.coarse.rotate==90) tr |= TR_R90; if (params.coarse.rotate==180) tr |= TR_R180; if (params.coarse.rotate==270) tr |= TR_R270; if (params.coarse.hflip) tr |= TR_HFLIP; if (params.coarse.vflip) tr |= TR_VFLIP; int fw, fh; imgsrc->getFullSize (fw, fh, tr); // check the crop params if (params.crop.x > fw || params.crop.y > fh) { // the crop is completely out of the image, so we disable the crop params.crop.enabled = false; // and we set the values to the defaults params.crop.x = 0; params.crop.y = 0; params.crop.w = fw; params.crop.h = fh; } else { if ((params.crop.x + params.crop.w) > fw) { // crop overflow in the width dimension ; we trim it params.crop.w = fw-params.crop.x; } if ((params.crop.y + params.crop.h) > fh) { // crop overflow in the height dimension ; we trim it params.crop.h = fh-params.crop.y; } } ImProcFunctions ipf (¶ms, true); // set the color temperature ColorTemp currWB = ColorTemp (params.wb.temperature, params.wb.green); if (params.wb.method=="Camera") currWB = imgsrc->getWB (); else if (params.wb.method=="Auto") currWB = imgsrc->getAutoWB (); PreviewProps pp (0, 0, fw, fh, 1); imgsrc->preprocess( params.raw ); if (pl) pl->setProgress (0.20); imgsrc->demosaic( params.raw ); if (pl) pl->setProgress (0.40); Imagefloat* baseImg = new Imagefloat (fw, fh); imgsrc->getImage (currWB, tr, baseImg, pp, params.hlrecovery, params.icm, params.raw); if (pl) pl->setProgress (0.45); // perform first analysis LUTu hist16 (65536); ipf.firstAnalysis (baseImg, ¶ms, hist16, imgsrc->getGamma()); // perform transform (excepted resizing) if (ipf.needsTransform()) { Imagefloat* trImg = new Imagefloat (fw, fh); ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh); delete baseImg; baseImg = trImg; } // update blurmap SHMap* shmap = NULL; if (params.sh.enabled) { shmap = new SHMap (fw, fh, true); double radius = sqrt (double(fw*fw+fh*fh)) / 2.0; double shradius = params.sh.radius; if (!params.sh.hq) shradius *= radius / 1800.0; shmap->update (baseImg, shradius, ipf.lumimul, params.sh.hq, 1); } // RGB processing //!!!// auto exposure!!! double br = params.toneCurve.expcomp; int bl = params.toneCurve.black; if (params.toneCurve.autoexp) { LUTu aehist; int aehistcompr; imgsrc->getAutoExpHistogram (aehist, aehistcompr); ipf.getAutoExp (aehist, aehistcompr, imgsrc->getDefGain(), params.toneCurve.clip, br, bl); } LUTf curve1 (65536,0); LUTf curve2 (65536,0); LUTf curve (65536,0); LUTf satcurve (65536,0); LUTu dummy; CurveFactory::complexCurve (br, bl/65535.0, params.toneCurve.hlcompr, params.toneCurve.hlcomprthresh, params.toneCurve.shcompr, params.toneCurve.brightness, params.toneCurve.contrast, imgsrc->getGamma(), true, params.toneCurve.curve, hist16, dummy, curve1, curve2, curve, dummy); LabImage* labView = new LabImage (fw,fh); ipf.rgbProc (baseImg, labView, curve1, curve2, curve, shmap, params.toneCurve.saturation); if (shmap) delete shmap; if (pl) pl->setProgress (0.5); // luminance histogram update hist16.clear(); for (int i=0; iL[i][j])))]++; // luminance processing CurveFactory::complexLCurve (params.labCurve.brightness, params.labCurve.contrast, params.labCurve.lcurve, hist16, hist16, curve, dummy, 1); ipf.luminanceCurve (labView, labView, curve); CurveFactory::complexsgnCurve (params.labCurve.saturation, params.labCurve.enable_saturationlimiter, params.labCurve.saturationlimit, \ params.labCurve.acurve, params.labCurve.bcurve, curve1, curve2, satcurve, 1); ipf.chrominanceCurve (labView, labView, curve1, curve2, satcurve); ipf.impulsedenoise (labView); ipf.defringe (labView); //ipf.lumadenoise (labView, buffer); ipf.dirpyrdenoise (labView); if (params.sharpening.enabled) { float** buffer = new float*[fh]; for (int i=0; isetProgress (0.60); // crop and convert to rgb16 int cx = 0, cy = 0, cw = labView->W, ch = labView->H; if (params.crop.enabled) { cx = params.crop.x; cy = params.crop.y; cw = params.crop.w; ch = params.crop.h; } if(params.icm.gamma != "default") { // if select gamma output between BT709, sRGB, linear, low, high, 2.2 , 1.8 Image16* readyImg = ipf.lab2rgb16b (labView, cx, cy, cw, ch, params.icm.output, params.icm.working, params.icm.gamma); delete labView; if (pl) pl->setProgress (0.70); // get the resize parameters int refw, refh; double tmpScale; if (params.resize.enabled) { if (params.crop.enabled && params.resize.appliesTo == "Cropped area") { // the resize values applies to the crop dimensions refw = cw; refh = ch; } else { // the resize values applies to the image dimensions // if a crop exists, it will be resized to the calculated scale refw = fw; refh = fh; } switch(params.resize.dataspec) { case (1): // Width tmpScale = (double)params.resize.width/(double)refw; break; case (2): // Height tmpScale = (double)params.resize.height/(double)refh; break; case (3): // FitBox if ((double)refw/(double)refh > (double)params.resize.width/(double)params.resize.height) { tmpScale = (double)params.resize.width/(double)refw; } else { tmpScale = (double)params.resize.height/(double)refh; } break; default: // Scale tmpScale = params.resize.scale; break; } // resize image if (fabs(tmpScale-1.0)>1e-5) { int imw, imh; if (params.crop.enabled && params.resize.appliesTo == "Full image") { imw = cw; imh = ch; } else { imw = refw; imh = refh; } imw = (int)( (double)imw * tmpScale + 0.5 ); imh = (int)( (double)imh * tmpScale + 0.5 ); Image16* tempImage = new Image16 (imw, imh); ipf.resize (readyImg, tempImage, tmpScale); delete readyImg; readyImg = tempImage; } } if (tunnelMetaData) readyImg->setMetadata (ii->getMetaData()->getExifData ()); else readyImg->setMetadata (ii->getMetaData()->getExifData (), params.exif, params.iptc); ProfileContent pc; Glib::ustring chpro; int present_space[7]={0,0,0,0,0,0,0}; std::vector opnames = rtengine::getOutputProfiles (); //test if files are in system for (int j=0; j<7;j++) { //one can modify "option" [Color Management] to adapt name of profile ih there are different for windows, MacOS, Linux ?? if(j==0) chpro=options.rtSettings.prophoto; else if(j==1) chpro=options.rtSettings.adobe; else if(j==2) chpro=options.rtSettings.widegamut; else if(j==3) chpro=options.rtSettings.beta; else if(j==4) chpro=options.rtSettings.best; else if(j==5) chpro=options.rtSettings.bruce; else if(j==6) chpro=options.rtSettings.srgb; for (int i=0; isetProgressStr ("Missing file..");pl->setProgress (0.0);}// display file not present: not very good display information...!! } //choose output profile if(params.icm.working=="ProPhoto" && present_space[0]==1) params.icm.output=options.rtSettings.prophoto; else if(params.icm.working=="Adobe RGB" && present_space[1]==1) params.icm.output=options.rtSettings.adobe; else if(params.icm.working=="WideGamut" && present_space[2]==1) params.icm.output=options.rtSettings.widegamut; else if(params.icm.working=="Beta RGB" && present_space[3]==1) params.icm.output=options.rtSettings.beta; else if(params.icm.working=="BestRGB" && present_space[4]==1) params.icm.output=options.rtSettings.best; else if(params.icm.working=="BruceRGB" && present_space[5]==1) params.icm.output=options.rtSettings.bruce; else params.icm.output=options.rtSettings.srgb; //if not found or choice=srgb if (params.icm.output.compare (0, 6, "No ICM") && params.icm.output!="") pc = iccStore->getContent (params.icm.output); readyImg->setOutputProfile (pc.data, pc.length); delete baseImg; if (!job->initialImage) ii->decreaseRef (); delete job; if (pl) pl->setProgress (0.75); return readyImg; } else {//if default mode : profil = selection by choice in list (Prophoto.icm, sRGB.icm, etc., etc.) , gamma = gamma of profile Image16* readyImg = ipf.lab2rgb16 (labView, cx, cy, cw, ch, params.icm.output); delete labView; if (pl) pl->setProgress (0.70); // get the resize parameters int refw, refh; double tmpScale; if (params.resize.enabled) { if (params.crop.enabled && params.resize.appliesTo == "Cropped area") { // the resize values applies to the crop dimensions refw = cw; refh = ch; } else { // the resize values applies to the image dimensions // if a crop exists, it will be resized to the calculated scale refw = fw; refh = fh; } switch(params.resize.dataspec) { case (1): // Width tmpScale = (double)params.resize.width/(double)refw; break; case (2): // Height tmpScale = (double)params.resize.height/(double)refh; break; case (3): // FitBox if ((double)refw/(double)refh > (double)params.resize.width/(double)params.resize.height) { tmpScale = (double)params.resize.width/(double)refw; } else { tmpScale = (double)params.resize.height/(double)refh; } break; default: // Scale tmpScale = params.resize.scale; break; } // resize image if (fabs(tmpScale-1.0)>1e-5) { int imw, imh; if (params.crop.enabled && params.resize.appliesTo == "Full image") { imw = cw; imh = ch; } else { imw = refw; imh = refh; } imw = (int)( (double)imw * tmpScale + 0.5 ); imh = (int)( (double)imh * tmpScale + 0.5 ); Image16* tempImage = new Image16 (imw, imh); ipf.resize (readyImg, tempImage, tmpScale); delete readyImg; readyImg = tempImage; } } if (tunnelMetaData) readyImg->setMetadata (ii->getMetaData()->getExifData ()); else readyImg->setMetadata (ii->getMetaData()->getExifData (), params.exif, params.iptc); ProfileContent pc; if (params.icm.output.compare (0, 6, "No ICM") && params.icm.output!="") pc = iccStore->getContent (params.icm.output); readyImg->setOutputProfile (pc.data, pc.length); delete baseImg; if (!job->initialImage) ii->decreaseRef (); delete job; if (pl) pl->setProgress (0.75); return readyImg; } } void batchProcessingThread (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData) { ProcessingJob* currentJob = job; while (currentJob) { int errorCode; IImage16* img = processImage (currentJob, errorCode, bpl, tunnelMetaData); if (errorCode) bpl->error ("Can not load input image."); currentJob = bpl->imageReady (img); } } void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData) { if (bpl) Glib::Thread::create(sigc::bind(sigc::ptr_fun(batchProcessingThread), job, bpl, tunnelMetaData), 0, true, true, Glib::THREAD_PRIORITY_LOW); } }