Spot Removal tool
It is not working yet, but the GUI is (almost) done. See issue #2239.
BIN
rtdata/images/Dark/actions/spot-active.png
Normal file
After Width: | Height: | Size: 330 B |
BIN
rtdata/images/Dark/actions/spot-normal.png
Normal file
After Width: | Height: | Size: 397 B |
BIN
rtdata/images/Dark/actions/spot-prelight.png
Normal file
After Width: | Height: | Size: 322 B |
BIN
rtdata/images/Light/actions/spot-active.png
Normal file
After Width: | Height: | Size: 330 B |
BIN
rtdata/images/Light/actions/spot-normal.png
Normal file
After Width: | Height: | Size: 397 B |
BIN
rtdata/images/Light/actions/spot-prelight.png
Normal file
After Width: | Height: | Size: 322 B |
@ -461,7 +461,7 @@ HISTORY_MSG_232;N&B - Type de courbe 'Avant'
|
||||
HISTORY_MSG_233;N&B - Courbe 'Après'
|
||||
HISTORY_MSG_234;N&B - Type de courbe 'Après'
|
||||
HISTORY_MSG_235;N&B - Mixeur - Mode auto
|
||||
HISTORY_MSG_236;--inutilisé--
|
||||
HISTORY_MSG_236;Retrait de taches - Modif. de points
|
||||
HISTORY_MSG_237;N&B - Mixeur
|
||||
HISTORY_MSG_238;FD - Étendu
|
||||
HISTORY_MSG_239;FD - Force
|
||||
@ -632,6 +632,7 @@ HISTORY_MSG_403;O - NB - Sensibilité des bords
|
||||
HISTORY_MSG_404;O - NB - Base amplification
|
||||
HISTORY_MSG_405;O - Débruitage - Niveau 4
|
||||
HISTORY_MSG_406;O - NB - Pixels voisins
|
||||
HISTORY_MSG_407;Retrait de taches
|
||||
HISTORY_NEWSNAPSHOT;Ajouter
|
||||
HISTORY_NEWSNAPSHOT_TOOLTIP;Raccourci: <b>Alt-s</b>
|
||||
HISTORY_SNAPSHOT;Capture
|
||||
@ -1643,6 +1644,9 @@ TP_SHARPENMICRO_AMOUNT;Quantité
|
||||
TP_SHARPENMICRO_LABEL;Microcontraste
|
||||
TP_SHARPENMICRO_MATRIX;Matrice 3×3 au lieu de 5×5
|
||||
TP_SHARPENMICRO_UNIFORMITY;Uniformité
|
||||
TP_SPOT_COUNTLABEL;%1 point(s)
|
||||
TP_SPOT_ENTRYCHANGED;Modification d'un point
|
||||
TP_SPOT_LABEL;Retrait de taches
|
||||
TP_VIBRANCE_AVOIDCOLORSHIFT;Éviter les dérives de teinte
|
||||
TP_VIBRANCE_CURVEEDITOR_SKINTONES;TT
|
||||
TP_VIBRANCE_CURVEEDITOR_SKINTONES_LABEL;Tons chair
|
||||
|
@ -465,7 +465,7 @@ HISTORY_MSG_232;B&W - 'Before' curve type
|
||||
HISTORY_MSG_233;B&W - 'After' curve
|
||||
HISTORY_MSG_234;B&W - 'After' curve type
|
||||
HISTORY_MSG_235;B&W - Auto channel mixer
|
||||
HISTORY_MSG_236;--unused--
|
||||
HISTORY_MSG_236;Spot removal - Point modif.
|
||||
HISTORY_MSG_237;B&W - Mixer
|
||||
HISTORY_MSG_238;GF - Feather
|
||||
HISTORY_MSG_239;GF - Strength
|
||||
@ -670,6 +670,7 @@ HISTORY_MSG_437;Retinex - M - Method
|
||||
HISTORY_MSG_438;Retinex - M - Equalizer
|
||||
HISTORY_MSG_439;Retinex - Preview
|
||||
HISTORY_MSG_440;CbDL - Method
|
||||
HISTORY_MSG_441;Spot removal
|
||||
HISTORY_NEWSNAPSHOT;Add
|
||||
HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: <b>Alt-s</b>
|
||||
HISTORY_SNAPSHOT;Snapshot
|
||||
@ -1774,6 +1775,9 @@ TP_SHARPENMICRO_AMOUNT;Quantity
|
||||
TP_SHARPENMICRO_LABEL;Microcontrast
|
||||
TP_SHARPENMICRO_MATRIX;3×3 matrix instead of 5×5
|
||||
TP_SHARPENMICRO_UNIFORMITY;Uniformity
|
||||
TP_SPOT_COUNTLABEL;%1 point(s)
|
||||
TP_SPOT_ENTRYCHANGED;Point changed
|
||||
TP_SPOT_LABEL;Spot removal
|
||||
TP_VIBRANCE_AVOIDCOLORSHIFT;Avoid color shift
|
||||
TP_VIBRANCE_CURVEEDITOR_SKINTONES;HH
|
||||
TP_VIBRANCE_CURVEEDITOR_SKINTONES_LABEL;Skin-tones
|
||||
|
@ -11,7 +11,7 @@ set (RTENGINESOURCEFILES colortemp.cc curves.cc flatcurves.cc diagonalcurves.cc
|
||||
dfmanager.cc ffmanager.cc gauss.cc rawimage.cc image8.cc image16.cc imagefloat.cc imagedata.cc imageio.cc improcfun.cc init.cc dcrop.cc
|
||||
loadinitial.cc procparams.cc rawimagesource.cc demosaic_algos.cc shmap.cc simpleprocess.cc refreshmap.cc
|
||||
fast_demo.cc amaze_demosaic_RT.cc CA_correct_RT.cc cfa_linedn_RT.cc green_equil_RT.cc hilite_recon.cc expo_before_b.cc
|
||||
stdimagesource.cc myfile.cc iccjpeg.cc improccoordinator.cc pipettebuffer.cc coord.cc
|
||||
stdimagesource.cc myfile.cc iccjpeg.cc improccoordinator.cc pipettebuffer.cc coord.cc alpha.cc spot.cc
|
||||
processingjob.cc rtthumbnail.cc utils.cc labimage.cc slicer.cc cieimage.cc
|
||||
iplab2rgb.cc ipsharpen.cc iptransform.cc ipresize.cc ipvibrance.cc
|
||||
imagedimensions.cc jpeg_memsrc.cc jdatasrc.cc iimage.cc
|
||||
|
75
rtengine/alpha.cc
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 "alpha.h"
|
||||
|
||||
namespace rtengine
|
||||
{
|
||||
|
||||
Alpha::Alpha () {}
|
||||
|
||||
Alpha::Alpha (int width, int height)
|
||||
{
|
||||
if (width > 0 && height > 0) {
|
||||
surface = Cairo::ImageSurface::create(Cairo::FORMAT_A8, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Alpha::~Alpha () {
|
||||
surface->unreference();
|
||||
}
|
||||
*/
|
||||
|
||||
void Alpha::setSize(int width, int height)
|
||||
{
|
||||
if (width > 0 && height > 0) {
|
||||
if (surface) {
|
||||
if (width != getWidth() && height != getHeight()) {
|
||||
surface.clear(); // does this delete the referenced object? Unreferencing doesn't work, since Cairo expect to have a non null refCount in the destructor!
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
surface = Cairo::ImageSurface::create(Cairo::FORMAT_A8, width, height);
|
||||
} else if (surface) {
|
||||
surface.clear();
|
||||
}
|
||||
}
|
||||
|
||||
int Alpha::getWidth()
|
||||
{
|
||||
if (surface) {
|
||||
return surface->get_width();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Alpha::getHeight()
|
||||
{
|
||||
if (surface) {
|
||||
return surface->get_height();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
77
rtengine/alpha.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#ifndef _ALPHA_H_
|
||||
#define _ALPHA_H_
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define CHECK_BOUNDS 0
|
||||
|
||||
namespace rtengine
|
||||
{
|
||||
|
||||
/// Alpha channel class (8 bits)
|
||||
class Alpha
|
||||
{
|
||||
protected:
|
||||
Cairo::RefPtr<Cairo::ImageSurface> surface;
|
||||
|
||||
public:
|
||||
Alpha ();
|
||||
Alpha (int width, int height);
|
||||
//~Alpha ();
|
||||
|
||||
void setSize(int width, int height);
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
|
||||
const Cairo::RefPtr<Cairo::ImageSurface> getSurface ();
|
||||
|
||||
// TODO: to make the editing faster, we should add an iterator class
|
||||
|
||||
// Will send back the start of a row
|
||||
unsigned char* operator () (unsigned row) const;
|
||||
// Will send back a value at a given row, col position
|
||||
unsigned char& operator () (unsigned row, unsigned col);
|
||||
const unsigned char operator () (unsigned row, unsigned col) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline const Cairo::RefPtr<Cairo::ImageSurface> Alpha::getSurface () {
|
||||
return surface; // to be used in bitmap edition
|
||||
}
|
||||
|
||||
inline const unsigned char Alpha::operator () (unsigned row,
|
||||
unsigned col) const {
|
||||
return *(surface->get_data () + row * surface->get_width () + col);
|
||||
}
|
||||
|
||||
inline unsigned char& Alpha::operator () (unsigned row, unsigned col) {
|
||||
return *(surface->get_data () + row * surface->get_width () + col);
|
||||
}
|
||||
|
||||
inline unsigned char* Alpha::operator () (unsigned row) const {
|
||||
return surface->get_data () + row * surface->get_width ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -31,7 +31,7 @@ namespace rtengine
|
||||
extern const Settings* settings;
|
||||
|
||||
Crop::Crop (ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow)
|
||||
: PipetteBuffer(editDataProvider), origCrop(NULL), laboCrop(NULL), labnCrop(NULL),
|
||||
: PipetteBuffer(editDataProvider), origCrop(NULL), spotCrop(NULL), laboCrop(NULL), labnCrop(NULL),
|
||||
cropImg(NULL), cbuf_real(NULL), cshmap(NULL), transCrop(NULL), cieCrop(NULL), cbuffer(NULL),
|
||||
updating(false), newUpdatePending(false), skip(10),
|
||||
cropx(0), cropy(0), cropw(-1), croph(-1),
|
||||
@ -215,18 +215,18 @@ void Crop::update (int todo)
|
||||
if(settings->leveldnautsimpl == 1) {
|
||||
if(params.dirpyrDenoise.Cmethod == "MAN" || params.dirpyrDenoise.Cmethod == "PON" ) {
|
||||
PreviewProps pp (trafx, trafy, trafw * skip, trafh * skip, skip);
|
||||
parent->imgsrc->getImage (parent->currWB, tr, origCrop, pp, params.toneCurve, params.icm, params.raw );
|
||||
parent->imgsrc->getImage (parent->currWB, tr, baseCrop, pp, params.toneCurve, params.icm, params.raw );
|
||||
}
|
||||
} else {
|
||||
if(params.dirpyrDenoise.C2method == "MANU") {
|
||||
PreviewProps pp (trafx, trafy, trafw * skip, trafh * skip, skip);
|
||||
parent->imgsrc->getImage (parent->currWB, tr, origCrop, pp, params.toneCurve, params.icm, params.raw );
|
||||
parent->imgsrc->getImage (parent->currWB, tr, baseCrop, pp, params.toneCurve, params.icm, params.raw );
|
||||
}
|
||||
}
|
||||
|
||||
if((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "PRE") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "PREV")) {
|
||||
PreviewProps pp (trafx, trafy, trafw * skip, trafh * skip, skip);
|
||||
parent->imgsrc->getImage (parent->currWB, tr, origCrop, pp, params.toneCurve, params.icm, params.raw );
|
||||
parent->imgsrc->getImage (parent->currWB, tr, baseCrop, pp, params.toneCurve, params.icm, params.raw );
|
||||
|
||||
if((!isDetailWindow) && parent->adnListener && skip == 1 && params.dirpyrDenoise.enabled) {
|
||||
float lowdenoise = 1.f;
|
||||
@ -308,15 +308,15 @@ void Crop::update (int todo)
|
||||
//setCropSizes (centerTile_X[poscenterX], centerTile_Y[poscenterY], trafw*skip,trafh*skip , skip, true);
|
||||
|
||||
// we only need image reduced to 1/4 here
|
||||
int W = origCrop->getWidth();
|
||||
int H = origCrop->getHeight();
|
||||
int W = baseCrop->getWidth();
|
||||
int H = baseCrop->getHeight();
|
||||
Imagefloat *provicalc = new Imagefloat ((W + 1) / 2, (H + 1) / 2); //for denoise curves
|
||||
|
||||
for(int ii = 0; ii < H; ii += 2) {
|
||||
for(int jj = 0; jj < W; jj += 2) {
|
||||
provicalc->r(ii >> 1, jj >> 1) = origCrop->r(ii, jj);
|
||||
provicalc->g(ii >> 1, jj >> 1) = origCrop->g(ii, jj);
|
||||
provicalc->b(ii >> 1, jj >> 1) = origCrop->b(ii, jj);
|
||||
provicalc->r(ii >> 1, jj >> 1) = baseCrop->r(ii, jj);
|
||||
provicalc->g(ii >> 1, jj >> 1) = baseCrop->g(ii, jj);
|
||||
provicalc->b(ii >> 1, jj >> 1) = baseCrop->b(ii, jj);
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,7 +337,7 @@ void Crop::update (int todo)
|
||||
LUTf gamcurve(65536, 0);
|
||||
float gam, gamthresh, gamslope;
|
||||
parent->ipf.RGB_denoise_infoGamCurve(params.dirpyrDenoise, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope);
|
||||
parent->ipf.RGB_denoise_info(origCrop, provicalc, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope, params.dirpyrDenoise, parent->imgsrc->getDirPyrDenoiseExpComp(), chaut, Nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, nresi, highresi, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc, true);
|
||||
parent->ipf.RGB_denoise_info(baseCrop, provicalc, parent->imgsrc->isRAW(), gamcurve, gam, gamthresh, gamslope, params.dirpyrDenoise, parent->imgsrc->getDirPyrDenoiseExpComp(), chaut, Nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, nresi, highresi, chromina, sigma, lumema, sigma_L, redyel, skinc, nsknc, true);
|
||||
// printf("redy=%f skin=%f pcskin=%f\n",redyel, skinc,nsknc);
|
||||
// printf("DCROP skip=%d cha=%4.0f Nb=%d red=%4.0f bl=%4.0f redM=%4.0f bluM=%4.0f L=%4.0f sigL=%4.0f Ch=%4.0f Si=%4.0f\n",skip, chaut,Nb, redaut,blueaut, maxredaut, maxblueaut, lumema, sigma_L, chromina, sigma);
|
||||
float multip = 1.f;
|
||||
@ -600,10 +600,10 @@ void Crop::update (int todo)
|
||||
//end evaluate noise
|
||||
}
|
||||
|
||||
// if(params.dirpyrDenoise.Cmethod=="AUT" || params.dirpyrDenoise.Cmethod=="PON") {//reinit origCrop after Auto
|
||||
if((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "AUT") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "AUTO")) { //reinit origCrop after Auto
|
||||
// if(params.dirpyrDenoise.Cmethod=="AUT" || params.dirpyrDenoise.Cmethod=="PON") {//reinit baseCrop after Auto
|
||||
if((settings->leveldnautsimpl == 1 && params.dirpyrDenoise.Cmethod == "AUT") || (settings->leveldnautsimpl == 0 && params.dirpyrDenoise.C2method == "AUTO")) { //reinit baseCrop after Auto
|
||||
PreviewProps pp (trafx, trafy, trafw * skip, trafh * skip, skip);
|
||||
parent->imgsrc->getImage (parent->currWB, tr, origCrop, pp, params.toneCurve, params.icm, params.raw );
|
||||
parent->imgsrc->getImage (parent->currWB, tr, baseCrop, pp, params.toneCurve, params.icm, params.raw );
|
||||
}
|
||||
|
||||
DirPyrDenoiseParams denoiseParams = params.dirpyrDenoise;
|
||||
@ -620,15 +620,15 @@ void Crop::update (int todo)
|
||||
|
||||
if((noiseLCurve || noiseCCurve ) && skip == 1 && denoiseParams.enabled) { //only allocate memory if enabled and skip
|
||||
// we only need image reduced to 1/4 here
|
||||
int W = origCrop->getWidth();
|
||||
int H = origCrop->getHeight();
|
||||
int W = baseCrop->getWidth();
|
||||
int H = baseCrop->getHeight();
|
||||
calclum = new Imagefloat ((W + 1) / 2, (H + 1) / 2); //for denoise curves
|
||||
|
||||
for(int ii = 0; ii < H; ii += 2) {
|
||||
for(int jj = 0; jj < W; jj += 2) {
|
||||
calclum->r(ii >> 1, jj >> 1) = origCrop->r(ii, jj);
|
||||
calclum->g(ii >> 1, jj >> 1) = origCrop->g(ii, jj);
|
||||
calclum->b(ii >> 1, jj >> 1) = origCrop->b(ii, jj);
|
||||
calclum->r(ii >> 1, jj >> 1) = baseCrop->r(ii, jj);
|
||||
calclum->g(ii >> 1, jj >> 1) = baseCrop->g(ii, jj);
|
||||
calclum->b(ii >> 1, jj >> 1) = baseCrop->b(ii, jj);
|
||||
}
|
||||
}
|
||||
|
||||
@ -644,7 +644,7 @@ void Crop::update (int todo)
|
||||
int kall = 0;
|
||||
|
||||
float chaut, redaut, blueaut, maxredaut, maxblueaut, nresi, highresi;
|
||||
parent->ipf.RGB_denoise(kall, origCrop, origCrop, calclum, ch_M, max_r, max_b, parent->imgsrc->isRAW(), /*Roffset,*/ denoiseParams, parent->imgsrc->getDirPyrDenoiseExpComp(), noiseLCurve, noiseCCurve, chaut, redaut, blueaut, maxredaut, maxblueaut, nresi, highresi);
|
||||
parent->ipf.RGB_denoise(kall, baseCrop, baseCrop, calclum, ch_M, max_r, max_b, parent->imgsrc->isRAW(), /*Roffset,*/ denoiseParams, parent->imgsrc->getDirPyrDenoiseExpComp(), noiseLCurve, noiseCCurve, chaut, redaut, blueaut, maxredaut, maxblueaut, nresi, highresi);
|
||||
|
||||
if (parent->adnListener) {
|
||||
parent->adnListener->noiseChanged(nresi, highresi);
|
||||
@ -663,7 +663,7 @@ void Crop::update (int todo)
|
||||
}
|
||||
}
|
||||
|
||||
parent->imgsrc->convertColorSpace(origCrop, params.icm, parent->currWB);
|
||||
parent->imgsrc->convertColorSpace(baseCrop, params.icm, parent->currWB);
|
||||
|
||||
delete [] ch_M;
|
||||
delete [] max_r;
|
||||
@ -771,6 +771,27 @@ void Crop::update (int todo)
|
||||
satLimitOpacity = 100.f * (moyS - 0.85f * eqty); //-0.85 sigma==>20% pixels with low saturation
|
||||
}
|
||||
|
||||
if (params.spot.enabled) {
|
||||
if (todo & M_SPOT) {
|
||||
if(!spotCrop) {
|
||||
spotCrop = new Imagefloat (cropw, croph);
|
||||
}
|
||||
baseCrop->copyData(spotCrop);
|
||||
|
||||
PreviewProps pp (cropx, cropy, cropw, croph, skip);
|
||||
parent->ipf.removeSpots(spotCrop, params.spot.entries, pp);
|
||||
}
|
||||
} else {
|
||||
if (spotCrop) {
|
||||
delete spotCrop;
|
||||
spotCrop = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (spotCrop) {
|
||||
baseCrop = spotCrop;
|
||||
}
|
||||
|
||||
if (todo & M_RGBCURVE) {
|
||||
double rrm, ggm, bbm;
|
||||
DCPProfile *dcpProf = parent->imgsrc->getDCP(params.icm, parent->currWB);
|
||||
|
@ -42,6 +42,7 @@ class Crop : public DetailedCrop, public PipetteBuffer
|
||||
protected:
|
||||
// --- permanently allocated in RAM and only renewed on size changes
|
||||
Imagefloat* origCrop; // "one chunk" allocation
|
||||
Imagefloat* spotCrop; // "one chunk" allocation
|
||||
LabImage* laboCrop; // "one chunk" allocation
|
||||
LabImage* labnCrop; // "one chunk" allocation
|
||||
Image8* cropImg; // "one chunk" allocation
|
||||
|
@ -325,6 +325,23 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Copy the a sub-region of the data to another PlanarRGBData */
|
||||
void copyData(PlanarWhateverData<T> *dest, int x, int y, int width, int height)
|
||||
{
|
||||
assert (dest != NULL);
|
||||
// Make sure that the size is the same, reallocate if necessary
|
||||
dest->allocate(width, height);
|
||||
|
||||
if (dest->width == -1) {
|
||||
printf("ERROR: PlanarRGBData::copyData >>> allocation failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = y, j = 0; i < y + height; ++i, ++j) {
|
||||
memcpy (dest->v(i) + x, v(j), width * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
void rotate (int deg)
|
||||
{
|
||||
|
||||
@ -727,6 +744,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Copy the a sub-region of the data to another PlanarRGBData */
|
||||
void copyData(PlanarRGBData<T> *dest, int x, int y, int width, int height)
|
||||
{
|
||||
assert (dest != NULL);
|
||||
// Make sure that the size is the same, reallocate if necessary
|
||||
dest->allocate(width, height);
|
||||
|
||||
if (dest->width == -1) {
|
||||
printf("ERROR: PlanarRGBData::copyData >>> allocation failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = y, j = 0; i < y + height; ++i, ++j) {
|
||||
memcpy (dest->r(i) + x, r(j), width * sizeof(T));
|
||||
memcpy (dest->g(i) + x, g(j), width * sizeof(T));
|
||||
memcpy (dest->b(i) + x, b(j), width * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
void rotate (int deg)
|
||||
{
|
||||
|
||||
@ -1342,6 +1378,23 @@ public:
|
||||
memcpy (dest->data, data, 3 * width * height * sizeof(T));
|
||||
}
|
||||
|
||||
/** Copy the a sub-region of the data to another PlanarRGBData */
|
||||
void copyData(ChunkyRGBData<T> *dest, int x, int y, int width, int height)
|
||||
{
|
||||
assert (dest != NULL);
|
||||
// Make sure that the size is the same, reallocate if necessary
|
||||
dest->allocate(width, height);
|
||||
|
||||
if (dest->width == -1) {
|
||||
printf("ERROR: PlanarRGBData::copyData >>> allocation failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = y, j = 0; i < y + height; ++i, ++j) {
|
||||
memcpy (dest->r(i) + x, r(j), 3 * width * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
void rotate (int deg)
|
||||
{
|
||||
|
||||
|
@ -124,7 +124,21 @@ Image16* Image16::copy ()
|
||||
return cp;
|
||||
}
|
||||
|
||||
void Image16::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, bool first, procparams::ToneCurveParams hrp)
|
||||
Image16* Image16::copySubRegion (int x, int y, int width, int height)
|
||||
{
|
||||
Image16* cp = NULL;
|
||||
int realWidth = LIM<int>(x + width, 0, this->width) - x;
|
||||
int realHeight = LIM<int>(y + height, 0, this->height) - y;
|
||||
|
||||
if (realWidth > 0 && realHeight > 0) {
|
||||
cp = new Image16 (realWidth, realHeight);
|
||||
copyData(cp, x, y, realWidth, realHeight);
|
||||
}
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
void Image16::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, const PreviewProps & pp, bool first, procparams::ToneCurveParams hrp)
|
||||
{
|
||||
|
||||
// compute channel multipliers
|
||||
|
@ -41,11 +41,12 @@ public:
|
||||
~Image16 ();
|
||||
|
||||
Image16* copy ();
|
||||
Image16* copySubRegion (int x, int y, int width, int height);
|
||||
|
||||
Image8* to8();
|
||||
Imagefloat* tofloat();
|
||||
|
||||
virtual void getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, bool first, procparams::ToneCurveParams hrp);
|
||||
virtual void getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, const PreviewProps & pp, bool first, procparams::ToneCurveParams hrp);
|
||||
|
||||
virtual const char* getType () const
|
||||
{
|
||||
|
@ -94,7 +94,7 @@ Image8* Image8::copy ()
|
||||
return cp;
|
||||
}
|
||||
|
||||
void Image8::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, bool first, procparams::ToneCurveParams hrp)
|
||||
void Image8::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, const PreviewProps & pp, bool first, procparams::ToneCurveParams hrp)
|
||||
{
|
||||
// compute channel multipliers
|
||||
double drm, dgm, dbm;
|
||||
|
@ -40,7 +40,7 @@ public:
|
||||
|
||||
Image8* copy ();
|
||||
|
||||
virtual void getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, bool first, procparams::ToneCurveParams hrp);
|
||||
virtual void getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, const PreviewProps & pp, bool first, procparams::ToneCurveParams hrp);
|
||||
|
||||
virtual const char* getType () const
|
||||
{
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "imagedimensions.h"
|
||||
#include "rtengine.h"
|
||||
|
||||
void ImageDimensions::transform (PreviewProps pp, int tran, int &sx1, int &sy1, int &sx2, int &sy2)
|
||||
void ImageDimensions::transform (const PreviewProps & pp, int tran, int &sx1, int &sy1, int &sx2, int &sy2)
|
||||
{
|
||||
|
||||
int sw = width, sh = height;
|
||||
|
@ -24,8 +24,9 @@ class PreviewProps
|
||||
{
|
||||
public:
|
||||
int x, y, w, h, skip;
|
||||
PreviewProps (int _x, int _y, int _w, int _h, int _skip)
|
||||
: x(_x), y(_y), w(_w), h(_h), skip(_skip) {}
|
||||
PreviewProps (int x, int y, int w, int h, int skip);
|
||||
|
||||
void set (int x, int y, int w, int h, int skip);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -39,25 +40,44 @@ public:
|
||||
int height;
|
||||
|
||||
public:
|
||||
ImageDimensions() : width(-1), height(-1) {}
|
||||
int getW ()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
int getH ()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
int getWidth () const
|
||||
{
|
||||
return width;
|
||||
}
|
||||
int getHeight () const
|
||||
{
|
||||
return height;
|
||||
}
|
||||
void transform (PreviewProps pp, int tran, int &sx1, int &sy1, int &sx2, int &sy2);
|
||||
ImageDimensions ();
|
||||
int getW ();
|
||||
int getH ();
|
||||
int getWidth () const;
|
||||
int getHeight () const;
|
||||
void transform (const PreviewProps & pp, int tran, int &sx1, int &sy1, int &sx2, int &sy2);
|
||||
};
|
||||
|
||||
inline PreviewProps::PreviewProps (int x, int y, int w, int h, int skip) :
|
||||
x (x), y (y), w (w), h (h), skip (skip) {
|
||||
}
|
||||
|
||||
inline void PreviewProps::set (int x, int y, int w, int h, int skip) {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
this->skip = skip;
|
||||
}
|
||||
|
||||
inline ImageDimensions::ImageDimensions () :
|
||||
width (-1), height (-1) {
|
||||
}
|
||||
|
||||
inline int ImageDimensions::getW () {
|
||||
return width;
|
||||
}
|
||||
|
||||
inline int ImageDimensions::getH () {
|
||||
return height;
|
||||
}
|
||||
|
||||
inline int ImageDimensions::getWidth () const {
|
||||
return width;
|
||||
}
|
||||
|
||||
inline int ImageDimensions::getHeight () const {
|
||||
return height;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -170,8 +170,22 @@ Imagefloat* Imagefloat::copy ()
|
||||
return cp;
|
||||
}
|
||||
|
||||
Imagefloat* Imagefloat::copySubRegion (int x, int y, int width, int height)
|
||||
{
|
||||
Imagefloat* cp = NULL;
|
||||
int realWidth = LIM<int>(x + width, 0, this->width) - x;
|
||||
int realHeight = LIM<int>(y + height, 0, this->height) - y;
|
||||
|
||||
if (realWidth > 0 && realHeight > 0) {
|
||||
cp = new Imagefloat (realWidth, realHeight);
|
||||
copyData(cp, x, y, realWidth, realHeight);
|
||||
}
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
// This is called by the StdImageSource class. We assume that fp images from StdImageSource don't have to deal with gamma
|
||||
void Imagefloat::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, bool first, procparams::ToneCurveParams hrp)
|
||||
void Imagefloat::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, const PreviewProps & pp, bool first, procparams::ToneCurveParams hrp)
|
||||
{
|
||||
|
||||
// compute channel multipliers
|
||||
|
@ -45,11 +45,12 @@ public:
|
||||
~Imagefloat ();
|
||||
|
||||
Imagefloat* copy ();
|
||||
Imagefloat* copySubRegion (int x, int y, int width, int height);
|
||||
|
||||
Image8* to8();
|
||||
Image16* to16();
|
||||
|
||||
virtual void getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, bool first, procparams::ToneCurveParams hrp);
|
||||
virtual void getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, const PreviewProps & pp, bool first, procparams::ToneCurveParams hrp);
|
||||
|
||||
virtual const char* getType () const
|
||||
{
|
||||
|
@ -130,7 +130,7 @@ public:
|
||||
return sampleArrangement;
|
||||
}
|
||||
|
||||
virtual void getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, bool first, procparams::ToneCurveParams hrp)
|
||||
virtual void getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, const PreviewProps & pp, bool first, procparams::ToneCurveParams hrp)
|
||||
{
|
||||
printf("getStdImage NULL!\n");
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public:
|
||||
virtual bool IsrgbSourceModified() = 0; // tracks whether cached rgb output of demosaic has been modified
|
||||
|
||||
// use right after demosaicing image, add coarse transformation and put the result in the provided Imagefloat*
|
||||
virtual void getImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, ToneCurveParams hlp, ColorManagementParams cmp, RAWParams raw) {}
|
||||
virtual void getImage (ColorTemp ctemp, int tran, Imagefloat* image, const PreviewProps & pp, ToneCurveParams hlp, ColorManagementParams cmp, RAWParams raw) {}
|
||||
virtual eSensorType getSensorType ()
|
||||
{
|
||||
return ST_NONE;
|
||||
|
@ -30,8 +30,8 @@ namespace rtengine
|
||||
extern const Settings* settings;
|
||||
|
||||
ImProcCoordinator::ImProcCoordinator ()
|
||||
: orig_prev(NULL), oprevi(NULL), oprevl(NULL), nprevl(NULL), previmg(NULL), workimg(NULL),
|
||||
ncie(NULL), imgsrc(NULL), shmap(NULL), lastAwbEqual(0.), ipf(¶ms, true), monitorIntent(RI_RELATIVE), scale(10),
|
||||
: orig_prev(NULL), oprevi(NULL), spotprevi(NULL), oprevl(NULL), nprevl(NULL), previmg(NULL), workimg(NULL),
|
||||
ncie(NULL), imgsrc(NULL), shmap(NULL), lastAwbEqual(0.), ipf(¶ms, true), previewProps(-1, -1, -1, -1, 1), monitorIntent(RI_RELATIVE), scale(10),
|
||||
highDetailPreprocessComputed(false), highDetailRawComputed(false), allocated(false),
|
||||
bwAutoR(-9000.f), bwAutoG(-9000.f), bwAutoB(-9000.f), CAMMean(0.),
|
||||
|
||||
@ -136,7 +136,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall)
|
||||
{
|
||||
|
||||
MyMutex::MyLock processingLock(mProcessing);
|
||||
int numofphases = 14;
|
||||
int numofphases = 15;
|
||||
int readyphase = 0;
|
||||
|
||||
bwAutoR = bwAutoG = bwAutoB = -9000.f;
|
||||
@ -316,11 +316,11 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall)
|
||||
|
||||
// Will (re)allocate the preview's buffers
|
||||
setScale (scale);
|
||||
PreviewProps pp (0, 0, fw, fh, scale);
|
||||
previewProps.set(0, 0, fw, fh, scale);
|
||||
// Tells to the ImProcFunctions' tools what is the preview scale, which may lead to some simplifications
|
||||
ipf.setScale (scale);
|
||||
|
||||
imgsrc->getImage (currWB, tr, orig_prev, pp, params.toneCurve, params.icm, params.raw);
|
||||
imgsrc->getImage (currWB, tr, orig_prev, previewProps, params.toneCurve, params.icm, params.raw);
|
||||
//ColorTemp::CAT02 (orig_prev, ¶ms) ;
|
||||
// printf("orig_prevW=%d\n scale=%d",orig_prev->width, scale);
|
||||
/* Issue 2785, disabled some 1:1 tools
|
||||
@ -377,11 +377,11 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall)
|
||||
|
||||
if (!needstransform && orig_prev != oprevi) {
|
||||
delete oprevi;
|
||||
oprevi = orig_prev;
|
||||
spotprevi = oprevi = orig_prev;
|
||||
}
|
||||
|
||||
if (needstransform && orig_prev == oprevi) {
|
||||
oprevi = new Imagefloat (pW, pH);
|
||||
spotprevi = oprevi = new Imagefloat (pW, pH);
|
||||
}
|
||||
|
||||
if ((todo & M_TRANSFORM) && needstransform)
|
||||
@ -433,6 +433,24 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall)
|
||||
}
|
||||
}
|
||||
|
||||
progress ("Spot Removal...", 100 * readyphase / numofphases);
|
||||
|
||||
if ((todo & M_SPOT) && params.spot.enabled && !params.spot.entries.empty()) {
|
||||
if(spotprevi == oprevi) {
|
||||
spotprevi = new Imagefloat (pW, pH);
|
||||
}
|
||||
|
||||
oprevi->copyData(spotprevi);
|
||||
ipf.removeSpots(spotprevi, params.spot.entries, previewProps);
|
||||
} else {
|
||||
if (spotprevi != oprevi) {
|
||||
delete spotprevi;
|
||||
spotprevi = oprevi;
|
||||
}
|
||||
}
|
||||
|
||||
readyphase++;
|
||||
|
||||
progress ("Exposure curve & CIELAB conversion...", 100 * readyphase / numofphases);
|
||||
|
||||
if ((todo & M_RGBCURVE) || (todo & M_CROP)) {
|
||||
@ -445,9 +463,9 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall)
|
||||
params.toneCurve.curveMode, params.toneCurve.curve, params.toneCurve.curveMode2, params.toneCurve.curve2,
|
||||
vhist16, histCropped, hltonecurve, shtonecurve, tonecurve, histToneCurve, customToneCurve1, customToneCurve2, scale == 1 ? 1 : 1);
|
||||
|
||||
CurveFactory::RGBCurve (params.rgbCurves.rcurve, rCurve, scale == 1 ? 1 : 1);
|
||||
CurveFactory::RGBCurve (params.rgbCurves.gcurve, gCurve, scale == 1 ? 1 : 1);
|
||||
CurveFactory::RGBCurve (params.rgbCurves.bcurve, bCurve, scale == 1 ? 1 : 1);
|
||||
CurveFactory::RGBCurve (params.rgbCurves.rcurve, rCurve, /*scale==1 ? 1 :*/ 1);
|
||||
CurveFactory::RGBCurve (params.rgbCurves.gcurve, gCurve, /*scale==1 ? 1 :*/ 1);
|
||||
CurveFactory::RGBCurve (params.rgbCurves.bcurve, bCurve, /*scale==1 ? 1 :*/ 1);
|
||||
|
||||
|
||||
TMatrix wprof = iccStore->workingSpaceMatrix (params.icm.working);
|
||||
@ -484,7 +502,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall)
|
||||
if(params.colorToning.enabled && params.colorToning.autosat) { //for colortoning evaluation of saturation settings
|
||||
float moyS = 0.f;
|
||||
float eqty = 0.f;
|
||||
ipf.moyeqt (oprevi, moyS, eqty);//return image : mean saturation and standard dev of saturation
|
||||
ipf.moyeqt (spotprevi, moyS, eqty);//return image : mean saturation and standard dev of saturation
|
||||
//printf("moy=%f ET=%f\n", moyS,eqty);
|
||||
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
|
||||
|
||||
@ -537,7 +555,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall)
|
||||
double bbm = 33.;
|
||||
|
||||
DCPProfile *dcpProf = imgsrc->getDCP(params.icm, currWB);
|
||||
ipf.rgbProc (oprevi, oprevl, NULL, hltonecurve, shtonecurve, tonecurve, shmap, params.toneCurve.saturation,
|
||||
ipf.rgbProc (spotprevi, oprevl, NULL, hltonecurve, shtonecurve, tonecurve, shmap, params.toneCurve.saturation,
|
||||
rCurve, gCurve, bCurve, satLimit , satLimitOpacity, 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);
|
||||
|
||||
if(params.blackwhite.enabled && params.blackwhite.autoc && abwListener) {
|
||||
@ -867,11 +885,16 @@ void ImProcCoordinator::freeAll ()
|
||||
}
|
||||
|
||||
if (allocated) {
|
||||
if (orig_prev != oprevi) {
|
||||
if (spotprevi && spotprevi != oprevi) {
|
||||
delete spotprevi;
|
||||
}
|
||||
spotprevi = NULL;
|
||||
|
||||
if (oprevi && oprevi != orig_prev) {
|
||||
delete oprevi;
|
||||
}
|
||||
|
||||
oprevi = NULL;
|
||||
|
||||
delete orig_prev;
|
||||
orig_prev = NULL;
|
||||
delete oprevl;
|
||||
@ -882,7 +905,6 @@ void ImProcCoordinator::freeAll ()
|
||||
if (ncie) {
|
||||
delete ncie;
|
||||
}
|
||||
|
||||
ncie = NULL;
|
||||
|
||||
if (imageListener) {
|
||||
@ -928,7 +950,7 @@ void ImProcCoordinator::setScale (int prevscale)
|
||||
prevscale--;
|
||||
PreviewProps pp (0, 0, fw, fh, prevscale);
|
||||
imgsrc->getSize (tr, pp, nW, nH);
|
||||
} while(nH < 400 && prevscale > 1 && (nW * nH < 1000000) ); // sctually hardcoded values, perhaps a better choice is possible
|
||||
} while(nH < 400 && prevscale > 1 && (nW * nH < 1000000) ); // actually hardcoded values, perhaps a better choice is possible
|
||||
|
||||
if (settings->verbose) {
|
||||
printf ("setscale starts (%d, %d)\n", nW, nH);
|
||||
@ -942,7 +964,7 @@ void ImProcCoordinator::setScale (int prevscale)
|
||||
pH = nH;
|
||||
|
||||
orig_prev = new Imagefloat (pW, pH);
|
||||
oprevi = orig_prev;
|
||||
spotprevi = oprevi = orig_prev;
|
||||
oprevl = new LabImage (pW, pH);
|
||||
nprevl = new LabImage (pW, pH);
|
||||
//ncie is only used in ImProcCoordinator::updatePreviewImage, it will be allocated on first use and deleted if not used anymore
|
||||
|
@ -55,6 +55,7 @@ class ImProcCoordinator : public StagedImageProcessor
|
||||
protected:
|
||||
Imagefloat *orig_prev;
|
||||
Imagefloat *oprevi;
|
||||
Imagefloat *spotprevi;
|
||||
LabImage *oprevl;
|
||||
LabImage *nprevl;
|
||||
Image8 *previmg;
|
||||
@ -71,6 +72,7 @@ protected:
|
||||
double lastAwbEqual;
|
||||
|
||||
ImProcFunctions ipf;
|
||||
PreviewProps previewProps;
|
||||
|
||||
Glib::ustring monitorProfile;
|
||||
|
||||
|
@ -354,6 +354,9 @@ public:
|
||||
float Mad(float * DataList, const int datalen);
|
||||
float MadRgb(float * DataList, const int datalen);
|
||||
|
||||
// spot removal tool
|
||||
void removeSpots (Imagefloat* img, const std::vector<SpotEntry> &entries, const PreviewProps &pp);
|
||||
|
||||
// pyramid wavelet
|
||||
void dirpyr_equalizer (float ** src, float ** dst, int srcwidth, int srcheight, float ** l_a, float ** l_b, float ** dest_a, float ** dest_b, const double * mult, const double dirpyrThreshold, const double skinprot, const bool gamutlab, float b_l, float t_l, float t_r, float b_r, int choice, int scale);//Emil's directional pyramid wavelet
|
||||
void dirpyr_equalizercam (CieImage* ncie, float ** src, float ** dst, int srcwidth, int srcheight, float ** h_p, float ** C_p, const double * mult, const double dirpyrThreshold, const double skinprot, bool execdir, const bool gamutlab, float b_l, float t_l, float t_r, float b_r, int choice, int scale);//Emil's directional pyramid wavelet
|
||||
|
@ -262,7 +262,7 @@ enum ProcEvent {
|
||||
EvBWAfterCurve = 232,
|
||||
EvBWAfterCurveMode = 233,
|
||||
EvAutoch = 234,
|
||||
// EvFixedch=235, -- can be reused --
|
||||
EvSpotEntry = 235,
|
||||
EvNeutralBW = 236,
|
||||
EvGradientFeather = 237,
|
||||
EvGradientStrength = 238,
|
||||
@ -467,6 +467,7 @@ enum ProcEvent {
|
||||
EvRetinexmapcurve = 437,
|
||||
EvviewMethod = 438,
|
||||
EvcbdlMethod = 439,
|
||||
EvSpotEnabled = 440,
|
||||
NUMOFEVENTS
|
||||
|
||||
};
|
||||
|
@ -46,6 +46,9 @@ const char *RAWParams::XTransSensor::methodstring[RAWParams::XTransSensor::numMe
|
||||
const char *RAWParams::ff_BlurTypestring[RAWParams::numFlatFileBlurTypes] = {/*"Parametric",*/ "Area Flatfield", "Vertical Flatfield", "Horizontal Flatfield", "V+H Flatfield"};
|
||||
std::vector<WBEntry*> WBParams::wbEntries;
|
||||
|
||||
const short SpotParams::minRadius = 5;
|
||||
const short SpotParams::maxRadius = 35;
|
||||
|
||||
bool ToneCurveParams::HLReconstructionNecessary(LUTu &histRedRaw, LUTu &histGreenRaw, LUTu &histBlueRaw)
|
||||
{
|
||||
if (options.rtSettings.verbose)
|
||||
@ -856,6 +859,12 @@ void CoarseTransformParams::setDefaults()
|
||||
vflip = false;
|
||||
}
|
||||
|
||||
void SpotParams::setDefaults()
|
||||
{
|
||||
enabled = false;
|
||||
entries.clear();
|
||||
}
|
||||
|
||||
void RAWParams::setDefaults()
|
||||
{
|
||||
bayersensor.method = RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze];
|
||||
@ -2488,7 +2497,7 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, bool fnameAbsol
|
||||
keyFile.set_integer ("Vignetting Correction", "CenterY", vignetting.centerY);
|
||||
}
|
||||
|
||||
|
||||
// save resizing settings
|
||||
if (!pedited || pedited->resize.enabled) {
|
||||
keyFile.set_boolean ("Resize", "Enabled", resize.enabled);
|
||||
}
|
||||
@ -2517,6 +2526,28 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, bool fnameAbsol
|
||||
keyFile.set_integer ("Resize", "Height", resize.height);
|
||||
}
|
||||
|
||||
// save spot removal settings
|
||||
if (!pedited || pedited->spot.enabled) {
|
||||
keyFile.set_boolean ("Spot removal", "Enabled", spot.enabled);
|
||||
}
|
||||
|
||||
if (!pedited || pedited->spot.entries) {
|
||||
std::vector<double> vEntries;
|
||||
|
||||
for (size_t i = 0; i < spot.entries.size(); ++i) {
|
||||
vEntries.push_back(double(spot.entries.at(i).sourcePos.x));
|
||||
vEntries.push_back(double(spot.entries.at(i).sourcePos.y));
|
||||
vEntries.push_back(double(spot.entries.at(i).targetPos.x));
|
||||
vEntries.push_back(double(spot.entries.at(i).targetPos.y));
|
||||
vEntries.push_back(double(spot.entries.at(i).radius));
|
||||
vEntries.push_back(double(spot.entries.at(i).feather));
|
||||
vEntries.push_back(double(spot.entries.at(i).opacity));
|
||||
}
|
||||
|
||||
Glib::ArrayHandle<double> entries = vEntries;
|
||||
keyFile.set_double_list("Spot removal", "Entries", entries);
|
||||
}
|
||||
|
||||
if (!pedited || pedited->prsharpening.enabled) {
|
||||
keyFile.set_boolean ("PostResizeSharpening", "Enabled", prsharpening.enabled);
|
||||
}
|
||||
@ -5669,6 +5700,36 @@ int ProcParams::load (Glib::ustring fname, ParamsEdited* pedited)
|
||||
}
|
||||
}
|
||||
|
||||
// load spot removal settings
|
||||
if (keyFile.has_group ("Spot removal")) {
|
||||
if (keyFile.has_key ("Spot removal", "Enabled")) {
|
||||
spot.enabled = keyFile.get_boolean ("Spot removal", "Enabled");
|
||||
|
||||
if (pedited) {
|
||||
pedited->spot.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyFile.has_key ("Spot removal", "Entries")) {
|
||||
Glib::ArrayHandle<double> entries = keyFile.get_double_list ("Spot removal", "Entries");
|
||||
const double epsilon = 0.001;
|
||||
SpotEntry entry;
|
||||
|
||||
for (size_t i = 0; i < entries.size(); i += 7) {
|
||||
entry.sourcePos.set(int(entries.data()[i ] + epsilon), int(entries.data()[i + 1] + epsilon));
|
||||
entry.targetPos.set(int(entries.data()[i + 2] + epsilon), int(entries.data()[i + 3] + epsilon));
|
||||
entry.radius = LIM<int>(int (entries.data()[i + 4] + epsilon), SpotParams::minRadius, SpotParams::maxRadius);
|
||||
entry.feather = float(entries.data()[i + 5]);
|
||||
entry.opacity = float(entries.data()[i + 6]);
|
||||
spot.entries.push_back(entry);
|
||||
}
|
||||
|
||||
if (pedited) {
|
||||
pedited->spot.entries = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load post resize sharpening
|
||||
if (keyFile.has_group ("PostResizeSharpening")) {
|
||||
if (keyFile.has_key ("PostResizeSharpening", "Enabled")) {
|
||||
@ -7787,6 +7848,8 @@ bool ProcParams::operator== (const ProcParams& other)
|
||||
&& resize.dataspec == other.resize.dataspec
|
||||
&& resize.width == other.resize.width
|
||||
&& resize.height == other.resize.height
|
||||
&& spot.enabled == other.spot.enabled
|
||||
&& spot.entries == other.spot.entries
|
||||
&& raw.bayersensor.method == other.raw.bayersensor.method
|
||||
&& raw.bayersensor.ccSteps == other.raw.bayersensor.ccSteps
|
||||
&& raw.bayersensor.black0 == other.raw.bayersensor.black0
|
||||
|
@ -946,6 +946,55 @@ public:
|
||||
int height;
|
||||
};
|
||||
|
||||
class SpotEntry
|
||||
{
|
||||
public:
|
||||
Coord sourcePos;
|
||||
Coord targetPos;
|
||||
int radius;
|
||||
float feather;
|
||||
float opacity;
|
||||
|
||||
SpotEntry() : radius(5), feather(1.f), opacity(1.f) {}
|
||||
|
||||
bool operator== (const SpotEntry& other) const
|
||||
{
|
||||
return other.sourcePos == sourcePos && other.targetPos == targetPos &&
|
||||
other.radius == radius && other.feather == feather && other.opacity == opacity;
|
||||
}
|
||||
|
||||
bool operator!= (const SpotEntry& other) const
|
||||
{
|
||||
return other.sourcePos != sourcePos || other.targetPos != targetPos ||
|
||||
other.radius != radius || other.feather != feather || other.opacity != opacity;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters of the dust removal tool
|
||||
*/
|
||||
class SpotParams
|
||||
{
|
||||
|
||||
public:
|
||||
// REWRITE TODO: all parameter should have getter and setter to maintain their validity
|
||||
// the following constant can be used to adjust the tools correctly
|
||||
static const short minRadius;
|
||||
static const short maxRadius;
|
||||
|
||||
bool enabled;
|
||||
std::vector<SpotEntry> entries;
|
||||
|
||||
SpotParams()
|
||||
{
|
||||
setDefaults();
|
||||
}
|
||||
void setDefaults();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parameters of the color spaces used during the processing
|
||||
*/
|
||||
@ -1279,6 +1328,7 @@ public:
|
||||
ChannelMixerParams chmixer; ///< Channel mixer parameters
|
||||
BlackWhiteParams blackwhite; ///< Black & White parameters
|
||||
ResizeParams resize; ///< Resize parameters
|
||||
SpotParams spot; ///< Spot removal tool
|
||||
ColorManagementParams icm; ///< profiles/color spaces used during the image processing
|
||||
RAWParams raw; ///< RAW parameters before demosaicing
|
||||
WaveletParams wavelet; ///< Wavelet parameters
|
||||
|
@ -617,7 +617,7 @@ calculate_scale_mul(float scale_mul[4], const float pre_mul_[4], const float c_w
|
||||
return gain;
|
||||
}
|
||||
|
||||
void RawImageSource::getImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, ToneCurveParams hrp, ColorManagementParams cmp, RAWParams raw )
|
||||
void RawImageSource::getImage (ColorTemp ctemp, int tran, Imagefloat* image, const PreviewProps & pp, ToneCurveParams hrp, ColorManagementParams cmp, RAWParams raw )
|
||||
{
|
||||
MyMutex::MyLock lock(getImageMutex);
|
||||
|
||||
|
@ -137,7 +137,7 @@ public:
|
||||
void cfaboxblur (RawImage *riFlatFile, float* cfablur, int boxH, int boxW );
|
||||
void scaleColors (int winx, int winy, int winw, int winh, const RAWParams &raw); // raw for cblack
|
||||
|
||||
void getImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, ToneCurveParams hrp, ColorManagementParams cmp, RAWParams raw);
|
||||
void getImage (ColorTemp ctemp, int tran, Imagefloat* image, const PreviewProps & pp, ToneCurveParams hrp, ColorManagementParams cmp, RAWParams raw);
|
||||
eSensorType getSensorType ()
|
||||
{
|
||||
return ri != NULL ? ri->getSensorType() : ST_NONE;
|
||||
|
@ -119,8 +119,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = {
|
||||
ALLNORAW, // EvDPDNLuma,
|
||||
ALLNORAW, // EvDPDNChroma,
|
||||
ALLNORAW, // EvDPDNGamma,
|
||||
ALLNORAW, // EvDirPyrEqualizer,
|
||||
ALLNORAW, // EvDirPyrEqlEnabled,
|
||||
ALLNORAW, // EvDirPyrEqualizer,
|
||||
ALLNORAW, // EvDirPyrEqlEnabled,
|
||||
LUMINANCECURVE, // EvLSaturation,
|
||||
LUMINANCECURVE, // EvLaCurve,
|
||||
LUMINANCECURVE, // EvLbCurve,
|
||||
@ -262,7 +262,7 @@ int refreshmap[rtengine::NUMOFEVENTS] = {
|
||||
RGBCURVE, // EvBWAfterCurve
|
||||
RGBCURVE, // EvBWAfterCurveMode
|
||||
RGBCURVE, // EvAutoch
|
||||
0, // --unused--
|
||||
SPOT, // EvSpotEntry
|
||||
RGBCURVE, // EvNeutralBW
|
||||
TRANSFORM, // EvGradientFeather
|
||||
TRANSFORM, // EvGradientStrength
|
||||
@ -275,12 +275,12 @@ int refreshmap[rtengine::NUMOFEVENTS] = {
|
||||
LUMINANCECURVE, // EvLCLCurve
|
||||
LUMINANCECURVE, // EvLLHCurve
|
||||
LUMINANCECURVE, // EvLHHCurve
|
||||
ALLNORAW, // EvDirPyrEqualizerThreshold
|
||||
ALLNORAW, // EvDirPyrEqualizerThreshold
|
||||
ALLNORAW, // EvDPDNenhance
|
||||
RGBCURVE, // EvBWMethodalg
|
||||
ALLNORAW, // EvDirPyrEqualizerSkin
|
||||
ALLNORAW, // EvDirPyrEqlgamutlab
|
||||
ALLNORAW, // EvDirPyrEqualizerHueskin
|
||||
ALLNORAW, // EvDirPyrEqualizerSkin
|
||||
ALLNORAW, // EvDirPyrEqlgamutlab
|
||||
ALLNORAW, // EvDirPyrEqualizerHueskin
|
||||
ALLNORAW, // EvDPDNmedian
|
||||
ALLNORAW, // EvDPDNmedmet
|
||||
RGBCURVE, // EvColorToningEnabled
|
||||
@ -465,7 +465,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = {
|
||||
RETINEX, // EvLradius
|
||||
RETINEX, // EvmapMethod
|
||||
DEMOSAIC, // EvRetinexmapcurve
|
||||
DEMOSAIC, // EvviewMethod
|
||||
ALLNORAW // EvcbdlMethod
|
||||
DEMOSAIC, // EvviewMethod
|
||||
ALLNORAW, // EvcbdlMethod
|
||||
SPOT // EvSpotEnabled
|
||||
};
|
||||
|
||||
|
@ -20,15 +20,16 @@
|
||||
#define __REFRESHMAP__
|
||||
|
||||
// Use M_VOID if you wish to update the proc params without updating the preview at all !
|
||||
#define M_VOID (1<<16)
|
||||
#define M_VOID (1<<31)
|
||||
// Use M_MINUPDATE if you wish to update the preview without modifying the image (think about it like a "refreshPreview")
|
||||
// Must NOT be used with other event (i.e. will be used for MINUPDATE only)
|
||||
#define M_MINUPDATE (1<<15)
|
||||
#define M_MINUPDATE (1<<30)
|
||||
// Force high quality
|
||||
#define M_HIGHQUAL (1<<14)
|
||||
#define M_HIGHQUAL (1<<29)
|
||||
|
||||
// Elementary functions that can be done to
|
||||
// the preview image when an event occurs
|
||||
#define M_SPOT (1<<14)
|
||||
#define M_MONITOR (1<<13)
|
||||
#define M_RETINEX (1<<12)
|
||||
#define M_CROP (1<<11)
|
||||
@ -46,22 +47,23 @@
|
||||
|
||||
// Bitfield of functions to do to the preview image when an event occurs
|
||||
// Use those or create new ones for your new events
|
||||
#define FIRST (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) // without HIGHQUAL
|
||||
#define ALL (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) // without HIGHQUAL
|
||||
#define DARKFRAME (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define FLATFIELD (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define DEMOSAIC (M_RAW|M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define ALLNORAW (M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define TRANSFORM (M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define AUTOEXP (M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define RGBCURVE (M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define LUMINANCECURVE (M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define SHARPENING (M_LUMINANCE|M_COLOR)
|
||||
#define IMPULSEDENOISE (M_LUMINANCE|M_COLOR)
|
||||
#define DEFRINGE (M_LUMINANCE|M_COLOR)
|
||||
#define DIRPYRDENOISE (M_LUMINANCE|M_COLOR)
|
||||
#define DIRPYREQUALIZER (M_LUMINANCE|M_COLOR)
|
||||
#define GAMMA (M_LUMINANCE|M_COLOR)
|
||||
#define FIRST (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_SPOT|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) // without HIGHQUAL
|
||||
#define ALL (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_SPOT|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) // without HIGHQUAL
|
||||
#define DARKFRAME (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_SPOT|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define FLATFIELD (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_SPOT|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define DEMOSAIC (M_RAW|M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_SPOT|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define ALLNORAW (M_INIT|M_LINDENOISE|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_SPOT|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define TRANSFORM (M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_SPOT|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define AUTOEXP (M_AUTOEXP|M_SPOT|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define SPOT (M_SPOT|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define RGBCURVE (M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define LUMINANCECURVE (M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define SHARPENING (M_LUMINANCE|M_COLOR)
|
||||
#define IMPULSEDENOISE (M_LUMINANCE|M_COLOR)
|
||||
#define DEFRINGE (M_LUMINANCE|M_COLOR)
|
||||
#define DIRPYRDENOISE (M_LUMINANCE|M_COLOR)
|
||||
#define DIRPYREQUALIZER (M_LUMINANCE|M_COLOR)
|
||||
#define GAMMA (M_LUMINANCE|M_COLOR)
|
||||
#define CROP M_CROP
|
||||
#define RESIZE M_VOID
|
||||
#define EXIF M_VOID
|
||||
|
@ -764,6 +764,10 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p
|
||||
shmap->update (baseImg, shradius, ipf.lumimul, params.sh.hq, 1);
|
||||
}
|
||||
|
||||
if (params.spot.enabled && !params.spot.entries.empty()) {
|
||||
ipf.removeSpots(baseImg, params.spot.entries, pp);
|
||||
}
|
||||
|
||||
// RGB processing
|
||||
|
||||
LUTf curve1 (65536);
|
||||
|
370
rtengine/spot.cc
Normal file
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* 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 "improcfun.h"
|
||||
#include "alpha.h"
|
||||
|
||||
namespace rtengine
|
||||
{
|
||||
|
||||
/* Code taken from Gimp 2.8.10 and converted for RawTherapee by Jean-Christophe FRISCH (aka Hombre) on 02.19.2014
|
||||
*
|
||||
* ORIGINAL NOTES
|
||||
*
|
||||
* The method used here is similar to the lighting invariant correction
|
||||
* method but slightly different: we do not divide the RGB components,
|
||||
* but substract them I2 = I0 - I1, where I0 is the sample image to be
|
||||
* corrected, I1 is the reference pattern. Then we solve DeltaI=0
|
||||
* (Laplace) with I2 Dirichlet conditions at the borders of the
|
||||
* mask. The solver is a unoptimized red/black checker Gauss-Siedel
|
||||
* with an over-relaxation factor of 1.8. It can benefit from a
|
||||
* multi-grid evaluation of an initial solution before the main
|
||||
* iteration loop.
|
||||
*
|
||||
* I reduced the convergence criteria to 0.1% (0.001) as we are
|
||||
* dealing here with RGB integer components, more is overkill.
|
||||
*
|
||||
* Jean-Yves Couleaud cjyves@free.fr
|
||||
*/
|
||||
|
||||
/* Original Algorithm Design:
|
||||
*
|
||||
* T. Georgiev, "Photoshop Healing Brush: a Tool for Seamless Cloning
|
||||
* http://www.tgeorgiev.net/Photoshop_Healing.pdf
|
||||
*/
|
||||
void ImProcFunctions::removeSpots (Imagefloat* img, const std::vector<SpotEntry> &entries, const PreviewProps &pp)
|
||||
{
|
||||
Alpha mask;
|
||||
|
||||
//printf("img(%04d, %04d)\n", img->width, img->height);
|
||||
|
||||
for (const auto entry : entries) {
|
||||
float srcX = float (entry.sourcePos.x);
|
||||
float srcY = float (entry.sourcePos.y);
|
||||
float dstX = float (entry.targetPos.x);
|
||||
float dstY = float (entry.targetPos.y);
|
||||
//float radius = float (entry.radius) + 0.5f;
|
||||
|
||||
float featherRadius = entry.radius * (1.f + entry.feather);
|
||||
int scaledFeatherRadius = featherRadius / pp.skip;
|
||||
|
||||
int src_XMin = int ((srcX - featherRadius - pp.x) / float (pp.skip) + 0.5f);
|
||||
int src_XMax = int ((srcX + featherRadius - pp.x) / float (pp.skip) + 0.5f);
|
||||
int src_YMin = int ((srcY - featherRadius - pp.y) / float (pp.skip) + 0.5f);
|
||||
int src_YMax = int ((srcY + featherRadius - pp.y) / float (pp.skip) + 0.5f);
|
||||
|
||||
int dst_XMin = int ((dstX - featherRadius - pp.x) / float (pp.skip) + 0.5f);
|
||||
int dst_XMax = int ((dstX + featherRadius - pp.x) / float (pp.skip) + 0.5f);
|
||||
int dst_YMin = int ((dstY - featherRadius - pp.y) / float (pp.skip) + 0.5f);
|
||||
int dst_YMax = int ((dstY + featherRadius - pp.y) / float (pp.skip) + 0.5f);
|
||||
|
||||
//printf(" -> X: %04d > %04d\n -> Y: %04d > %04d\n", dst_XMin, dst_XMax, dst_YMin, dst_YMax);
|
||||
|
||||
// scaled spot is too small, we do not preview it
|
||||
if (scaledFeatherRadius < 2 && pp.skip != 1) {
|
||||
#ifndef NDEBUG
|
||||
if (options.rtSettings.verbose) {
|
||||
printf ("Skipping spot located at %d x %d, too small for the preview zoom rate\n", entry.sourcePos.x, entry.sourcePos.y);
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
// skipping entries totally transparent
|
||||
if (entry.opacity == 0.) {
|
||||
#ifndef NDEBUG
|
||||
if (options.rtSettings.verbose) {
|
||||
printf ("Skipping spot located at %d x %d: opacity=%.3f\n", entry.sourcePos.x, entry.sourcePos.y, entry.opacity);
|
||||
}
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
|
||||
// skipping entries where the source circle isn't completely inside the image bounds
|
||||
if (src_XMin < 0 || src_XMax >= img->width || src_YMin < 0 || src_YMax >= img->height) {
|
||||
#ifndef NDEBUG
|
||||
if (options.rtSettings.verbose) {
|
||||
printf ("Skipping spot located at %d x %d, from the data at %d x %d, radius=%d, feather=%.3f, opacity=%.3f: source out of bounds\n", entry.sourcePos.x, entry.sourcePos.y, entry.targetPos.x, entry.targetPos.y, entry.radius, entry.feather, entry.opacity);
|
||||
printf ("%d < 0 || %d >= %d || %d < 0 || %d >= %d\n",
|
||||
src_XMin, src_XMax, img->width, src_YMin, src_YMax, img->height);
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
// skipping entries where the dest circle is completely outside the image bounds
|
||||
if (dst_XMin >= img->width || dst_XMax <= 0 || dst_YMin >= img->height || dst_YMax <= 0) {
|
||||
#ifndef NDEBUG
|
||||
if (options.rtSettings.verbose) {
|
||||
printf ("Skipping spot located at %d x %d, from the data at %d x %d, radius=%d, feather=%.3f, opacity=%.3f: source out of bounds\n", entry.sourcePos.x, entry.sourcePos.y, entry.targetPos.x, entry.targetPos.y, entry.radius, entry.feather, entry.opacity);
|
||||
printf ("%d >= %d || %d <= 0 || %d >= %d || %d <= 0\n",
|
||||
dst_XMin, img->width, dst_XMax, dst_YMin, img->height, dst_YMax);
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
// ----------------- Core function -----------------
|
||||
|
||||
#if 0
|
||||
int scaledPPX = pp.x / pp.skip;
|
||||
int scaledPPY = pp.y / pp.skip;
|
||||
int scaledPPW = pp.w / pp.skip + (pp.w % pp.skip > 0);
|
||||
int scaledPPH = pp.h / pp.skip + (pp.h % pp.skip > 0);
|
||||
|
||||
int sizeX = dst_XMax - dst_XMin + 1;
|
||||
int sizeY = dst_YMax - dst_YMin + 1;
|
||||
|
||||
Imagefloat matrix (sizeX, sizeY);
|
||||
Imagefloat solution (sizeX, sizeY);
|
||||
|
||||
// allocate the mask and draw it
|
||||
mask.setSize (sizeX, sizeY);
|
||||
{
|
||||
Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create (mask.getSurface());
|
||||
|
||||
// clear the bitmap
|
||||
cr->set_source_rgba (0., 0., 0., 0.);
|
||||
cr->rectangle (0., 0., sizeX, sizeY);
|
||||
cr->set_line_width (0.);
|
||||
cr->fill();
|
||||
|
||||
// draw the mask
|
||||
cr->set_antialias (Cairo::ANTIALIAS_GRAY);
|
||||
cr->set_line_width (featherRadius);
|
||||
double gradientCenterX = double (sizeX) / 2.;
|
||||
double gradientCenterY = double (sizeY) / 2.;
|
||||
{
|
||||
Cairo::RefPtr<Cairo::RadialGradient> radialGradient = Cairo::RadialGradient::create (
|
||||
gradientCenterX, gradientCenterY, radius,
|
||||
gradientCenterX, gradientCenterY, featherRadius
|
||||
);
|
||||
radialGradient->add_color_stop_rgb (0., 0., 0., 1.);
|
||||
radialGradient->add_color_stop_rgb (1., 0., 0., 0.);
|
||||
cr->set_source_rgba (0., 0., 0., 1.);
|
||||
cr->mask (radialGradient);
|
||||
cr->rectangle (0., 0., sizeX, sizeY);
|
||||
cr->fill();
|
||||
}
|
||||
}
|
||||
|
||||
// copy the src part to a temporary buffer to avoid possible self modified source
|
||||
Imagefloat *srcBuff = img->copySubRegion (srcX, srcY, sizeX, sizeY);
|
||||
|
||||
|
||||
// subtract pattern to image and store the result as a double in matrix
|
||||
for (int i = 0, i2 = dst_YMin; i2 < sizeY - 1; ++i, ++i2) {
|
||||
for (int j = 0, j2 = dst_XMin; i2 < sizeX - 1; ++j, ++j2) {
|
||||
matrix.r (i, j) = img->r (i2, j2) - srcBuff->r (i, j);
|
||||
matrix.g (i, j) = img->g (i2, j2) - srcBuff->g (i, j);
|
||||
matrix.b (i, j) = img->b (i2, j2) - srcBuff->b (i, j);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FIXME: is a faster implementation needed?
|
||||
#define EPSILON 0.001
|
||||
#define MAX_ITER 500
|
||||
|
||||
// repeat until convergence or max iterations
|
||||
for (int n = 0; n < MAX_ITER; ++n) {
|
||||
|
||||
printf ("<<< n=#%d\n", n);
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/* Perform one iteration of the Laplace solver for matrix. Store the
|
||||
* result in solution and get the square of the cumulative error
|
||||
* of the solution.
|
||||
*/
|
||||
int i, j;
|
||||
double tmp, diff;
|
||||
double sqr_err_r = 0.0;
|
||||
double sqr_err_g = 0.0;
|
||||
double sqr_err_b = 0.0;
|
||||
const double w = 1.80 * 0.25; /* Over-relaxation = 1.8 */
|
||||
|
||||
// we use a red/black checker model of the discretization grid
|
||||
|
||||
// do reds
|
||||
for (i = 0; i < matrix.getHeight(); ++i) {
|
||||
for (j = i % 2; j < matrix.getWidth(); j += 2) {
|
||||
printf ("/%d,%d", j, i);
|
||||
|
||||
if ((0 == mask (i, j)) || (i == 0) || (i == (matrix.getHeight() - 1)) || (j == 0) || (j == (matrix.getWidth() - 1))) {
|
||||
// do nothing at the boundary or outside mask
|
||||
solution.r (i, j) = matrix.r (i, j);
|
||||
solution.g (i, j) = matrix.g (i, j);
|
||||
solution.b (i, j) = matrix.b (i, j);
|
||||
} else {
|
||||
// Use Gauss Siedel to get the correction factor then over-relax it
|
||||
tmp = solution.r (i, j);
|
||||
solution.r (i, j) = (matrix.r (i, j) + w *
|
||||
(
|
||||
matrix.r (i, j - 1) + // west
|
||||
matrix.r (i, j + 1) + // east
|
||||
matrix.r (i - 1, j) + // north
|
||||
matrix.r (i + 1, j) - 4.0 * matrix.r (i, j) // south
|
||||
)
|
||||
);
|
||||
|
||||
diff = solution.r (i, j) - tmp;
|
||||
sqr_err_r += diff * diff;
|
||||
|
||||
|
||||
tmp = solution.g (i, j);
|
||||
solution.g (i, j) = (matrix.g (i, j) + w *
|
||||
(
|
||||
matrix.g (i, j - 1) + // west
|
||||
matrix.g (i, j + 1) + // east
|
||||
matrix.g (i - 1, j) + // north
|
||||
matrix.g (i + 1, j) - 4.0 * matrix.g (i, j) // south
|
||||
)
|
||||
);
|
||||
|
||||
diff = solution.g (i, j) - tmp;
|
||||
sqr_err_g += diff * diff;
|
||||
|
||||
|
||||
|
||||
tmp = solution.b (i, j);
|
||||
solution.b (i, j) = (matrix.b (i, j) + w *
|
||||
(
|
||||
matrix.b (i, j - 1) + // west
|
||||
matrix.b (i, j + 1) + // east
|
||||
matrix.b (i - 1, j) + // north
|
||||
matrix.b (i + 1, j) - 4.0 * matrix.b (i, j) // south
|
||||
)
|
||||
);
|
||||
|
||||
diff = solution.b (i, j) - tmp;
|
||||
sqr_err_b += diff * diff;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Do blacks
|
||||
*
|
||||
* As we've done the reds earlier, we can use them right now to
|
||||
* accelerate the convergence. So we have "solution" in the solver
|
||||
* instead of "matrix" above
|
||||
*/
|
||||
for (i = 0; i < matrix.getHeight(); i++) {
|
||||
for (j = (i % 2) ? 0 : 1; j < matrix.getWidth(); j += 2) {
|
||||
printf (":%d,%d", j, i);
|
||||
|
||||
if ((0 == mask (i, j)) || (i == 0) || (i == (matrix.getHeight() - 1)) || (j == 0) || (j == (matrix.getWidth() - 1))) {
|
||||
// do nothing at the boundary or outside mask
|
||||
solution.r (i, j) = matrix.r (i, j);
|
||||
solution.g (i, j) = matrix.g (i, j);
|
||||
solution.b (i, j) = matrix.b (i, j);
|
||||
} else {
|
||||
// Use Gauss Siedel to get the correction factor then over-relax it
|
||||
tmp = solution.r (i, j);
|
||||
solution.r (i, j) = (matrix.r (i, j) + w *
|
||||
(
|
||||
matrix.r (i, j - 1) + // west
|
||||
matrix.r (i, j + 1) + // east
|
||||
matrix.r (i - 1, j) + // north
|
||||
matrix.r (i + 1, j) - 4.0 * matrix.r (i, j) // south
|
||||
)
|
||||
);
|
||||
|
||||
diff = solution.r (i, j) - tmp;
|
||||
sqr_err_r += diff * diff;
|
||||
|
||||
|
||||
|
||||
tmp = solution.g (i, j);
|
||||
solution.g (i, j) = (matrix.g (i, j) + w *
|
||||
(
|
||||
matrix.g (i, j - 1) + // west
|
||||
matrix.g (i, j + 1) + // east
|
||||
matrix.g (i - 1, j) + // north
|
||||
matrix.g (i + 1, j) - 4.0 * matrix.g (i, j) // south
|
||||
)
|
||||
);
|
||||
|
||||
diff = solution.g (i, j) - tmp;
|
||||
sqr_err_g += diff * diff;
|
||||
|
||||
|
||||
|
||||
tmp = solution.b (i, j);
|
||||
solution.b (i, j) = (matrix.b (i, j) + w *
|
||||
(
|
||||
matrix.b (i, j - 1) + // west
|
||||
matrix.b (i, j + 1) + // east
|
||||
matrix.b (i - 1, j) + // north
|
||||
matrix.b (i + 1, j) - 4.0 * matrix.b (i, j) // south
|
||||
)
|
||||
);
|
||||
|
||||
diff = solution.b (i, j) - tmp;
|
||||
sqr_err_b += diff * diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
// copy solution to matrix
|
||||
solution.copyData (&matrix);
|
||||
|
||||
if (sqr_err_r < EPSILON && sqr_err_g < EPSILON && sqr_err_b < EPSILON) {
|
||||
break;
|
||||
}
|
||||
|
||||
printf ("\n>>> n=#%d\n", n);
|
||||
}
|
||||
|
||||
printf ("\n");
|
||||
#endif
|
||||
|
||||
// add solution to original image and store in tempPR
|
||||
for (int i = 0, i2 = dst_YMin; i2 < dst_YMax - 1; ++i, ++i2) {
|
||||
if (i2 < 0 || i2 >= img->height) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0, j2 = dst_XMin; j2 < dst_XMax - 1; ++j, ++j2) {
|
||||
if (j2 < 0 || j2 >= img->width) {
|
||||
continue;
|
||||
}
|
||||
//float c2 = float (mask (i, j)) / 255.f;
|
||||
//float c1 = 1.f - c2;
|
||||
//resultPR->r(i,j) = (unsigned char) CLAMP0255 ( ROUND( double(first->r(i,j)) + double(secondPR->r(i,j)) ) );
|
||||
|
||||
|
||||
img->r (i2, j2) = 65535.0f; //img->r(i2,j2)*c1 + srcBuff->r(i,j)*c2;
|
||||
img->g (i2, j2) = 0.0f; //img->g(i2,j2)*c1 + srcBuff->g(i,j)*c2;
|
||||
img->b (i2, j2) = 0.0f; //img->b(i2,j2)*c1 + srcBuff->b(i,j)*c2;
|
||||
/*
|
||||
img->r(i2,j2) = img->r(i2,j2)*c1 + (solution.r(i,j) + srcBuff->r(i,j))*c2;
|
||||
img->g(i2,j2) = img->g(i2,j2)*c1 + (solution.g(i,j) + srcBuff->g(i,j))*c2;
|
||||
img->b(i2,j2) = img->b(i2,j2)*c1 + (solution.b(i,j) + srcBuff->b(i,j))*c2;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -213,7 +213,7 @@ int StdImageSource::load (Glib::ustring fname, bool batch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StdImageSource::getImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, ToneCurveParams hrp, ColorManagementParams cmp, RAWParams raw)
|
||||
void StdImageSource::getImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps & pp, ToneCurveParams hrp, ColorManagementParams cmp, RAWParams raw)
|
||||
{
|
||||
|
||||
// the code will use OpenMP as of now.
|
||||
|
@ -45,7 +45,7 @@ public:
|
||||
~StdImageSource ();
|
||||
|
||||
int load (Glib::ustring fname, bool batch = false);
|
||||
void getImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps pp, ToneCurveParams hrp, ColorManagementParams cmp, RAWParams raw);
|
||||
void getImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewProps & pp, ToneCurveParams hrp, ColorManagementParams cmp, RAWParams raw);
|
||||
ColorTemp getWB ()
|
||||
{
|
||||
return wb;
|
||||
|
@ -6,7 +6,7 @@ set (BASESOURCEFILES
|
||||
cachemanager.cc cacheimagedata.cc shcselector.cc perspective.cc thresholdselector.cc thresholdadjuster.cc
|
||||
clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc coloredbar.cc edit.cc coordinateadjuster.cc
|
||||
coarsepanel.cc cacorrection.cc chmixer.cc blackwhite.cc
|
||||
resize.cc icmpanel.cc crop.cc shadowshighlights.cc
|
||||
resize.cc icmpanel.cc crop.cc shadowshighlights.cc spot.cc
|
||||
impulsedenoise.cc dirpyrdenoise.cc epd.cc
|
||||
exifpanel.cc toolpanel.cc lensprofile.cc
|
||||
sharpening.cc vibrance.cc rgbcurves.cc colortoning.cc
|
||||
|
@ -342,6 +342,10 @@ void ParamsEdited::set (bool v)
|
||||
resize.width = v;
|
||||
resize.height = v;
|
||||
resize.enabled = v;
|
||||
|
||||
spot.enabled = v;
|
||||
spot.entries = v;
|
||||
|
||||
icm.input = v;
|
||||
icm.toneCurve = v;
|
||||
icm.applyLookTable = v;
|
||||
@ -835,6 +839,8 @@ void ParamsEdited::initFrom (const std::vector<rtengine::procparams::ProcParams>
|
||||
resize.width = resize.width && p.resize.width == other.resize.width;
|
||||
resize.height = resize.height && p.resize.height == other.resize.height;
|
||||
resize.enabled = resize.enabled && p.resize.enabled == other.resize.enabled;
|
||||
spot.enabled = spot.enabled && p.spot.enabled == other.spot.enabled;
|
||||
spot.entries = spot.entries && p.spot.entries == other.spot.entries;
|
||||
icm.input = icm.input && p.icm.input == other.icm.input;
|
||||
icm.toneCurve = icm.toneCurve && p.icm.toneCurve == other.icm.toneCurve;
|
||||
icm.applyLookTable = icm.applyLookTable && p.icm.applyLookTable == other.icm.applyLookTable;
|
||||
@ -2164,6 +2170,14 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten
|
||||
toEdit.resize.enabled = mods.resize.enabled;
|
||||
}
|
||||
|
||||
if (spot.enabled) {
|
||||
toEdit.spot.enabled = mods.spot.enabled;
|
||||
}
|
||||
|
||||
if (spot.entries) {
|
||||
toEdit.spot.entries = mods.spot.entries;
|
||||
}
|
||||
|
||||
if (icm.input) {
|
||||
toEdit.icm.input = mods.icm.input;
|
||||
}
|
||||
|
@ -529,6 +529,13 @@ public:
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
class SpotParamsEdited
|
||||
{
|
||||
public:
|
||||
bool enabled;
|
||||
bool entries;
|
||||
};
|
||||
|
||||
class ColorManagementParamsEdited
|
||||
{
|
||||
|
||||
@ -773,6 +780,7 @@ public:
|
||||
ChannelMixerParamsEdited chmixer;
|
||||
BlackWhiteParamsEdited blackwhite;
|
||||
ResizeParamsEdited resize;
|
||||
SpotParamsEdited spot;
|
||||
ColorManagementParamsEdited icm;
|
||||
RAWParamsEdited raw;
|
||||
DirPyrEqualizerParamsEdited dirpyrequalizer;
|
||||
|
690
rtgui/spot.cc
Normal file
@ -0,0 +1,690 @@
|
||||
/*
|
||||
* This file is part of RawTherapee.
|
||||
*/
|
||||
#include "spot.h"
|
||||
#include "rtimage.h"
|
||||
#include <iomanip>
|
||||
#include "../rtengine/rt_math.h"
|
||||
#include "guiutils.h"
|
||||
|
||||
using namespace rtengine;
|
||||
using namespace rtengine::procparams;
|
||||
|
||||
#define STATIC_VISIBLE_OBJ_NBR 6
|
||||
#define STATIC_MO_OBJ_NBR 6
|
||||
|
||||
Spot::Spot() : FoldableToolPanel (this, "spot", M ("TP_SPOT_LABEL"), true, true), EditSubscriber (ET_OBJECTS), lastObject (-1), activeSpot (-1),
|
||||
sourceIcon ("spot-normal.png", "spot-active.png", "spot-active.png", "spot-prelight.png", "", Geometry::DP_CENTERCENTER), editedCheckBox(NULL)
|
||||
{
|
||||
countLabel = Gtk::manage (new Gtk::Label (Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0)));
|
||||
|
||||
edit = Gtk::manage (new Gtk::ToggleButton());
|
||||
edit->add (*Gtk::manage (new RTImage ("editmodehand.png")));
|
||||
editConn = edit->signal_toggled().connect ( sigc::mem_fun (*this, &Spot::editToggled) );
|
||||
|
||||
reset = Gtk::manage (new Gtk::Button ());
|
||||
reset->add (*Gtk::manage (new RTImage ("gtk-undo-ltr-small.png", "gtk-undo-rtl-small.png")));
|
||||
reset->set_relief (Gtk::RELIEF_NONE);
|
||||
reset->set_border_width (0);
|
||||
reset->signal_clicked().connect ( sigc::mem_fun (*this, &Spot::resetPressed) );
|
||||
|
||||
labelBox = Gtk::manage (new Gtk::HBox());
|
||||
labelBox->set_spacing (2);
|
||||
labelBox->pack_start (*countLabel, false, false, 0);
|
||||
labelBox->pack_end (*edit, false, false, 0);
|
||||
labelBox->pack_end (*reset, false, false, 0);
|
||||
pack_start (*labelBox);
|
||||
|
||||
sourceIcon.datum = Geometry::IMAGE;
|
||||
sourceIcon.setActive (false);
|
||||
sourceIcon.state = Geometry::ACTIVE;
|
||||
sourceCircle.datum = Geometry::IMAGE;
|
||||
sourceCircle.setActive (false);
|
||||
sourceCircle.radiusInImageSpace = true;
|
||||
sourceMODisc.datum = Geometry::IMAGE;
|
||||
sourceMODisc.setActive (false);
|
||||
sourceMODisc.radiusInImageSpace = true;
|
||||
sourceMODisc.filled = true;
|
||||
sourceMODisc.innerLineWidth = 0.;
|
||||
targetCircle.datum = Geometry::IMAGE;
|
||||
targetCircle.setActive (false);
|
||||
targetCircle.radiusInImageSpace = true;
|
||||
targetMODisc.datum = Geometry::IMAGE;
|
||||
targetMODisc.setActive (false);
|
||||
targetMODisc.radiusInImageSpace = true;
|
||||
targetMODisc.filled = true;
|
||||
targetMODisc.innerLineWidth = 0.;
|
||||
sourceFeatherCircle.datum = Geometry::IMAGE;
|
||||
sourceFeatherCircle.setActive (false);
|
||||
sourceFeatherCircle.radiusInImageSpace = true;
|
||||
targetFeatherCircle.datum = Geometry::IMAGE;
|
||||
targetFeatherCircle.setActive (false);
|
||||
targetFeatherCircle.radiusInImageSpace = true;
|
||||
link.datum = Geometry::IMAGE;
|
||||
link.setActive (false);
|
||||
|
||||
show_all();
|
||||
}
|
||||
|
||||
Spot::~Spot()
|
||||
{
|
||||
// delete all dynamically allocated geometry
|
||||
if (EditSubscriber::visibleGeometry.size()) {
|
||||
for (size_t i = 0; i < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i) { // static visible geometry at the end if the list
|
||||
delete EditSubscriber::visibleGeometry.at (i);
|
||||
}
|
||||
}
|
||||
// We do not delete the mouseOverGeometry, because the referenced objects are either
|
||||
// shared with visibleGeometry or instantiated by the class's ctor
|
||||
}
|
||||
|
||||
void Spot::read (const ProcParams* pp, const ParamsEdited* pedited)
|
||||
{
|
||||
disableListener ();
|
||||
|
||||
size_t oldSize = spots.size();
|
||||
spots = pp->spot.entries;
|
||||
|
||||
if (pedited) {
|
||||
set_inconsistent (multiImage && !pedited->spot.enabled);
|
||||
}
|
||||
|
||||
setEnabled (pp->spot.enabled);
|
||||
lastEnabled = pp->spot.enabled;
|
||||
|
||||
if (spots.size() != oldSize) {
|
||||
createGeometry();
|
||||
}
|
||||
|
||||
updateGeometry();
|
||||
|
||||
enableListener ();
|
||||
}
|
||||
|
||||
void Spot::write (ProcParams* pp, ParamsEdited* pedited)
|
||||
{
|
||||
pp->spot.enabled = getEnabled();
|
||||
pp->spot.entries = spots;
|
||||
|
||||
if (pedited) {
|
||||
pedited->spot.enabled = !get_inconsistent();
|
||||
pedited->spot.entries = !editedCheckBox->get_active();
|
||||
}
|
||||
}
|
||||
|
||||
void Spot::resetPressed()
|
||||
{
|
||||
if (batchMode) {
|
||||
// no need to handle the Geometry in batch mode, since point editing is disabled
|
||||
spots.clear();
|
||||
editedConn.block (true);
|
||||
editedCheckBox->set_active (true);
|
||||
editedConn.block (false);
|
||||
|
||||
if (listener) {
|
||||
listener->panelChanged (EvSpotEntry, Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), spots.size()));
|
||||
}
|
||||
} else {
|
||||
if (!spots.empty()) {
|
||||
spots.clear();
|
||||
activeSpot = -1;
|
||||
lastObject = -1;
|
||||
createGeometry();
|
||||
updateGeometry();
|
||||
|
||||
if (listener) {
|
||||
listener->panelChanged (EvSpotEntry, Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spot::setBatchMode (bool batchMode)
|
||||
{
|
||||
ToolPanel::setBatchMode (batchMode);
|
||||
|
||||
if (batchMode) {
|
||||
removeIfThere (labelBox, edit, false);
|
||||
|
||||
if (!editedCheckBox) {
|
||||
removeIfThere (labelBox, countLabel, false);
|
||||
countLabel = NULL;
|
||||
editedCheckBox = Gtk::manage (new Gtk::CheckButton (Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0)));
|
||||
labelBox->pack_start (*editedCheckBox, Gtk::PACK_SHRINK, 2);
|
||||
labelBox->reorder_child (*editedCheckBox, 0);
|
||||
editedConn = editedCheckBox->signal_toggled().connect ( sigc::mem_fun (*this, &Spot::editedToggled) );
|
||||
editedCheckBox->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spot::editedToggled ()
|
||||
{
|
||||
if (listener) {
|
||||
listener->panelChanged (EvSpotEntry, !editedCheckBox->get_active() ? M ("GENERAL_UNCHANGED") : Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), spots.size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Spot::enabledChanged ()
|
||||
{
|
||||
if (listener) {
|
||||
if (get_inconsistent()) {
|
||||
listener->panelChanged (EvSpotEnabled, M("GENERAL_UNCHANGED"));
|
||||
} else if (getEnabled()) {
|
||||
listener->panelChanged (EvSpotEnabled, M("GENERAL_ENABLED"));
|
||||
} else {
|
||||
listener->panelChanged (EvSpotEnabled, M("GENERAL_DISABLED"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spot::setEditProvider (EditDataProvider* provider)
|
||||
{
|
||||
EditSubscriber::setEditProvider (provider);
|
||||
}
|
||||
|
||||
void Spot::editToggled ()
|
||||
{
|
||||
if (edit->get_active()) {
|
||||
subscribe();
|
||||
} else {
|
||||
unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
Geometry* Spot::getVisibleGeometryFromMO (int MOID)
|
||||
{
|
||||
if (MOID == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (MOID == 0) {
|
||||
return getActiveSpotIcon();
|
||||
}
|
||||
|
||||
if (MOID == 1) { // sourceMODisc
|
||||
return &sourceIcon;
|
||||
}
|
||||
|
||||
return EditSubscriber::mouseOverGeometry.at (MOID);
|
||||
}
|
||||
|
||||
void Spot::createGeometry ()
|
||||
{
|
||||
int nbrEntry = spots.size();
|
||||
|
||||
if (!batchMode) {
|
||||
countLabel->set_text(Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), nbrEntry));
|
||||
}
|
||||
|
||||
//printf("CreateGeometry(%d)\n", nbrEntry);
|
||||
// delete all dynamically allocated geometry
|
||||
if (EditSubscriber::visibleGeometry.size() > STATIC_VISIBLE_OBJ_NBR)
|
||||
for (size_t i = 0; i < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i) { // static visible geometry at the end if the list
|
||||
delete EditSubscriber::visibleGeometry.at (i);
|
||||
}
|
||||
|
||||
size_t i = 0, j = 0;
|
||||
EditSubscriber::mouseOverGeometry.resize (STATIC_MO_OBJ_NBR + nbrEntry);
|
||||
EditSubscriber::visibleGeometry.resize (nbrEntry + STATIC_VISIBLE_OBJ_NBR);
|
||||
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &targetMODisc; // STATIC_MO_OBJ_NBR + 0
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &sourceMODisc; // STATIC_MO_OBJ_NBR + 1
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &targetCircle; // STATIC_MO_OBJ_NBR + 2
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &sourceCircle; // STATIC_MO_OBJ_NBR + 3
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &targetFeatherCircle; // STATIC_MO_OBJ_NBR + 4
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &sourceFeatherCircle; // STATIC_MO_OBJ_NBR + 5
|
||||
|
||||
// recreate all spots geometry
|
||||
Cairo::RefPtr<Cairo::ImageSurface> normalImg = sourceIcon.getNormalImg();
|
||||
Cairo::RefPtr<Cairo::ImageSurface> prelightImg = sourceIcon.getPrelightImg();
|
||||
Cairo::RefPtr<Cairo::ImageSurface> activeImg = sourceIcon.getActiveImg();
|
||||
|
||||
for (; j < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i, ++j) {
|
||||
EditSubscriber::mouseOverGeometry.at (i) = EditSubscriber::visibleGeometry.at (j) = new OPIcon (normalImg, activeImg, prelightImg, Cairo::RefPtr<Cairo::ImageSurface>(NULL), Cairo::RefPtr<Cairo::ImageSurface>(NULL), Geometry::DP_CENTERCENTER);
|
||||
EditSubscriber::visibleGeometry.at (j)->setActive (true);
|
||||
EditSubscriber::visibleGeometry.at (j)->datum = Geometry::IMAGE;
|
||||
EditSubscriber::visibleGeometry.at (j)->state = Geometry::NORMAL;
|
||||
//printf("mouseOverGeometry.at(%d) = %p\n", (unsigned int)i, (void*)EditSubscriber::mouseOverGeometry.at(i));
|
||||
}
|
||||
|
||||
EditSubscriber::visibleGeometry.at (j++) = &sourceIcon; // STATIC_VISIBLE_OBJ_NBR + 0
|
||||
EditSubscriber::visibleGeometry.at (j++) = &sourceFeatherCircle; // STATIC_VISIBLE_OBJ_NBR + 1
|
||||
EditSubscriber::visibleGeometry.at (j++) = &link; // STATIC_VISIBLE_OBJ_NBR + 2
|
||||
EditSubscriber::visibleGeometry.at (j++) = &sourceCircle; // STATIC_VISIBLE_OBJ_NBR + 3
|
||||
EditSubscriber::visibleGeometry.at (j++) = &targetFeatherCircle; // STATIC_VISIBLE_OBJ_NBR + 4
|
||||
EditSubscriber::visibleGeometry.at (j++) = &targetCircle; // STATIC_VISIBLE_OBJ_NBR + 5
|
||||
}
|
||||
|
||||
void Spot::updateGeometry()
|
||||
{
|
||||
EditDataProvider* dataProvider = getEditProvider();
|
||||
|
||||
if (dataProvider) {
|
||||
int imW, imH;
|
||||
dataProvider->getImageSize (imW, imH);
|
||||
|
||||
if (activeSpot > -1) {
|
||||
// Target point circle
|
||||
targetCircle.center = spots.at (activeSpot).targetPos;
|
||||
targetCircle.radius = spots.at (activeSpot).radius;
|
||||
targetCircle.setActive (true);
|
||||
|
||||
// Target point Mouse Over disc
|
||||
targetMODisc.center = targetCircle.center;
|
||||
targetMODisc.radius = targetCircle.radius;
|
||||
targetMODisc.setActive (true);
|
||||
|
||||
// Source point Icon
|
||||
sourceIcon.position = spots.at (activeSpot).sourcePos;
|
||||
sourceIcon.setActive (true);
|
||||
|
||||
// Source point circle
|
||||
sourceCircle.center = spots.at (activeSpot).sourcePos;
|
||||
sourceCircle.radius = spots.at (activeSpot).radius;
|
||||
sourceCircle.setActive (true);
|
||||
|
||||
// Source point Mouse Over disc
|
||||
sourceMODisc.center = sourceCircle.center;
|
||||
sourceMODisc.radius = sourceCircle.radius;
|
||||
sourceMODisc.setActive (true);
|
||||
|
||||
// Target point feather circle
|
||||
targetFeatherCircle.center = spots.at (activeSpot).targetPos;
|
||||
targetFeatherCircle.radius = float (spots.at (activeSpot).radius) * (1.f + spots.at (activeSpot).feather);
|
||||
targetFeatherCircle.radiusInImageSpace = true;
|
||||
targetFeatherCircle.setActive (true);
|
||||
|
||||
// Source point feather circle
|
||||
sourceFeatherCircle.center = spots.at (activeSpot).sourcePos;
|
||||
sourceFeatherCircle.radius = targetFeatherCircle.radius;
|
||||
sourceFeatherCircle.setActive (true);
|
||||
|
||||
// Link line
|
||||
PolarCoord p;
|
||||
p = targetCircle.center - sourceCircle.center;
|
||||
|
||||
if (p.radius > sourceCircle.radius + targetCircle.radius) {
|
||||
PolarCoord p2 (sourceCircle.radius, p.angle);
|
||||
Coord p3;
|
||||
p3 = p2;
|
||||
link.begin = sourceCircle.center + p3;
|
||||
p2.set (targetCircle.radius, p.angle + 180);
|
||||
p3 = p2;
|
||||
link.end = targetCircle.center + p3;
|
||||
link.setActive (true);
|
||||
} else {
|
||||
link.setActive (false);
|
||||
}
|
||||
} else {
|
||||
targetCircle.setActive (false);
|
||||
targetMODisc.setActive (false);
|
||||
sourceIcon.setActive (false);
|
||||
sourceCircle.setActive (false);
|
||||
sourceMODisc.setActive (false);
|
||||
targetFeatherCircle.setActive (false);
|
||||
sourceFeatherCircle.setActive (false);
|
||||
link.setActive (false);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < spots.size(); ++i) {
|
||||
// Target point icon
|
||||
OPIcon* geom = static_cast<OPIcon*> (EditSubscriber::visibleGeometry.at (i));
|
||||
geom->position = spots.at (i).targetPos;
|
||||
geom->setActive (true);
|
||||
|
||||
if (int (i) == activeSpot) {
|
||||
geom->setHoverable (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OPIcon *Spot::getActiveSpotIcon()
|
||||
{
|
||||
if (activeSpot > -1) {
|
||||
return static_cast<OPIcon*> (EditSubscriber::visibleGeometry.at (activeSpot));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Spot::addNewEntry()
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
// we create a new entry
|
||||
SpotEntry se;
|
||||
se.targetPos = editProvider->posImage;
|
||||
se.sourcePos = se.targetPos;
|
||||
spots.push_back (se); // this make a copy of se ...
|
||||
activeSpot = spots.size() - 1;
|
||||
lastObject = 1;
|
||||
|
||||
//printf("ActiveSpot = %d\n", activeSpot);
|
||||
|
||||
createGeometry();
|
||||
updateGeometry();
|
||||
EditSubscriber::visibleGeometry.at (activeSpot)->state = Geometry::ACTIVE;
|
||||
sourceIcon.state = Geometry::DRAGGED;
|
||||
// TODO: find a way to disable the active spot's Mouse Over geometry but still displaying its location...
|
||||
|
||||
if (listener) {
|
||||
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
||||
}
|
||||
}
|
||||
|
||||
void Spot::deleteSelectedEntry()
|
||||
{
|
||||
// delete the activeSpot
|
||||
if (activeSpot > -1) {
|
||||
std::vector<rtengine::SpotEntry>::iterator i = spots.begin();
|
||||
for (int j = 0; j < activeSpot; ++j) {
|
||||
++i;
|
||||
}
|
||||
spots.erase (i);
|
||||
}
|
||||
|
||||
lastObject = -1;
|
||||
activeSpot = -1;
|
||||
|
||||
createGeometry();
|
||||
updateGeometry();
|
||||
|
||||
if (listener) {
|
||||
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
CursorShape Spot::getCursor (const int objectID)
|
||||
{
|
||||
return CSOpenHand;
|
||||
}
|
||||
|
||||
bool Spot::mouseOver (const int modifierKey)
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
|
||||
if (editProvider && editProvider->object != lastObject) {
|
||||
if (lastObject > -1) {
|
||||
if (EditSubscriber::mouseOverGeometry.at (lastObject) == &targetMODisc) {
|
||||
getVisibleGeometryFromMO (lastObject)->state = Geometry::ACTIVE;
|
||||
} else {
|
||||
getVisibleGeometryFromMO (lastObject)->state = Geometry::NORMAL;
|
||||
}
|
||||
|
||||
sourceIcon.state = Geometry::ACTIVE;
|
||||
}
|
||||
|
||||
if (editProvider->object > -1) {
|
||||
getVisibleGeometryFromMO (editProvider->object)->state = Geometry::PRELIGHT;
|
||||
|
||||
if (editProvider->object >= STATIC_MO_OBJ_NBR) {
|
||||
// a Spot is being edited
|
||||
int oldActiveSpot = activeSpot;
|
||||
activeSpot = editProvider->object - STATIC_MO_OBJ_NBR;
|
||||
|
||||
if (activeSpot != oldActiveSpot) {
|
||||
if (oldActiveSpot > -1) {
|
||||
EditSubscriber::visibleGeometry.at (oldActiveSpot)->state = Geometry::NORMAL;
|
||||
EditSubscriber::mouseOverGeometry.at (oldActiveSpot + STATIC_MO_OBJ_NBR)->state = Geometry::NORMAL;
|
||||
}
|
||||
|
||||
EditSubscriber::visibleGeometry.at (activeSpot)->state = Geometry::PRELIGHT;
|
||||
EditSubscriber::mouseOverGeometry.at (activeSpot + STATIC_MO_OBJ_NBR)->state = Geometry::PRELIGHT;
|
||||
//printf("ActiveSpot = %d (was %d before)\n", activeSpot, oldActiveSpot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastObject = editProvider->object;
|
||||
|
||||
if (lastObject > -1 && EditSubscriber::mouseOverGeometry.at (lastObject) == getActiveSpotIcon()) {
|
||||
lastObject = 0; // targetMODisc
|
||||
}
|
||||
|
||||
updateGeometry();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new Target and Source point or start the drag of a Target point under the cursor
|
||||
bool Spot::button1Pressed (const int modifierKey)
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
|
||||
if (editProvider) {
|
||||
if (lastObject == -1 && (modifierKey & GDK_CONTROL_MASK)) {
|
||||
addNewEntry();
|
||||
EditSubscriber::action = ES_ACTION_DRAGGING;
|
||||
return true;
|
||||
} else if (lastObject > -1) {
|
||||
getVisibleGeometryFromMO (lastObject)->state = Geometry::DRAGGED;
|
||||
EditSubscriber::action = ES_ACTION_DRAGGING;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// End the drag of a Target point
|
||||
bool Spot::button1Released()
|
||||
{
|
||||
Geometry *loGeom = getVisibleGeometryFromMO (lastObject);
|
||||
|
||||
if (!loGeom) {
|
||||
EditSubscriber::action = ES_ACTION_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
loGeom->state = Geometry::PRELIGHT;
|
||||
EditSubscriber::action = ES_ACTION_NONE;
|
||||
updateGeometry();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete a point
|
||||
bool Spot::button2Pressed (const int modifierKey)
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
|
||||
if (!editProvider || lastObject == -1 || activeSpot==-1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(modifierKey & (GDK_SHIFT_MASK|GDK_SHIFT_MASK))) {
|
||||
EditSubscriber::action = ES_ACTION_PICKING;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new Target and Source point or start the drag of a Target point under the cursor
|
||||
bool Spot::button3Pressed (const int modifierKey)
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
|
||||
if (!editProvider || lastObject == -1 || activeSpot==-1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((modifierKey & GDK_CONTROL_MASK) && (EditSubscriber::mouseOverGeometry.at (lastObject) == &targetMODisc || lastObject >= STATIC_MO_OBJ_NBR)) {
|
||||
lastObject = 1; // sourceMODisc
|
||||
sourceIcon.state = Geometry::DRAGGED;
|
||||
EditSubscriber::action = ES_ACTION_DRAGGING;
|
||||
return true;
|
||||
} else if (!(modifierKey & (GDK_SHIFT_MASK|GDK_SHIFT_MASK))) {
|
||||
EditSubscriber::action = ES_ACTION_PICKING;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spot::button3Released()
|
||||
{
|
||||
Geometry *loGeom = getVisibleGeometryFromMO (lastObject);
|
||||
|
||||
if (!loGeom) {
|
||||
EditSubscriber::action = ES_ACTION_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
lastObject = -1;
|
||||
sourceIcon.state = Geometry::ACTIVE;
|
||||
updateGeometry();
|
||||
EditSubscriber::action = ES_ACTION_NONE;
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spot::drag1 (const int modifierKey)
|
||||
{
|
||||
EditDataProvider *editProvider = getEditProvider();
|
||||
int imW, imH;
|
||||
editProvider->getImageSize (imW, imH);
|
||||
bool modified = false;
|
||||
|
||||
//printf("Drag1 / LastObject=%d\n", lastObject);
|
||||
|
||||
Geometry *loGeom = EditSubscriber::mouseOverGeometry.at (lastObject);
|
||||
|
||||
if (loGeom == &sourceMODisc) {
|
||||
//printf("sourceMODisc / deltaPrevImage = %d / %d\n", editProvider->deltaPrevImage.x, editProvider->deltaPrevImage.y);
|
||||
rtengine::Coord currPos = spots.at (activeSpot).sourcePos;
|
||||
spots.at (activeSpot).sourcePos += editProvider->deltaPrevImage;
|
||||
spots.at (activeSpot).sourcePos.clip (imW, imH);
|
||||
|
||||
if (spots.at (activeSpot).sourcePos != currPos) {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
EditSubscriber::mouseOverGeometry.at (activeSpot + STATIC_MO_OBJ_NBR)->state = Geometry::DRAGGED;
|
||||
} else if (loGeom == &targetMODisc || lastObject >= STATIC_MO_OBJ_NBR) {
|
||||
//printf("targetMODisc / deltaPrevImage = %d / %d\n", editProvider->deltaPrevImage.x, editProvider->deltaPrevImage.y);
|
||||
rtengine::Coord currPos = spots.at (activeSpot).targetPos;
|
||||
spots.at (activeSpot).targetPos += editProvider->deltaPrevImage;
|
||||
spots.at (activeSpot).targetPos.clip (imW, imH);
|
||||
|
||||
if (spots.at (activeSpot).targetPos != currPos) {
|
||||
modified = true;
|
||||
}
|
||||
} else if (loGeom == &sourceCircle) {
|
||||
//printf("sourceCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
||||
int lastRadius = spots.at (activeSpot).radius;
|
||||
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
||||
rtengine::PolarCoord currPolar = currPos - spots.at (activeSpot).sourcePos;
|
||||
spots.at (activeSpot).radius = LIM<int> (int (currPolar.radius), SpotParams::minRadius, SpotParams::maxRadius);
|
||||
|
||||
if (spots.at (activeSpot).radius != lastRadius) {
|
||||
modified = true;
|
||||
}
|
||||
} else if (loGeom == &targetCircle) {
|
||||
//printf("targetCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
||||
int lastRadius = spots.at (activeSpot).radius;
|
||||
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
||||
rtengine::PolarCoord currPolar = currPos - spots.at (activeSpot).targetPos;
|
||||
spots.at (activeSpot).radius = LIM<int> (int (currPolar.radius), SpotParams::minRadius, SpotParams::maxRadius);
|
||||
|
||||
if (spots.at (activeSpot).radius != lastRadius) {
|
||||
modified = true;
|
||||
}
|
||||
} else if (loGeom == &sourceFeatherCircle) {
|
||||
//printf("sourceFeatherCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
||||
float currFeather = spots.at (activeSpot).feather;
|
||||
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
||||
rtengine::PolarCoord currPolar = currPos -spots.at (activeSpot).sourcePos;
|
||||
spots.at (activeSpot).feather = LIM01<float> ((currPolar.radius - double (spots.at (activeSpot).radius)) / double (spots.at (activeSpot).radius));
|
||||
|
||||
if (spots.at (activeSpot).feather != currFeather) {
|
||||
modified = true;
|
||||
}
|
||||
} else if (loGeom == &targetFeatherCircle) {
|
||||
//printf("targetFeatherCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
||||
float currFeather = spots.at (activeSpot).feather;
|
||||
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
||||
rtengine::PolarCoord currPolar = currPos - spots.at (activeSpot).targetPos;
|
||||
spots.at (activeSpot).feather = LIM01<float> ((currPolar.radius - double (spots.at (activeSpot).radius)) / double (spots.at (activeSpot).radius));
|
||||
|
||||
if (spots.at (activeSpot).feather != currFeather) {
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (listener && modified) {
|
||||
updateGeometry();
|
||||
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool Spot::drag3 (const int modifierKey)
|
||||
{
|
||||
EditDataProvider *editProvider = getEditProvider();
|
||||
int imW, imH;
|
||||
editProvider->getImageSize (imW, imH);
|
||||
bool modified = false;
|
||||
|
||||
Geometry *loGeom = EditSubscriber::mouseOverGeometry.at (lastObject);
|
||||
|
||||
if (loGeom == &sourceMODisc) {
|
||||
rtengine::Coord currPos = spots.at (activeSpot).sourcePos;
|
||||
spots.at (activeSpot).sourcePos += editProvider->deltaPrevImage;
|
||||
spots.at (activeSpot).sourcePos.clip (imW, imH);
|
||||
|
||||
if (spots.at (activeSpot).sourcePos != currPos) {
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (listener) {
|
||||
updateGeometry();
|
||||
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool Spot::pick2(const bool picked)
|
||||
{
|
||||
return pick3(picked);
|
||||
}
|
||||
|
||||
bool Spot::pick3 (const bool picked)
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
|
||||
if (!picked) {
|
||||
if (editProvider->object != lastObject) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Object is picked, we delete it
|
||||
deleteSelectedEntry();
|
||||
EditSubscriber::action = ES_ACTION_NONE;
|
||||
updateGeometry();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Spot::switchOffEditMode ()
|
||||
{
|
||||
if (edit->get_active()) {
|
||||
// switching off the toggle button
|
||||
bool wasBlocked = editConn.block (true);
|
||||
edit->set_active (false);
|
||||
|
||||
if (!wasBlocked) {
|
||||
editConn.block (false);
|
||||
}
|
||||
}
|
||||
|
||||
EditSubscriber::switchOffEditMode(); // disconnect
|
||||
}
|
||||
|
101
rtgui/spot.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* This file is part of RawTherapee.
|
||||
*/
|
||||
#ifndef _SPOT_H_
|
||||
#define _SPOT_H_
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include "toolpanel.h"
|
||||
#include "edit.h"
|
||||
|
||||
/**
|
||||
* @brief Let the user create/edit/delete points for Spot Removal tool
|
||||
*
|
||||
* User Interface:
|
||||
*
|
||||
* For the rest of this documentation, T represent a "target" point (where the image is edited) and
|
||||
* S represent the "source" location (where the edition takes its source data).
|
||||
*
|
||||
* When the edit button is active, all T points are shown by a small "dot". When the user
|
||||
* move the cursor over one of them, a circle is displayed to show the radius of the brush, as well
|
||||
* as a second circle representing the source data (S point). The user can then use the left mouse button
|
||||
* over the icon to drag the T point. The left mouse button can be used over the S circle or the right
|
||||
* mouse button can be used over the T point to move the S point.
|
||||
*
|
||||
* Using the left mouse button over the circle of the T point will let the user adjust its radius.
|
||||
*
|
||||
* Using the left mouse button over the feather circle will let the user adjust its radius by setting
|
||||
* a coefficient (0.0 = same radius than the inner circle ; 1.0 = 2 times the inner radius).
|
||||
*
|
||||
* To create a new point, just move over a free area, and press the left mouse button while holding
|
||||
* the CTRL key. This will create a new S and T pair of points. The CTRL key can be released, but keep
|
||||
* the left mouse button pressed and move away to position the S point.
|
||||
*
|
||||
* To delete a point, move your mouse over any of its geometry press the middle or right mouse button
|
||||
* (the point will be deleted on button release).
|
||||
*/
|
||||
class Spot : public ToolParamBlock, public FoldableToolPanel, public EditSubscriber
|
||||
{
|
||||
|
||||
private:
|
||||
int lastObject; // current object that is hovered
|
||||
int activeSpot; // currently active spot, being edited
|
||||
std::vector<rtengine::SpotEntry> spots; // list of edited spots
|
||||
OPIcon sourceIcon; // to show the source location
|
||||
Circle sourceCircle; // to show and change the Source radius
|
||||
Circle sourceMODisc; // to change the Source position
|
||||
Circle targetCircle; // to show and change the Target radius
|
||||
Circle targetMODisc; // to change the Target position
|
||||
Circle sourceFeatherCircle; // to show the Feather radius at the Source position
|
||||
Circle targetFeatherCircle; // to show the Feather radius at the Target position
|
||||
Line link; // to show the link between the Source and Target position
|
||||
|
||||
OPIcon *getActiveSpotIcon ();
|
||||
void updateGeometry ();
|
||||
void createGeometry ();
|
||||
void addNewEntry ();
|
||||
void deleteSelectedEntry ();
|
||||
void resetPressed ();
|
||||
|
||||
protected:
|
||||
Gtk::HBox* labelBox;
|
||||
Gtk::CheckButton* editedCheckBox;
|
||||
Gtk::Label* countLabel;
|
||||
Gtk::ToggleButton* edit;
|
||||
Gtk::Button* reset;
|
||||
sigc::connection editConn, editedConn;
|
||||
|
||||
void editToggled ();
|
||||
void editedToggled ();
|
||||
Geometry* getVisibleGeometryFromMO (int MOID);
|
||||
|
||||
public:
|
||||
|
||||
Spot ();
|
||||
~Spot ();
|
||||
|
||||
void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = NULL);
|
||||
void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = NULL);
|
||||
|
||||
void enabledChanged ();
|
||||
|
||||
void setEditProvider (EditDataProvider* provider);
|
||||
|
||||
void setBatchMode (bool batchMode);
|
||||
|
||||
// EditSubscriber interface
|
||||
CursorShape getCursor (const int objectID);
|
||||
bool mouseOver (const int modifierKey);
|
||||
bool button1Pressed (const int modifierKey);
|
||||
bool button1Released ();
|
||||
bool button2Pressed (const int modifierKey);
|
||||
bool button3Pressed (const int modifierKey);
|
||||
bool button3Released ();
|
||||
bool drag1 (const int modifierKey);
|
||||
bool drag3 (const int modifierKey);
|
||||
bool pick2 (const bool picked);
|
||||
bool pick3 (const bool picked);
|
||||
void switchOffEditMode ();
|
||||
};
|
||||
|
||||
#endif
|
@ -39,8 +39,8 @@ public:
|
||||
Job(ThumbBrowserEntryBase* tbe, bool* priority, bool upgrade,
|
||||
ThumbImageUpdateListener* listener):
|
||||
tbe_(tbe),
|
||||
/*pparams_(pparams),
|
||||
height_(height), */
|
||||
/*pparams_(pparams),
|
||||
height_(height), */
|
||||
priority_(priority),
|
||||
upgrade_(upgrade),
|
||||
listener_(listener)
|
||||
|
@ -43,6 +43,7 @@ ToolPanelCoordinator::ToolPanelCoordinator () : ipc(nullptr), editDataProvider(n
|
||||
shadowshighlights = Gtk::manage (new ShadowsHighlights ());
|
||||
impulsedenoise = Gtk::manage (new ImpulseDenoise ());
|
||||
defringe = Gtk::manage (new Defringe ());
|
||||
spot = Gtk::manage (new Spot ());
|
||||
dirpyrdenoise = Gtk::manage (new DirPyrDenoise ());
|
||||
epd = Gtk::manage (new EdgePreservingDecompositionUI ());
|
||||
sharpening = Gtk::manage (new Sharpening ());
|
||||
@ -110,6 +111,8 @@ ToolPanelCoordinator::ToolPanelCoordinator () : ipc(nullptr), editDataProvider(n
|
||||
toolPanels.push_back (blackwhite);
|
||||
addPanel (exposurePanel, shadowshighlights);
|
||||
toolPanels.push_back (shadowshighlights);
|
||||
addPanel (detailsPanel, spot);
|
||||
toolPanels.push_back (spot);
|
||||
addPanel (detailsPanel, sharpening);
|
||||
toolPanels.push_back (sharpening);
|
||||
addPanel (detailsPanel, sharpenEdge);
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include "vignetting.h"
|
||||
#include "retinex.h"
|
||||
#include "gradient.h"
|
||||
#include "spot.h"
|
||||
#include "pcvignette.h"
|
||||
#include "toolbar.h"
|
||||
#include "lensgeom.h"
|
||||
@ -118,6 +119,7 @@ protected:
|
||||
Crop* crop;
|
||||
ToneCurve* toneCurve;
|
||||
ShadowsHighlights* shadowshighlights;
|
||||
Spot* spot;
|
||||
Defringe* defringe;
|
||||
ImpulseDenoise* impulsedenoise;
|
||||
DirPyrDenoise* dirpyrdenoise;
|
||||
|
1
tools/source_icons/scalable/spot-active.file
Normal file
@ -0,0 +1 @@
|
||||
spot-active.png,w12,actions
|
81
tools/source_icons/scalable/spot-active.svg
Normal file
@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="11"
|
||||
height="11"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="spot-active.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#a94a4a"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="1.6872889"
|
||||
inkscape:cy="2.4074225"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1025"
|
||||
inkscape:window-x="-2"
|
||||
inkscape:window-y="-3"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1041.3622)">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:0.39215687;stroke:none"
|
||||
d="M 6 2 L 6 5 L 7 5 L 7 2 L 6 2 z M 2 6 L 2 7 L 5 7 L 5 6 L 2 6 z M 6 6 L 6 7 L 6 10 L 6 11 L 7 11 L 7 7 L 11 7 L 11 6 L 10 6 L 7 6 L 6 6 z "
|
||||
transform="translate(0,1041.3622)"
|
||||
id="rect2987" />
|
||||
<path
|
||||
id="path3784"
|
||||
d="m 5,1042.3622 0,4 -4,0 0,1 4,0 0,4 1,0 0,-4 4,0 0,-1 -4,0 0,-4 -1,0 z"
|
||||
style="fill:#ffffff;fill-opacity:0.58823532;stroke:none"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
id="rect3788"
|
||||
width="1"
|
||||
height="3"
|
||||
x="5"
|
||||
y="1045.3622" />
|
||||
<rect
|
||||
y="4"
|
||||
x="-1047.3622"
|
||||
height="3"
|
||||
width="1"
|
||||
id="rect3790"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
transform="matrix(0,-1,1,0,0,0)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
1
tools/source_icons/scalable/spot-normal.file
Normal file
@ -0,0 +1 @@
|
||||
spot-normal.png,w16,actions
|
70
tools/source_icons/scalable/spot-normal.svg
Normal file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16px"
|
||||
height="16px"
|
||||
id="svg2985"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="New document 2">
|
||||
<defs
|
||||
id="defs2987" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#e43a3a"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="16"
|
||||
inkscape:cx="6.2977477"
|
||||
inkscape:cy="8.0892157"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1025"
|
||||
inkscape:window-x="-2"
|
||||
inkscape:window-y="-3"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata2990">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
d="M 8 0 C 3.581722 1.1842379e-15 0 3.581722 0 8 C -1.1842379e-15 12.418278 3.581722 16 8 16 C 12.418278 16 16 12.418278 16 8 C 16 3.581722 12.418278 -1.7763568e-15 8 0 z M 8 1.5 C 11.597233 1.5 14.5 4.4027671 14.5 8 C 14.5 11.597233 11.597233 14.5 8 14.5 C 4.4027671 14.5 1.5 11.597233 1.5 8 C 1.5 4.4027671 4.4027671 1.5 8 1.5 z "
|
||||
id="path2993" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
id="path3766"
|
||||
sodipodi:cx="7.9962873"
|
||||
sodipodi:cy="7.9586635"
|
||||
sodipodi:rx="7.9962873"
|
||||
sodipodi:ry="7.9962873"
|
||||
d="m 15.992575,7.9586635 a 7.9962873,7.9962873 0 1 1 -15.992575,0 7.9962873,7.9962873 0 1 1 15.992575,0 z"
|
||||
transform="matrix(0.53285867,0,0,0.53285867,3.7391089,3.7591563)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
1
tools/source_icons/scalable/spot-prelight.file
Normal file
@ -0,0 +1 @@
|
||||
spot-prelight.png,w12,actions
|
74
tools/source_icons/scalable/spot-prelight.svg
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="11"
|
||||
height="11"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="spot-prelight.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#a94a4a"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="1.6872889"
|
||||
inkscape:cy="2.4074225"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1025"
|
||||
inkscape:window-x="-2"
|
||||
inkscape:window-y="-3"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1041.3622)">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:0.66666669;stroke:none"
|
||||
d="m 6,1043.3622 0,4 -4,0 0,1 4,0 0,4 1,0 0,-4 4,0 0,-1 -4,0 0,-4 -1,0 z"
|
||||
id="rect2987" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
id="rect2985"
|
||||
width="9"
|
||||
height="1"
|
||||
x="1"
|
||||
y="1046.3622" />
|
||||
<rect
|
||||
y="1042.3622"
|
||||
x="5"
|
||||
height="9"
|
||||
width="1"
|
||||
id="rect2989"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |