469 lines
17 KiB
C++
469 lines
17 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "dcrop.h"
|
|
#include "curves.h"
|
|
#include "mytime.h"
|
|
#include "refreshmap.h"
|
|
#include "rt_math.h"
|
|
#include "colortemp.h"
|
|
|
|
#define SKIPS(a,b) ((a) / (b) + ((a) % (b) > 0))
|
|
|
|
namespace rtengine {
|
|
|
|
extern const Settings* settings;
|
|
|
|
Crop::Crop (ImProcCoordinator* parent)
|
|
: resizeCrop(NULL), transCrop(NULL), updating(false),
|
|
skip(10),cropw(-1), croph(-1), trafw(-1), trafh(-1),
|
|
borderRequested(32), cropAllocated(false),
|
|
cropImageListener(NULL), parent(parent)
|
|
{
|
|
parent->crops.push_back (this);
|
|
}
|
|
|
|
Crop::~Crop () {
|
|
|
|
cropMutex.lock ();
|
|
parent->mProcessing.lock ();
|
|
std::vector<Crop*>::iterator i = std::find (parent->crops.begin(), parent->crops.end(), this);
|
|
if (i!=parent->crops.end ())
|
|
parent->crops.erase (i);
|
|
|
|
freeAll ();
|
|
parent->mProcessing.unlock ();
|
|
cropMutex.unlock ();
|
|
}
|
|
|
|
void Crop::setListener (DetailedCropListener* il) {
|
|
// We can make reads in the IF, because the mProcessing lock is only needed for change
|
|
if (cropImageListener!=il) {
|
|
MyMutex::MyLock lock(cropMutex);
|
|
cropImageListener = il;
|
|
}
|
|
}
|
|
|
|
void Crop::update (int todo) {
|
|
MyMutex::MyLock lock(cropMutex);
|
|
|
|
ProcParams& params = parent->params;
|
|
|
|
// give possibility to the listener to modify crop window (as the full image dimensions are already known at this point)
|
|
int wx, wy, ww, wh, ws;
|
|
bool overrideWindow = false;
|
|
if (cropImageListener)
|
|
overrideWindow = cropImageListener->getWindow (wx, wy, ww, wh, ws);
|
|
|
|
// re-allocate sub-images and arrays if their dimensions changed
|
|
bool needsinitupdate = false;
|
|
if (!overrideWindow)
|
|
needsinitupdate = setCropSizes (rqcropx, rqcropy, rqcropw, rqcroph, skip, true);
|
|
else
|
|
needsinitupdate = setCropSizes (wx, wy, ww, wh, ws, true); // this set skip=ws
|
|
// it something has been reallocated, all processing steps have to be performed
|
|
if (needsinitupdate || (todo & M_HIGHQUAL))
|
|
todo = ALL;
|
|
|
|
// set improcfuncions' scale now that skip has been updated
|
|
parent->ipf.setScale (skip);
|
|
|
|
baseCrop = origCrop;
|
|
|
|
bool needstransform = parent->ipf.needsTransform();
|
|
|
|
if (todo & (M_INIT|M_LINDENOISE)) {
|
|
MyMutex::MyLock lock(parent->minit); // Also used in improccoord
|
|
|
|
int tr = TR_NONE;
|
|
if (params.coarse.rotate==90) tr |= TR_R90;
|
|
else if (params.coarse.rotate==180) tr |= TR_R180;
|
|
else if (params.coarse.rotate==270) tr |= TR_R270;
|
|
|
|
if (params.coarse.hflip) tr |= TR_HFLIP;
|
|
if (params.coarse.vflip) tr |= TR_VFLIP;
|
|
|
|
if (!needsinitupdate)
|
|
setCropSizes (rqcropx, rqcropy, rqcropw, rqcroph, skip, true);
|
|
PreviewProps pp (trafx, trafy, trafw*skip, trafh*skip, skip);
|
|
parent->imgsrc->getImage (parent->currWB, tr, origCrop, pp, params.hlrecovery, params.icm, params.raw );
|
|
//ColorTemp::CAT02 (origCrop, ¶ms) ;
|
|
|
|
//parent->imgsrc->convertColorSpace(origCrop, params.icm);
|
|
|
|
if (todo & M_LINDENOISE) {
|
|
if (skip==1 && params.dirpyrDenoise.enabled) {
|
|
parent->ipf.RGB_denoise(origCrop, origCrop, parent->imgsrc->isRAW(), /*Roffset,*/ params.dirpyrDenoise, params.defringe, parent->imgsrc->getDirPyrDenoiseExpComp());
|
|
}
|
|
}
|
|
parent->imgsrc->convertColorSpace(origCrop, params.icm, params.raw);
|
|
}
|
|
|
|
// transform
|
|
if ((!needstransform && transCrop) || (transCrop && (transCrop->width!=cropw || transCrop->height!=croph))) {
|
|
delete transCrop;
|
|
transCrop = NULL;
|
|
}
|
|
if (needstransform && !transCrop)
|
|
transCrop = new Imagefloat (cropw, croph);
|
|
if ((todo & M_TRANSFORM) && needstransform)
|
|
parent->ipf.transform (baseCrop, transCrop, cropx/skip, cropy/skip, trafx/skip, trafy/skip, SKIPS(parent->fw,skip), SKIPS(parent->fh,skip),
|
|
parent->imgsrc->getMetaData()->getFocalLen(), parent->imgsrc->getMetaData()->getFocalLen35mm(),
|
|
parent->imgsrc->getMetaData()->getFocusDist(), parent->imgsrc->getRotateDegree(), false);
|
|
if (transCrop)
|
|
baseCrop = transCrop;
|
|
|
|
// blurmap for shadow & highlights
|
|
if ((todo & M_BLURMAP) && params.sh.enabled) {
|
|
double radius = sqrt (double(SKIPS(parent->fw,skip)*SKIPS(parent->fw,skip)+SKIPS(parent->fh,skip)*SKIPS(parent->fh,skip))) / 2.0;
|
|
double shradius = params.sh.radius;
|
|
if (!params.sh.hq) shradius *= radius / 1800.0;
|
|
cshmap->update (baseCrop, shradius, parent->ipf.lumimul, params.sh.hq, skip);
|
|
cshmap->forceStat (parent->shmap->max_f, parent->shmap->min_f, parent->shmap->avg);
|
|
}
|
|
|
|
// shadows & highlights & tone curve & convert to cielab
|
|
/*int xref,yref;
|
|
xref=000;yref=000;
|
|
if (colortest && cropw>115 && croph>115)
|
|
for(int j=1;j<5;j++){
|
|
xref+=j*30;yref+=j*30;
|
|
if (settings->verbose) printf("before rgbProc RGB Xr%i Yr%i Skip=%d R=%f G=%f B=%f gamma=%f \n",xref,yref,skip,
|
|
baseCrop->r[(int)(xref/skip)][(int)(yref/skip)]/256,
|
|
baseCrop->g[(int)(xref/skip)][(int)(yref/skip)]/256,
|
|
baseCrop->b[(int)(xref/skip)][(int)(yref/skip)]/256,
|
|
parent->imgsrc->getGamma());
|
|
}*/
|
|
|
|
if (todo & M_RGBCURVE)
|
|
parent->ipf.rgbProc (baseCrop, laboCrop, parent->hltonecurve, parent->shtonecurve, parent->tonecurve, cshmap,
|
|
params.toneCurve.saturation, parent->rCurve, parent->gCurve, parent->bCurve, parent->customToneCurve1, parent->customToneCurve2 );
|
|
|
|
/*xref=000;yref=000;
|
|
if (colortest && cropw>115 && croph>115)
|
|
for(int j=1;j<5;j++){
|
|
xref+=j*30;yref+=j*30;
|
|
if (settings->verbose) {
|
|
printf("after rgbProc RGB Xr%i Yr%i Skip=%d R=%f G=%f B=%f \n",xref,yref,skip,
|
|
baseCrop->r[(int)(xref/skip)][(int)(yref/skip)]/256,
|
|
baseCrop->g[(int)(xref/skip)][(int)(yref/skip)]/256,
|
|
baseCrop->b[(int)(xref/skip)][(int)(yref/skip)]/256);
|
|
printf("after rgbProc Lab Xr%i Yr%i Skip=%d l=%f a=%f b=%f \n",xref,yref,skip,
|
|
laboCrop->L[(int)(xref/skip)][(int)(yref/skip)]/327,
|
|
laboCrop->a[(int)(xref/skip)][(int)(yref/skip)]/327,
|
|
laboCrop->b[(int)(xref/skip)][(int)(yref/skip)]/327);
|
|
}
|
|
}*/
|
|
|
|
// apply luminance operations
|
|
if (todo & (M_LUMINANCE+M_COLOR)) {
|
|
//I made a little change here. Rather than have luminanceCurve (and others) use in/out lab images, we can do more if we copy right here.
|
|
labnCrop->CopyFrom(laboCrop);
|
|
|
|
|
|
// parent->ipf.luminanceCurve (labnCrop, labnCrop, parent->lumacurve);
|
|
bool utili=true;
|
|
bool autili=true;
|
|
bool butili=true;
|
|
bool ccutili=true;
|
|
bool cclutili=true;
|
|
|
|
|
|
LUTu dummy;
|
|
parent->ipf.chromiLuminanceCurve (1,labnCrop, labnCrop, parent->chroma_acurve, parent->chroma_bcurve, parent->satcurve, parent->lhskcurve, parent->lumacurve, utili, autili, butili, ccutili,cclutili, dummy);
|
|
parent->ipf.vibrance (labnCrop);
|
|
if((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) parent->ipf.EPDToneMap(labnCrop,5,1);
|
|
// parent->ipf.EPDToneMap(labnCrop, 5, 1); //Go with much fewer than normal iterates for fast redisplay.
|
|
// for all treatments Defringe, Sharpening, Contrast detail , Microcontrast they are activated if "CIECAM" function are disabled
|
|
if (skip==1) {
|
|
if((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) {
|
|
parent->ipf.impulsedenoise (labnCrop);}
|
|
if((params.colorappearance.enabled && !settings->autocielab) ||(!params.colorappearance.enabled) ) {parent->ipf.defringe (labnCrop);}
|
|
parent->ipf.MLsharpen (labnCrop);
|
|
if((params.colorappearance.enabled && !settings->autocielab) || (!params.colorappearance.enabled)) {
|
|
parent->ipf.MLmicrocontrast (labnCrop);
|
|
parent->ipf.sharpening (labnCrop, (float**)cbuffer);
|
|
parent->ipf.dirpyrequalizer (labnCrop);
|
|
}
|
|
}
|
|
|
|
if(params.colorappearance.enabled){
|
|
float fnum = parent->imgsrc->getMetaData()->getFNumber ();// F number
|
|
float fiso = parent->imgsrc->getMetaData()->getISOSpeed () ;// ISO
|
|
float fspeed = parent->imgsrc->getMetaData()->getShutterSpeed () ;//speed
|
|
float fcomp = parent->imgsrc->getMetaData()->getExpComp ();//compensation + -
|
|
float adap2,adap;
|
|
double ada, ada2;
|
|
if(fnum < 0.3f || fiso < 5.f || fspeed < 0.00001f) {adap=adap=2000.f;ada=ada2=2000.;}//if no exif data or wrong
|
|
else {
|
|
float E_V = fcomp + log2 ((fnum*fnum) / fspeed / (fiso/100.f));
|
|
float expo2= params.toneCurve.expcomp;// exposure compensation in tonecurve ==> direct EV
|
|
E_V += expo2;
|
|
float expo1;//exposure raw white point
|
|
expo1=log2(params.raw.expos);//log2 ==>linear to EV
|
|
E_V += expo1;
|
|
adap2 = adap= powf(2.f, E_V-3.f);//cd / m2
|
|
ada=ada2=(double) adap;
|
|
//end calculation adaptation scene luminosity
|
|
}
|
|
|
|
int begh = 0, endh = labnCrop->H;
|
|
bool execsharp=false;
|
|
float d;
|
|
double dd;
|
|
if(skip==1) execsharp=true;
|
|
if(settings->ciecamfloat) {parent->ipf.ciecam_02float (cieCrop, adap, begh, endh, 1, 2,labnCrop, ¶ms,parent->customColCurve1,parent->customColCurve2,parent->customColCurve3, dummy, dummy, 5, 1,(float**)cbuffer, execsharp, d);
|
|
}
|
|
else {parent->ipf.ciecam_02 (cieCrop,ada, begh, endh, 1, 2, labnCrop, ¶ms,parent->customColCurve1,parent->customColCurve2,parent->customColCurve3, dummy, dummy, 5, 1,(float**)cbuffer, execsharp, dd);}
|
|
}
|
|
}
|
|
// switch back to rgb
|
|
parent->ipf.lab2monitorRgb (labnCrop, cropImg);
|
|
|
|
//parent->ipf.lab2monitorRgb (laboCrop, cropImg);
|
|
|
|
//cropImg = baseCrop->to8();
|
|
/*
|
|
// int xref,yref;
|
|
xref=000;yref=000;
|
|
if (colortest && cropw>115 && croph>115)
|
|
for(int j=1;j<5;j++){
|
|
xref+=j*30;yref+=j*30;
|
|
int rlin = (CurveFactory::igamma2((float)cropImg->data[3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))]/255.0) * 255.0);
|
|
int glin = (CurveFactory::igamma2((float)cropImg->data[3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))+1]/255.0) * 255.0);
|
|
int blin = (CurveFactory::igamma2((float)cropImg->data[3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))+2]/255.0) * 255.0);
|
|
|
|
printf("after lab2rgb RGB lab2 Xr%i Yr%i Skip=%d R=%d G=%d B=%d \n",xref,yref,skip,
|
|
rlin,glin,blin);
|
|
//cropImg->data[3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))],
|
|
//cropImg->data[(3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))+1)],
|
|
//cropImg->data[(3*((int)(xref/skip)*cropImg->width+(int)(yref/skip))+2)]);
|
|
//printf("after lab2rgb Lab lab2 Xr%i Yr%i Skip=%d l=%f a=%f b=%f \n",xref,yref,skip, labnCrop->L[(int)(xref/skip)][(int)(yref/skip)]/327,labnCrop->a[(int)(xref/skip)][(int)(yref/skip)]/327,labnCrop->b[(int)(xref/skip)][(int)(yref/skip)]/327);
|
|
printf("after lab2rgb Lab Xr%i Yr%i Skip=%d l=%f a=%f b=%f \n",xref,yref,skip,
|
|
labnCrop->L[(int)(xref/skip)][(int)(yref/skip)]/327,
|
|
labnCrop->a[(int)(xref/skip)][(int)(yref/skip)]/327,
|
|
labnCrop->b[(int)(xref/skip)][(int)(yref/skip)]/327)q;
|
|
}
|
|
*/
|
|
/*
|
|
if (colortest && cropImg->height>115 && cropImg->width>115) {//for testing
|
|
xref=000;yref=000;
|
|
printf("dcrop final R= %d G= %d B= %d \n",
|
|
cropImg->data[3*xref/(skip)*(cropImg->width+1)],
|
|
cropImg->data[3*xref/(skip)*(cropImg->width+1)+1],
|
|
cropImg->data[3*xref/(skip)*(cropImg->width+1)+2]);
|
|
}
|
|
*/
|
|
if (cropImageListener) {
|
|
// this in output space held in parallel to allow analysis like shadow/highlight
|
|
Glib::ustring outProfile=params.icm.output;
|
|
if (params.icm.output=="" || params.icm.output==ColorManagementParams::NoICMString) outProfile="sRGB";
|
|
Image8 *cropImgtrue = parent->ipf.lab2rgb (labnCrop, 0,0,cropw,croph, outProfile);
|
|
|
|
int finalW = rqcropw;
|
|
if (cropImg->getWidth()-leftBorder < finalW)
|
|
finalW = cropImg->getWidth()-leftBorder;
|
|
int finalH = rqcroph;
|
|
if (cropImg->getHeight()-upperBorder < finalH)
|
|
finalH = cropImg->getHeight()-upperBorder;
|
|
|
|
Image8* final = new Image8 (finalW, finalH);
|
|
Image8* finaltrue = new Image8 (finalW, finalH);
|
|
for (int i=0; i<finalH; i++) {
|
|
memcpy (final->data + 3*i*finalW, cropImg->data + 3*(i+upperBorder)*cropw + 3*leftBorder, 3*finalW);
|
|
memcpy (finaltrue->data + 3*i*finalW, cropImgtrue->data + 3*(i+upperBorder)*cropw + 3*leftBorder, 3*finalW);
|
|
}
|
|
cropImageListener->setDetailedCrop (final, finaltrue, params.icm, params.crop, rqcropx, rqcropy, rqcropw, rqcroph, skip);
|
|
delete final;
|
|
delete finaltrue;
|
|
delete cropImgtrue;
|
|
}
|
|
}
|
|
|
|
void Crop::freeAll () {
|
|
|
|
if (settings->verbose) printf ("freeallcrop starts %d\n", (int)cropAllocated);
|
|
|
|
if (cropAllocated) {
|
|
delete origCrop;
|
|
if (transCrop)
|
|
delete transCrop;
|
|
transCrop = NULL;
|
|
if (resizeCrop)
|
|
delete resizeCrop;
|
|
resizeCrop = NULL;
|
|
delete laboCrop;
|
|
delete cieCrop;
|
|
delete labnCrop;
|
|
delete cropImg;
|
|
delete cshmap;
|
|
// for (int i=0; i<croph; i++)
|
|
// delete [] cbuffer[i];
|
|
delete [] cbuf_real;
|
|
delete [] cbuffer;
|
|
}
|
|
cropAllocated = false;
|
|
}
|
|
|
|
bool Crop::setCropSizes (int rcx, int rcy, int rcw, int rch, int skip, bool internal) {
|
|
|
|
if (settings->verbose) printf ("setcropsizes before lock\n");
|
|
|
|
if (!internal)
|
|
cropMutex.lock ();
|
|
|
|
bool changed = false;
|
|
|
|
rqcropx = rcx;
|
|
rqcropy = rcy;
|
|
rqcropw = rcw;
|
|
rqcroph = rch;
|
|
|
|
// store and set requested crop size
|
|
int rqx1 = LIM(rqcropx,0,parent->fullw-1);
|
|
int rqy1 = LIM(rqcropy,0,parent->fullh-1);
|
|
int rqx2 = rqx1 + rqcropw - 1;
|
|
int rqy2 = rqy1 + rqcroph - 1;
|
|
rqx2 = LIM(rqx2,0,parent->fullw-1);
|
|
rqy2 = LIM(rqy2,0,parent->fullh-1);
|
|
|
|
this->skip = skip;
|
|
|
|
// add border, if possible
|
|
int bx1 = rqx1 - skip*borderRequested;
|
|
int by1 = rqy1 - skip*borderRequested;
|
|
int bx2 = rqx2 + skip*borderRequested;
|
|
int by2 = rqy2 + skip*borderRequested;
|
|
// clip it to fit into image area
|
|
bx1 = LIM(bx1,0,parent->fullw-1);
|
|
by1 = LIM(by1,0,parent->fullh-1);
|
|
bx2 = LIM(bx2,0,parent->fullw-1);
|
|
by2 = LIM(by2,0,parent->fullh-1);
|
|
int bw = bx2 - bx1 + 1;
|
|
int bh = by2 - by1 + 1;
|
|
|
|
// determine which part of the source image is required to compute the crop rectangle
|
|
int orx, ory, orw, orh;
|
|
ProcParams& params = parent->params;
|
|
parent->ipf.transCoord (parent->fw, parent->fh, bx1, by1, bw, bh, orx, ory, orw, orh);
|
|
|
|
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;
|
|
|
|
PreviewProps cp (orx, ory, orw, orh, skip);
|
|
int orW, orH;
|
|
parent->imgsrc->getSize (tr, cp, orW, orH);
|
|
|
|
int cw = SKIPS(bw,skip);
|
|
int ch = SKIPS(bh,skip);
|
|
|
|
leftBorder = SKIPS(rqx1-bx1,skip);
|
|
upperBorder = SKIPS(rqy1-by1,skip);
|
|
|
|
if (settings->verbose)
|
|
printf ("setsizes starts (%d, %d, %d, %d, %d, %d)\n", orW, orH, trafw, trafh,cw,ch);
|
|
|
|
if (cw!=cropw || ch!=croph || orW!=trafw || orH!=trafh) {
|
|
|
|
freeAll ();
|
|
|
|
cropw = cw;
|
|
croph = ch;
|
|
trafw = orW;
|
|
trafh = orH;
|
|
|
|
origCrop = new Imagefloat (trafw, trafh);
|
|
laboCrop = new LabImage (cropw, croph);
|
|
labnCrop = new LabImage (cropw, croph);
|
|
cropImg = new Image8 (cropw, croph);
|
|
cieCrop = new CieImage (cropw, croph);
|
|
|
|
cshmap = new SHMap (cropw, croph, true);
|
|
|
|
cbuffer = new float*[croph];
|
|
cbuf_real= new float[(croph+2)*cropw];
|
|
for (int i=0; i<croph; i++)
|
|
cbuffer[i] = cbuf_real+cropw*i+cropw;
|
|
|
|
resizeCrop = NULL;
|
|
transCrop = NULL;
|
|
|
|
cropAllocated = true;
|
|
|
|
changed = true;
|
|
}
|
|
|
|
cropx = bx1;
|
|
cropy = by1;
|
|
trafx = orx;
|
|
trafy = ory;
|
|
|
|
if (settings->verbose) printf ("setsizes ends\n");
|
|
|
|
if (!internal)
|
|
cropMutex.unlock ();
|
|
|
|
return changed;
|
|
}
|
|
|
|
// Try a simple, threadless update flag first
|
|
bool Crop::tryUpdate() {
|
|
bool needsFullUpdate = true;
|
|
|
|
// If there are more update request, the following WHILE will collect it
|
|
if (updating) {
|
|
needsNext = true;
|
|
needsFullUpdate = false;
|
|
} else updating = true;
|
|
|
|
return needsFullUpdate;
|
|
}
|
|
|
|
// Full update, should be called via thread
|
|
void Crop::fullUpdate () {
|
|
|
|
parent->updaterThreadStart.lock ();
|
|
if (parent->updaterRunning && parent->thread) {
|
|
// Do NOT reset changes here, since in a long chain of events it will lead to chroma_scale not being updated,
|
|
// causing Color::lab2rgb to return a black image on some opens
|
|
//parent->changeSinceLast = 0;
|
|
parent->thread->join ();
|
|
}
|
|
parent->updaterThreadStart.unlock ();
|
|
|
|
if (parent->plistener)
|
|
parent->plistener->setProgressState (true);
|
|
|
|
needsNext = true;
|
|
while (needsNext) {
|
|
needsNext = false;
|
|
update (ALL);
|
|
}
|
|
updating = false;
|
|
|
|
if (parent->plistener)
|
|
parent->plistener->setProgressState (false);
|
|
}
|
|
|
|
}
|
|
|