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_SH_COLORSPACE;O/HL - Espace couleur
HISTORY_MSG_SOFTLIGHT_ENABLED;Lumière douce HISTORY_MSG_SOFTLIGHT_ENABLED;Lumière douce
HISTORY_MSG_SOFTLIGHT_STRENGTH;Lumière douce - Force 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_MSG_TM_FATTAL_ANCHOR;CPD - Ancre
HISTORY_NEWSNAPSHOT;Ajouter HISTORY_NEWSNAPSHOT;Ajouter
HISTORY_NEWSNAPSHOT_TOOLTIP;Raccourci: <b>Alt-s</b> HISTORY_NEWSNAPSHOT_TOOLTIP;Raccourci: <b>Alt-s</b>
@ -1024,6 +1026,7 @@ PARTIALPASTE_SHARPENEDGE;Bords
PARTIALPASTE_SHARPENING;Netteté PARTIALPASTE_SHARPENING;Netteté
PARTIALPASTE_SHARPENMICRO;Microcontraste PARTIALPASTE_SHARPENMICRO;Microcontraste
PARTIALPASTE_SOFTLIGHT;Lumière douce PARTIALPASTE_SOFTLIGHT;Lumière douce
PARTIALPASTE_SPOT;Retrait de taches
PARTIALPASTE_TM_FATTAL;Compression de plage dynamique PARTIALPASTE_TM_FATTAL;Compression de plage dynamique
PARTIALPASTE_VIBRANCE;Vibrance PARTIALPASTE_VIBRANCE;Vibrance
PARTIALPASTE_VIGNETTING;Correction du vignettage 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_SHARPENMICRO_UNIFORMITY;Uniformité
TP_SOFTLIGHT_LABEL;Lumière douce TP_SOFTLIGHT_LABEL;Lumière douce
TP_SOFTLIGHT_STRENGTH;Force 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_AMOUNT;Quantité
TP_TM_FATTAL_ANCHOR;Ancre TP_TM_FATTAL_ANCHOR;Ancre
TP_TM_FATTAL_LABEL;Compression de Plage Dynamique 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_SIGMATON;Toning Attenuation response
HISTORY_MSG_SOFTLIGHT_ENABLED;Soft light HISTORY_MSG_SOFTLIGHT_ENABLED;Soft light
HISTORY_MSG_SOFTLIGHT_STRENGTH;Soft light - Strength 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_TEMPOUT;CAM02 automatic temperature
HISTORY_MSG_THRESWAV;Balance threshold HISTORY_MSG_THRESWAV;Balance threshold
HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anchor HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anchor
@ -1698,6 +1700,7 @@ PARTIALPASTE_SHARPENEDGE;Edges
PARTIALPASTE_SHARPENING;Sharpening (USM/RL) PARTIALPASTE_SHARPENING;Sharpening (USM/RL)
PARTIALPASTE_SHARPENMICRO;Microcontrast PARTIALPASTE_SHARPENMICRO;Microcontrast
PARTIALPASTE_SOFTLIGHT;Soft light PARTIALPASTE_SOFTLIGHT;Soft light
PARTIALPASTE_SPOT;Spot removal
PARTIALPASTE_TM_FATTAL;Dynamic range compression PARTIALPASTE_TM_FATTAL;Dynamic range compression
PARTIALPASTE_VIBRANCE;Vibrance PARTIALPASTE_VIBRANCE;Vibrance
PARTIALPASTE_VIGNETTING;Vignetting correction PARTIALPASTE_VIGNETTING;Vignetting correction
@ -3598,6 +3601,10 @@ TP_SHARPENMICRO_MATRIX;3×3 matrix instead of 5×5
TP_SHARPENMICRO_UNIFORMITY;Uniformity TP_SHARPENMICRO_UNIFORMITY;Uniformity
TP_SOFTLIGHT_LABEL;Soft Light TP_SOFTLIGHT_LABEL;Soft Light
TP_SOFTLIGHT_STRENGTH;Strength 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_AMOUNT;Amount
TP_TM_FATTAL_ANCHOR;Anchor TP_TM_FATTAL_ANCHOR;Anchor
TP_TM_FATTAL_LABEL;Dynamic Range Compression TP_TM_FATTAL_LABEL;Dynamic Range Compression

View File

@ -64,6 +64,7 @@ endif()
set(CAMCONSTSFILE "camconst.json") set(CAMCONSTSFILE "camconst.json")
set(RTENGINESOURCEFILES set(RTENGINESOURCEFILES
alpha.cc
ahd_demosaic_RT.cc ahd_demosaic_RT.cc
amaze_demosaic_RT.cc amaze_demosaic_RT.cc
badpixels.cc badpixels.cc
@ -163,6 +164,7 @@ set(RTENGINESOURCEFILES
rtthumbnail.cc rtthumbnail.cc
shmap.cc shmap.cc
simpleprocess.cc simpleprocess.cc
spot.cc
stdimagesource.cc stdimagesource.cc
tmo_fattal02.cc tmo_fattal02.cc
utils.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) 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), cropImg(nullptr), shbuf_real(nullptr), transCrop(nullptr), cieCrop(nullptr), shbuffer(nullptr),
updating(false), newUpdatePending(false), skip(10), updating(false), newUpdatePending(false), skip(10),
cropx(0), cropy(0), cropw(-1), croph(-1), 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) // 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; int wx, wy, ww, wh, ws;
const bool overrideWindow = cropImageListener; const bool overrideWindow = cropImageListener;
bool spotsDone = false;
if (overrideWindow) { if (overrideWindow) {
cropImageListener->getWindow(wx, wy, ww, wh, ws); 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); 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; DirPyrDenoiseParams denoiseParams = params.dirpyrDenoise;
if (params.dirpyrDenoise.Lmethod == "CUR") { 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. // 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); 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; std::unique_ptr<Imagefloat> fattalCrop;
if ((todo & M_HDR) && (params.fattal.enabled || params.dehaze.enabled)) { 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->getImage(parent->currWB, tr, f, pp, params.toneCurve, params.raw);
parent->imgsrc->convertColorSpace(f, params.icm, parent->currWB); 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 // copy the denoised crop
int oy = trafy / skip; int oy = trafy / skip;
int ox = trafx / skip; int ox = trafx / skip;

View File

@ -38,6 +38,7 @@ class Crop final : public DetailedCrop, public PipetteBuffer
protected: protected:
// --- permanently allocated in RAM and only renewed on size changes // --- permanently allocated in RAM and only renewed on size changes
Imagefloat* origCrop; // "one chunk" allocation Imagefloat* origCrop; // "one chunk" allocation
Imagefloat* spotCrop; // "one chunk" allocation
LabImage* laboCrop; // "one chunk" allocation LabImage* laboCrop; // "one chunk" allocation
LabImage* labnCrop; // "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 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 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 void rotate (int deg) final
{ {
@ -1392,6 +1428,23 @@ public:
memcpy (dest->data, data, 3 * width * height * sizeof(T)); 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 void rotate (int deg) final
{ {

View File

@ -134,7 +134,21 @@ Image16* Image16::copy() const
return cp; 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 // compute channel multipliers

View File

@ -39,8 +39,9 @@ public:
~Image16() override; ~Image16() override;
Image16* copy() const; 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 const char* getType() const override
{ {

View File

@ -100,7 +100,7 @@ Image8* Image8::copy () const
return cp; 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 // compute channel multipliers
float rm = 1.f, gm = 1.f, bm = 1.f; float rm = 1.f, gm = 1.f, bm = 1.f;

View File

@ -38,7 +38,7 @@ public:
Image8* copy () const; 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 const char* getType () const override
{ {

View File

@ -54,6 +54,14 @@ int PreviewProps::getSkip() const
return skip; 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() : ImageDimensions::ImageDimensions() :
width(-1), width(-1),
height(-1) height(-1)

View File

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

View File

@ -165,8 +165,22 @@ Imagefloat* Imagefloat::copy () const
return cp; 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 // 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 // compute channel multipliers

View File

@ -44,8 +44,9 @@ public:
~Imagefloat () override; ~Imagefloat () override;
Imagefloat* copy () const; 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 const char* getType () const override
{ {

View File

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

View File

@ -42,6 +42,7 @@ namespace rtengine
using namespace procparams; using namespace procparams;
class Crop; class Crop;
class TweakOperator;
/** @brief Manages the image processing, espc. of the preview windows /** @brief Manages the image processing, espc. of the preview windows
* *
@ -62,6 +63,7 @@ class ImProcCoordinator final : public StagedImageProcessor, public HistogramObs
protected: protected:
Imagefloat *orig_prev; Imagefloat *orig_prev;
Imagefloat *oprevi; Imagefloat *oprevi;
Imagefloat *spotprev;
LabImage *oprevl; LabImage *oprevl;
LabImage *nprevl; LabImage *nprevl;
Imagefloat *fattal_11_dcrop_cache; // global cache for ToneMapFattal02 used in 1:1 detail windows (except when denoise is active) 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? MyMutex minit; // to gain mutually exclusive access to ... to what exactly?
void backupParams();
void restoreParams();
void allocCache (Imagefloat* &imgfloat);
void notifyHistogramChanged(); void notifyHistogramChanged();
void reallocAll(); void reallocAll();
/// Updates L, R, G, and B histograms. Returns true unless not updated. /// Updates L, R, G, and B histograms. Returns true unless not updated.
@ -220,7 +225,9 @@ protected:
void updatePreviewImage (int todo, bool panningRelatedChange); void updatePreviewImage (int todo, bool panningRelatedChange);
MyMutex mProcessing; 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 // for optimization purpose, the output profile, output rendering intent and
// output BPC will trigger a regeneration of the profile on parameter change only // output BPC will trigger a regeneration of the profile on parameter change only
@ -369,7 +376,7 @@ public:
~ImProcCoordinator () override; ~ImProcCoordinator () override;
void assign (ImageSource* imgsrc); void assign (ImageSource* imgsrc);
void getParams (procparams::ProcParams* dst) override; void getParams (procparams::ProcParams* dst, bool tweaked=false) override;
void startProcessing (int changeCode) override; void startProcessing (int changeCode) override;
ProcParams* beginUpdateParams () override; ProcParams* beginUpdateParams () override;
@ -410,6 +417,8 @@ public:
DetailedCrop* createCrop (::EditDataProvider *editDataProvider, bool isDetailWindow) override; 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; bool getAutoWB (double& temp, double& green, double equal, double tempBias) override;
void getCamWB (double& temp, double& green) override; void getCamWB (double& temp, double& green) override;
void getSpotWB (int x, int y, int rectSize, 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 "coord2d.h"
#include "gamutwarning.h" #include "gamutwarning.h"
#include "imagedimensions.h"
#include "jaggedarray.h" #include "jaggedarray.h"
#include "pipettebuffer.h" #include "pipettebuffer.h"
#include "array2D.h" #include "array2D.h"
@ -78,12 +79,15 @@ class Image8;
class Imagefloat; class Imagefloat;
class LabImage; class LabImage;
class wavelet_decomposition; class wavelet_decomposition;
class ImageSource;
class ColorTemp;
namespace procparams namespace procparams
{ {
class ProcParams; class ProcParams;
struct SpotEntry;
struct DehazeParams; struct DehazeParams;
struct FattalToneMappingParams; struct FattalToneMappingParams;
struct ColorManagementParams; struct ColorManagementParams;
@ -448,6 +452,9 @@ enum class BlurType {
float Mad(const float * DataList, int datalen); float Mad(const float * DataList, int datalen);
float MadRgb(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 // 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 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 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 namespace procparams
{ {
const short SpotParams::minRadius = 5;
const short SpotParams::maxRadius = 100;
ToneCurveParams::ToneCurveParams() : ToneCurveParams::ToneCurveParams() :
autoexp(false), autoexp(false),
clip(0.02), clip(0.02),
@ -1691,6 +1695,57 @@ bool EPDParams::operator !=(const EPDParams& other) const
return !(*this == other); 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() : FattalToneMappingParams::FattalToneMappingParams() :
enabled(false), enabled(false),
threshold(30), 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.hueskin2, "Wavelet", "HueRange", wavelet.hueskin2.toVector(), keyFile);
saveToKeyfile(!pedited || pedited->wavelet.contrast, "Wavelet", "Contrast", wavelet.contrast, 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 // Directional pyramid equalizer
saveToKeyfile(!pedited || pedited->dirpyrequalizer.enabled, "Directional Pyramid Equalizer", "Enabled", dirpyrequalizer.enabled, keyFile); saveToKeyfile(!pedited || pedited->dirpyrequalizer.enabled, "Directional Pyramid Equalizer", "Enabled", dirpyrequalizer.enabled, keyFile);
saveToKeyfile(!pedited || pedited->dirpyrequalizer.gamutlab, "Directional Pyramid Equalizer", "Gamutlab", dirpyrequalizer.gamutlab, 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")) { if (keyFile.has_group("PostDemosaicSharpening")) {
assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Enabled", pedited, pdsharpening.enabled, pedited->pdsharpening.enabled); assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Enabled", pedited, pdsharpening.enabled, pedited->pdsharpening.enabled);
assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Contrast", pedited, pdsharpening.contrast, pedited->pdsharpening.contrast); assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Contrast", pedited, pdsharpening.contrast, pedited->pdsharpening.contrast);
@ -9679,6 +9781,7 @@ bool ProcParams::operator ==(const ProcParams& other) const
&& chmixer == other.chmixer && chmixer == other.chmixer
&& blackwhite == other.blackwhite && blackwhite == other.blackwhite
&& resize == other.resize && resize == other.resize
&& spot == other.spot
&& raw == other.raw && raw == other.raw
&& icm == other.icm && icm == other.icm
&& wavelet == other.wavelet && wavelet == other.wavelet

View File

@ -27,6 +27,7 @@
#include <glibmm/ustring.h> #include <glibmm/ustring.h>
#include <lcms2.h> #include <lcms2.h>
#include "coord.h"
#include "noncopyable.h" #include "noncopyable.h"
struct ParamsEdited; struct ParamsEdited;
@ -1697,6 +1698,41 @@ struct ResizeParams {
bool operator !=(const ResizeParams& other) const; 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 * Parameters of the color spaces used during the processing
*/ */
@ -2386,6 +2422,7 @@ public:
ChannelMixerParams chmixer; ///< Channel mixer parameters ChannelMixerParams chmixer; ///< Channel mixer parameters
BlackWhiteParams blackwhite; ///< Black& White parameters BlackWhiteParams blackwhite; ///< Black& White parameters
ResizeParams resize; ///< Resize parameters ResizeParams resize; ///< Resize parameters
SpotParams spot; ///< Spot removal tool
ColorManagementParams icm; ///< profiles/color spaces used during the image processing ColorManagementParams icm; ///< profiles/color spaces used during the image processing
RAWParams raw; ///< RAW parameters before demosaicing RAWParams raw; ///< RAW parameters before demosaicing
WaveletParams wavelet; ///< Wavelet parameters WaveletParams wavelet; ///< Wavelet parameters

View File

@ -32,6 +32,7 @@
// Elementary functions that can be done to // Elementary functions that can be done to
// the preview image when an event occurs // the preview image when an event occurs
#define M_SPOT (1<<19)
#define M_CSHARP (1<<18) #define M_CSHARP (1<<18)
#define M_MONITOR (1<<14) #define M_MONITOR (1<<14)
#define M_RETINEX (1<<13) #define M_RETINEX (1<<13)
@ -51,16 +52,17 @@
// Bitfield of functions to do to the preview image when an event occurs // Bitfield of functions to do to the preview image when an event occurs
// Use those or create new ones for your new events // 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 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_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) // without HIGHQUAL #define ALL (M_PREPROC|M_RAW|M_INIT|M_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_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) #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_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_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_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_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_CSHARP) #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_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) #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 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 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 RGBCURVE (M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define LUMINANCECURVE (M_LUMACURVE|M_LUMINANCE|M_COLOR) #define LUMINANCECURVE (M_LUMACURVE|M_LUMINANCE|M_COLOR)
#define SHARPENING (M_LUMINANCE|M_COLOR) #define SHARPENING (M_LUMINANCE|M_COLOR)

View File

@ -81,6 +81,7 @@ class IImage8;
class IImage16; class IImage16;
class IImagefloat; class IImagefloat;
class ImageSource; class ImageSource;
class TweakOperator;
/** /**
* This class provides functions to obtain exif and IPTC metadata information * 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. /** Returns the initial image corresponding to the image processor.
* @return the initial image corresponding to the image processor */ * @return the initial image corresponding to the image processor */
virtual InitialImage* getInitialImage () = 0; 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. /** 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) */ * Since the ProcParams can be tweaked by a GUI to operate on the image at a specific stage or with disabled tool,
virtual void getParams (procparams::ProcParams* dst) = 0; * 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 /** 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 * processing parameters, that you have to update to reflect the changed situation. When ready, call the paramsUpdateReady
* function to start the image update. * function to start the image update.

View File

@ -796,6 +796,11 @@ private:
params.toneCurve.black = 0; 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 // 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... // commented out because it makes the application crash when batch processing...
// TODO: find a better place to flush rawData and rawRGB // 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 softlight.cc
soundman.cc soundman.cc
splash.cc splash.cc
spot.cc
threadutils.cc threadutils.cc
thresholdadjuster.cc thresholdadjuster.cc
thresholdselector.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) void BatchToolPanelCoordinator::getAutoWB (double& temp, double& green, double equal, double tempBias)
{ {

View File

@ -56,6 +56,8 @@ public:
// toolpanellistener interface // toolpanellistener interface
void panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr) override; 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 // profilechangelistener interface
void profileChange( void profileChange(

View File

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

View File

@ -109,7 +109,7 @@ public:
bool pick1(bool picked) override; bool pick1(bool picked) override;
bool pick3(bool picked) override; bool pick3(bool picked) override;
bool drag1(int modifierKey) 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; bool mouseOver(int modifierKey) override;
void switchOffEditMode(void) 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); // printf("Object ID: %d\n", objectID);

View File

@ -267,7 +267,7 @@ private:
void updateControlSpotCurve(const Gtk::TreeModel::Row& row); void updateControlSpotCurve(const Gtk::TreeModel::Row& row);
void deleteControlSpotCurve(Gtk::TreeModel::Row& row); void deleteControlSpotCurve(Gtk::TreeModel::Row& row);
void updateCurveOpacity(const Gtk::TreeModel::Row& selectedRow); 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 mouseOver(int modifierKey) override;
bool button1Pressed(int modifierKey) override; bool button1Pressed(int modifierKey) override;
bool button1Released() 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); cropgl->cropInit (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h);
} else if (iarea->getToolMode () == TMHand) { } else if (iarea->getToolMode () == TMHand) {
if (editSubscriber) { 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); needRedraw = editSubscriber->button1Pressed(bstate);
if (editSubscriber->isDragging()) { if (editSubscriber->isDragging()) {
state = SEditDrag1; state = SEditDrag1;
@ -1128,7 +1128,7 @@ void CropWindow::pointerMoved (int bstate, int x, int y)
rtengine::StagedImageProcessor* ipc = iarea->getImProcCoordinator(); rtengine::StagedImageProcessor* ipc = iarea->getImProcCoordinator();
if(ipc) { if(ipc) {
procparams::ProcParams params; 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); 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) { if(isRaw) {
ImageSource *isrc = static_cast<ImageSource*>(ipc->getInitialImage()); ImageSource *isrc = static_cast<ImageSource*>(ipc->getInitialImage());
@ -1293,7 +1293,10 @@ void CropWindow::updateCursor (int x, int y)
} else if (onArea (CropToolBar, x, y)) { } else if (onArea (CropToolBar, x, y)) {
newType = CSMove; newType = CSMove;
} else if (iarea->getObject() > -1 && editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) { } 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)) { } else if (onArea (CropResize, x, y)) {
newType = CSResizeDiagonal; newType = CSResizeDiagonal;
} else if (tm == TMColorPicker && hoveredPicker) { } else if (tm == TMColorPicker && hoveredPicker) {
@ -1320,7 +1323,10 @@ void CropWindow::updateCursor (int x, int y)
} }
if (objectID > -1) { 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) { } else if (tm == TMHand) {
if (onArea (CropObserved, x, y)) { if (onArea (CropObserved, x, y)) {
newType = CSMove; newType = CSMove;
@ -1346,7 +1352,10 @@ void CropWindow::updateCursor (int x, int y)
} }
if (objectID > -1) { if (objectID > -1) {
newType = editSubscriber->getCursor(objectID); int cursorX;
int cursorY;
screenCoordToImage (x, y, cursorX, cursorY);
newType = editSubscriber->getCursor(objectID, cursorX, cursorY);
} else { } else {
newType = CSArrow; newType = CSArrow;
} }
@ -1375,6 +1384,16 @@ void CropWindow::updateCursor (int x, int y)
newType = CSResizeDiagonal; newType = CSResizeDiagonal;
} else if (state == SDragPicker) { } else if (state == SDragPicker) {
newType = CSMove2D; 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) { if (newType != cursor_type) {

View File

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

View File

@ -133,7 +133,7 @@ public:
bool button1Pressed(int modifierKey) override; bool button1Pressed(int modifierKey) override;
bool button1Released() override; bool button1Released() override;
bool drag1(int modifierKey) 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; pipetteVal3 = newVal;
} }
CursorShape EditDataProvider::getCursor(int objectID) const CursorShape EditDataProvider::getCursor(int objectID, int xPos, int yPos) const
{ {
if (currSubscriber) { if (currSubscriber) {
currSubscriber->getCursor(objectID); currSubscriber->getCursor(objectID, xPos, yPos);
} }
return CSHandOpen; return CSHandOpen;
@ -187,12 +187,12 @@ EditSubscriber* EditDataProvider::getCurrSubscriber() const
return currSubscriber; return currSubscriber;
} }
EditDataProvider* EditSubscriber::getEditProvider() EditDataProvider* EditSubscriber::getEditProvider() const
{ {
return provider; return provider;
} }
CursorShape EditSubscriber::getCursor(int objectID) const CursorShape EditSubscriber::getCursor(int objectID, int xPos, int yPos) const
{ {
return CSHandOpen; return CSHandOpen;
} }

View File

@ -57,7 +57,7 @@ public:
virtual ~EditSubscriber () = default; virtual ~EditSubscriber () = default;
void setEditProvider(EditDataProvider *provider); void setEditProvider(EditDataProvider *provider);
EditDataProvider* getEditProvider (); EditDataProvider* getEditProvider () const;
void setEditID(EditUniqueID ID, BufferType buffType); void setEditID(EditUniqueID ID, BufferType buffType);
bool isCurrentSubscriber() const; bool isCurrentSubscriber() const;
virtual void subscribe(); virtual void subscribe();
@ -70,8 +70,10 @@ public:
bool isPicking() const; /// Returns true if something is being picked bool isPicking() const; /// Returns true if something is being picked
/** @brief Get the cursor to be displayed when above handles /** @brief Get the cursor to be displayed when above handles
@param objectID object currently "hovered" */ @param objectID object currently "hovered"
virtual CursorShape getCursor (int objectID) const; @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 /** @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 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 setPipetteVal1(float newVal);
void setPipetteVal2(float newVal); void setPipetteVal2(float newVal);
void setPipetteVal3(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; int getPipetteRectSize () const;
EditSubscriber* getCurrSubscriber() const; EditSubscriber* getCurrSubscriber() const;
virtual void getImageSize (int &w, int&h) = 0; virtual void getImageSize (int &w, int&h) = 0;

View File

@ -22,7 +22,11 @@
#include "editbuffer.h" #include "editbuffer.h"
#include "editcallbacks.h" #include "editcallbacks.h"
#include "rtsurface.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 () RGBColor Geometry::getInnerLineColor ()
{ {
@ -67,7 +71,8 @@ RGBColor Geometry::getOuterLineColor ()
void Circle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) 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; RGBColor color;
if (flags & F_AUTO_COLOR) { if (flags & F_AUTO_COLOR) {
@ -76,8 +81,9 @@ void Circle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
color = outerLineColor; color = outerLineColor;
} }
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
cr->set_line_width( getOuterLineWidth() ); cr->set_line_width (lineWidth);
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
rtengine::Coord center_ = center; rtengine::Coord center_ = center;
double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius);
@ -107,10 +113,11 @@ void Circle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
color = innerLineColor; 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; rtengine::Coord center_ = center;
double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); 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; center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen;
} }
if (filled && state != INSENSITIVE) { if (flags & F_DASHED) {
cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); 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) { if (innerLineWidth > 0.f) {
cr->fill_preserve(); cr->fill_preserve();
cr->stroke(); cr->stroke();
@ -134,28 +144,20 @@ void Circle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
} }
} else if (innerLineWidth > 0.f) { } else if (innerLineWidth > 0.f) {
cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI);
cr->stroke();
}
if (state == INSENSITIVE) { if (flags & F_DASHED) {
std::valarray<double> ds(1); cr->unset_dash();
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();
}
}
} }
} }
}
void Circle::drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) void Circle::drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
{ {
if (flags & F_HOVERABLE) { if (flags & F_HOVERABLE) {
cr->set_line_width( getMouseOverLineWidth() ); cr->set_line_width( getMouseOverLineWidth() );
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
rtengine::Coord center_ = center; rtengine::Coord center_ = center;
double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); 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) 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; RGBColor color;
if (flags & F_AUTO_COLOR) { if (flags & F_AUTO_COLOR) {
@ -199,8 +202,9 @@ void Line::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *
color = outerLineColor; color = outerLineColor;
} }
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.0); cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
cr->set_line_width( getOuterLineWidth() ); cr->set_line_width (lineWidth);
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
rtengine::Coord begin_ = begin; rtengine::Coord begin_ = begin;
rtengine::Coord end_ = end; rtengine::Coord end_ = end;
@ -234,10 +238,11 @@ void Line::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *
color = innerLineColor; 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 begin_ = begin; rtengine::Coord begin_ = begin;
rtengine::Coord end_ = end; rtengine::Coord end_ = end;
@ -253,21 +258,16 @@ void Line::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *
end_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; 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->move_to(begin_.x + 0.5, begin_.y + 0.5);
cr->line_to(end_.x + 0.5, end_.y + 0.5); cr->line_to(end_.x + 0.5, end_.y + 0.5);
cr->stroke();
if (state == INSENSITIVE) { if (flags & F_DASHED) {
std::valarray<double> ds(1); cr->unset_dash();
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();
} }
} }
} }
@ -276,6 +276,7 @@ void Line::drawToMOChannel(Cairo::RefPtr<Cairo::Context> &cr, unsigned short id,
{ {
if (flags & F_HOVERABLE) { if (flags & F_HOVERABLE) {
cr->set_line_width( getMouseOverLineWidth() ); cr->set_line_width( getMouseOverLineWidth() );
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
rtengine::Coord begin_ = begin; rtengine::Coord begin_ = begin;
rtengine::Coord end_ = end; 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) 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; RGBColor color;
if (flags & F_AUTO_COLOR) { if (flags & F_AUTO_COLOR) {
@ -313,8 +315,10 @@ void Polyline::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuff
color = outerLineColor; color = outerLineColor;
} }
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
cr->set_line_width( getOuterLineWidth() ); cr->set_line_width (lineWidth);
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
rtengine::Coord currPos; rtengine::Coord currPos;
@ -357,10 +361,16 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuff
color = innerLineColor; 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) { if (filled && state != INSENSITIVE) {
rtengine::Coord currPos; 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->line_to(currPos.x + 0.5, currPos.y + 0.5);
} }
} }
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();
} }
} }
} }
@ -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_source_rgba (0., 0., 0., (id + 1) / 65535.);
} }
for (unsigned int i = 0; i < points.size(); ++i) {
cr->set_line_width( getMouseOverLineWidth() ); 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) {
currPos = points.at(i); currPos = points.at(i);
if (datum == IMAGE) { 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) 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; RGBColor color;
if (flags & F_AUTO_COLOR) { if (flags & F_AUTO_COLOR) {
@ -506,8 +511,9 @@ void Rectangle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
color = outerLineColor; color = outerLineColor;
} }
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
cr->set_line_width( getOuterLineWidth() ); cr->set_line_width (lineWidth);
cr->set_line_join(Cairo::LINE_JOIN_BEVEL);
rtengine::Coord tl, br; rtengine::Coord tl, br;
@ -550,10 +556,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
color = innerLineColor; 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; 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; 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); cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y);
if (innerLineWidth > 0.f) { if (innerLineWidth > 0.f) {
@ -584,20 +595,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
} }
} else if (innerLineWidth > 0.f) { } else if (innerLineWidth > 0.f) {
cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y);
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();
} }
} }
} }
@ -606,6 +608,7 @@ void Rectangle::drawToMOChannel(Cairo::RefPtr<Cairo::Context> &cr, unsigned shor
{ {
if (flags & F_HOVERABLE) { if (flags & F_HOVERABLE) {
cr->set_line_width( getMouseOverLineWidth() ); cr->set_line_width( getMouseOverLineWidth() );
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
rtengine::Coord tl, br; rtengine::Coord tl, br;

View File

@ -24,10 +24,11 @@
#include <glibmm/ustring.h> #include <glibmm/ustring.h>
#include "editcoordsys.h" #include "editcoordsys.h"
#include "rtsurface.h"
#include "../rtengine/coord.h" #include "../rtengine/coord.h"
#include "../rtengine/rt_math.h"
class ObjectMOBuffer; class ObjectMOBuffer;
class RTSurface;
/** @file /** @file
* *
@ -210,6 +211,8 @@ public:
F_VISIBLE = 1 << 0, /// true if the geometry have to be drawn on the visible layer 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_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_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: /// @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: protected:
static const std::vector<double> dash;
RGBColor innerLineColor; RGBColor innerLineColor;
RGBColor outerLineColor; RGBColor outerLineColor;
short flags; short flags;
@ -252,6 +256,8 @@ public:
void setVisible (bool visible); void setVisible (bool visible);
bool isHoverable (); bool isHoverable ();
void setHoverable (bool visible); void setHoverable (bool visible);
bool isDashed ();
void setDashed (bool dashed);
// setActive will enable/disable the visible and hoverable flags in one shot! // 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 () { inline double Geometry::getMouseOverLineWidth () {
return getOuterLineWidth () + 2.; return rtengine::max(double(innerLineWidth), 1.) + 2.;
} }
inline void Geometry::setAutoColor (bool aColor) { 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) { inline void Geometry::setActive (bool active) {
if (active) { if (active) {
flags |= (F_VISIBLE | F_HOVERABLE); flags |= (F_VISIBLE | F_HOVERABLE);

View File

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

View File

@ -71,7 +71,7 @@ public:
void setEditProvider(EditDataProvider* provider) override; void setEditProvider(EditDataProvider* provider) override;
// EditSubscriber interface // EditSubscriber interface
CursorShape getCursor(int objectID) const override; CursorShape getCursor(int objectID, int xPos, int yPos) const override;
bool mouseOver(int modifierKey) override; bool mouseOver(int modifierKey) override;
bool button1Pressed(int modifierKey) override; bool button1Pressed(int modifierKey) override;
bool button1Released() 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) { switch (objectID) {
case (0): case (0):

View File

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

View File

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

View File

@ -1087,6 +1087,13 @@ struct ResizeParamsEdited {
bool allowUpscaling; bool allowUpscaling;
}; };
class SpotParamsEdited
{
public:
bool enabled;
bool entries;
};
struct ColorManagementParamsEdited { struct ColorManagementParamsEdited {
bool inputProfile; bool inputProfile;
bool toneCurve; bool toneCurve;
@ -1437,6 +1444,7 @@ struct ParamsEdited {
ChannelMixerParamsEdited chmixer; ChannelMixerParamsEdited chmixer;
BlackWhiteParamsEdited blackwhite; BlackWhiteParamsEdited blackwhite;
ResizeParamsEdited resize; ResizeParamsEdited resize;
SpotParamsEdited spot;
ColorManagementParamsEdited icm; ColorManagementParamsEdited icm;
RAWParamsEdited raw; RAWParamsEdited raw;
DirPyrEqualizerParamsEdited dirpyrequalizer; 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"))); labcurve = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_LABCURVE")));
// Detail Settings: // Detail Settings:
spot = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SPOT")));
sharpen = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SHARPENING"))); sharpen = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SHARPENING")));
localcontrast = Gtk::manage(new Gtk::CheckButton(M("PARTIALPASTE_LOCALCONTRAST"))); localcontrast = Gtk::manage(new Gtk::CheckButton(M("PARTIALPASTE_LOCALCONTRAST")));
sharpenedge = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SHARPENEDGE"))); sharpenedge = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SHARPENEDGE")));
@ -339,6 +340,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
//DETAIL //DETAIL
vboxes[1]->pack_start (*detail, Gtk::PACK_SHRINK, 2); vboxes[1]->pack_start (*detail, Gtk::PACK_SHRINK, 2);
vboxes[1]->pack_start (*hseps[1], 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 (*sharpen, Gtk::PACK_SHRINK, 2);
vboxes[1]->pack_start (*localcontrast, Gtk::PACK_SHRINK, 2); vboxes[1]->pack_start (*localcontrast, Gtk::PACK_SHRINK, 2);
vboxes[1]->pack_start (*sharpenedge, 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)); labcurveConn = labcurve->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true));
// Detail Settings: // 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)); 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)); 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)); 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 () void PartialPasteDlg::detailToggled ()
{ {
ConnectionBlocker spotBlocker(spotConn);
ConnectionBlocker sharpenBlocker(sharpenConn); ConnectionBlocker sharpenBlocker(sharpenConn);
ConnectionBlocker localcontrastBlocker(localcontrastConn); ConnectionBlocker localcontrastBlocker(localcontrastConn);
ConnectionBlocker gradsharpenBlocker(gradsharpenConn); ConnectionBlocker gradsharpenBlocker(gradsharpenConn);
@ -731,6 +735,7 @@ void PartialPasteDlg::detailToggled ()
detail->set_inconsistent (false); detail->set_inconsistent (false);
spot->set_active (detail->get_active ());
sharpen->set_active (detail->get_active ()); sharpen->set_active (detail->get_active ());
localcontrast->set_active(detail->get_active()); localcontrast->set_active(detail->get_active());
sharpenedge->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; filterPE.colorappearance = falsePE.colorappearance;
} }
if (!spot->get_active ()) {
filterPE.spot = falsePE.spot;
}
if (!sharpen->get_active ()) { if (!sharpen->get_active ()) {
filterPE.sharpening = falsePE.sharpening; filterPE.sharpening = falsePE.sharpening;
} }

View File

@ -142,6 +142,7 @@ public:
Gtk::CheckButton* colorappearance; Gtk::CheckButton* colorappearance;
// options in detail: // options in detail:
Gtk::CheckButton* spot;
Gtk::CheckButton* sharpen; Gtk::CheckButton* sharpen;
Gtk::CheckButton* sharpenedge; Gtk::CheckButton* sharpenedge;
Gtk::CheckButton* sharpenmicro; Gtk::CheckButton* sharpenmicro;
@ -224,7 +225,7 @@ public:
sigc::connection everythingConn, basicConn, detailConn, colorConn, lensConn, compositionConn, metaConn, rawConn, advancedConn; sigc::connection everythingConn, basicConn, detailConn, colorConn, lensConn, compositionConn, metaConn, rawConn, advancedConn;
sigc::connection locallabConn; sigc::connection locallabConn;
sigc::connection wbConn, exposureConn, localcontrastConn, shConn, pcvignetteConn, gradientConn, labcurveConn, colorappearanceConn; 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 vibranceConn, chmixerConn, hsveqConn, rgbcurvesConn, chmixerbwConn, colortoningConn, filmSimulationConn, softlightConn;
sigc::connection distortionConn, cacorrConn, vignettingConn, lcpConn; sigc::connection distortionConn, cacorrConn, vignettingConn, lcpConn;
sigc::connection coarserotConn, finerotConn, cropConn, resizeConn, prsharpeningConn, perspectiveConn, commonTransConn; sigc::connection coarserotConn, finerotConn, cropConn, resizeConn, prsharpeningConn, perspectiveConn, commonTransConn;

View File

@ -23,7 +23,6 @@
#include "guiutils.h" #include "guiutils.h"
#include "options.h" #include "options.h"
#include "../rtengine/procparams.h" #include "../rtengine/procparams.h"
using namespace rtengine; 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

@ -39,12 +39,21 @@ namespace procparams
class ProcParams; class ProcParams;
} }
} }
class EditDataProvider;
class ToolPanelListener class ToolPanelListener
{ {
public: public:
virtual ~ToolPanelListener() = default; 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; 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. */ /// @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 ()); shadowshighlights = Gtk::manage (new ShadowsHighlights ());
impulsedenoise = Gtk::manage (new ImpulseDenoise ()); impulsedenoise = Gtk::manage (new ImpulseDenoise ());
defringe = Gtk::manage (new Defringe ()); defringe = Gtk::manage (new Defringe ());
spot = Gtk::manage (new Spot ());
dirpyrdenoise = Gtk::manage (new DirPyrDenoise ()); dirpyrdenoise = Gtk::manage (new DirPyrDenoise ());
epd = Gtk::manage (new EdgePreservingDecompositionUI ()); epd = Gtk::manage (new EdgePreservingDecompositionUI ());
sharpening = Gtk::manage (new Sharpening ()); sharpening = Gtk::manage (new Sharpening ());
@ -117,6 +118,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
addfavoritePanel (colorPanel, chmixer); addfavoritePanel (colorPanel, chmixer);
addfavoritePanel (colorPanel, blackwhite); addfavoritePanel (colorPanel, blackwhite);
addfavoritePanel (exposurePanel, shadowshighlights); addfavoritePanel (exposurePanel, shadowshighlights);
addfavoritePanel (detailsPanel, spot);
addfavoritePanel (detailsPanel, sharpening); addfavoritePanel (detailsPanel, sharpening);
addfavoritePanel (detailsPanel, localContrast); addfavoritePanel (detailsPanel, localContrast);
addfavoritePanel (detailsPanel, sharpenEdge); 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) void ToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr)
{ {

View File

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

View File

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