Moved debayer and preprocessing parameters to class ProcParams for every single image. Added tab RAW for changing those parameters. Progress bar shows only load step (work to do)
364 lines
11 KiB
C++
364 lines
11 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>
|
|
#define CLIPTO(a,b,c) ((a)>b?((a)<c?(a):c):b)
|
|
#define CLIP(a) ((a)<65535 ? (a) : (65535));
|
|
#define SKIPS(a,b) ((a) / (b) + ((a) % (b) > 0))
|
|
|
|
namespace rtengine {
|
|
|
|
extern Settings* settings;
|
|
|
|
Crop::Crop (ImProcCoordinator* parent)
|
|
: parent(parent), cropAllocated(false),
|
|
cropw(-1), croph(-1), trafw(-1), trafh(-1),
|
|
cropImageListener(NULL), updating(false),
|
|
resizeCrop(NULL), transCrop(NULL), borderRequested(32)
|
|
{
|
|
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) {
|
|
|
|
parent->mProcessing.lock();
|
|
cropImageListener = il;
|
|
parent->mProcessing.unlock();
|
|
}
|
|
|
|
void Crop::update (int todo, bool internal) {
|
|
|
|
if (!internal)
|
|
parent->mProcessing.lock ();
|
|
|
|
ProcParams& params = parent->params;
|
|
cropMutex.lock ();
|
|
|
|
if (!params.resize.enabled)
|
|
params.resize.scale = 1.0;
|
|
else if (params.resize.dataspec==1)
|
|
params.resize.scale = (double)params.resize.width / (params.coarse.rotate==90 || params.coarse.rotate==270 ? parent->fh : parent->fw);
|
|
else if (params.resize.dataspec==2)
|
|
params.resize.scale = (double)params.resize.height / (params.coarse.rotate==90 || params.coarse.rotate==270 ? parent->fw : parent->fh);
|
|
|
|
parent->ipf.setScale (skip);
|
|
|
|
// 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);
|
|
// it something has been reallocated, all processing steps have to be performed
|
|
if (needsinitupdate)
|
|
todo = ALL;
|
|
|
|
if (resizeCrop)
|
|
baseCrop = resizeCrop;
|
|
else
|
|
baseCrop = origCrop;
|
|
|
|
bool needstransform = parent->ipf.needsTransform();
|
|
|
|
if (todo & M_INIT) {
|
|
parent->minit.lock ();
|
|
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;
|
|
|
|
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 );
|
|
|
|
if (fabs(params.resize.scale-1.0)<1e-7) {
|
|
if (resizeCrop) {
|
|
delete resizeCrop;
|
|
resizeCrop = NULL;
|
|
}
|
|
baseCrop = origCrop;
|
|
}
|
|
else {
|
|
int rcw = trafw*params.resize.scale;
|
|
int rch = trafh*params.resize.scale;
|
|
if (!needstransform) {
|
|
rcw = cropw;
|
|
rch = croph;
|
|
}
|
|
if (resizeCrop && (resizeCrop->width!=rcw || resizeCrop->height!=rch)) {
|
|
delete resizeCrop;
|
|
resizeCrop = NULL;
|
|
}
|
|
if (!resizeCrop)
|
|
resizeCrop = new Image16 (rcw, rch);
|
|
parent->ipf.resize (origCrop, resizeCrop);
|
|
baseCrop = resizeCrop;
|
|
}
|
|
parent->minit.unlock ();
|
|
}
|
|
|
|
// transform
|
|
if ((!needstransform && transCrop) || (transCrop && (transCrop->width!=cropw || transCrop->height!=croph))) {
|
|
delete transCrop;
|
|
transCrop = NULL;
|
|
}
|
|
if (needstransform && !transCrop)
|
|
transCrop = new Image16 (cropw, croph);
|
|
if ((todo & M_TRANSFORM) && needstransform)
|
|
parent->ipf.transform (baseCrop, transCrop, cropx/skip, cropy/skip, trafx*params.resize.scale/skip, trafy*params.resize.scale/skip, SKIPS(parent->fw,skip), SKIPS(parent->fh,skip));
|
|
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 = radius / 1800.0 * params.sh.radius;
|
|
cshmap->update (baseCrop, (unsigned short**)cbuffer, shradius, parent->ipf.lumimul, params.sh.hq);
|
|
cshmap->forceStat (parent->shmap->max, parent->shmap->min, parent->shmap->avg);
|
|
}
|
|
|
|
// shadows & highlights & tone curve & convert to cielab
|
|
if (todo & M_RGBCURVE)
|
|
parent->ipf.rgbProc (baseCrop, laboCrop, parent->tonecurve, cshmap);
|
|
|
|
// apply luminance operations
|
|
if (todo & M_LUMINANCE) {
|
|
parent->ipf.luminanceCurve (laboCrop, labnCrop, parent->lumacurve, 0, croph);
|
|
if (skip==1) {
|
|
parent->ipf.lumadenoise (labnCrop, cbuffer);
|
|
parent->ipf.sharpening (labnCrop, (unsigned short**)cbuffer);
|
|
parent->ipf.waveletEqualizer(labnCrop, true, false);
|
|
}
|
|
}
|
|
|
|
// apply color operations
|
|
if (todo & M_COLOR) {
|
|
parent->ipf.colorCurve (laboCrop, labnCrop);
|
|
if (skip==1) {
|
|
parent->ipf.colordenoise (labnCrop, cbuffer);
|
|
parent->ipf.waveletEqualizer(labnCrop, false, true);
|
|
}
|
|
}
|
|
|
|
|
|
// switch back to rgb
|
|
parent->ipf.lab2rgb (labnCrop, cropImg);
|
|
|
|
if (cropImageListener) {
|
|
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);
|
|
for (int i=0; i<finalH; i++)
|
|
memcpy (final->data + 3*i*finalW, cropImg->data + 3*(i+upperBorder)*cropw + 3*leftBorder, 3*finalW);
|
|
cropImageListener->setDetailedCrop (final, params.crop, rqcropx, rqcropy, rqcropw, rqcroph, skip);
|
|
delete final;
|
|
}
|
|
|
|
cropMutex.unlock ();
|
|
|
|
if (!internal)
|
|
parent->mProcessing.unlock ();
|
|
}
|
|
|
|
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 labnCrop;
|
|
delete cropImg;
|
|
delete cshmap;
|
|
for (int i=0; i<croph; i++)
|
|
delete [] cbuffer[i];
|
|
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 = CLIPTO(rqcropx,0,parent->fullw-1);
|
|
int rqy1 = CLIPTO(rqcropy,0,parent->fullh-1);
|
|
int rqx2 = rqx1 + rqcropw - 1;
|
|
int rqy2 = rqy1 + rqcroph - 1;
|
|
rqx2 = CLIPTO(rqx2,0,parent->fullw-1);
|
|
rqy2 = CLIPTO(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 = CLIPTO(bx1,0,parent->fullw-1);
|
|
by1 = CLIPTO(by1,0,parent->fullh-1);
|
|
bx2 = CLIPTO(bx2,0,parent->fullw-1);
|
|
by2 = CLIPTO(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)\n", orW, orH, trafw, trafh);
|
|
|
|
if (cw!=cropw || ch!=croph || orW!=trafw || orH!=trafh) {
|
|
|
|
freeAll ();
|
|
|
|
cropw = cw;
|
|
croph = ch;
|
|
trafw = orW;
|
|
trafh = orH;
|
|
|
|
origCrop = new Image16 (trafw, trafh);
|
|
laboCrop = new LabImage (cropw, croph);
|
|
labnCrop = new LabImage (cropw, croph);
|
|
cropImg = new Image8 (cropw, croph);
|
|
cshmap = new SHMap (cropw, croph, true);
|
|
|
|
cbuffer = new int*[croph];
|
|
for (int i=0; i<croph; i++)
|
|
cbuffer[i] = new int[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;
|
|
}
|
|
|
|
void Crop::fullUpdate () {
|
|
|
|
if (updating) {
|
|
needsNext = true;
|
|
return;
|
|
}
|
|
|
|
updating = true;
|
|
|
|
parent->updaterThreadStart.lock ();
|
|
if (parent->updaterRunning && parent->thread) {
|
|
parent->changeSinceLast = 0;
|
|
parent->thread->join ();
|
|
}
|
|
|
|
if (parent->plistener)
|
|
parent->plistener->setProgressState (1);
|
|
|
|
needsNext = true;
|
|
while (needsNext) {
|
|
needsNext = false;
|
|
update (ALL, true);
|
|
}
|
|
updating = false;
|
|
|
|
if (parent->plistener)
|
|
parent->plistener->setProgressState (0);
|
|
|
|
parent->updaterThreadStart.unlock ();
|
|
}
|
|
|
|
}
|
|
|