Merge pull request #6227 from Beep6581/spot-removal-tool

Spot removal tool
This commit is contained in:
Lawrence37 2021-05-16 12:44:45 -07:00 committed by GitHub
commit d11d834045
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 2652 additions and 163 deletions

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,73 @@
<?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.92.3 (2405546, 2018-03-11)"
sodipodi:docname="spot-normal.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="8.1603829"
inkscape:cy="7.1741798"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1005"
inkscape:window-x="-9"
inkscape:window-y="-9"
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.66666667;stroke:none"
d="M 6,1043.3622 5.378906,1046.7411 2,1047.3622 v 1 l 3.378906,0.6211 L 6,1052.3622 H 7 L 7.621094,1048.9833 11,1048.3622 v -1 L 7.621094,1046.7411 7,1043.3622 Z"
id="path818"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 5,1042.3622 h 1 l 0.8286408,4.5 L 6,1051.3622 H 5 l -0.8286408,-4.5 z"
id="rect2989"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0"
id="path11"
d="m 1,1047.3622 v -1 l 4.5,-0.8286 4.5,0.8286 v 1 l -4.5,0.8286 z"
style="fill:#ffffff;fill-opacity:1;stroke:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

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

View File

@ -764,6 +764,8 @@ HISTORY_MSG_SHARPENING_CONTRAST;Netteté - Seuil de contraste
HISTORY_MSG_SH_COLORSPACE;O/HL - Espace couleur
HISTORY_MSG_SOFTLIGHT_ENABLED;Lumière douce
HISTORY_MSG_SOFTLIGHT_STRENGTH;Lumière douce - Force
HISTORY_MSG_SPOT;Retrait de taches
HISTORY_MSG_SPOT_ENTRY;Retrait de taches - Modif. de points
HISTORY_MSG_TM_FATTAL_ANCHOR;CPD - Ancre
HISTORY_NEWSNAPSHOT;Ajouter
HISTORY_NEWSNAPSHOT_TOOLTIP;Raccourci: <b>Alt-s</b>
@ -1024,6 +1026,7 @@ PARTIALPASTE_SHARPENEDGE;Bords
PARTIALPASTE_SHARPENING;Netteté
PARTIALPASTE_SHARPENMICRO;Microcontraste
PARTIALPASTE_SOFTLIGHT;Lumière douce
PARTIALPASTE_SPOT;Retrait de taches
PARTIALPASTE_TM_FATTAL;Compression de plage dynamique
PARTIALPASTE_VIBRANCE;Vibrance
PARTIALPASTE_VIGNETTING;Correction du vignettage
@ -2745,6 +2748,10 @@ TP_SHARPENMICRO_MATRIX;Matrice 3×3 au lieu de 5×5
TP_SHARPENMICRO_UNIFORMITY;Uniformité
TP_SOFTLIGHT_LABEL;Lumière douce
TP_SOFTLIGHT_STRENGTH;Force
TP_SPOT_COUNTLABEL;%1 point(s)
TP_SPOT_ENTRYCHANGED;Modification d'un point
TP_SPOT_HINT;Cliquez sur ce bouton pour pouvoir opérer sur la zone de prévisualisation.\n\nPour ajouter un spot, pressez Ctrl et le bouton gauche de la souris, tirez le cercle (la touche Ctrl peut être relâchée) vers la position source, puis relâchez le bouton de la souris.\n\nPour éditer un spot, placez le curseur au-dessus de la marque blanche situant une zone éditée, faisant apparaître la géométrie d'édition.\n\nPour déplacer le spot source ou destination, placez le curseur en son centre et tirez le.\n\nLe cercle intérieur (zone d'effet maximum) et le cercle "d'adoucicement" peuvent être redimmensionné en plaçant le curseur dessus (le cercle devient orange) et en le tirant (le cercle devient rouge).\n\nQuand les changements sont terminés, un clic droit en dehors de tout spot termine le mode d'édition, ou cliquez à nouveau sur ce bouton.
TP_SPOT_LABEL;Retrait de taches
TP_TM_FATTAL_AMOUNT;Quantité
TP_TM_FATTAL_ANCHOR;Ancre
TP_TM_FATTAL_LABEL;Compression de Plage Dynamique

View File

@ -1396,6 +1396,8 @@ HISTORY_MSG_SIGMAFIN;Final contrast Attenuation response
HISTORY_MSG_SIGMATON;Toning Attenuation response
HISTORY_MSG_SOFTLIGHT_ENABLED;Soft light
HISTORY_MSG_SOFTLIGHT_STRENGTH;Soft light - Strength
HISTORY_MSG_SPOT;Spot removal
HISTORY_MSG_SPOT_ENTRY;Spot removal - Point modif.
HISTORY_MSG_TEMPOUT;CAM02 automatic temperature
HISTORY_MSG_THRESWAV;Balance threshold
HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anchor
@ -1698,6 +1700,7 @@ PARTIALPASTE_SHARPENEDGE;Edges
PARTIALPASTE_SHARPENING;Sharpening (USM/RL)
PARTIALPASTE_SHARPENMICRO;Microcontrast
PARTIALPASTE_SOFTLIGHT;Soft light
PARTIALPASTE_SPOT;Spot removal
PARTIALPASTE_TM_FATTAL;Dynamic range compression
PARTIALPASTE_VIBRANCE;Vibrance
PARTIALPASTE_VIGNETTING;Vignetting correction
@ -3598,6 +3601,10 @@ TP_SHARPENMICRO_MATRIX;3×3 matrix instead of 5×5
TP_SHARPENMICRO_UNIFORMITY;Uniformity
TP_SOFTLIGHT_LABEL;Soft Light
TP_SOFTLIGHT_STRENGTH;Strength
TP_SPOT_COUNTLABEL;%1 point(s)
TP_SPOT_ENTRYCHANGED;Point changed
TP_SPOT_HINT;Click on this button to be able to operate on the preview area.\n\nTo edit a spot, hover the white mark locating an edited area, making the editing geometry appear.\n\nTo add a spot, press Ctrl and left mouse button, drag the circle (Ctrl key can be released) to a source location, then release the mouse button.\n\nTo move the source or destination spot, hover its center then drag it.\n\nThe inner circle (maximum effect area) and the "feather" circle can be resized by hovering them (the circle becomes orange) and dragging it (the circle becomes red).\n\nWhen the changes are done, right click outside any spot to end the Spot editing mode, or click on this button again.
TP_SPOT_LABEL;Spot Removal
TP_TM_FATTAL_AMOUNT;Amount
TP_TM_FATTAL_ANCHOR;Anchor
TP_TM_FATTAL_LABEL;Dynamic Range Compression

View File

@ -64,6 +64,7 @@ endif()
set(CAMCONSTSFILE "camconst.json")
set(RTENGINESOURCEFILES
alpha.cc
ahd_demosaic_RT.cc
amaze_demosaic_RT.cc
badpixels.cc
@ -163,6 +164,7 @@ set(RTENGINESOURCEFILES
rtthumbnail.cc
shmap.cc
simpleprocess.cc
spot.cc
stdimagesource.cc
tmo_fattal02.cc
utils.cc

96
rtengine/alpha.cc Normal file
View File

@ -0,0 +1,96 @@
/*
* 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() const
{
if (surface) {
return surface->get_width();
}
return -1;
}
int Alpha::getHeight() const
{
if (surface) {
return surface->get_height();
}
return -1;
}
Cairo::RefPtr<Cairo::ImageSurface> Alpha::getSurface () const
{
return surface; // to be used in bitmap edition
}
unsigned char Alpha::operator () (unsigned row, unsigned col) const
{
return * (surface->get_data () + row * surface->get_width () + col);
}
unsigned char& Alpha::operator () (unsigned row, unsigned col)
{
return * (surface->get_data () + row * surface->get_width () + col);
}
unsigned char* Alpha::operator () (unsigned row) const
{
return surface->get_data () + row * surface->get_width ();
}
}

58
rtengine/alpha.h Normal file
View File

@ -0,0 +1,58 @@
/*
* 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() const;
int getHeight() const;
Cairo::RefPtr<Cairo::ImageSurface> getSurface () const;
// 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);
unsigned char operator () (unsigned row, unsigned col) const;
};
}
#endif

View File

@ -54,7 +54,7 @@ namespace rtengine
{
Crop::Crop(ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow)
: PipetteBuffer(editDataProvider), origCrop(nullptr), laboCrop(nullptr), labnCrop(nullptr),
: PipetteBuffer(editDataProvider), origCrop(nullptr), spotCrop(nullptr), laboCrop(nullptr), labnCrop(nullptr),
cropImg(nullptr), shbuf_real(nullptr), transCrop(nullptr), cieCrop(nullptr), shbuffer(nullptr),
updating(false), newUpdatePending(false), skip(10),
cropx(0), cropy(0), cropw(-1), croph(-1),
@ -153,6 +153,7 @@ void Crop::update(int todo)
// give possibility to the listener to modify crop window (as the full image dimensions are already known at this point)
int wx, wy, ww, wh, ws;
const bool overrideWindow = cropImageListener;
bool spotsDone = false;
if (overrideWindow) {
cropImageListener->getWindow(wx, wy, ww, wh, ws);
@ -615,6 +616,13 @@ void Crop::update(int todo)
parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw);
}
if ((todo & M_SPOT) && params.spot.enabled && !params.spot.entries.empty()) {
spotsDone = true;
PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip);
//parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw);
parent->ipf.removeSpots(origCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, nullptr, tr);
}
DirPyrDenoiseParams denoiseParams = params.dirpyrDenoise;
if (params.dirpyrDenoise.Lmethod == "CUR") {
@ -697,6 +705,28 @@ void Crop::update(int todo)
// has to be called after setCropSizes! Tools prior to this point can't handle the Edit mechanism, but that shouldn't be a problem.
createBuffer(cropw, croph);
// Apply Spot removal
if ((todo & M_SPOT) && !spotsDone) {
if (params.spot.enabled && !params.spot.entries.empty()) {
if(!spotCrop) {
spotCrop = new Imagefloat (cropw, croph);
}
baseCrop->copyData (spotCrop);
PreviewProps pp (trafx, trafy, trafw * skip, trafh * skip, skip);
int tr = getCoarseBitMask(params.coarse);
parent->ipf.removeSpots (spotCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, &params.icm, tr);
} else {
if (spotCrop) {
delete spotCrop;
spotCrop = nullptr;
}
}
}
if (spotCrop) {
baseCrop = spotCrop;
}
std::unique_ptr<Imagefloat> fattalCrop;
if ((todo & M_HDR) && (params.fattal.enabled || params.dehaze.enabled)) {
@ -722,7 +752,7 @@ void Crop::update(int todo)
parent->imgsrc->getImage(parent->currWB, tr, f, pp, params.toneCurve, params.raw);
parent->imgsrc->convertColorSpace(f, params.icm, parent->currWB);
if (params.dirpyrDenoise.enabled || params.filmNegative.enabled) {
if (params.dirpyrDenoise.enabled || params.filmNegative.enabled || params.spot.enabled) {
// copy the denoised crop
int oy = trafy / skip;
int ox = trafx / skip;

View File

@ -38,6 +38,7 @@ class Crop final : 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 ; displayed image in monitor color space, showing the output profile as well (soft-proofing enabled, which then correspond to workimg) or not

View File

@ -347,6 +347,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) override
{
@ -756,6 +773,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) final
{
@ -1392,6 +1428,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) final
{

View File

@ -134,7 +134,21 @@ Image16* Image16::copy() const
return cp;
}
void Image16::getStdImage(const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const
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(const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const
{
// compute channel multipliers

View File

@ -39,8 +39,9 @@ public:
~Image16() override;
Image16* copy() const;
Image16* copySubRegion (int x, int y, int width, int height);
void getStdImage(const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const override;
void getStdImage(const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const override;
const char* getType() const override
{

View File

@ -100,7 +100,7 @@ Image8* Image8::copy () const
return cp;
}
void Image8::getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const
void Image8::getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const
{
// compute channel multipliers
float rm = 1.f, gm = 1.f, bm = 1.f;

View File

@ -38,7 +38,7 @@ public:
Image8* copy () const;
void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const override;
void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const override;
const char* getType () const override
{

View File

@ -54,6 +54,14 @@ int PreviewProps::getSkip() const
return skip;
}
void PreviewProps::set (int x, int y, int w, int h, int skip) {
this->x = x;
this->y = y;
this->width = w;
this->height = h;
this->skip = skip;
}
ImageDimensions::ImageDimensions() :
width(-1),
height(-1)

View File

@ -29,6 +29,7 @@ public:
int getWidth() const;
int getHeight() const;
int getSkip() const;
void set (int x, int y, int w, int h, int skip);
private:
int x;

View File

@ -165,8 +165,22 @@ Imagefloat* Imagefloat::copy () const
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 (const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const
void Imagefloat::getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const
{
// compute channel multipliers

View File

@ -44,8 +44,9 @@ public:
~Imagefloat () override;
Imagefloat* copy () const;
Imagefloat* copySubRegion (int x, int y, int width, int height);
void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const override;
void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const override;
const char* getType () const override
{

View File

@ -93,7 +93,7 @@ public:
void setSampleArrangement(IIOSampleArrangement sArrangement);
IIOSampleArrangement getSampleArrangement() const;
virtual void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const = 0;
virtual void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const = 0;
virtual int getBPS () const = 0;
virtual void getScanline (int row, unsigned char* buffer, int bps, bool isFloat = false) const = 0;
virtual void setScanline (int row, const unsigned char* buffer, int bps, unsigned int numSamples = 3) = 0;

View File

@ -36,6 +36,7 @@
#include "labimage.h"
#include "lcp.h"
#include "procparams.h"
#include "tweakoperator.h"
#include "refreshmap.h"
#include "utils.h"
@ -58,6 +59,7 @@ namespace rtengine
ImProcCoordinator::ImProcCoordinator() :
orig_prev(nullptr),
oprevi(nullptr),
spotprev(nullptr),
oprevl(nullptr),
nprevl(nullptr),
fattal_11_dcrop_cache(nullptr),
@ -167,6 +169,7 @@ ImProcCoordinator::ImProcCoordinator() :
hListener(nullptr),
resultValid(false),
params(new procparams::ProcParams),
tweakOperator(nullptr),
lastOutputProfile("BADFOOD"),
lastOutputIntent(RI__COUNT),
lastOutputBPC(false),
@ -280,9 +283,32 @@ void ImProcCoordinator::assign(ImageSource* imgsrc)
this->imgsrc = imgsrc;
}
void ImProcCoordinator::getParams(procparams::ProcParams* dst)
void ImProcCoordinator::getParams(procparams::ProcParams* dst, bool tweaked)
{
*dst = *params;
if (!tweaked && paramsBackup.operator bool()) {
*dst = *paramsBackup;
} else {
*dst = *params;
}
}
void ImProcCoordinator::backupParams()
{
if (!params) {
return;
}
if (!paramsBackup) {
paramsBackup.reset(new ProcParams());
}
*paramsBackup = *params;
}
void ImProcCoordinator::restoreParams()
{
if (!paramsBackup || !params) {
return;
}
*params = *paramsBackup;
}
DetailedCrop* ImProcCoordinator::createCrop(::EditDataProvider *editDataProvider, bool isDetailWindow)
@ -322,6 +348,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
RAWParams rp = params->raw;
ColorManagementParams cmp = params->icm;
LCurveParams lcur = params->labCurve;
bool spotsDone = false;
if (!highDetailNeeded) {
// if below 100% magnification, take a fast path
@ -585,6 +612,13 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
ipf.setScale(scale);
imgsrc->getImage(currWB, tr, orig_prev, pp, params->toneCurve, params->raw);
if ((todo & M_SPOT) && params->spot.enabled && !params->spot.entries.empty()) {
spotsDone = true;
PreviewProps pp(0, 0, fw, fh, scale);
ipf.removeSpots(orig_prev, imgsrc, params->spot.entries, pp, currWB, nullptr, tr);
}
denoiseInfoStore.valid = false;
//ColorTemp::CAT02 (orig_prev, &params) ;
// printf("orig_prevW=%d\n scale=%d",orig_prev->width, scale);
@ -654,6 +688,25 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
ipf.firstAnalysis(orig_prev, *params, vhist16);
}
oprevi = orig_prev;
if ((todo & M_SPOT) && !spotsDone) {
if (params->spot.enabled && !params->spot.entries.empty()) {
allocCache(spotprev);
orig_prev->copyData(spotprev);
PreviewProps pp(0, 0, fw, fh, scale);
ipf.removeSpots(spotprev, imgsrc, params->spot.entries, pp, currWB, &params->icm, tr);
} else {
if (spotprev) {
delete spotprev;
spotprev = nullptr;
}
}
}
if (spotprev) {
spotprev->copyData(orig_prev);
}
if ((todo & M_HDR) && (params->fattal.enabled || params->dehaze.enabled)) {
if (fattal_11_dcrop_cache) {
delete fattal_11_dcrop_cache;
@ -668,12 +721,11 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
}
}
oprevi = orig_prev;
// Remove transformation if unneeded
bool needstransform = ipf.needsTransform(fw, fh, imgsrc->getRotateDegree(), imgsrc->getMetaData());
if ((needstransform || ((todo & (M_TRANSFORM | M_RGBCURVE)) && params->dirpyrequalizer.cbdlMethod == "bef" && params->dirpyrequalizer.enabled && !params->colorappearance.enabled))) {
// Forking the image
assert(oprevi);
Imagefloat *op = oprevi;
oprevi = new Imagefloat(pW, pH);
@ -1885,15 +1937,31 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
delete oprevi;
oprevi = nullptr;
}
}
void ImProcCoordinator::setTweakOperator (TweakOperator *tOperator)
{
if (tOperator) {
tweakOperator = tOperator;
}
}
void ImProcCoordinator::unsetTweakOperator (TweakOperator *tOperator)
{
if (tOperator && tOperator == tweakOperator) {
tweakOperator = nullptr;
}
}
void ImProcCoordinator::freeAll()
{
if (allocated) {
if (spotprev && spotprev != oprevi) {
delete spotprev;
}
spotprev = nullptr;
if (orig_prev != oprevi) {
delete oprevi;
}
@ -1926,6 +1994,15 @@ void ImProcCoordinator::freeAll()
allocated = false;
}
void ImProcCoordinator::allocCache (Imagefloat* &imgfloat)
{
if (imgfloat == nullptr) {
imgfloat = new Imagefloat(pW, pH);
} else {
imgfloat->allocate(pW, pH);
}
}
/** @brief Handles image buffer (re)allocation and trigger sizeChanged of SizeListener[s]
* If the scale change, this method will free all buffers and reallocate ones of the new size.
* It will then tell to the SizeListener that size has changed (sizeChanged)
@ -1944,9 +2021,9 @@ void ImProcCoordinator::setScale(int prevscale)
do {
prevscale--;
PreviewProps pp(0, 0, fw, fh, prevscale);
imgsrc->getSize(pp, nW, nH);
} while (nH < 400 && prevscale > 1 && (nW * nH < 1000000)); // sctually hardcoded values, perhaps a better choice is possible
PreviewProps pp (0, 0, fw, fh, prevscale);
imgsrc->getSize (pp, nW, nH);
} while (nH < 400 && prevscale > 1 && (nW * nH < 1000000)); // actually hardcoded values, perhaps a better choice is possible
if (nW != pW || nH != pH) {
@ -2387,35 +2464,37 @@ void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool a
MyMutex::MyLock lock(mProcessing);
int fW, fH;
std::unique_ptr<ProcParams> validParams(new ProcParams());
getParams(validParams.get());
int tr = getCoarseBitMask(params->coarse);
int tr = getCoarseBitMask(validParams->coarse);
imgsrc->getFullSize(fW, fH, tr);
PreviewProps pp(0, 0, fW, fH, 1);
ProcParams ppar = *params;
ProcParams ppar = *validParams;
ppar.toneCurve.hrenabled = false;
ppar.icm.inputProfile = "(none)";
Imagefloat* im = new Imagefloat(fW, fH);
imgsrc->preprocess(ppar.raw, ppar.lensProf, ppar.coarse);
double dummy = 0.0;
imgsrc->demosaic(ppar.raw, false, dummy);
ColorTemp currWB = ColorTemp(params->wb.temperature, params->wb.green, params->wb.equal, params->wb.method);
ColorTemp currWB = ColorTemp(validParams->wb.temperature, validParams->wb.green, validParams->wb.equal, validParams->wb.method);
if (params->wb.method == "Camera") {
if (validParams->wb.method == "Camera") {
currWB = imgsrc->getWB();
} else if (params->wb.method == "autold") {
if (lastAwbEqual != params->wb.equal || lastAwbTempBias != params->wb.tempBias) {
} else if (validParams->wb.method == "autold") {
if (lastAwbEqual != validParams->wb.equal || lastAwbTempBias != validParams->wb.tempBias) {
double rm, gm, bm;
imgsrc->getAutoWBMultipliers(rm, gm, bm);
if (rm != -1.) {
autoWB.update(rm, gm, bm, params->wb.equal, params->wb.tempBias);
lastAwbEqual = params->wb.equal;
lastAwbTempBias = params->wb.tempBias;
autoWB.update(rm, gm, bm, validParams->wb.equal, validParams->wb.tempBias);
lastAwbEqual = validParams->wb.equal;
lastAwbTempBias = validParams->wb.tempBias;
} else {
lastAwbEqual = -1.;
lastAwbTempBias = 0.0;
autoWB.useDefaults(params->wb.equal);
autoWB.useDefaults(validParams->wb.equal);
}
}
@ -2437,12 +2516,12 @@ void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool a
im = trImg;
}
if (params->crop.enabled) {
Imagefloat *tmpim = new Imagefloat(params->crop.w, params->crop.h);
int cx = params->crop.x;
int cy = params->crop.y;
int cw = params->crop.w;
int ch = params->crop.h;
if (validParams->crop.enabled) {
Imagefloat *tmpim = new Imagefloat(validParams->crop.w, validParams->crop.h);
int cx = validParams->crop.x;
int cy = validParams->crop.y;
int cw = validParams->crop.w;
int ch = validParams->crop.h;
#ifdef _OPENMP
#pragma omp parallel for
#endif
@ -2473,7 +2552,7 @@ void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool a
}
int imw, imh;
double tmpScale = ipf.resizeScale(params.get(), fW, fH, imw, imh);
double tmpScale = ipf.resizeScale(validParams.get(), fW, fH, imw, imh);
if (tmpScale != 1.0) {
Imagefloat* tempImage = new Imagefloat(imw, imh);
@ -2582,12 +2661,22 @@ void ImProcCoordinator::process()
|| params->dehaze != nextParams->dehaze
|| params->pdsharpening != nextParams->pdsharpening
|| params->filmNegative != nextParams->filmNegative
|| params->spot.enabled != nextParams->spot.enabled
|| sharpMaskChanged;
sharpMaskChanged = false;
*params = *nextParams;
int change = changeSinceLast;
changeSinceLast = 0;
if (tweakOperator) {
// TWEAKING THE PROCPARAMS FOR THE SPOT ADJUSTMENT MODE
backupParams();
tweakOperator->tweakParams(*params);
} else if (paramsBackup) {
paramsBackup.release();
}
paramsUpdateMutex.unlock();
// M_VOID means no update, and is a bit higher that the rest

View File

@ -42,6 +42,7 @@ namespace rtengine
using namespace procparams;
class Crop;
class TweakOperator;
/** @brief Manages the image processing, espc. of the preview windows
*
@ -62,6 +63,7 @@ class ImProcCoordinator final : public StagedImageProcessor, public HistogramObs
protected:
Imagefloat *orig_prev;
Imagefloat *oprevi;
Imagefloat *spotprev;
LabImage *oprevl;
LabImage *nprevl;
Imagefloat *fattal_11_dcrop_cache; // global cache for ToneMapFattal02 used in 1:1 detail windows (except when denoise is active)
@ -206,6 +208,9 @@ protected:
MyMutex minit; // to gain mutually exclusive access to ... to what exactly?
void backupParams();
void restoreParams();
void allocCache (Imagefloat* &imgfloat);
void notifyHistogramChanged();
void reallocAll();
/// Updates L, R, G, and B histograms. Returns true unless not updated.
@ -220,7 +225,9 @@ protected:
void updatePreviewImage (int todo, bool panningRelatedChange);
MyMutex mProcessing;
const std::unique_ptr<ProcParams> params;
const std::unique_ptr<ProcParams> params; // used for the rendering, can be eventually tweaked
std::unique_ptr<ProcParams> paramsBackup; // backup of the untweaked procparams
TweakOperator* tweakOperator;
// for optimization purpose, the output profile, output rendering intent and
// output BPC will trigger a regeneration of the profile on parameter change only
@ -369,7 +376,7 @@ public:
~ImProcCoordinator () override;
void assign (ImageSource* imgsrc);
void getParams (procparams::ProcParams* dst) override;
void getParams (procparams::ProcParams* dst, bool tweaked=false) override;
void startProcessing (int changeCode) override;
ProcParams* beginUpdateParams () override;
@ -410,6 +417,8 @@ public:
DetailedCrop* createCrop (::EditDataProvider *editDataProvider, bool isDetailWindow) override;
void setTweakOperator (TweakOperator *tOperator) override;
void unsetTweakOperator (TweakOperator *tOperator) override;
bool getAutoWB (double& temp, double& green, double equal, double tempBias) override;
void getCamWB (double& temp, double& green) override;
void getSpotWB (int x, int y, int rectSize, double& temp, double& green) override;

View File

@ -23,6 +23,7 @@
#include "coord2d.h"
#include "gamutwarning.h"
#include "imagedimensions.h"
#include "jaggedarray.h"
#include "pipettebuffer.h"
#include "array2D.h"
@ -78,12 +79,15 @@ class Image8;
class Imagefloat;
class LabImage;
class wavelet_decomposition;
class ImageSource;
class ColorTemp;
namespace procparams
{
class ProcParams;
struct SpotEntry;
struct DehazeParams;
struct FattalToneMappingParams;
struct ColorManagementParams;
@ -448,6 +452,9 @@ enum class BlurType {
float Mad(const float * DataList, int datalen);
float MadRgb(const float * DataList, int datalen);
// spot removal tool
void removeSpots (rtengine::Imagefloat* img, rtengine::ImageSource* imgsrc, const std::vector<procparams::SpotEntry> &entries, const PreviewProps &pp, const rtengine::ColorTemp &currWB, const procparams::ColorManagementParams *cmp, int tr);
// pyramid wavelet
void cbdl_local_temp(float ** src, float ** loctemp, int srcwidth, int srcheight, const float * mult, float kchro, const double dirpyrThreshold, const float mergeL, const float contres, const double skinprot, const bool gamutlab, float b_l, float t_l, float t_r, float b_r, int choice, int scale, bool multiThread);
void dirpyr_equalizer(const float * const * src, float ** dst, int srcwidth, int srcheight, const float * const * l_a, const float * const * l_b, const double * mult, double dirpyrThreshold, double skinprot, float b_l, float t_l, float t_r, int scale); //Emil's directional pyramid wavelet

View File

@ -365,6 +365,10 @@ namespace rtengine
namespace procparams
{
const short SpotParams::minRadius = 5;
const short SpotParams::maxRadius = 100;
ToneCurveParams::ToneCurveParams() :
autoexp(false),
clip(0.02),
@ -1691,6 +1695,57 @@ bool EPDParams::operator !=(const EPDParams& other) const
return !(*this == other);
}
SpotEntry::SpotEntry() :
radius(25),
feather(1.f),
opacity(1.f)
{
}
float SpotEntry::getFeatherRadius() const
{
return radius * (1.f + feather);
}
bool SpotEntry::operator ==(const SpotEntry& other) const
{
return other.sourcePos == sourcePos && other.targetPos == targetPos &&
other.radius == radius && other.feather == feather && other.opacity == opacity;
}
bool SpotEntry::operator !=(const SpotEntry& other) const
{
return other.sourcePos != sourcePos || other.targetPos != targetPos ||
other.radius != radius || other.feather != feather || other.opacity != opacity;
}
SpotParams::SpotParams() :
enabled(false)
{
entries.clear ();
}
bool SpotParams::operator ==(const SpotParams& other) const
{
if (enabled != other.enabled || entries.size() != other.entries.size()) {
return false;
}
size_t i = 0;
for (auto entry : entries) {
if (entry != other.entries[i]) {
return false;
}
}
return true;
}
bool SpotParams::operator !=(const SpotParams& other) const
{
return !(*this == other);
}
FattalToneMappingParams::FattalToneMappingParams() :
enabled(false),
threshold(30),
@ -6790,6 +6845,25 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
saveToKeyfile(!pedited || pedited->wavelet.hueskin2, "Wavelet", "HueRange", wavelet.hueskin2.toVector(), keyFile);
saveToKeyfile(!pedited || pedited->wavelet.contrast, "Wavelet", "Contrast", wavelet.contrast, keyFile);
//Spot removal
saveToKeyfile(!pedited || pedited->spot.enabled, "Spot removal", "Enabled", spot.enabled, keyFile);
for (size_t i = 0; i < spot.entries.size (); ++i) {
std::vector<double> entry(7);
entry[0] = double (spot.entries.at (i).sourcePos.x);
entry[1] = double (spot.entries.at (i).sourcePos.y);
entry[2] = double (spot.entries.at (i).targetPos.x);
entry[3] = double (spot.entries.at (i).targetPos.y);
entry[4] = double (spot.entries.at (i).radius);
entry[5] = double (spot.entries.at (i).feather);
entry[6] = double (spot.entries.at (i).opacity);
std::stringstream ss;
ss << "Spot" << (i + 1);
saveToKeyfile(!pedited || pedited->spot.entries, "Spot removal", ss.str(), entry, keyFile);
}
// Directional pyramid equalizer
saveToKeyfile(!pedited || pedited->dirpyrequalizer.enabled, "Directional Pyramid Equalizer", "Enabled", dirpyrequalizer.enabled, keyFile);
saveToKeyfile(!pedited || pedited->dirpyrequalizer.gamutlab, "Directional Pyramid Equalizer", "Gamutlab", dirpyrequalizer.gamutlab, keyFile);
@ -8520,6 +8594,34 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
}
}
if (keyFile.has_group ("Spot removal")) {
assignFromKeyfile(keyFile, "Spot removal", "Enabled", pedited, spot.enabled, pedited->spot.enabled);
int i = 0;
do {
std::stringstream ss;
ss << "Spot" << (i++ + 1);
if (keyFile.has_key ("Spot removal", ss.str())) {
Glib::ArrayHandle<double> entry = keyFile.get_double_list ("Spot removal", ss.str());
const double epsilon = 0.001; // to circumvent rounding of integer saved as double
SpotEntry se;
se.sourcePos.set(int(entry.data()[0] + epsilon), int(entry.data()[1] + epsilon));
se.targetPos.set(int(entry.data()[2] + epsilon), int(entry.data()[3] + epsilon));
se.radius = LIM<int>(int (entry.data()[4] + epsilon), SpotParams::minRadius, SpotParams::maxRadius);
se.feather = float(entry.data()[5]);
se.opacity = float(entry.data()[6]);
spot.entries.push_back(se);
if (pedited) {
pedited->spot.entries = true;
}
} else {
break;
}
} while (1);
}
if (keyFile.has_group("PostDemosaicSharpening")) {
assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Enabled", pedited, pdsharpening.enabled, pedited->pdsharpening.enabled);
assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Contrast", pedited, pdsharpening.contrast, pedited->pdsharpening.contrast);
@ -9679,6 +9781,7 @@ bool ProcParams::operator ==(const ProcParams& other) const
&& chmixer == other.chmixer
&& blackwhite == other.blackwhite
&& resize == other.resize
&& spot == other.spot
&& raw == other.raw
&& icm == other.icm
&& wavelet == other.wavelet

View File

@ -27,6 +27,7 @@
#include <glibmm/ustring.h>
#include <lcms2.h>
#include "coord.h"
#include "noncopyable.h"
struct ParamsEdited;
@ -1697,6 +1698,41 @@ struct ResizeParams {
bool operator !=(const ResizeParams& other) const;
};
/**
* Parameters entry
*/
struct SpotEntry {
Coord sourcePos;
Coord targetPos;
int radius;
float feather;
float opacity;
SpotEntry();
float getFeatherRadius() const;
bool operator ==(const SpotEntry& other) const;
bool operator !=(const SpotEntry& other) const;
};
/**
* Parameters of the dust removal tool
*/
struct SpotParams {
bool enabled;
std::vector<SpotEntry> entries;
// the following constant can be used for experimentation before the final merge
static const short minRadius;
static const short maxRadius;
SpotParams();
bool operator ==(const SpotParams& other) const;
bool operator !=(const SpotParams& other) const;
};
/**
* Parameters of the color spaces used during the processing
*/
@ -2386,6 +2422,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

@ -32,6 +32,7 @@
// Elementary functions that can be done to
// the preview image when an event occurs
#define M_SPOT (1<<19)
#define M_CSHARP (1<<18)
#define M_MONITOR (1<<14)
#define M_RETINEX (1<<13)
@ -51,23 +52,24 @@
// 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_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_MONITOR) // without HIGHQUAL
#define ALL (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_HDR|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_HDR|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_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define DEMOSAIC (M_RAW|M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define ALLNORAW (M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define CAPTURESHARPEN (M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_CSHARP)
#define HDR (M_LINDENOISE|M_HDR|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_HDR|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 FIRST (M_PREPROC|M_RAW|M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_MONITOR) // without HIGHQUAL
#define ALL (M_PREPROC|M_RAW|M_INIT|M_SPOT|M_LINDENOISE|M_HDR|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_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define FLATFIELD (M_PREPROC|M_RAW|M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define DEMOSAIC (M_RAW|M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define ALLNORAW (M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define CAPTURESHARPEN (M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_CSHARP)
#define HDR (M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define SPOTADJUST (M_SPOT|M_HDR|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_SPOT|M_HDR|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_VOID //M_MONITOR
#define CROP M_CROP
#define RESIZE M_VOID

View File

@ -81,6 +81,7 @@ class IImage8;
class IImage16;
class IImagefloat;
class ImageSource;
class TweakOperator;
/**
* This class provides functions to obtain exif and IPTC metadata information
@ -553,9 +554,20 @@ public:
/** Returns the initial image corresponding to the image processor.
* @return the initial image corresponding to the image processor */
virtual InitialImage* getInitialImage () = 0;
/** Set the TweakOperator
* @param tOperator is a pointer to the object that will alter the ProcParams for the rendering */
virtual void setTweakOperator (TweakOperator *tOperator) = 0;
/** Unset the TweakOperator
* @param tOperator is a pointer to the object that were altering the ProcParams for the rendering
* It will only unset the tweak operator if tOperator is the same than the currently set operator.
* If it doesn't match, the currently set TweakOperator will remain set. */
virtual void unsetTweakOperator (TweakOperator *tOperator) = 0;
/** Returns the current processing parameters.
* @param dst is the location where the image processing parameters are copied (it is assumed that the memory is allocated by the caller) */
virtual void getParams (procparams::ProcParams* dst) = 0;
* Since the ProcParams can be tweaked by a GUI to operate on the image at a specific stage or with disabled tool,
* you'll have to specify if you want the tweaked version for the current special mode, or the untweaked one.
* @param dst is the location where the image processing parameters are copied (it is assumed that the memory is allocated by the caller)
* @param tweaked is used to chose betwen the tweaked ProcParams (if there is one) or the untweaked one */
virtual void getParams (procparams::ProcParams* dst, bool tweaked=false) = 0;
/** An essential member function. Call this when a setting has been changed. This function returns a pointer to the
* processing parameters, that you have to update to reflect the changed situation. When ready, call the paramsUpdateReady
* function to start the image update.

View File

@ -796,6 +796,11 @@ private:
params.toneCurve.black = 0;
}
// Spot Removal
if (params.spot.enabled && !params.spot.entries.empty ()) {
ipf.removeSpots (baseImg, imgsrc, params.spot.entries, pp, currWB, nullptr, tr);
}
// at this stage, we can flush the raw data to free up quite an important amount of memory
// commented out because it makes the application crash when batch processing...
// TODO: find a better place to flush rawData and rawRGB

580
rtengine/spot.cc Normal file
View File

@ -0,0 +1,580 @@
/*
* 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"
#include "procparams.h"
#include "imagesource.h"
#include "imagefloat.h"
#include "rt_math.h"
#include <iostream>
#include <set>
#include <unordered_set>
namespace rtengine
{
class SpotBox;
}
namespace
{
using Boxes = std::vector<std::shared_ptr<rtengine::SpotBox>>;
/**
* Add the spot and its dependencies to a set of dependencies.
*
* @param spotNum The spot's index.
* @param dependencies A set to place the dependencies in. Spots that are
* already in the set must have all their dependencies included already.
* @param srcSpots Information on spot sources.
* @param dstSpots Information on spot destinations.
*/
void addSpotDependencies(int spotNum, std::unordered_set<int> &dependencies, const Boxes &srcSpots, const Boxes &dstSpots);
/**
* Returns the supplied spots and all their dependencies.
*
* @param visibleSpots The spots to get dependencies for.
* @param srcSpots Information on spot sources.
* @param dstSpots Information on spot destinations.
*/
std::unordered_set<int> calcSpotDependencies(const std::set<int> &visibleSpots, const Boxes &srcSpots, const Boxes &dstSpots);
}
namespace rtengine
{
class SpotBox {
public:
enum class Type {
SOURCE,
TARGET,
FINAL
};
struct Rectangle {
int x1;
int y1;
int x2;
int y2;
Rectangle() : Rectangle(0, 0, 0, 0) {}
Rectangle(int X1, int Y1, int X2, int Y2) : x1(X1), y1(Y1), x2(X2), y2(Y2) {}
int getWidth() {
return x2 - x1 + 1;
}
int getHeight() {
return y2 - y1 + 1;
}
bool intersects(const Rectangle &other) const {
return (other.x1 <= x2 && other.x2 >= x1)
&& (other.y1 <= y2 && other.y2 >= y1);
}
bool getIntersection(const Rectangle &other, std::unique_ptr<Rectangle> &intersection) const {
if (intersects(other)) {
std::unique_ptr<Rectangle> intsec(
new Rectangle(
rtengine::max(x1, other.x1),
rtengine::max(y1, other.y1),
rtengine::min(x2, other.x2),
rtengine::min(y2, other.y2)
)
);
if (intsec->x1 > intsec->x2 || intsec->y1 > intsec->y2) {
return false;
}
intersection = std::move(intsec);
return true;
}
if (intersection) {
// There's no intersection, we delete the Rectangle structure
intersection.release();
}
return false;
}
Rectangle& operator+=(const Coord &v) {
x1 += v.x;
y1 += v.y;
x2 += v.x;
y2 += v.y;
return *this;
}
Rectangle& operator-=(const Coord &v) {
x1 -= v.x;
y1 -= v.y;
x2 -= v.x;
y2 -= v.y;
return *this;
}
Rectangle& operator/=(int v) {
if (v == 1) {
return *this;
}
int w = x2 - x1 + 1;
int h = y2 - y1 + 1;
w = w / v + static_cast<bool>(w % v);
h = h / v + static_cast<bool>(h % v);
x1 /= v;
y1 /= v;
x2 = x1 + w - 1;
y2 = y1 + h - 1;
return *this;
}
};
private:
Type type;
Imagefloat* image;
public:
// top/left and bottom/right coordinates of the spot in image space (at some point divided by scale factor)
Rectangle spotArea;
// top/left and bottom/right coordinates of the spot in scaled image space (on borders, imgArea won't cover spotArea)
Rectangle imgArea;
// top/left and bottom/right coordinates of useful part of the image in scaled image space (rounding error workaround)
Rectangle intersectionArea;
float radius;
float featherRadius;
SpotBox (int tl_x, int tl_y, int br_x, int br_y, int radius, int feather_radius, Imagefloat* image, Type type) :
type(type),
image(image),
spotArea(tl_x, tl_y, br_x, br_y),
imgArea(spotArea),
intersectionArea(),
radius(radius),
featherRadius(feather_radius)
{}
SpotBox (int tl_x, int tl_y, int radius, int feather_radius, Imagefloat* image, Type type) :
type(type),
image(image),
spotArea(tl_x, tl_y, image ? tl_x + image->getWidth() - 1 : 0, image ? tl_y + image->getHeight() - 1 : 0),
imgArea(spotArea),
intersectionArea(),
radius(radius),
featherRadius(feather_radius)
{}
SpotBox (SpotEntry &spot, Type type) :
type(type),
image(nullptr),
intersectionArea(),
radius(spot.radius),
featherRadius(int(spot.getFeatherRadius() + 0.5f)) // rounding to int before resizing
{
spotArea.x1 = int ((type == Type::SOURCE ? spot.sourcePos.x : spot.targetPos.x) - featherRadius);
spotArea.x2 = int ((type == Type::SOURCE ? spot.sourcePos.x : spot.targetPos.x) + featherRadius);
spotArea.y1 = int ((type == Type::SOURCE ? spot.sourcePos.y : spot.targetPos.y) - featherRadius);
spotArea.y2 = int ((type == Type::SOURCE ? spot.sourcePos.y : spot.targetPos.y) + featherRadius);
imgArea = spotArea;
}
~SpotBox() {
if (image && type != Type::FINAL) {
delete image;
}
}
SpotBox& operator /=(int v) {
if (v == 1) {
return *this;
}
spotArea /= v;
imgArea /= v;
radius /= float(v);
featherRadius = getWidth() / 2.f;
// intersectionArea doesn't need resize, because it's set after resizing
return *this;
}
int getWidth() {
return spotArea.getWidth();
}
int getHeight() {
return spotArea.getHeight();
}
int getImageWidth() {
return imgArea.getWidth();
}
int getImageHeight() {
return imgArea.getHeight();
}
int getIntersectionWidth() {
return intersectionArea.getWidth();
}
int getIntersectionHeight() {
return intersectionArea.getHeight();
}
bool checkImageSize() {
if (!image || getImageWidth() != image->getWidth() || getImageHeight() != image->getHeight()) {
return false;
}
return true;
}
void tuneImageSize() {
if (!image) {
return;
}
if (getImageWidth() > image->getWidth()) {
imgArea.x2 = imgArea.x1 + image->getWidth() - 1;
}
if (getImageHeight() > image->getHeight()) {
imgArea.y2 = imgArea.y1 + image->getHeight() - 1;
}
}
Imagefloat *getImage() { // TODO: this should send back a const value, but getImage don't want it to be const...
return image;
}
void allocImage() {
int newW = imgArea.x2 - imgArea.x1 + 1;
int newH = imgArea.y2 - imgArea.y1 + 1;
if (image && type != Type::FINAL && (image->getWidth() != newW || image->getHeight() != newH)) {
delete image;
image = nullptr;
}
if (image == nullptr) {
image = new Imagefloat(newW, newH);
}
}
bool spotIntersects(const SpotBox &other) const {
return spotArea.intersects(other.spotArea);
}
bool getSpotIntersection(const SpotBox &other, std::unique_ptr<Rectangle> &intersection) const {
return spotArea.getIntersection(other.spotArea, intersection);
}
bool imageIntersects(const SpotBox &other, bool atDestLocation=false) const {
if (atDestLocation) {
Coord v(other.spotArea.x1 - spotArea.x1, other.spotArea.y1 - spotArea.y1);
Rectangle imgArea2(imgArea.x1, imgArea.y1, imgArea.x2, imgArea.y2);
imgArea2 += v;
return imgArea2.intersects(other.imgArea);
}
return imgArea.intersects(other.imgArea);
}
bool mutuallyClipImageArea(SpotBox &other) {
Coord v(other.spotArea.x1 - spotArea.x1, other.spotArea.y1 - spotArea.y1);
Rectangle imgArea2 = imgArea;
imgArea2 += v;
std::unique_ptr<Rectangle> intersection;
if (!imgArea2.getIntersection(other.imgArea, intersection)) {
return false;
}
other.intersectionArea = *intersection;
Coord v2(-v.x, -v.y);
*intersection -= v;
intersectionArea = *intersection;
return true;
}
bool setIntersectionWith(const SpotBox &other) {
if (!spotIntersects(other)) {
return false;
}
imgArea.x1 = rtengine::max(spotArea.x1, other.spotArea.x1);
imgArea.x2 = rtengine::min(spotArea.x2, other.spotArea.x2);
imgArea.y1 = rtengine::max(spotArea.y1, other.spotArea.y1);
imgArea.y2 = rtengine::min(spotArea.y2, other.spotArea.y2);
if (imgArea.x1 > imgArea.x2 || imgArea.y1 > imgArea.y2) {
return false;
}
return true;
}
bool processIntersectionWith(SpotBox &destBox) {
Imagefloat *dstImg = destBox.image;
if (image == nullptr || dstImg == nullptr) {
std::cerr << "One of the source or destination SpotBox image is missing !" << std::endl;
return false;
}
int srcImgY = intersectionArea.y1 - imgArea.y1;
int dstImgY = destBox.intersectionArea.y1 - destBox.imgArea.y1;
for (int y = intersectionArea.y1; y <= intersectionArea.y2; ++y) {
float dy = float(y - spotArea.y1) - featherRadius;
int srcImgX = intersectionArea.x1 - imgArea.x1;
int dstImgX = destBox.intersectionArea.x1 - destBox.imgArea.x1;
for (int x = intersectionArea.x1; x <= intersectionArea.x2; ++x) {
float dx = float(x - spotArea.x1) - featherRadius;
float r = sqrt(dx * dx + dy * dy);
if (r >= featherRadius) {
++srcImgX;
++dstImgX;
continue;
}
if (r <= radius) {
dstImg->r(dstImgY, dstImgX) = image->r(srcImgY, srcImgX);
dstImg->g(dstImgY, dstImgX) = image->g(srcImgY, srcImgX);
dstImg->b(dstImgY, dstImgX) = image->b(srcImgY, srcImgX);
} else {
float opacity = (featherRadius - r) / (featherRadius - radius);
dstImg->r(dstImgY, dstImgX) = (image->r(srcImgY, srcImgX) - dstImg->r(dstImgY, dstImgX)) * opacity + dstImg->r(dstImgY,dstImgX);
dstImg->g(dstImgY, dstImgX) = (image->g(srcImgY, srcImgX) - dstImg->g(dstImgY, dstImgX)) * opacity + dstImg->g(dstImgY,dstImgX);
dstImg->b(dstImgY, dstImgX) = (image->b(srcImgY, srcImgX) - dstImg->b(dstImgY, dstImgX)) * opacity + dstImg->b(dstImgY,dstImgX);
}
++srcImgX;
++dstImgX;
}
++srcImgY;
++dstImgY;
}
return true;
}
// Copy the intersecting part
bool copyImgTo(SpotBox &destBox) {
Imagefloat *destImg = destBox.image;
if (image == nullptr || destImg == nullptr) {
std::cerr << "One of the source or destination SpotBox image is missing !" << std::endl;
return false;
}
std::unique_ptr<Rectangle> intersection;
if (!intersectionArea.getIntersection(destBox.intersectionArea, intersection)) {
return false;
}
Imagefloat *srcImg = image;
Imagefloat *dstImg = destBox.image;
int srcImgY = intersection->y1 - imgArea.y1;
int dstImgY = intersection->y1 - destBox.imgArea.y1;
for (int y = intersection->y1; y <= intersection->y2; ++y) {
int srcImgX = intersection->x1 - imgArea.x1;
int dstImgX = intersection->x1 - destBox.imgArea.x1;
for (int x = intersection->x1; x <= intersection->x2; ++x) {
dstImg->r(dstImgY, dstImgX) = srcImg->r(srcImgY, srcImgX);
dstImg->g(dstImgY, dstImgX) = srcImg->g(srcImgY, srcImgX);
dstImg->b(dstImgY, dstImgX) = srcImg->b(srcImgY, srcImgX);
++srcImgX;
++dstImgX;
}
++srcImgY;
++dstImgY;
}
return true;
}
};
void ImProcFunctions::removeSpots (Imagefloat* img, ImageSource* imgsrc, const std::vector<SpotEntry> &entries, const PreviewProps &pp, const ColorTemp &currWB, const ColorManagementParams *cmp, int tr)
{
//Get the clipped image areas (src & dst) from the source image
std::vector< std::shared_ptr<SpotBox> > srcSpotBoxs;
std::vector< std::shared_ptr<SpotBox> > dstSpotBoxs;
int fullImgWidth = 0;
int fullImgHeight = 0;
imgsrc->getFullSize(fullImgWidth, fullImgHeight, tr);
SpotBox fullImageBox(0, 0, fullImgWidth - 1, fullImgHeight - 1, 0, 0, nullptr, SpotBox::Type::FINAL);
SpotBox cropBox(pp.getX(), pp.getY(),
pp.getX() + pp.getWidth() - 1, pp.getY() + pp.getHeight() - 1,
0, 0, img, SpotBox::Type::FINAL);
std::set<int> visibleSpots; // list of dest spots intersecting the preview's crop
int i = 0;
for (auto entry : params->spot.entries) {
std::shared_ptr<SpotBox> srcSpotBox(new SpotBox(entry, SpotBox::Type::SOURCE));
std::shared_ptr<SpotBox> dstSpotBox(new SpotBox(entry, SpotBox::Type::TARGET));
if ( !srcSpotBox->setIntersectionWith(fullImageBox)
|| !dstSpotBox->setIntersectionWith(fullImageBox)
|| !srcSpotBox->imageIntersects(*dstSpotBox, true))
{
continue;
}
// If spot intersect the preview image, add it to the visible spots
if (dstSpotBox->spotIntersects(cropBox)) {
visibleSpots.insert(i);
}
++i;
// Source area
PreviewProps spp(srcSpotBox->imgArea.x1, srcSpotBox->imgArea.y1,
srcSpotBox->getImageWidth(), srcSpotBox->getImageHeight(), pp.getSkip());
int w = 0;
int h = 0;
imgsrc->getSize(spp, w, h);
*srcSpotBox /= pp.getSkip();
srcSpotBox->allocImage();
Imagefloat *srcImage = srcSpotBox->getImage();
for (int y = 0; y < (int)srcImage->getHeight(); ++y) {
for (int x = 0; x < (int)srcImage->getWidth(); ++x) {
srcImage->r(y, x) = 60000.f;
srcImage->g(y, x) = 500.f;
srcImage->b(y, x) = 500.f;
}
}
imgsrc->getImage(currWB, tr, srcSpotBox->getImage(), spp, params->toneCurve, params->raw);
if (cmp) {
imgsrc->convertColorSpace(srcImage, *cmp, currWB);
}
assert(srcSpotBox->checkImageSize());
// Destination area
spp.set(dstSpotBox->imgArea.x1, dstSpotBox->imgArea.y1, dstSpotBox->getImageWidth(),
dstSpotBox->getImageHeight(), pp.getSkip());
*dstSpotBox /= pp.getSkip();
dstSpotBox->allocImage();
Imagefloat *dstImage = dstSpotBox->getImage();
for (int y = 0; y < (int)dstImage->getHeight(); ++y) {
for (int x = 0; x < (int)dstImage->getWidth(); ++x) {
dstImage->r(y, x) = 500.f;
dstImage->g(y, x) = 500.f;
dstImage->b(y, x) = 60000.f;
}
}
imgsrc->getImage(currWB, tr, dstSpotBox->getImage(), spp, params->toneCurve, params->raw);
if (cmp) {
imgsrc->convertColorSpace(dstImage, *cmp, currWB);
}
assert(dstSpotBox->checkImageSize());
// Update the intersectionArea between src and dest
if (srcSpotBox->mutuallyClipImageArea(*dstSpotBox)) {
srcSpotBoxs.push_back(srcSpotBox);
dstSpotBoxs.push_back(dstSpotBox);
}
}
// Construct list of upstream dependancies
std::unordered_set<int> requiredSpotsSet = calcSpotDependencies(visibleSpots, srcSpotBoxs, dstSpotBoxs);
std::vector<int> requiredSpots(requiredSpotsSet.size());
std::copy(requiredSpotsSet.begin(), requiredSpotsSet.end(), requiredSpots.begin());
std::sort(requiredSpots.begin(), requiredSpots.end());
// Process spots and copy them downstream
for (auto i = requiredSpots.begin(); i != requiredSpots.end(); i++) {
// Process
srcSpotBoxs.at(*i)->processIntersectionWith(*dstSpotBoxs.at(*i));
// Propagate
std::set<int> positiveSpots; // For DEBUG purpose only !
auto j = i;
++j;
while (j != requiredSpots.end()) {
bool intersectionFound = false;
int i_ = *i;
int j_ = *j;
intersectionFound |= dstSpotBoxs.at(i_)->copyImgTo(*srcSpotBoxs.at(j_));
intersectionFound |= dstSpotBoxs.at(i_)->copyImgTo(*dstSpotBoxs.at(j_));
if (intersectionFound) {
positiveSpots.insert(j_);
}
++j;
}
}
// Copy the dest spot to the preview image
cropBox /= pp.getSkip();
cropBox.tuneImageSize();
cropBox.intersectionArea = cropBox.imgArea;
int f = 0;
for (auto i : visibleSpots) {
f += dstSpotBoxs.at(i)->copyImgTo(cropBox) ? 1 : 0;
}
}
}
namespace
{
void addSpotDependencies(int spotNum, std::unordered_set<int> &dependencies, const Boxes &srcSpots, const Boxes &dstSpots)
{
dependencies.insert(spotNum);
// Our spot can depend on previous spots.
for (int i = spotNum - 1; i >= 0; --i) {
if (dependencies.find(i) != dependencies.end()) {
continue; // Spot already has its dependencies added.
}
// Check if our spot depends on this previous spot.
if (srcSpots.at(spotNum)->imageIntersects(*dstSpots.at(i))) {
// If so, add it and its dependencies.
addSpotDependencies(i, dependencies, srcSpots, dstSpots);
}
}
}
std::unordered_set<int> calcSpotDependencies(const std::set<int> &visibleSpots, const Boxes &srcSpots, const Boxes &dstSpots)
{
std::unordered_set<int> dependencies;
std::vector<int> visibleSpotsOrdered(visibleSpots.size());
std::copy(visibleSpots.begin(), visibleSpots.end(), visibleSpotsOrdered.begin());
std::sort(visibleSpotsOrdered.begin(), visibleSpotsOrdered.end());
// Add dependencies, starting with the last spot.
for (auto i = visibleSpotsOrdered.crbegin(); i != visibleSpotsOrdered.crend(); ++i) {
if (dependencies.find(*i) != dependencies.end()) {
continue; // Spot already has its dependencies added.
}
addSpotDependencies(*i, dependencies, srcSpots, dstSpots);
}
return dependencies;
}
}

45
rtengine/tweakoperator.h Normal file
View File

@ -0,0 +1,45 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2019 Jean-Christophe FRISCH <natureh.510@gmail.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/>.
*/
#pragma once
namespace rtengine
{
namespace procparams
{
class ProcParams;
}
/** This class can let objects alter the collected values of the ProcParams for a specific
* purpose, e.g. displaying a preview image at a specific point in the pipeline or with
* disabled tools. Before starting the rendering, the engine will call the TweakOperator
* (if set) to modify the ProcParams. The untweaked one will still exist as a backup, and
* can be sent back if necessary. */
class TweakOperator
{
public:
virtual ~TweakOperator() {}
/** Callback that will alter the ProcParams before hte image is computed. */
virtual void tweakParams(procparams::ProcParams& pparams) = 0;
};
}

View File

@ -150,6 +150,7 @@ set(NONCLISOURCEFILES
softlight.cc
soundman.cc
splash.cc
spot.cc
threadutils.cc
thresholdadjuster.cc
thresholdselector.cc

View File

@ -578,6 +578,14 @@ void BatchToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, c
}
}
void BatchToolPanelCoordinator::setTweakOperator (rtengine::TweakOperator *tOperator)
{
}
void BatchToolPanelCoordinator::unsetTweakOperator (rtengine::TweakOperator *tOperator)
{
}
void BatchToolPanelCoordinator::getAutoWB (double& temp, double& green, double equal, double tempBias)
{

View File

@ -56,6 +56,8 @@ public:
// toolpanellistener interface
void panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr) override;
void setTweakOperator (rtengine::TweakOperator *tOperator) override;
void unsetTweakOperator (rtengine::TweakOperator *tOperator) override;
// profilechangelistener interface
void profileChange(

View File

@ -268,7 +268,7 @@ bool ControlLineManager::getEdited(void) const
return edited;
}
CursorShape ControlLineManager::getCursor(int objectID) const
CursorShape ControlLineManager::getCursor(int objectID, int xPos, int yPos) const
{
return cursor;
}

View File

@ -109,7 +109,7 @@ public:
bool pick1(bool picked) override;
bool pick3(bool picked) override;
bool drag1(int modifierKey) override;
CursorShape getCursor(int objectID) const override;
CursorShape getCursor(int objectID, int xPos, int yPos) const override;
bool mouseOver(int modifierKey) override;
void switchOffEditMode(void) override;
};

View File

@ -2197,7 +2197,7 @@ void ControlSpotPanel::updateCurveOpacity(const Gtk::TreeModel::Row& selectedRow
}
}
CursorShape ControlSpotPanel::getCursor(int objectID) const
CursorShape ControlSpotPanel::getCursor(int objectID, int xPos, int yPos) const
{
// printf("Object ID: %d\n", objectID);

View File

@ -267,7 +267,7 @@ private:
void updateControlSpotCurve(const Gtk::TreeModel::Row& row);
void deleteControlSpotCurve(Gtk::TreeModel::Row& row);
void updateCurveOpacity(const Gtk::TreeModel::Row& selectedRow);
CursorShape getCursor(int objectID) const override;
CursorShape getCursor(int objectID, int xPos, int yPos) const override;
bool mouseOver(int modifierKey) override;
bool button1Pressed(int modifierKey) override;
bool button1Released() override;

View File

@ -510,7 +510,7 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y)
cropgl->cropInit (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h);
} else if (iarea->getToolMode () == TMHand) {
if (editSubscriber) {
if ((cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && editSubscriber->getEditingType() == ET_PIPETTE && (bstate & GDK_CONTROL_MASK))) {
if ((cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && (editSubscriber->getEditingType() == ET_PIPETTE && (bstate & GDK_CONTROL_MASK))) || editSubscriber->getEditingType() == ET_OBJECTS) {
needRedraw = editSubscriber->button1Pressed(bstate);
if (editSubscriber->isDragging()) {
state = SEditDrag1;
@ -1128,7 +1128,7 @@ void CropWindow::pointerMoved (int bstate, int x, int y)
rtengine::StagedImageProcessor* ipc = iarea->getImProcCoordinator();
if(ipc) {
procparams::ProcParams params;
ipc->getParams(&params);
ipc->getParams(&params, true);
isRaw = params.raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::NONE) || params.raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::NONE);
if(isRaw) {
ImageSource *isrc = static_cast<ImageSource*>(ipc->getInitialImage());
@ -1293,7 +1293,10 @@ void CropWindow::updateCursor (int x, int y)
} else if (onArea (CropToolBar, x, y)) {
newType = CSMove;
} else if (iarea->getObject() > -1 && editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
newType = editSubscriber->getCursor(iarea->getObject());
int cursorX;
int cursorY;
screenCoordToImage (x, y, cursorX, cursorY);
newType = editSubscriber->getCursor(iarea->getObject(), cursorX, cursorY);
} else if (onArea (CropResize, x, y)) {
newType = CSResizeDiagonal;
} else if (tm == TMColorPicker && hoveredPicker) {
@ -1320,7 +1323,10 @@ void CropWindow::updateCursor (int x, int y)
}
if (objectID > -1) {
newType = editSubscriber->getCursor(objectID);
int cursorX;
int cursorY;
screenCoordToImage (x, y, cursorX, cursorY);
newType = editSubscriber->getCursor(objectID, cursorX, cursorY);
} else if (tm == TMHand) {
if (onArea (CropObserved, x, y)) {
newType = CSMove;
@ -1346,7 +1352,10 @@ void CropWindow::updateCursor (int x, int y)
}
if (objectID > -1) {
newType = editSubscriber->getCursor(objectID);
int cursorX;
int cursorY;
screenCoordToImage (x, y, cursorX, cursorY);
newType = editSubscriber->getCursor(objectID, cursorX, cursorY);
} else {
newType = CSArrow;
}
@ -1375,6 +1384,16 @@ void CropWindow::updateCursor (int x, int y)
newType = CSResizeDiagonal;
} else if (state == SDragPicker) {
newType = CSMove2D;
} else if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
int objectID = iarea->getObject();
if (objectID > -1) {
int cursorX;
int cursorY;
screenCoordToImage (x, y, cursorX, cursorY);
newType = editSubscriber->getCursor(objectID, cursorX, cursorY);
} else {
newType = CSArrow;
}
}
if (newType != cursor_type) {

View File

@ -504,7 +504,7 @@ bool CurveEditor::drag1(int modifierKey)
return false;
}
CursorShape CurveEditor::getCursor(int objectID) const
CursorShape CurveEditor::getCursor(int objectID, int xPos, int yPos) const
{
if (remoteDrag) {
return CSResizeHeight;

View File

@ -133,7 +133,7 @@ public:
bool button1Pressed(int modifierKey) override;
bool button1Released() override;
bool drag1(int modifierKey) override;
CursorShape getCursor(int objectID) const override;
CursorShape getCursor(int objectID, int xPos, int yPos) const override;
};

View File

@ -173,10 +173,10 @@ void EditDataProvider::setPipetteVal3(float newVal)
pipetteVal3 = newVal;
}
CursorShape EditDataProvider::getCursor(int objectID) const
CursorShape EditDataProvider::getCursor(int objectID, int xPos, int yPos) const
{
if (currSubscriber) {
currSubscriber->getCursor(objectID);
currSubscriber->getCursor(objectID, xPos, yPos);
}
return CSHandOpen;
@ -187,12 +187,12 @@ EditSubscriber* EditDataProvider::getCurrSubscriber() const
return currSubscriber;
}
EditDataProvider* EditSubscriber::getEditProvider()
EditDataProvider* EditSubscriber::getEditProvider() const
{
return provider;
}
CursorShape EditSubscriber::getCursor(int objectID) const
CursorShape EditSubscriber::getCursor(int objectID, int xPos, int yPos) const
{
return CSHandOpen;
}

View File

@ -57,7 +57,7 @@ public:
virtual ~EditSubscriber () = default;
void setEditProvider(EditDataProvider *provider);
EditDataProvider* getEditProvider ();
EditDataProvider* getEditProvider () const;
void setEditID(EditUniqueID ID, BufferType buffType);
bool isCurrentSubscriber() const;
virtual void subscribe();
@ -70,8 +70,10 @@ public:
bool isPicking() const; /// Returns true if something is being picked
/** @brief Get the cursor to be displayed when above handles
@param objectID object currently "hovered" */
virtual CursorShape getCursor (int objectID) const;
@param objectID object currently "hovered"
@param xPos X cursor position in image space
@param yPos Y cursor position in image space */
virtual CursorShape getCursor (int objectID, int xPos, int yPos) const;
/** @brief Triggered when the mouse is moving over an object
This method is also triggered when the cursor is moving over the image in ET_PIPETTE mode
@ -189,7 +191,7 @@ public:
void setPipetteVal1(float newVal);
void setPipetteVal2(float newVal);
void setPipetteVal3(float newVal);
virtual CursorShape getCursor(int objectID) const;
virtual CursorShape getCursor(int objectID, int xPos, int yPos) const;
int getPipetteRectSize () const;
EditSubscriber* getCurrSubscriber() const;
virtual void getImageSize (int &w, int&h) = 0;

View File

@ -22,7 +22,11 @@
#include "editbuffer.h"
#include "editcallbacks.h"
#include "rtsurface.h"
#include "../rtengine/rt_math.h"
const std::vector<double> Geometry::dash = {3., 1.5};
#define INNERGEOM_OPACITY 1.
#define OUTERGEOM_OPACITY 0.7
RGBColor Geometry::getInnerLineColor ()
{
@ -67,7 +71,8 @@ RGBColor Geometry::getOuterLineColor ()
void Circle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
{
if ((flags & F_VISIBLE) && state != INSENSITIVE) {
double lineWidth = getOuterLineWidth();
if ((flags & F_VISIBLE) && state != INSENSITIVE && lineWidth > 0. && innerLineWidth > 0.) {
RGBColor color;
if (flags & F_AUTO_COLOR) {
@ -76,8 +81,9 @@ void Circle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
color = outerLineColor;
}
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
cr->set_line_width( getOuterLineWidth() );
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
cr->set_line_width (lineWidth);
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
rtengine::Coord center_ = center;
double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius);
@ -107,10 +113,11 @@ void Circle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
color = innerLineColor;
}
cr->set_source_rgba(color.getR(), color.getG(), color.getB(), opacity / 100.);
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY);
}
cr->set_line_width( innerLineWidth );
cr->set_line_width(innerLineWidth);
cr->set_line_cap(flags & F_DASHED ? Cairo::LINE_CAP_BUTT : Cairo::LINE_CAP_ROUND);
rtengine::Coord center_ = center;
double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius);
@ -123,9 +130,12 @@ void Circle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen;
}
if (filled && state != INSENSITIVE) {
cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI);
if (flags & F_DASHED) {
cr->set_dash(dash, 0.);
}
if (filled) {
cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI);
if (innerLineWidth > 0.f) {
cr->fill_preserve();
cr->stroke();
@ -134,28 +144,20 @@ void Circle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
}
} else if (innerLineWidth > 0.f) {
cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI);
if (state == INSENSITIVE) {
std::valarray<double> ds(1);
ds[0] = 4;
cr->set_source_rgba(1.0, 1.0, 1.0, 0.618);
cr->stroke_preserve();
cr->set_source_rgba(0.0, 0.0, 0.0, 0.618);
cr->set_dash(ds, 0);
cr->stroke();
ds.resize(0);
cr->set_dash(ds, 0);
} else {
cr->stroke();
}
cr->stroke();
}
}
if (flags & F_DASHED) {
cr->unset_dash();
}
}
}
void Circle::drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
{
if (flags & F_HOVERABLE) {
cr->set_line_width( getMouseOverLineWidth() );
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
rtengine::Coord center_ = center;
double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius);
@ -190,7 +192,8 @@ void Circle::drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short
void Line::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
{
if ((flags & F_VISIBLE) && state != INSENSITIVE) {
double lineWidth = getOuterLineWidth();
if ((flags & F_VISIBLE) && state != INSENSITIVE && lineWidth > 0. && innerLineWidth > 0.) {
RGBColor color;
if (flags & F_AUTO_COLOR) {
@ -199,8 +202,9 @@ void Line::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *
color = outerLineColor;
}
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.0);
cr->set_line_width( getOuterLineWidth() );
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
cr->set_line_width (lineWidth);
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
rtengine::Coord begin_ = begin;
rtengine::Coord end_ = end;
@ -234,10 +238,11 @@ void Line::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *
color = innerLineColor;
}
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY);
}
cr->set_line_width(innerLineWidth);
cr->set_line_cap(flags & F_DASHED ? Cairo::LINE_CAP_BUTT : Cairo::LINE_CAP_ROUND);
rtengine::Coord begin_ = begin;
rtengine::Coord end_ = end;
@ -253,21 +258,16 @@ void Line::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *
end_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen;
}
if (flags & F_DASHED) {
cr->set_dash(dash, 0.);
}
cr->move_to(begin_.x + 0.5, begin_.y + 0.5);
cr->line_to(end_.x + 0.5, end_.y + 0.5);
cr->stroke();
if (state == INSENSITIVE) {
std::valarray<double> ds(1);
ds[0] = 4;
cr->set_source_rgba(1.0, 1.0, 1.0, 0.618);
cr->stroke_preserve();
cr->set_source_rgba(0.0, 0.0, 0.0, 0.618);
cr->set_dash(ds, 0);
cr->stroke();
ds.resize(0);
cr->set_dash(ds, 0);
} else {
cr->stroke();
if (flags & F_DASHED) {
cr->unset_dash();
}
}
}
@ -276,6 +276,7 @@ void Line::drawToMOChannel(Cairo::RefPtr<Cairo::Context> &cr, unsigned short id,
{
if (flags & F_HOVERABLE) {
cr->set_line_width( getMouseOverLineWidth() );
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
rtengine::Coord begin_ = begin;
rtengine::Coord end_ = end;
@ -304,7 +305,8 @@ void Line::drawToMOChannel(Cairo::RefPtr<Cairo::Context> &cr, unsigned short id,
void Polyline::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
{
if ((flags & F_VISIBLE) && state != INSENSITIVE && points.size() > 1) {
double lineWidth = getOuterLineWidth();
if ((flags & F_VISIBLE) && state != INSENSITIVE && points.size() > 1 && lineWidth > 0.) {
RGBColor color;
if (flags & F_AUTO_COLOR) {
@ -313,8 +315,10 @@ void Polyline::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuff
color = outerLineColor;
}
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
cr->set_line_width( getOuterLineWidth() );
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
cr->set_line_width (lineWidth);
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
rtengine::Coord currPos;
@ -357,10 +361,16 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuff
color = innerLineColor;
}
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY);
}
cr->set_line_width( innerLineWidth );
cr->set_line_width(innerLineWidth);
cr->set_line_cap(flags & F_DASHED ? Cairo::LINE_CAP_BUTT : Cairo::LINE_CAP_ROUND);
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
if (flags & F_DASHED) {
cr->set_dash(dash, 0.);
}
if (filled && state != INSENSITIVE) {
rtengine::Coord currPos;
@ -409,20 +419,11 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuff
cr->line_to(currPos.x + 0.5, currPos.y + 0.5);
}
}
cr->stroke();
}
if (state == INSENSITIVE) {
std::valarray<double> ds(1);
ds[0] = 4;
cr->set_source_rgba(1.0, 1.0, 1.0, 0.618);
cr->stroke_preserve();
cr->set_source_rgba(0.0, 0.0, 0.0, 0.618);
cr->set_dash(ds, 0);
cr->stroke();
ds.resize(0);
cr->set_dash(ds, 0);
} else {
cr->stroke();
}
if (flags & F_DASHED) {
cr->unset_dash();
}
}
}
@ -439,8 +440,11 @@ void Polyline::drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned shor
cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.);
}
cr->set_line_width( getMouseOverLineWidth() );
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
for (unsigned int i = 0; i < points.size(); ++i) {
cr->set_line_width( getMouseOverLineWidth() );
currPos = points.at(i);
if (datum == IMAGE) {
@ -497,7 +501,8 @@ void Rectangle::setXYXY(rtengine::Coord topLeft, rtengine::Coord bottomRight)
void Rectangle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
{
if ((flags & F_VISIBLE) && state != INSENSITIVE) {
double lineWidth = getOuterLineWidth();
if ((flags & F_VISIBLE) && state != INSENSITIVE && lineWidth > 0. && innerLineWidth > 0.) {
RGBColor color;
if (flags & F_AUTO_COLOR) {
@ -506,8 +511,9 @@ void Rectangle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
color = outerLineColor;
}
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
cr->set_line_width( getOuterLineWidth() );
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
cr->set_line_width (lineWidth);
cr->set_line_join(Cairo::LINE_JOIN_BEVEL);
rtengine::Coord tl, br;
@ -550,10 +556,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
color = innerLineColor;
}
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY);
}
cr->set_line_width( innerLineWidth );
cr->set_line_width(innerLineWidth);
cr->set_line_join(Cairo::LINE_JOIN_BEVEL);
rtengine::Coord tl, br;
@ -573,7 +580,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
br = bottomRight + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen;
}
if (filled && state != INSENSITIVE) {
if (flags & F_DASHED) {
cr->set_dash(dash, 0.);
}
if (filled) {
cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y);
if (innerLineWidth > 0.f) {
@ -584,20 +595,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
}
} else if (innerLineWidth > 0.f) {
cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y);
cr->stroke();
}
if (state == INSENSITIVE) {
std::valarray<double> ds(1);
ds[0] = 4;
cr->set_source_rgba(1.0, 1.0, 1.0, 0.618);
cr->stroke_preserve();
cr->set_source_rgba(0.0, 0.0, 0.0, 0.618);
cr->set_dash(ds, 0);
cr->stroke();
ds.resize(0);
cr->set_dash(ds, 0);
} else {
cr->stroke();
}
if (flags & F_DASHED) {
cr->unset_dash();
}
}
}
@ -606,6 +608,7 @@ void Rectangle::drawToMOChannel(Cairo::RefPtr<Cairo::Context> &cr, unsigned shor
{
if (flags & F_HOVERABLE) {
cr->set_line_width( getMouseOverLineWidth() );
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
rtengine::Coord tl, br;

View File

@ -24,10 +24,11 @@
#include <glibmm/ustring.h>
#include "editcoordsys.h"
#include "rtsurface.h"
#include "../rtengine/coord.h"
#include "../rtengine/rt_math.h"
class ObjectMOBuffer;
class RTSurface;
/** @file
*
@ -210,6 +211,8 @@ public:
F_VISIBLE = 1 << 0, /// true if the geometry have to be drawn on the visible layer
F_HOVERABLE = 1 << 1, /// true if the geometry have to be drawn on the "mouse over" layer
F_AUTO_COLOR = 1 << 2, /// true if the color depend on the state value, not the color field above
F_DASHED = 1 << 3, /// true if the geometry have to be drawn as a dash line
// (TODO: add a F_LARGE_DASH to have two different dash size ?)
};
/// @brief Key point of the image's rectangle that is used to locate the icon copy to the target point:
@ -226,6 +229,7 @@ public:
};
protected:
static const std::vector<double> dash;
RGBColor innerLineColor;
RGBColor outerLineColor;
short flags;
@ -252,6 +256,8 @@ public:
void setVisible (bool visible);
bool isHoverable ();
void setHoverable (bool visible);
bool isDashed ();
void setDashed (bool dashed);
// setActive will enable/disable the visible and hoverable flags in one shot!
@ -449,7 +455,7 @@ inline void Geometry::setOuterLineColor (char r, char g, char b) {
}
inline double Geometry::getMouseOverLineWidth () {
return getOuterLineWidth () + 2.;
return rtengine::max(double(innerLineWidth), 1.) + 2.;
}
inline void Geometry::setAutoColor (bool aColor) {
@ -484,6 +490,18 @@ inline void Geometry::setHoverable (bool hoverable) {
}
}
inline bool Geometry::isDashed () {
return flags & F_DASHED;
}
inline void Geometry::setDashed (bool dashed) {
if (dashed) {
flags |= F_DASHED;
} else {
flags &= ~F_DASHED;
}
}
inline void Geometry::setActive (bool active) {
if (active) {
flags |= (F_VISIBLE | F_HOVERABLE);

View File

@ -540,7 +540,7 @@ void FilmNegative::setEditProvider(EditDataProvider* provider)
EditSubscriber::setEditProvider(provider);
}
CursorShape FilmNegative::getCursor(int objectID) const
CursorShape FilmNegative::getCursor(int objectID, int xPos, int yPos) const
{
return CSSpotWB;
}

View File

@ -71,7 +71,7 @@ public:
void setEditProvider(EditDataProvider* provider) override;
// EditSubscriber interface
CursorShape getCursor(int objectID) const override;
CursorShape getCursor(int objectID, int xPos, int yPos) const override;
bool mouseOver(int modifierKey) override;
bool button1Pressed(int modifierKey) override;
bool button1Released() override;

View File

@ -329,7 +329,7 @@ void Gradient::editToggled ()
}
}
CursorShape Gradient::getCursor(int objectID) const
CursorShape Gradient::getCursor(int objectID, int xPos, int yPos) const
{
switch (objectID) {
case (0):

View File

@ -55,7 +55,7 @@ public:
void setEditProvider (EditDataProvider* provider) override;
// EditSubscriber interface
CursorShape getCursor(int objectID) const override;
CursorShape getCursor(int objectID, int xPos, int yPos) const override;
bool mouseOver(int modifierKey) override;
bool button1Pressed(int modifierKey) override;
bool button1Released() override;

View File

@ -426,6 +426,10 @@ void ParamsEdited::set(bool v)
resize.width = v;
resize.height = v;
resize.enabled = v;
spot.enabled = v;
spot.entries = v;
resize.allowUpscaling = v;
icm.inputProfile = v;
icm.toneCurve = v;
@ -1715,6 +1719,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;
resize.allowUpscaling = resize.allowUpscaling && p.resize.allowUpscaling == other.resize.allowUpscaling;
icm.inputProfile = icm.inputProfile && p.icm.inputProfile == other.icm.inputProfile;
icm.toneCurve = icm.toneCurve && p.icm.toneCurve == other.icm.toneCurve;
@ -5526,6 +5532,14 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
}
if (spot.enabled) {
toEdit.spot.enabled = mods.spot.enabled;
}
if (spot.entries) {
toEdit.spot.entries = mods.spot.entries;
}
if (pcvignette.enabled) {
toEdit.pcvignette.enabled = mods.pcvignette.enabled;
}

View File

@ -1087,6 +1087,13 @@ struct ResizeParamsEdited {
bool allowUpscaling;
};
class SpotParamsEdited
{
public:
bool enabled;
bool entries;
};
struct ColorManagementParamsEdited {
bool inputProfile;
bool toneCurve;
@ -1437,6 +1444,7 @@ struct ParamsEdited {
ChannelMixerParamsEdited chmixer;
BlackWhiteParamsEdited blackwhite;
ResizeParamsEdited resize;
SpotParamsEdited spot;
ColorManagementParamsEdited icm;
RAWParamsEdited raw;
DirPyrEqualizerParamsEdited dirpyrequalizer;

View File

@ -226,6 +226,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
labcurve = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_LABCURVE")));
// Detail Settings:
spot = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SPOT")));
sharpen = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SHARPENING")));
localcontrast = Gtk::manage(new Gtk::CheckButton(M("PARTIALPASTE_LOCALCONTRAST")));
sharpenedge = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SHARPENEDGE")));
@ -339,6 +340,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
//DETAIL
vboxes[1]->pack_start (*detail, Gtk::PACK_SHRINK, 2);
vboxes[1]->pack_start (*hseps[1], Gtk::PACK_SHRINK, 2);
vboxes[1]->pack_start (*spot, Gtk::PACK_SHRINK, 2);
vboxes[1]->pack_start (*sharpen, Gtk::PACK_SHRINK, 2);
vboxes[1]->pack_start (*localcontrast, Gtk::PACK_SHRINK, 2);
vboxes[1]->pack_start (*sharpenedge, Gtk::PACK_SHRINK, 2);
@ -501,6 +503,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
labcurveConn = labcurve->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true));
// Detail Settings:
spotConn = spot->signal_toggled().connect (sigc::bind (sigc::mem_fun(*detail, &Gtk::CheckButton::set_inconsistent), true));
sharpenConn = sharpen->signal_toggled().connect (sigc::bind (sigc::mem_fun(*detail, &Gtk::CheckButton::set_inconsistent), true));
localcontrastConn = localcontrast->signal_toggled().connect (sigc::bind (sigc::mem_fun(*detail, &Gtk::CheckButton::set_inconsistent), true));
gradsharpenConn = sharpenedge->signal_toggled().connect (sigc::bind (sigc::mem_fun(*detail, &Gtk::CheckButton::set_inconsistent), true));
@ -719,6 +722,7 @@ void PartialPasteDlg::basicToggled ()
void PartialPasteDlg::detailToggled ()
{
ConnectionBlocker spotBlocker(spotConn);
ConnectionBlocker sharpenBlocker(sharpenConn);
ConnectionBlocker localcontrastBlocker(localcontrastConn);
ConnectionBlocker gradsharpenBlocker(gradsharpenConn);
@ -731,6 +735,7 @@ void PartialPasteDlg::detailToggled ()
detail->set_inconsistent (false);
spot->set_active (detail->get_active ());
sharpen->set_active (detail->get_active ());
localcontrast->set_active(detail->get_active());
sharpenedge->set_active (detail->get_active ());
@ -912,6 +917,10 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param
filterPE.colorappearance = falsePE.colorappearance;
}
if (!spot->get_active ()) {
filterPE.spot = falsePE.spot;
}
if (!sharpen->get_active ()) {
filterPE.sharpening = falsePE.sharpening;
}

View File

@ -142,6 +142,7 @@ public:
Gtk::CheckButton* colorappearance;
// options in detail:
Gtk::CheckButton* spot;
Gtk::CheckButton* sharpen;
Gtk::CheckButton* sharpenedge;
Gtk::CheckButton* sharpenmicro;
@ -224,7 +225,7 @@ public:
sigc::connection everythingConn, basicConn, detailConn, colorConn, lensConn, compositionConn, metaConn, rawConn, advancedConn;
sigc::connection locallabConn;
sigc::connection wbConn, exposureConn, localcontrastConn, shConn, pcvignetteConn, gradientConn, labcurveConn, colorappearanceConn;
sigc::connection sharpenConn, gradsharpenConn, microcontrastConn, impdenConn, dirpyrdenConn, defringeConn, epdConn, fattalConn, dirpyreqConn, waveletConn, retinexConn, dehazeConn;
sigc::connection spotConn, sharpenConn, gradsharpenConn, microcontrastConn, impdenConn, dirpyrdenConn, defringeConn, epdConn, fattalConn, dirpyreqConn, waveletConn, retinexConn, dehazeConn;
sigc::connection vibranceConn, chmixerConn, hsveqConn, rgbcurvesConn, chmixerbwConn, colortoningConn, filmSimulationConn, softlightConn;
sigc::connection distortionConn, cacorrConn, vignettingConn, lcpConn;
sigc::connection coarserotConn, finerotConn, cropConn, resizeConn, prsharpeningConn, perspectiveConn, commonTransConn;

View File

@ -23,7 +23,6 @@
#include "guiutils.h"
#include "options.h"
#include "../rtengine/procparams.h"
using namespace rtengine;

817
rtgui/spot.cc Normal file
View File

@ -0,0 +1,817 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2019 Jean-Christophe FRISCH <natureh.510@gmail.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 "editcallbacks.h"
#include "spot.h"
#include "rtimage.h"
#include <iomanip>
#include "../rtengine/rt_math.h"
#include "guiutils.h"
#include "eventmapper.h"
#include "../rtengine/refreshmap.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),
draggedSide(DraggedSide::NONE),
lastObject(-1),
activeSpot(-1),
sourceIcon("spot-normal.png", "spot-active.png", "spot-prelight.png", "", "", Geometry::DP_CENTERCENTER),
editedCheckBox(nullptr)
{
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 ("edit-point.png")));
editConn = edit->signal_toggled().connect ( sigc::mem_fun (*this, &Spot::editToggled) );
edit->set_tooltip_text(M("TP_SPOT_HINT"));
reset = Gtk::manage (new Gtk::Button ());
reset->add (*Gtk::manage (new RTImage ("undo-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::Box());
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;
sourceCircle.setDashed(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;
sourceFeatherCircle.setDashed(true);
sourceFeatherCircle.innerLineWidth = 0.7;
targetFeatherCircle.datum = Geometry::IMAGE;
targetFeatherCircle.setActive (false);
targetFeatherCircle.radiusInImageSpace = true;
targetFeatherCircle.innerLineWidth = 0.7;
link.datum = Geometry::IMAGE;
link.setActive (false);
auto m = ProcEventMapper::getInstance();
EvSpotEnabled = m->newEvent(ALLNORAW, "TP_SPOT_LABEL");
EvSpotEnabledOPA = m->newEvent(SPOTADJUST, "TP_SPOT_LABEL");
EvSpotEntry = m->newEvent(SPOTADJUST, "HISTORY_MSG_SPOT_ENTRY");
EvSpotEntryOPA = m->newEvent(SPOTADJUST, "HISTORY_MSG_SPOT_ENTRY");
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;
activeSpot = -1;
lastObject = -1;
if (batchMode) {
editedCheckBox->set_label(Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), spots.size()));
}
else {
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);
editedCheckBox->set_label(Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), spots.size()));
if (listener) {
listener->panelChanged (EvSpotEntry, Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0));
}
} else {
if (!spots.empty()) {
spots.clear();
activeSpot = -1;
lastObject = -1;
createGeometry();
updateGeometry();
if (listener) {
listener->panelChanged (edit->get_active() ? EvSpotEntryOPA : 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 = nullptr;
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 (edit->get_active() ? EvSpotEnabledOPA : EvSpotEnabled, M ("GENERAL_UNCHANGED"));
} else if (getEnabled()) {
listener->panelChanged (edit->get_active() ? EvSpotEnabledOPA : EvSpotEnabled, M ("GENERAL_ENABLED"));
} else {
listener->panelChanged (edit->get_active() ? EvSpotEnabledOPA : EvSpotEnabled, M ("GENERAL_DISABLED"));
}
}
}
void Spot::setEditProvider (EditDataProvider* provider)
{
EditSubscriber::setEditProvider (provider);
}
void Spot::editToggled ()
{
if (listener) {
if (edit->get_active()) {
listener->setTweakOperator(this);
listener->refreshPreview(EvSpotEnabledOPA); // reprocess the preview w/o creating History entry
subscribe();
} else {
unsubscribe();
listener->unsetTweakOperator(this);
listener->refreshPreview(EvSpotEnabled); // reprocess the preview w/o creating History entry
}
}
}
Geometry* Spot::getVisibleGeometryFromMO (int MOID)
{
if (MOID == -1) {
return nullptr;
}
if (MOID == 0) {
return getActiveSpotIcon();
}
if (MOID == 1) { // sourceMODisc
return &sourceIcon;
}
if (MOID > STATIC_MO_OBJ_NBR) {
return EditSubscriber::visibleGeometry.at(MOID - STATIC_MO_OBJ_NBR);
}
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);
}
// mouse over geometry starts with the static geometry, then the spot's icon geometry
EditSubscriber::mouseOverGeometry.resize (STATIC_MO_OBJ_NBR + nbrEntry);
// visible geometry starts with the spot's icon geometry, then the static geometry
EditSubscriber::visibleGeometry.resize (nbrEntry + STATIC_VISIBLE_OBJ_NBR);
size_t i = 0, j = 0;
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<RTSurface> normalImg = sourceIcon.getNormalImg();
Cairo::RefPtr<RTSurface> prelightImg = sourceIcon.getPrelightImg();
Cairo::RefPtr<RTSurface> 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<RTSurface> (nullptr), Cairo::RefPtr<RTSurface> (nullptr), 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);
}
sourceCircle.setVisible(draggedSide != DraggedSide::SOURCE);
targetCircle.setVisible(draggedSide != DraggedSide::TARGET);
link.setVisible(draggedSide == DraggedSide::NONE);
} 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 nullptr;
}
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 (EvSpotEntryOPA, M ("TP_SPOT_ENTRYCHANGED"));
}
}
void Spot::deleteSelectedEntry()
{
// delete the activeSpot
if (activeSpot > -1) {
std::vector<rtengine::procparams::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"));
}
}
CursorShape Spot::getCursor (int objectID, int xPos, int yPos) const
{
const EditDataProvider* editProvider = getEditProvider();
if (editProvider && activeSpot > -1) {
if (draggedSide != DraggedSide::NONE) {
return CSEmpty;
}
if (objectID == 0 || objectID == 1) {
return CSMove2D;
}
if (objectID >= 2 && objectID <= 5) {
Coord delta(Coord(xPos, yPos) - ((objectID == 3 || objectID == 5) ? spots.at(activeSpot).sourcePos : spots.at(activeSpot).targetPos));
PolarCoord polarPos(delta);
if (polarPos.angle < 0.) {
polarPos.angle += 180.;
}
if (polarPos.angle < 22.5 || polarPos.angle >= 157.5) {
return CSMove1DH;
}
if (polarPos.angle < 67.5) {
return CSResizeBottomRight;
}
if (polarPos.angle < 112.5) {
return CSMove1DV;
}
return CSResizeBottomLeft;
}
}
return CSCrosshair;
}
bool Spot::mouseOver (int modifierKey)
{
EditDataProvider* editProvider = getEditProvider();
if (editProvider && editProvider->getObject() != 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->getObject() > -1) {
getVisibleGeometryFromMO (editProvider->getObject())->state = Geometry::PRELIGHT;
if (editProvider->getObject() >= STATIC_MO_OBJ_NBR) {
// a Spot is being edited
int oldActiveSpot = activeSpot;
activeSpot = editProvider->getObject() - 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->getObject();
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 (int modifierKey)
{
EditDataProvider* editProvider = getEditProvider();
if (editProvider) {
if (lastObject == -1 && (modifierKey & GDK_CONTROL_MASK)) {
draggedSide = DraggedSide::SOURCE;
addNewEntry();
EditSubscriber::action = EditSubscriber::Action::DRAGGING;
return true;
} else if (lastObject > -1) {
draggedSide = lastObject == 0 ? DraggedSide::TARGET : lastObject == 1 ? DraggedSide::SOURCE : DraggedSide::NONE;
getVisibleGeometryFromMO (lastObject)->state = Geometry::DRAGGED;
EditSubscriber::action = EditSubscriber::Action::DRAGGING;
return true;
}
}
return false;
}
// End the drag of a Target point
bool Spot::button1Released()
{
Geometry *loGeom = getVisibleGeometryFromMO (lastObject);
if (!loGeom) {
EditSubscriber::action = EditSubscriber::Action::NONE;
return false;
}
loGeom->state = Geometry::PRELIGHT;
EditSubscriber::action = EditSubscriber::Action::NONE;
draggedSide = DraggedSide::NONE;
updateGeometry();
return true;
}
// Delete a point
bool Spot::button2Pressed (int modifierKey)
{
EditDataProvider* editProvider = getEditProvider();
if (!editProvider || lastObject == -1 || activeSpot == -1) {
return false;
}
if (! (modifierKey & (GDK_SHIFT_MASK | GDK_SHIFT_MASK))) {
EditSubscriber::action = EditSubscriber::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 (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 = EditSubscriber::Action::DRAGGING;
draggedSide = DraggedSide::SOURCE;
return true;
} else if (! (modifierKey & (GDK_SHIFT_MASK | GDK_SHIFT_MASK))) {
EditSubscriber::action = EditSubscriber::Action::PICKING;
}
return false;
}
bool Spot::button3Released()
{
Geometry *loGeom = getVisibleGeometryFromMO (lastObject);
if (!loGeom) {
EditSubscriber::action = EditSubscriber::Action::NONE;
return false;
}
lastObject = -1;
sourceIcon.state = Geometry::ACTIVE;
draggedSide = DraggedSide::NONE;
updateGeometry();
EditSubscriber::action = EditSubscriber::Action::NONE;
return true;
return false;
}
bool Spot::drag1 (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 (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 (bool picked)
{
return pick3 (picked);
}
bool Spot::pick3 (bool picked)
{
EditDataProvider* editProvider = getEditProvider();
if (!picked) {
if (editProvider->getObject() != lastObject) {
return false;
}
}
// Object is picked, we delete it
deleteSelectedEntry();
EditSubscriber::action = EditSubscriber::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
listener->unsetTweakOperator(this);
listener->refreshPreview(EvSpotEnabled); // reprocess the preview w/o creating History entry
}
void Spot::tweakParams(procparams::ProcParams& pparams)
{
//params->raw.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::FAST);
//params->raw.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST);
// -> disabling all transform
//pparams.coarse = CoarseTransformParams();
pparams.lensProf = LensProfParams();
pparams.cacorrection = CACorrParams();
pparams.distortion = DistortionParams();
pparams.rotate = RotateParams();
pparams.perspective = PerspectiveParams();
pparams.vignetting = VignettingParams();
// -> disabling standard crop
pparams.crop.enabled = false;
// -> disabling time consuming and unnecessary tool
pparams.sh.enabled = false;
pparams.blackwhite.enabled = false;
pparams.dehaze.enabled = false;
pparams.wavelet.enabled = false;
pparams.filmSimulation.enabled = false;
pparams.sharpenEdge.enabled = false;
pparams.sharpenMicro.enabled = false;
pparams.sharpening.enabled = false;
pparams.softlight.enabled = false;
pparams.gradient.enabled = false;
pparams.pcvignette.enabled = false;
pparams.colorappearance.enabled = false;
}

135
rtgui/spot.h Normal file
View File

@ -0,0 +1,135 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2019 Jean-Christophe FRISCH <natureh.510@gmail.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 _SPOT_H_
#define _SPOT_H_
#include <gtkmm.h>
#include "toolpanel.h"
#include "editwidgets.h"
#include "../rtengine/procparams.h"
#include "../rtengine/tweakoperator.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 rtengine::TweakOperator, public EditSubscriber
{
private:
enum class DraggedSide {
NONE,
SOURCE,
TARGET
};
DraggedSide draggedSide; // tells which of source or target is being dragged
int lastObject; // current object that is hovered
int activeSpot; // currently active spot, being edited
std::vector<rtengine::procparams::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::Box* 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 = nullptr) override;
void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override;
void enabledChanged () override;
void setEditProvider (EditDataProvider* provider) override;
void setBatchMode (bool batchMode) override;
// EditSubscriber interface
CursorShape getCursor (int objectID, int xPos, int yPos) const override;
bool mouseOver (int modifierKey) override;
bool button1Pressed (int modifierKey) override;
bool button1Released () override;
bool button2Pressed (int modifierKey) override;
bool button3Pressed (int modifierKey) override;
bool button3Released () override;
bool drag1 (int modifierKey) override;
bool drag3 (int modifierKey) override;
bool pick2 (bool picked) override;
bool pick3 (bool picked) override;
void switchOffEditMode () override;
//TweakOperator interface
void tweakParams(rtengine::procparams::ProcParams& pparams) override;
rtengine::ProcEvent EvSpotEnabled;
rtengine::ProcEvent EvSpotEnabledOPA; // used to toggle-on the Spot 'On Preview Adjustment' mode
rtengine::ProcEvent EvSpotEntry;
rtengine::ProcEvent EvSpotEntryOPA;
};
#endif

View File

@ -47,8 +47,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

@ -39,12 +39,21 @@ namespace procparams
class ProcParams;
}
}
class EditDataProvider;
class ToolPanelListener
{
public:
virtual ~ToolPanelListener() = default;
/// @brief Ask to refresh the preview not triggered by a parameter change (e.g. 'On Preview' editing).
virtual void refreshPreview(const rtengine::ProcEvent& event) = 0;
/// @brief Used to notify all listeners that a parameters has been effectively changed
virtual void panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr) = 0;
/// @brief Set the TweakOperator to the StagedImageProcessor, to let some tool enter into special modes
virtual void setTweakOperator (rtengine::TweakOperator *tOperator) = 0;
/// @brief Unset the TweakOperator to the StagedImageProcessor
virtual void unsetTweakOperator (rtengine::TweakOperator *tOperator) = 0;
};
/// @brief This class control the space around the group of tools inside a tab, as well as the space separating each tool. */

View File

@ -49,6 +49,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
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 ());
@ -117,6 +118,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
addfavoritePanel (colorPanel, chmixer);
addfavoritePanel (colorPanel, blackwhite);
addfavoritePanel (exposurePanel, shadowshighlights);
addfavoritePanel (detailsPanel, spot);
addfavoritePanel (detailsPanel, sharpening);
addfavoritePanel (detailsPanel, localContrast);
addfavoritePanel (detailsPanel, sharpenEdge);
@ -451,6 +453,33 @@ void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtr
}
void ToolPanelCoordinator::setTweakOperator (rtengine::TweakOperator *tOperator)
{
if (ipc && tOperator) {
ipc->setTweakOperator(tOperator);
}
}
void ToolPanelCoordinator::unsetTweakOperator (rtengine::TweakOperator *tOperator)
{
if (ipc && tOperator) {
ipc->unsetTweakOperator(tOperator);
}
}
void ToolPanelCoordinator::refreshPreview (const rtengine::ProcEvent& event)
{
if (!ipc) {
return;
}
ProcParams* params = ipc->beginUpdateParams ();
for (auto toolPanel : toolPanels) {
toolPanel->write (params);
}
ipc->endUpdateParams (event); // starts the IPC processing
}
void ToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr)
{

View File

@ -76,6 +76,7 @@
#include "sharpening.h"
#include "sharpenmicro.h"
#include "softlight.h"
#include "spot.h"
#include "tonecurve.h"
#include "toolbar.h"
#include "toolpanel.h"
@ -133,6 +134,7 @@ protected:
ToneCurve* toneCurve;
ShadowsHighlights* shadowshighlights;
LocalContrast *localContrast;
Spot* spot;
Defringe* defringe;
ImpulseDenoise* impulsedenoise;
DirPyrDenoise* dirpyrdenoise;
@ -252,8 +254,12 @@ public:
}
// toolpanellistener interface
void refreshPreview(const rtengine::ProcEvent& event) override;
void panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr) override;
void setTweakOperator (rtengine::TweakOperator *tOperator) override;
void unsetTweakOperator (rtengine::TweakOperator *tOperator) override;
// FilmNegProvider interface
void imageTypeChanged (bool isRaw, bool isBayer, bool isXtrans, bool isMono = false) override;
// profilechangelistener interface

View File

@ -22,7 +22,6 @@
#include "guiutils.h"
#include "options.h"
#include "../rtengine/procparams.h"
using namespace rtengine;