Spot Removal tool

It is not working yet, but the GUI is (almost) done.
See issue #2239.
This commit is contained in:
Hombre 2016-04-23 00:43:48 +02:00
parent 43329b89b1
commit 56dafcf8c1
52 changed files with 1955 additions and 106 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

View File

@ -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

View File

@ -465,7 +465,7 @@ HISTORY_MSG_232;B&amp;W - 'Before' curve type
HISTORY_MSG_233;B&amp;W - 'After' curve
HISTORY_MSG_234;B&amp;W - 'After' curve type
HISTORY_MSG_235;B&amp;W - Auto channel mixer
HISTORY_MSG_236;--unused--
HISTORY_MSG_236;Spot removal - Point modif.
HISTORY_MSG_237;B&amp;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

View File

@ -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
View 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
View 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

View File

@ -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);

View File

@ -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

View File

@ -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)
{

View File

@ -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

View File

@ -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
{

View File

@ -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;

View File

@ -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
{

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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
{

View File

@ -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");
}

View File

@ -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;

View File

@ -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(&params, 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(&params, 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, &params) ;
// 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

View File

@ -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;

View File

@ -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

View File

@ -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
};

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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
};

View File

@ -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

View File

@ -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
View 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;
*/
}
}
}
}
}

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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
View 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
View 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

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -0,0 +1 @@
spot-active.png,w12,actions

View 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

View File

@ -0,0 +1 @@
spot-normal.png,w16,actions

View 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

View File

@ -0,0 +1 @@
spot-prelight.png,w12,actions

View 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