Merge pull request #6227 from Beep6581/spot-removal-tool
Spot removal tool
This commit is contained in:
commit
d11d834045
81
rtdata/images/svg/spot-active.svg
Normal file
81
rtdata/images/svg/spot-active.svg
Normal file
@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="11"
|
||||
height="11"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="spot-active.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#a94a4a"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="1.6872889"
|
||||
inkscape:cy="2.4074225"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1025"
|
||||
inkscape:window-x="-2"
|
||||
inkscape:window-y="-3"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1041.3622)">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:0.39215687;stroke:none"
|
||||
d="M 6 2 L 6 5 L 7 5 L 7 2 L 6 2 z M 2 6 L 2 7 L 5 7 L 5 6 L 2 6 z M 6 6 L 6 7 L 6 10 L 6 11 L 7 11 L 7 7 L 11 7 L 11 6 L 10 6 L 7 6 L 6 6 z "
|
||||
transform="translate(0,1041.3622)"
|
||||
id="rect2987" />
|
||||
<path
|
||||
id="path3784"
|
||||
d="m 5,1042.3622 0,4 -4,0 0,1 4,0 0,4 1,0 0,-4 4,0 0,-1 -4,0 0,-4 -1,0 z"
|
||||
style="fill:#ffffff;fill-opacity:0.58823532;stroke:none"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
id="rect3788"
|
||||
width="1"
|
||||
height="3"
|
||||
x="5"
|
||||
y="1045.3622" />
|
||||
<rect
|
||||
y="4"
|
||||
x="-1047.3622"
|
||||
height="3"
|
||||
width="1"
|
||||
id="rect3790"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
transform="matrix(0,-1,1,0,0,0)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
73
rtdata/images/svg/spot-normal.svg
Normal file
73
rtdata/images/svg/spot-normal.svg
Normal 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 |
74
rtdata/images/svg/spot-prelight.svg
Normal file
74
rtdata/images/svg/spot-prelight.svg
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="11"
|
||||
height="11"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="spot-prelight.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#a94a4a"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="1.6872889"
|
||||
inkscape:cy="2.4074225"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1025"
|
||||
inkscape:window-x="-2"
|
||||
inkscape:window-y="-3"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1041.3622)">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:0.66666669;stroke:none"
|
||||
d="m 6,1043.3622 0,4 -4,0 0,1 4,0 0,4 1,0 0,-4 4,0 0,-1 -4,0 0,-4 -1,0 z"
|
||||
id="rect2987" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
id="rect2985"
|
||||
width="9"
|
||||
height="1"
|
||||
x="1"
|
||||
y="1046.3622" />
|
||||
<rect
|
||||
y="1042.3622"
|
||||
x="5"
|
||||
height="9"
|
||||
width="1"
|
||||
id="rect2989"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
@ -764,6 +764,8 @@ HISTORY_MSG_SHARPENING_CONTRAST;Netteté - Seuil de contraste
|
||||
HISTORY_MSG_SH_COLORSPACE;O/HL - Espace couleur
|
||||
HISTORY_MSG_SOFTLIGHT_ENABLED;Lumière douce
|
||||
HISTORY_MSG_SOFTLIGHT_STRENGTH;Lumière douce - Force
|
||||
HISTORY_MSG_SPOT;Retrait de taches
|
||||
HISTORY_MSG_SPOT_ENTRY;Retrait de taches - Modif. de points
|
||||
HISTORY_MSG_TM_FATTAL_ANCHOR;CPD - Ancre
|
||||
HISTORY_NEWSNAPSHOT;Ajouter
|
||||
HISTORY_NEWSNAPSHOT_TOOLTIP;Raccourci: <b>Alt-s</b>
|
||||
@ -1024,6 +1026,7 @@ PARTIALPASTE_SHARPENEDGE;Bords
|
||||
PARTIALPASTE_SHARPENING;Netteté
|
||||
PARTIALPASTE_SHARPENMICRO;Microcontraste
|
||||
PARTIALPASTE_SOFTLIGHT;Lumière douce
|
||||
PARTIALPASTE_SPOT;Retrait de taches
|
||||
PARTIALPASTE_TM_FATTAL;Compression de plage dynamique
|
||||
PARTIALPASTE_VIBRANCE;Vibrance
|
||||
PARTIALPASTE_VIGNETTING;Correction du vignettage
|
||||
@ -2745,6 +2748,10 @@ TP_SHARPENMICRO_MATRIX;Matrice 3×3 au lieu de 5×5
|
||||
TP_SHARPENMICRO_UNIFORMITY;Uniformité
|
||||
TP_SOFTLIGHT_LABEL;Lumière douce
|
||||
TP_SOFTLIGHT_STRENGTH;Force
|
||||
TP_SPOT_COUNTLABEL;%1 point(s)
|
||||
TP_SPOT_ENTRYCHANGED;Modification d'un point
|
||||
TP_SPOT_HINT;Cliquez sur ce bouton pour pouvoir opérer sur la zone de prévisualisation.\n\nPour ajouter un spot, pressez Ctrl et le bouton gauche de la souris, tirez le cercle (la touche Ctrl peut être relâchée) vers la position source, puis relâchez le bouton de la souris.\n\nPour éditer un spot, placez le curseur au-dessus de la marque blanche situant une zone éditée, faisant apparaître la géométrie d'édition.\n\nPour déplacer le spot source ou destination, placez le curseur en son centre et tirez le.\n\nLe cercle intérieur (zone d'effet maximum) et le cercle "d'adoucicement" peuvent être redimmensionné en plaçant le curseur dessus (le cercle devient orange) et en le tirant (le cercle devient rouge).\n\nQuand les changements sont terminés, un clic droit en dehors de tout spot termine le mode d'édition, ou cliquez à nouveau sur ce bouton.
|
||||
TP_SPOT_LABEL;Retrait de taches
|
||||
TP_TM_FATTAL_AMOUNT;Quantité
|
||||
TP_TM_FATTAL_ANCHOR;Ancre
|
||||
TP_TM_FATTAL_LABEL;Compression de Plage Dynamique
|
||||
|
@ -1396,6 +1396,8 @@ HISTORY_MSG_SIGMAFIN;Final contrast Attenuation response
|
||||
HISTORY_MSG_SIGMATON;Toning Attenuation response
|
||||
HISTORY_MSG_SOFTLIGHT_ENABLED;Soft light
|
||||
HISTORY_MSG_SOFTLIGHT_STRENGTH;Soft light - Strength
|
||||
HISTORY_MSG_SPOT;Spot removal
|
||||
HISTORY_MSG_SPOT_ENTRY;Spot removal - Point modif.
|
||||
HISTORY_MSG_TEMPOUT;CAM02 automatic temperature
|
||||
HISTORY_MSG_THRESWAV;Balance threshold
|
||||
HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anchor
|
||||
@ -1698,6 +1700,7 @@ PARTIALPASTE_SHARPENEDGE;Edges
|
||||
PARTIALPASTE_SHARPENING;Sharpening (USM/RL)
|
||||
PARTIALPASTE_SHARPENMICRO;Microcontrast
|
||||
PARTIALPASTE_SOFTLIGHT;Soft light
|
||||
PARTIALPASTE_SPOT;Spot removal
|
||||
PARTIALPASTE_TM_FATTAL;Dynamic range compression
|
||||
PARTIALPASTE_VIBRANCE;Vibrance
|
||||
PARTIALPASTE_VIGNETTING;Vignetting correction
|
||||
@ -3598,6 +3601,10 @@ TP_SHARPENMICRO_MATRIX;3×3 matrix instead of 5×5
|
||||
TP_SHARPENMICRO_UNIFORMITY;Uniformity
|
||||
TP_SOFTLIGHT_LABEL;Soft Light
|
||||
TP_SOFTLIGHT_STRENGTH;Strength
|
||||
TP_SPOT_COUNTLABEL;%1 point(s)
|
||||
TP_SPOT_ENTRYCHANGED;Point changed
|
||||
TP_SPOT_HINT;Click on this button to be able to operate on the preview area.\n\nTo edit a spot, hover the white mark locating an edited area, making the editing geometry appear.\n\nTo add a spot, press Ctrl and left mouse button, drag the circle (Ctrl key can be released) to a source location, then release the mouse button.\n\nTo move the source or destination spot, hover its center then drag it.\n\nThe inner circle (maximum effect area) and the "feather" circle can be resized by hovering them (the circle becomes orange) and dragging it (the circle becomes red).\n\nWhen the changes are done, right click outside any spot to end the Spot editing mode, or click on this button again.
|
||||
TP_SPOT_LABEL;Spot Removal
|
||||
TP_TM_FATTAL_AMOUNT;Amount
|
||||
TP_TM_FATTAL_ANCHOR;Anchor
|
||||
TP_TM_FATTAL_LABEL;Dynamic Range Compression
|
||||
|
@ -64,6 +64,7 @@ endif()
|
||||
set(CAMCONSTSFILE "camconst.json")
|
||||
|
||||
set(RTENGINESOURCEFILES
|
||||
alpha.cc
|
||||
ahd_demosaic_RT.cc
|
||||
amaze_demosaic_RT.cc
|
||||
badpixels.cc
|
||||
@ -163,6 +164,7 @@ set(RTENGINESOURCEFILES
|
||||
rtthumbnail.cc
|
||||
shmap.cc
|
||||
simpleprocess.cc
|
||||
spot.cc
|
||||
stdimagesource.cc
|
||||
tmo_fattal02.cc
|
||||
utils.cc
|
||||
|
96
rtengine/alpha.cc
Normal file
96
rtengine/alpha.cc
Normal 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
58
rtengine/alpha.h
Normal 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
|
@ -54,7 +54,7 @@ namespace rtengine
|
||||
{
|
||||
|
||||
Crop::Crop(ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow)
|
||||
: PipetteBuffer(editDataProvider), origCrop(nullptr), laboCrop(nullptr), labnCrop(nullptr),
|
||||
: PipetteBuffer(editDataProvider), origCrop(nullptr), spotCrop(nullptr), laboCrop(nullptr), labnCrop(nullptr),
|
||||
cropImg(nullptr), shbuf_real(nullptr), transCrop(nullptr), cieCrop(nullptr), shbuffer(nullptr),
|
||||
updating(false), newUpdatePending(false), skip(10),
|
||||
cropx(0), cropy(0), cropw(-1), croph(-1),
|
||||
@ -153,6 +153,7 @@ void Crop::update(int todo)
|
||||
// give possibility to the listener to modify crop window (as the full image dimensions are already known at this point)
|
||||
int wx, wy, ww, wh, ws;
|
||||
const bool overrideWindow = cropImageListener;
|
||||
bool spotsDone = false;
|
||||
|
||||
if (overrideWindow) {
|
||||
cropImageListener->getWindow(wx, wy, ww, wh, ws);
|
||||
@ -615,6 +616,13 @@ void Crop::update(int todo)
|
||||
parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw);
|
||||
}
|
||||
|
||||
if ((todo & M_SPOT) && params.spot.enabled && !params.spot.entries.empty()) {
|
||||
spotsDone = true;
|
||||
PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip);
|
||||
//parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw);
|
||||
parent->ipf.removeSpots(origCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, nullptr, tr);
|
||||
}
|
||||
|
||||
DirPyrDenoiseParams denoiseParams = params.dirpyrDenoise;
|
||||
|
||||
if (params.dirpyrDenoise.Lmethod == "CUR") {
|
||||
@ -697,6 +705,28 @@ void Crop::update(int todo)
|
||||
// has to be called after setCropSizes! Tools prior to this point can't handle the Edit mechanism, but that shouldn't be a problem.
|
||||
createBuffer(cropw, croph);
|
||||
|
||||
// Apply Spot removal
|
||||
if ((todo & M_SPOT) && !spotsDone) {
|
||||
if (params.spot.enabled && !params.spot.entries.empty()) {
|
||||
if(!spotCrop) {
|
||||
spotCrop = new Imagefloat (cropw, croph);
|
||||
}
|
||||
baseCrop->copyData (spotCrop);
|
||||
PreviewProps pp (trafx, trafy, trafw * skip, trafh * skip, skip);
|
||||
int tr = getCoarseBitMask(params.coarse);
|
||||
parent->ipf.removeSpots (spotCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, ¶ms.icm, tr);
|
||||
} else {
|
||||
if (spotCrop) {
|
||||
delete spotCrop;
|
||||
spotCrop = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spotCrop) {
|
||||
baseCrop = spotCrop;
|
||||
}
|
||||
|
||||
std::unique_ptr<Imagefloat> fattalCrop;
|
||||
|
||||
if ((todo & M_HDR) && (params.fattal.enabled || params.dehaze.enabled)) {
|
||||
@ -722,7 +752,7 @@ void Crop::update(int todo)
|
||||
parent->imgsrc->getImage(parent->currWB, tr, f, pp, params.toneCurve, params.raw);
|
||||
parent->imgsrc->convertColorSpace(f, params.icm, parent->currWB);
|
||||
|
||||
if (params.dirpyrDenoise.enabled || params.filmNegative.enabled) {
|
||||
if (params.dirpyrDenoise.enabled || params.filmNegative.enabled || params.spot.enabled) {
|
||||
// copy the denoised crop
|
||||
int oy = trafy / skip;
|
||||
int ox = trafx / skip;
|
||||
|
@ -38,6 +38,7 @@ class Crop final : public DetailedCrop, public PipetteBuffer
|
||||
protected:
|
||||
// --- permanently allocated in RAM and only renewed on size changes
|
||||
Imagefloat* origCrop; // "one chunk" allocation
|
||||
Imagefloat* spotCrop; // "one chunk" allocation
|
||||
LabImage* laboCrop; // "one chunk" allocation
|
||||
LabImage* labnCrop; // "one chunk" allocation
|
||||
Image8* cropImg; // "one chunk" allocation ; displayed image in monitor color space, showing the output profile as well (soft-proofing enabled, which then correspond to workimg) or not
|
||||
|
@ -347,6 +347,23 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Copy the a sub-region of the data to another PlanarRGBData */
|
||||
void copyData(PlanarWhateverData<T> *dest, int x, int y, int width, int height)
|
||||
{
|
||||
assert (dest != NULL);
|
||||
// Make sure that the size is the same, reallocate if necessary
|
||||
dest->allocate(width, height);
|
||||
|
||||
if (dest->width == -1) {
|
||||
printf("ERROR: PlanarRGBData::copyData >>> allocation failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = y, j = 0; i < y + height; ++i, ++j) {
|
||||
memcpy (dest->v(i) + x, v(j), width * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
void rotate (int deg) override
|
||||
{
|
||||
|
||||
@ -756,6 +773,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Copy the a sub-region of the data to another PlanarRGBData */
|
||||
void copyData(PlanarRGBData<T> *dest, int x, int y, int width, int height)
|
||||
{
|
||||
assert (dest != NULL);
|
||||
// Make sure that the size is the same, reallocate if necessary
|
||||
dest->allocate(width, height);
|
||||
|
||||
if (dest->width == -1) {
|
||||
printf("ERROR: PlanarRGBData::copyData >>> allocation failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = y, j = 0; i < y + height; ++i, ++j) {
|
||||
memcpy (dest->r(i) + x, r(j), width * sizeof(T));
|
||||
memcpy (dest->g(i) + x, g(j), width * sizeof(T));
|
||||
memcpy (dest->b(i) + x, b(j), width * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
void rotate (int deg) final
|
||||
{
|
||||
|
||||
@ -1392,6 +1428,23 @@ public:
|
||||
memcpy (dest->data, data, 3 * width * height * sizeof(T));
|
||||
}
|
||||
|
||||
/** Copy the a sub-region of the data to another PlanarRGBData */
|
||||
void copyData(ChunkyRGBData<T> *dest, int x, int y, int width, int height)
|
||||
{
|
||||
assert (dest != NULL);
|
||||
// Make sure that the size is the same, reallocate if necessary
|
||||
dest->allocate(width, height);
|
||||
|
||||
if (dest->width == -1) {
|
||||
printf("ERROR: PlanarRGBData::copyData >>> allocation failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = y, j = 0; i < y + height; ++i, ++j) {
|
||||
memcpy (dest->r(i) + x, r(j), 3 * width * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
void rotate (int deg) final
|
||||
{
|
||||
|
||||
|
@ -134,7 +134,21 @@ Image16* Image16::copy() const
|
||||
return cp;
|
||||
}
|
||||
|
||||
void Image16::getStdImage(const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const
|
||||
Image16* Image16::copySubRegion (int x, int y, int width, int height)
|
||||
{
|
||||
Image16* cp = NULL;
|
||||
int realWidth = LIM<int>(x + width, 0, this->width) - x;
|
||||
int realHeight = LIM<int>(y + height, 0, this->height) - y;
|
||||
|
||||
if (realWidth > 0 && realHeight > 0) {
|
||||
cp = new Image16 (realWidth, realHeight);
|
||||
copyData(cp, x, y, realWidth, realHeight);
|
||||
}
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
void Image16::getStdImage(const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const
|
||||
{
|
||||
|
||||
// compute channel multipliers
|
||||
|
@ -39,8 +39,9 @@ public:
|
||||
~Image16() override;
|
||||
|
||||
Image16* copy() const;
|
||||
Image16* copySubRegion (int x, int y, int width, int height);
|
||||
|
||||
void getStdImage(const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const override;
|
||||
void getStdImage(const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const override;
|
||||
|
||||
const char* getType() const override
|
||||
{
|
||||
|
@ -100,7 +100,7 @@ Image8* Image8::copy () const
|
||||
return cp;
|
||||
}
|
||||
|
||||
void Image8::getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const
|
||||
void Image8::getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const
|
||||
{
|
||||
// compute channel multipliers
|
||||
float rm = 1.f, gm = 1.f, bm = 1.f;
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
|
||||
Image8* copy () const;
|
||||
|
||||
void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const override;
|
||||
void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const override;
|
||||
|
||||
const char* getType () const override
|
||||
{
|
||||
|
@ -54,6 +54,14 @@ int PreviewProps::getSkip() const
|
||||
return skip;
|
||||
}
|
||||
|
||||
void PreviewProps::set (int x, int y, int w, int h, int skip) {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
this->width = w;
|
||||
this->height = h;
|
||||
this->skip = skip;
|
||||
}
|
||||
|
||||
ImageDimensions::ImageDimensions() :
|
||||
width(-1),
|
||||
height(-1)
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
int getSkip() const;
|
||||
void set (int x, int y, int w, int h, int skip);
|
||||
|
||||
private:
|
||||
int x;
|
||||
|
@ -165,8 +165,22 @@ Imagefloat* Imagefloat::copy () const
|
||||
return cp;
|
||||
}
|
||||
|
||||
Imagefloat* Imagefloat::copySubRegion (int x, int y, int width, int height)
|
||||
{
|
||||
Imagefloat* cp = NULL;
|
||||
int realWidth = LIM<int>(x + width, 0, this->width) - x;
|
||||
int realHeight = LIM<int>(y + height, 0, this->height) - y;
|
||||
|
||||
if (realWidth > 0 && realHeight > 0) {
|
||||
cp = new Imagefloat (realWidth, realHeight);
|
||||
copyData(cp, x, y, realWidth, realHeight);
|
||||
}
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
// This is called by the StdImageSource class. We assume that fp images from StdImageSource don't have to deal with gamma
|
||||
void Imagefloat::getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const
|
||||
void Imagefloat::getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const
|
||||
{
|
||||
|
||||
// compute channel multipliers
|
||||
|
@ -44,8 +44,9 @@ public:
|
||||
~Imagefloat () override;
|
||||
|
||||
Imagefloat* copy () const;
|
||||
Imagefloat* copySubRegion (int x, int y, int width, int height);
|
||||
|
||||
void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const override;
|
||||
void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const override;
|
||||
|
||||
const char* getType () const override
|
||||
{
|
||||
|
@ -93,7 +93,7 @@ public:
|
||||
void setSampleArrangement(IIOSampleArrangement sArrangement);
|
||||
IIOSampleArrangement getSampleArrangement() const;
|
||||
|
||||
virtual void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, PreviewProps pp) const = 0;
|
||||
virtual void getStdImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const = 0;
|
||||
virtual int getBPS () const = 0;
|
||||
virtual void getScanline (int row, unsigned char* buffer, int bps, bool isFloat = false) const = 0;
|
||||
virtual void setScanline (int row, const unsigned char* buffer, int bps, unsigned int numSamples = 3) = 0;
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "labimage.h"
|
||||
#include "lcp.h"
|
||||
#include "procparams.h"
|
||||
#include "tweakoperator.h"
|
||||
#include "refreshmap.h"
|
||||
#include "utils.h"
|
||||
|
||||
@ -58,6 +59,7 @@ namespace rtengine
|
||||
ImProcCoordinator::ImProcCoordinator() :
|
||||
orig_prev(nullptr),
|
||||
oprevi(nullptr),
|
||||
spotprev(nullptr),
|
||||
oprevl(nullptr),
|
||||
nprevl(nullptr),
|
||||
fattal_11_dcrop_cache(nullptr),
|
||||
@ -167,6 +169,7 @@ ImProcCoordinator::ImProcCoordinator() :
|
||||
hListener(nullptr),
|
||||
resultValid(false),
|
||||
params(new procparams::ProcParams),
|
||||
tweakOperator(nullptr),
|
||||
lastOutputProfile("BADFOOD"),
|
||||
lastOutputIntent(RI__COUNT),
|
||||
lastOutputBPC(false),
|
||||
@ -280,9 +283,32 @@ void ImProcCoordinator::assign(ImageSource* imgsrc)
|
||||
this->imgsrc = imgsrc;
|
||||
}
|
||||
|
||||
void ImProcCoordinator::getParams(procparams::ProcParams* dst)
|
||||
void ImProcCoordinator::getParams(procparams::ProcParams* dst, bool tweaked)
|
||||
{
|
||||
*dst = *params;
|
||||
if (!tweaked && paramsBackup.operator bool()) {
|
||||
*dst = *paramsBackup;
|
||||
} else {
|
||||
*dst = *params;
|
||||
}
|
||||
}
|
||||
|
||||
void ImProcCoordinator::backupParams()
|
||||
{
|
||||
if (!params) {
|
||||
return;
|
||||
}
|
||||
if (!paramsBackup) {
|
||||
paramsBackup.reset(new ProcParams());
|
||||
}
|
||||
*paramsBackup = *params;
|
||||
}
|
||||
|
||||
void ImProcCoordinator::restoreParams()
|
||||
{
|
||||
if (!paramsBackup || !params) {
|
||||
return;
|
||||
}
|
||||
*params = *paramsBackup;
|
||||
}
|
||||
|
||||
DetailedCrop* ImProcCoordinator::createCrop(::EditDataProvider *editDataProvider, bool isDetailWindow)
|
||||
@ -322,6 +348,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
|
||||
RAWParams rp = params->raw;
|
||||
ColorManagementParams cmp = params->icm;
|
||||
LCurveParams lcur = params->labCurve;
|
||||
bool spotsDone = false;
|
||||
|
||||
if (!highDetailNeeded) {
|
||||
// if below 100% magnification, take a fast path
|
||||
@ -585,6 +612,13 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
|
||||
ipf.setScale(scale);
|
||||
|
||||
imgsrc->getImage(currWB, tr, orig_prev, pp, params->toneCurve, params->raw);
|
||||
|
||||
if ((todo & M_SPOT) && params->spot.enabled && !params->spot.entries.empty()) {
|
||||
spotsDone = true;
|
||||
PreviewProps pp(0, 0, fw, fh, scale);
|
||||
ipf.removeSpots(orig_prev, imgsrc, params->spot.entries, pp, currWB, nullptr, tr);
|
||||
}
|
||||
|
||||
denoiseInfoStore.valid = false;
|
||||
//ColorTemp::CAT02 (orig_prev, ¶ms) ;
|
||||
// printf("orig_prevW=%d\n scale=%d",orig_prev->width, scale);
|
||||
@ -654,6 +688,25 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
|
||||
ipf.firstAnalysis(orig_prev, *params, vhist16);
|
||||
}
|
||||
|
||||
oprevi = orig_prev;
|
||||
|
||||
if ((todo & M_SPOT) && !spotsDone) {
|
||||
if (params->spot.enabled && !params->spot.entries.empty()) {
|
||||
allocCache(spotprev);
|
||||
orig_prev->copyData(spotprev);
|
||||
PreviewProps pp(0, 0, fw, fh, scale);
|
||||
ipf.removeSpots(spotprev, imgsrc, params->spot.entries, pp, currWB, ¶ms->icm, tr);
|
||||
} else {
|
||||
if (spotprev) {
|
||||
delete spotprev;
|
||||
spotprev = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (spotprev) {
|
||||
spotprev->copyData(orig_prev);
|
||||
}
|
||||
|
||||
if ((todo & M_HDR) && (params->fattal.enabled || params->dehaze.enabled)) {
|
||||
if (fattal_11_dcrop_cache) {
|
||||
delete fattal_11_dcrop_cache;
|
||||
@ -668,12 +721,11 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
|
||||
}
|
||||
}
|
||||
|
||||
oprevi = orig_prev;
|
||||
|
||||
// Remove transformation if unneeded
|
||||
bool needstransform = ipf.needsTransform(fw, fh, imgsrc->getRotateDegree(), imgsrc->getMetaData());
|
||||
|
||||
if ((needstransform || ((todo & (M_TRANSFORM | M_RGBCURVE)) && params->dirpyrequalizer.cbdlMethod == "bef" && params->dirpyrequalizer.enabled && !params->colorappearance.enabled))) {
|
||||
// Forking the image
|
||||
assert(oprevi);
|
||||
Imagefloat *op = oprevi;
|
||||
oprevi = new Imagefloat(pW, pH);
|
||||
@ -1885,15 +1937,31 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
|
||||
delete oprevi;
|
||||
oprevi = nullptr;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ImProcCoordinator::setTweakOperator (TweakOperator *tOperator)
|
||||
{
|
||||
if (tOperator) {
|
||||
tweakOperator = tOperator;
|
||||
}
|
||||
}
|
||||
|
||||
void ImProcCoordinator::unsetTweakOperator (TweakOperator *tOperator)
|
||||
{
|
||||
if (tOperator && tOperator == tweakOperator) {
|
||||
tweakOperator = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ImProcCoordinator::freeAll()
|
||||
{
|
||||
|
||||
if (allocated) {
|
||||
if (spotprev && spotprev != oprevi) {
|
||||
delete spotprev;
|
||||
}
|
||||
spotprev = nullptr;
|
||||
|
||||
if (orig_prev != oprevi) {
|
||||
delete oprevi;
|
||||
}
|
||||
@ -1926,6 +1994,15 @@ void ImProcCoordinator::freeAll()
|
||||
allocated = false;
|
||||
}
|
||||
|
||||
void ImProcCoordinator::allocCache (Imagefloat* &imgfloat)
|
||||
{
|
||||
if (imgfloat == nullptr) {
|
||||
imgfloat = new Imagefloat(pW, pH);
|
||||
} else {
|
||||
imgfloat->allocate(pW, pH);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Handles image buffer (re)allocation and trigger sizeChanged of SizeListener[s]
|
||||
* If the scale change, this method will free all buffers and reallocate ones of the new size.
|
||||
* It will then tell to the SizeListener that size has changed (sizeChanged)
|
||||
@ -1944,9 +2021,9 @@ void ImProcCoordinator::setScale(int prevscale)
|
||||
|
||||
do {
|
||||
prevscale--;
|
||||
PreviewProps pp(0, 0, fw, fh, prevscale);
|
||||
imgsrc->getSize(pp, nW, nH);
|
||||
} while (nH < 400 && prevscale > 1 && (nW * nH < 1000000)); // sctually hardcoded values, perhaps a better choice is possible
|
||||
PreviewProps pp (0, 0, fw, fh, prevscale);
|
||||
imgsrc->getSize (pp, nW, nH);
|
||||
} while (nH < 400 && prevscale > 1 && (nW * nH < 1000000)); // actually hardcoded values, perhaps a better choice is possible
|
||||
|
||||
if (nW != pW || nH != pH) {
|
||||
|
||||
@ -2387,35 +2464,37 @@ void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool a
|
||||
MyMutex::MyLock lock(mProcessing);
|
||||
|
||||
int fW, fH;
|
||||
std::unique_ptr<ProcParams> validParams(new ProcParams());
|
||||
getParams(validParams.get());
|
||||
|
||||
int tr = getCoarseBitMask(params->coarse);
|
||||
int tr = getCoarseBitMask(validParams->coarse);
|
||||
|
||||
imgsrc->getFullSize(fW, fH, tr);
|
||||
PreviewProps pp(0, 0, fW, fH, 1);
|
||||
ProcParams ppar = *params;
|
||||
ProcParams ppar = *validParams;
|
||||
ppar.toneCurve.hrenabled = false;
|
||||
ppar.icm.inputProfile = "(none)";
|
||||
Imagefloat* im = new Imagefloat(fW, fH);
|
||||
imgsrc->preprocess(ppar.raw, ppar.lensProf, ppar.coarse);
|
||||
double dummy = 0.0;
|
||||
imgsrc->demosaic(ppar.raw, false, dummy);
|
||||
ColorTemp currWB = ColorTemp(params->wb.temperature, params->wb.green, params->wb.equal, params->wb.method);
|
||||
ColorTemp currWB = ColorTemp(validParams->wb.temperature, validParams->wb.green, validParams->wb.equal, validParams->wb.method);
|
||||
|
||||
if (params->wb.method == "Camera") {
|
||||
if (validParams->wb.method == "Camera") {
|
||||
currWB = imgsrc->getWB();
|
||||
} else if (params->wb.method == "autold") {
|
||||
if (lastAwbEqual != params->wb.equal || lastAwbTempBias != params->wb.tempBias) {
|
||||
} else if (validParams->wb.method == "autold") {
|
||||
if (lastAwbEqual != validParams->wb.equal || lastAwbTempBias != validParams->wb.tempBias) {
|
||||
double rm, gm, bm;
|
||||
imgsrc->getAutoWBMultipliers(rm, gm, bm);
|
||||
|
||||
if (rm != -1.) {
|
||||
autoWB.update(rm, gm, bm, params->wb.equal, params->wb.tempBias);
|
||||
lastAwbEqual = params->wb.equal;
|
||||
lastAwbTempBias = params->wb.tempBias;
|
||||
autoWB.update(rm, gm, bm, validParams->wb.equal, validParams->wb.tempBias);
|
||||
lastAwbEqual = validParams->wb.equal;
|
||||
lastAwbTempBias = validParams->wb.tempBias;
|
||||
} else {
|
||||
lastAwbEqual = -1.;
|
||||
lastAwbTempBias = 0.0;
|
||||
autoWB.useDefaults(params->wb.equal);
|
||||
autoWB.useDefaults(validParams->wb.equal);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2437,12 +2516,12 @@ void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool a
|
||||
im = trImg;
|
||||
}
|
||||
|
||||
if (params->crop.enabled) {
|
||||
Imagefloat *tmpim = new Imagefloat(params->crop.w, params->crop.h);
|
||||
int cx = params->crop.x;
|
||||
int cy = params->crop.y;
|
||||
int cw = params->crop.w;
|
||||
int ch = params->crop.h;
|
||||
if (validParams->crop.enabled) {
|
||||
Imagefloat *tmpim = new Imagefloat(validParams->crop.w, validParams->crop.h);
|
||||
int cx = validParams->crop.x;
|
||||
int cy = validParams->crop.y;
|
||||
int cw = validParams->crop.w;
|
||||
int ch = validParams->crop.h;
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
@ -2473,7 +2552,7 @@ void ImProcCoordinator::saveInputICCReference(const Glib::ustring& fname, bool a
|
||||
}
|
||||
|
||||
int imw, imh;
|
||||
double tmpScale = ipf.resizeScale(params.get(), fW, fH, imw, imh);
|
||||
double tmpScale = ipf.resizeScale(validParams.get(), fW, fH, imw, imh);
|
||||
|
||||
if (tmpScale != 1.0) {
|
||||
Imagefloat* tempImage = new Imagefloat(imw, imh);
|
||||
@ -2582,12 +2661,22 @@ void ImProcCoordinator::process()
|
||||
|| params->dehaze != nextParams->dehaze
|
||||
|| params->pdsharpening != nextParams->pdsharpening
|
||||
|| params->filmNegative != nextParams->filmNegative
|
||||
|| params->spot.enabled != nextParams->spot.enabled
|
||||
|| sharpMaskChanged;
|
||||
|
||||
sharpMaskChanged = false;
|
||||
*params = *nextParams;
|
||||
int change = changeSinceLast;
|
||||
changeSinceLast = 0;
|
||||
|
||||
if (tweakOperator) {
|
||||
// TWEAKING THE PROCPARAMS FOR THE SPOT ADJUSTMENT MODE
|
||||
backupParams();
|
||||
tweakOperator->tweakParams(*params);
|
||||
} else if (paramsBackup) {
|
||||
paramsBackup.release();
|
||||
}
|
||||
|
||||
paramsUpdateMutex.unlock();
|
||||
|
||||
// M_VOID means no update, and is a bit higher that the rest
|
||||
|
@ -42,6 +42,7 @@ namespace rtengine
|
||||
using namespace procparams;
|
||||
|
||||
class Crop;
|
||||
class TweakOperator;
|
||||
|
||||
/** @brief Manages the image processing, espc. of the preview windows
|
||||
*
|
||||
@ -62,6 +63,7 @@ class ImProcCoordinator final : public StagedImageProcessor, public HistogramObs
|
||||
protected:
|
||||
Imagefloat *orig_prev;
|
||||
Imagefloat *oprevi;
|
||||
Imagefloat *spotprev;
|
||||
LabImage *oprevl;
|
||||
LabImage *nprevl;
|
||||
Imagefloat *fattal_11_dcrop_cache; // global cache for ToneMapFattal02 used in 1:1 detail windows (except when denoise is active)
|
||||
@ -206,6 +208,9 @@ protected:
|
||||
|
||||
MyMutex minit; // to gain mutually exclusive access to ... to what exactly?
|
||||
|
||||
void backupParams();
|
||||
void restoreParams();
|
||||
void allocCache (Imagefloat* &imgfloat);
|
||||
void notifyHistogramChanged();
|
||||
void reallocAll();
|
||||
/// Updates L, R, G, and B histograms. Returns true unless not updated.
|
||||
@ -220,7 +225,9 @@ protected:
|
||||
void updatePreviewImage (int todo, bool panningRelatedChange);
|
||||
|
||||
MyMutex mProcessing;
|
||||
const std::unique_ptr<ProcParams> params;
|
||||
const std::unique_ptr<ProcParams> params; // used for the rendering, can be eventually tweaked
|
||||
std::unique_ptr<ProcParams> paramsBackup; // backup of the untweaked procparams
|
||||
TweakOperator* tweakOperator;
|
||||
|
||||
// for optimization purpose, the output profile, output rendering intent and
|
||||
// output BPC will trigger a regeneration of the profile on parameter change only
|
||||
@ -369,7 +376,7 @@ public:
|
||||
~ImProcCoordinator () override;
|
||||
void assign (ImageSource* imgsrc);
|
||||
|
||||
void getParams (procparams::ProcParams* dst) override;
|
||||
void getParams (procparams::ProcParams* dst, bool tweaked=false) override;
|
||||
|
||||
void startProcessing (int changeCode) override;
|
||||
ProcParams* beginUpdateParams () override;
|
||||
@ -410,6 +417,8 @@ public:
|
||||
|
||||
DetailedCrop* createCrop (::EditDataProvider *editDataProvider, bool isDetailWindow) override;
|
||||
|
||||
void setTweakOperator (TweakOperator *tOperator) override;
|
||||
void unsetTweakOperator (TweakOperator *tOperator) override;
|
||||
bool getAutoWB (double& temp, double& green, double equal, double tempBias) override;
|
||||
void getCamWB (double& temp, double& green) override;
|
||||
void getSpotWB (int x, int y, int rectSize, double& temp, double& green) override;
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "coord2d.h"
|
||||
#include "gamutwarning.h"
|
||||
#include "imagedimensions.h"
|
||||
#include "jaggedarray.h"
|
||||
#include "pipettebuffer.h"
|
||||
#include "array2D.h"
|
||||
@ -78,12 +79,15 @@ class Image8;
|
||||
class Imagefloat;
|
||||
class LabImage;
|
||||
class wavelet_decomposition;
|
||||
class ImageSource;
|
||||
class ColorTemp;
|
||||
|
||||
namespace procparams
|
||||
{
|
||||
|
||||
class ProcParams;
|
||||
|
||||
struct SpotEntry;
|
||||
struct DehazeParams;
|
||||
struct FattalToneMappingParams;
|
||||
struct ColorManagementParams;
|
||||
@ -448,6 +452,9 @@ enum class BlurType {
|
||||
float Mad(const float * DataList, int datalen);
|
||||
float MadRgb(const float * DataList, int datalen);
|
||||
|
||||
// spot removal tool
|
||||
void removeSpots (rtengine::Imagefloat* img, rtengine::ImageSource* imgsrc, const std::vector<procparams::SpotEntry> &entries, const PreviewProps &pp, const rtengine::ColorTemp &currWB, const procparams::ColorManagementParams *cmp, int tr);
|
||||
|
||||
// pyramid wavelet
|
||||
void cbdl_local_temp(float ** src, float ** loctemp, int srcwidth, int srcheight, const float * mult, float kchro, const double dirpyrThreshold, const float mergeL, const float contres, const double skinprot, const bool gamutlab, float b_l, float t_l, float t_r, float b_r, int choice, int scale, bool multiThread);
|
||||
void dirpyr_equalizer(const float * const * src, float ** dst, int srcwidth, int srcheight, const float * const * l_a, const float * const * l_b, const double * mult, double dirpyrThreshold, double skinprot, float b_l, float t_l, float t_r, int scale); //Emil's directional pyramid wavelet
|
||||
|
@ -365,6 +365,10 @@ namespace rtengine
|
||||
namespace procparams
|
||||
{
|
||||
|
||||
const short SpotParams::minRadius = 5;
|
||||
const short SpotParams::maxRadius = 100;
|
||||
|
||||
|
||||
ToneCurveParams::ToneCurveParams() :
|
||||
autoexp(false),
|
||||
clip(0.02),
|
||||
@ -1691,6 +1695,57 @@ bool EPDParams::operator !=(const EPDParams& other) const
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
SpotEntry::SpotEntry() :
|
||||
radius(25),
|
||||
feather(1.f),
|
||||
opacity(1.f)
|
||||
{
|
||||
}
|
||||
|
||||
float SpotEntry::getFeatherRadius() const
|
||||
{
|
||||
return radius * (1.f + feather);
|
||||
}
|
||||
|
||||
bool SpotEntry::operator ==(const SpotEntry& other) const
|
||||
{
|
||||
return other.sourcePos == sourcePos && other.targetPos == targetPos &&
|
||||
other.radius == radius && other.feather == feather && other.opacity == opacity;
|
||||
}
|
||||
|
||||
bool SpotEntry::operator !=(const SpotEntry& other) const
|
||||
{
|
||||
return other.sourcePos != sourcePos || other.targetPos != targetPos ||
|
||||
other.radius != radius || other.feather != feather || other.opacity != opacity;
|
||||
}
|
||||
|
||||
SpotParams::SpotParams() :
|
||||
enabled(false)
|
||||
{
|
||||
entries.clear ();
|
||||
}
|
||||
|
||||
bool SpotParams::operator ==(const SpotParams& other) const
|
||||
{
|
||||
if (enabled != other.enabled || entries.size() != other.entries.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
for (auto entry : entries) {
|
||||
if (entry != other.entries[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpotParams::operator !=(const SpotParams& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
FattalToneMappingParams::FattalToneMappingParams() :
|
||||
enabled(false),
|
||||
threshold(30),
|
||||
@ -6790,6 +6845,25 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
|
||||
saveToKeyfile(!pedited || pedited->wavelet.hueskin2, "Wavelet", "HueRange", wavelet.hueskin2.toVector(), keyFile);
|
||||
saveToKeyfile(!pedited || pedited->wavelet.contrast, "Wavelet", "Contrast", wavelet.contrast, keyFile);
|
||||
|
||||
//Spot removal
|
||||
saveToKeyfile(!pedited || pedited->spot.enabled, "Spot removal", "Enabled", spot.enabled, keyFile);
|
||||
for (size_t i = 0; i < spot.entries.size (); ++i) {
|
||||
std::vector<double> entry(7);
|
||||
|
||||
entry[0] = double (spot.entries.at (i).sourcePos.x);
|
||||
entry[1] = double (spot.entries.at (i).sourcePos.y);
|
||||
entry[2] = double (spot.entries.at (i).targetPos.x);
|
||||
entry[3] = double (spot.entries.at (i).targetPos.y);
|
||||
entry[4] = double (spot.entries.at (i).radius);
|
||||
entry[5] = double (spot.entries.at (i).feather);
|
||||
entry[6] = double (spot.entries.at (i).opacity);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Spot" << (i + 1);
|
||||
|
||||
saveToKeyfile(!pedited || pedited->spot.entries, "Spot removal", ss.str(), entry, keyFile);
|
||||
}
|
||||
|
||||
// Directional pyramid equalizer
|
||||
saveToKeyfile(!pedited || pedited->dirpyrequalizer.enabled, "Directional Pyramid Equalizer", "Enabled", dirpyrequalizer.enabled, keyFile);
|
||||
saveToKeyfile(!pedited || pedited->dirpyrequalizer.gamutlab, "Directional Pyramid Equalizer", "Gamutlab", dirpyrequalizer.gamutlab, keyFile);
|
||||
@ -8520,6 +8594,34 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
|
||||
}
|
||||
}
|
||||
|
||||
if (keyFile.has_group ("Spot removal")) {
|
||||
assignFromKeyfile(keyFile, "Spot removal", "Enabled", pedited, spot.enabled, pedited->spot.enabled);
|
||||
int i = 0;
|
||||
do {
|
||||
std::stringstream ss;
|
||||
ss << "Spot" << (i++ + 1);
|
||||
|
||||
if (keyFile.has_key ("Spot removal", ss.str())) {
|
||||
Glib::ArrayHandle<double> entry = keyFile.get_double_list ("Spot removal", ss.str());
|
||||
const double epsilon = 0.001; // to circumvent rounding of integer saved as double
|
||||
SpotEntry se;
|
||||
|
||||
se.sourcePos.set(int(entry.data()[0] + epsilon), int(entry.data()[1] + epsilon));
|
||||
se.targetPos.set(int(entry.data()[2] + epsilon), int(entry.data()[3] + epsilon));
|
||||
se.radius = LIM<int>(int (entry.data()[4] + epsilon), SpotParams::minRadius, SpotParams::maxRadius);
|
||||
se.feather = float(entry.data()[5]);
|
||||
se.opacity = float(entry.data()[6]);
|
||||
spot.entries.push_back(se);
|
||||
|
||||
if (pedited) {
|
||||
pedited->spot.entries = true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
if (keyFile.has_group("PostDemosaicSharpening")) {
|
||||
assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Enabled", pedited, pdsharpening.enabled, pedited->pdsharpening.enabled);
|
||||
assignFromKeyfile(keyFile, "PostDemosaicSharpening", "Contrast", pedited, pdsharpening.contrast, pedited->pdsharpening.contrast);
|
||||
@ -9679,6 +9781,7 @@ bool ProcParams::operator ==(const ProcParams& other) const
|
||||
&& chmixer == other.chmixer
|
||||
&& blackwhite == other.blackwhite
|
||||
&& resize == other.resize
|
||||
&& spot == other.spot
|
||||
&& raw == other.raw
|
||||
&& icm == other.icm
|
||||
&& wavelet == other.wavelet
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <glibmm/ustring.h>
|
||||
#include <lcms2.h>
|
||||
|
||||
#include "coord.h"
|
||||
#include "noncopyable.h"
|
||||
|
||||
struct ParamsEdited;
|
||||
@ -1697,6 +1698,41 @@ struct ResizeParams {
|
||||
bool operator !=(const ResizeParams& other) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters entry
|
||||
*/
|
||||
struct SpotEntry {
|
||||
Coord sourcePos;
|
||||
Coord targetPos;
|
||||
int radius;
|
||||
float feather;
|
||||
float opacity;
|
||||
|
||||
SpotEntry();
|
||||
float getFeatherRadius() const;
|
||||
|
||||
bool operator ==(const SpotEntry& other) const;
|
||||
bool operator !=(const SpotEntry& other) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters of the dust removal tool
|
||||
*/
|
||||
struct SpotParams {
|
||||
bool enabled;
|
||||
std::vector<SpotEntry> entries;
|
||||
|
||||
// the following constant can be used for experimentation before the final merge
|
||||
static const short minRadius;
|
||||
static const short maxRadius;
|
||||
|
||||
SpotParams();
|
||||
|
||||
bool operator ==(const SpotParams& other) const;
|
||||
bool operator !=(const SpotParams& other) const;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parameters of the color spaces used during the processing
|
||||
*/
|
||||
@ -2386,6 +2422,7 @@ public:
|
||||
ChannelMixerParams chmixer; ///< Channel mixer parameters
|
||||
BlackWhiteParams blackwhite; ///< Black& White parameters
|
||||
ResizeParams resize; ///< Resize parameters
|
||||
SpotParams spot; ///< Spot removal tool
|
||||
ColorManagementParams icm; ///< profiles/color spaces used during the image processing
|
||||
RAWParams raw; ///< RAW parameters before demosaicing
|
||||
WaveletParams wavelet; ///< Wavelet parameters
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
// Elementary functions that can be done to
|
||||
// the preview image when an event occurs
|
||||
#define M_SPOT (1<<19)
|
||||
#define M_CSHARP (1<<18)
|
||||
#define M_MONITOR (1<<14)
|
||||
#define M_RETINEX (1<<13)
|
||||
@ -51,23 +52,24 @@
|
||||
|
||||
// Bitfield of functions to do to the preview image when an event occurs
|
||||
// Use those or create new ones for your new events
|
||||
#define FIRST (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_MONITOR) // without HIGHQUAL
|
||||
#define ALL (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) // without HIGHQUAL
|
||||
#define DARKFRAME (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define FLATFIELD (M_PREPROC|M_RAW|M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define DEMOSAIC (M_RAW|M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define ALLNORAW (M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define CAPTURESHARPEN (M_INIT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_CSHARP)
|
||||
#define HDR (M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define TRANSFORM (M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define AUTOEXP (M_HDR|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define RGBCURVE (M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define LUMINANCECURVE (M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define SHARPENING (M_LUMINANCE|M_COLOR)
|
||||
#define IMPULSEDENOISE (M_LUMINANCE|M_COLOR)
|
||||
#define DEFRINGE (M_LUMINANCE|M_COLOR)
|
||||
#define DIRPYRDENOISE (M_LUMINANCE|M_COLOR)
|
||||
#define DIRPYREQUALIZER (M_LUMINANCE|M_COLOR)
|
||||
#define FIRST (M_PREPROC|M_RAW|M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_MONITOR) // without HIGHQUAL
|
||||
#define ALL (M_PREPROC|M_RAW|M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR) // without HIGHQUAL
|
||||
#define DARKFRAME (M_PREPROC|M_RAW|M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define FLATFIELD (M_PREPROC|M_RAW|M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define DEMOSAIC (M_RAW|M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define ALLNORAW (M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define CAPTURESHARPEN (M_INIT|M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR|M_CSHARP)
|
||||
#define HDR (M_SPOT|M_LINDENOISE|M_HDR|M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define SPOTADJUST (M_SPOT|M_HDR|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define TRANSFORM (M_TRANSFORM|M_BLURMAP|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define AUTOEXP (M_SPOT|M_HDR|M_AUTOEXP|M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define RGBCURVE (M_RGBCURVE|M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define LUMINANCECURVE (M_LUMACURVE|M_LUMINANCE|M_COLOR)
|
||||
#define SHARPENING (M_LUMINANCE|M_COLOR)
|
||||
#define IMPULSEDENOISE (M_LUMINANCE|M_COLOR)
|
||||
#define DEFRINGE (M_LUMINANCE|M_COLOR)
|
||||
#define DIRPYRDENOISE (M_LUMINANCE|M_COLOR)
|
||||
#define DIRPYREQUALIZER (M_LUMINANCE|M_COLOR)
|
||||
#define GAMMA M_VOID //M_MONITOR
|
||||
#define CROP M_CROP
|
||||
#define RESIZE M_VOID
|
||||
|
@ -81,6 +81,7 @@ class IImage8;
|
||||
class IImage16;
|
||||
class IImagefloat;
|
||||
class ImageSource;
|
||||
class TweakOperator;
|
||||
|
||||
/**
|
||||
* This class provides functions to obtain exif and IPTC metadata information
|
||||
@ -553,9 +554,20 @@ public:
|
||||
/** Returns the initial image corresponding to the image processor.
|
||||
* @return the initial image corresponding to the image processor */
|
||||
virtual InitialImage* getInitialImage () = 0;
|
||||
/** Set the TweakOperator
|
||||
* @param tOperator is a pointer to the object that will alter the ProcParams for the rendering */
|
||||
virtual void setTweakOperator (TweakOperator *tOperator) = 0;
|
||||
/** Unset the TweakOperator
|
||||
* @param tOperator is a pointer to the object that were altering the ProcParams for the rendering
|
||||
* It will only unset the tweak operator if tOperator is the same than the currently set operator.
|
||||
* If it doesn't match, the currently set TweakOperator will remain set. */
|
||||
virtual void unsetTweakOperator (TweakOperator *tOperator) = 0;
|
||||
/** Returns the current processing parameters.
|
||||
* @param dst is the location where the image processing parameters are copied (it is assumed that the memory is allocated by the caller) */
|
||||
virtual void getParams (procparams::ProcParams* dst) = 0;
|
||||
* Since the ProcParams can be tweaked by a GUI to operate on the image at a specific stage or with disabled tool,
|
||||
* you'll have to specify if you want the tweaked version for the current special mode, or the untweaked one.
|
||||
* @param dst is the location where the image processing parameters are copied (it is assumed that the memory is allocated by the caller)
|
||||
* @param tweaked is used to chose betwen the tweaked ProcParams (if there is one) or the untweaked one */
|
||||
virtual void getParams (procparams::ProcParams* dst, bool tweaked=false) = 0;
|
||||
/** An essential member function. Call this when a setting has been changed. This function returns a pointer to the
|
||||
* processing parameters, that you have to update to reflect the changed situation. When ready, call the paramsUpdateReady
|
||||
* function to start the image update.
|
||||
|
@ -796,6 +796,11 @@ private:
|
||||
params.toneCurve.black = 0;
|
||||
}
|
||||
|
||||
// Spot Removal
|
||||
if (params.spot.enabled && !params.spot.entries.empty ()) {
|
||||
ipf.removeSpots (baseImg, imgsrc, params.spot.entries, pp, currWB, nullptr, tr);
|
||||
}
|
||||
|
||||
// at this stage, we can flush the raw data to free up quite an important amount of memory
|
||||
// commented out because it makes the application crash when batch processing...
|
||||
// TODO: find a better place to flush rawData and rawRGB
|
||||
|
580
rtengine/spot.cc
Normal file
580
rtengine/spot.cc
Normal 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
45
rtengine/tweakoperator.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@ -150,6 +150,7 @@ set(NONCLISOURCEFILES
|
||||
softlight.cc
|
||||
soundman.cc
|
||||
splash.cc
|
||||
spot.cc
|
||||
threadutils.cc
|
||||
thresholdadjuster.cc
|
||||
thresholdselector.cc
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
|
@ -56,6 +56,8 @@ public:
|
||||
|
||||
// toolpanellistener interface
|
||||
void panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr) override;
|
||||
void setTweakOperator (rtengine::TweakOperator *tOperator) override;
|
||||
void unsetTweakOperator (rtengine::TweakOperator *tOperator) override;
|
||||
|
||||
// profilechangelistener interface
|
||||
void profileChange(
|
||||
|
@ -268,7 +268,7 @@ bool ControlLineManager::getEdited(void) const
|
||||
return edited;
|
||||
}
|
||||
|
||||
CursorShape ControlLineManager::getCursor(int objectID) const
|
||||
CursorShape ControlLineManager::getCursor(int objectID, int xPos, int yPos) const
|
||||
{
|
||||
return cursor;
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ public:
|
||||
bool pick1(bool picked) override;
|
||||
bool pick3(bool picked) override;
|
||||
bool drag1(int modifierKey) override;
|
||||
CursorShape getCursor(int objectID) const override;
|
||||
CursorShape getCursor(int objectID, int xPos, int yPos) const override;
|
||||
bool mouseOver(int modifierKey) override;
|
||||
void switchOffEditMode(void) override;
|
||||
};
|
||||
|
@ -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);
|
||||
|
||||
|
@ -267,7 +267,7 @@ private:
|
||||
void updateControlSpotCurve(const Gtk::TreeModel::Row& row);
|
||||
void deleteControlSpotCurve(Gtk::TreeModel::Row& row);
|
||||
void updateCurveOpacity(const Gtk::TreeModel::Row& selectedRow);
|
||||
CursorShape getCursor(int objectID) const override;
|
||||
CursorShape getCursor(int objectID, int xPos, int yPos) const override;
|
||||
bool mouseOver(int modifierKey) override;
|
||||
bool button1Pressed(int modifierKey) override;
|
||||
bool button1Released() override;
|
||||
|
@ -510,7 +510,7 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y)
|
||||
cropgl->cropInit (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h);
|
||||
} else if (iarea->getToolMode () == TMHand) {
|
||||
if (editSubscriber) {
|
||||
if ((cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && editSubscriber->getEditingType() == ET_PIPETTE && (bstate & GDK_CONTROL_MASK))) {
|
||||
if ((cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && (editSubscriber->getEditingType() == ET_PIPETTE && (bstate & GDK_CONTROL_MASK))) || editSubscriber->getEditingType() == ET_OBJECTS) {
|
||||
needRedraw = editSubscriber->button1Pressed(bstate);
|
||||
if (editSubscriber->isDragging()) {
|
||||
state = SEditDrag1;
|
||||
@ -1128,7 +1128,7 @@ void CropWindow::pointerMoved (int bstate, int x, int y)
|
||||
rtengine::StagedImageProcessor* ipc = iarea->getImProcCoordinator();
|
||||
if(ipc) {
|
||||
procparams::ProcParams params;
|
||||
ipc->getParams(¶ms);
|
||||
ipc->getParams(¶ms, true);
|
||||
isRaw = params.raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::NONE) || params.raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::NONE);
|
||||
if(isRaw) {
|
||||
ImageSource *isrc = static_cast<ImageSource*>(ipc->getInitialImage());
|
||||
@ -1293,7 +1293,10 @@ void CropWindow::updateCursor (int x, int y)
|
||||
} else if (onArea (CropToolBar, x, y)) {
|
||||
newType = CSMove;
|
||||
} else if (iarea->getObject() > -1 && editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
|
||||
newType = editSubscriber->getCursor(iarea->getObject());
|
||||
int cursorX;
|
||||
int cursorY;
|
||||
screenCoordToImage (x, y, cursorX, cursorY);
|
||||
newType = editSubscriber->getCursor(iarea->getObject(), cursorX, cursorY);
|
||||
} else if (onArea (CropResize, x, y)) {
|
||||
newType = CSResizeDiagonal;
|
||||
} else if (tm == TMColorPicker && hoveredPicker) {
|
||||
@ -1320,7 +1323,10 @@ void CropWindow::updateCursor (int x, int y)
|
||||
}
|
||||
|
||||
if (objectID > -1) {
|
||||
newType = editSubscriber->getCursor(objectID);
|
||||
int cursorX;
|
||||
int cursorY;
|
||||
screenCoordToImage (x, y, cursorX, cursorY);
|
||||
newType = editSubscriber->getCursor(objectID, cursorX, cursorY);
|
||||
} else if (tm == TMHand) {
|
||||
if (onArea (CropObserved, x, y)) {
|
||||
newType = CSMove;
|
||||
@ -1346,7 +1352,10 @@ void CropWindow::updateCursor (int x, int y)
|
||||
}
|
||||
|
||||
if (objectID > -1) {
|
||||
newType = editSubscriber->getCursor(objectID);
|
||||
int cursorX;
|
||||
int cursorY;
|
||||
screenCoordToImage (x, y, cursorX, cursorY);
|
||||
newType = editSubscriber->getCursor(objectID, cursorX, cursorY);
|
||||
} else {
|
||||
newType = CSArrow;
|
||||
}
|
||||
@ -1375,6 +1384,16 @@ void CropWindow::updateCursor (int x, int y)
|
||||
newType = CSResizeDiagonal;
|
||||
} else if (state == SDragPicker) {
|
||||
newType = CSMove2D;
|
||||
} else if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
|
||||
int objectID = iarea->getObject();
|
||||
if (objectID > -1) {
|
||||
int cursorX;
|
||||
int cursorY;
|
||||
screenCoordToImage (x, y, cursorX, cursorY);
|
||||
newType = editSubscriber->getCursor(objectID, cursorX, cursorY);
|
||||
} else {
|
||||
newType = CSArrow;
|
||||
}
|
||||
}
|
||||
|
||||
if (newType != cursor_type) {
|
||||
|
@ -504,7 +504,7 @@ bool CurveEditor::drag1(int modifierKey)
|
||||
return false;
|
||||
}
|
||||
|
||||
CursorShape CurveEditor::getCursor(int objectID) const
|
||||
CursorShape CurveEditor::getCursor(int objectID, int xPos, int yPos) const
|
||||
{
|
||||
if (remoteDrag) {
|
||||
return CSResizeHeight;
|
||||
|
@ -133,7 +133,7 @@ public:
|
||||
bool button1Pressed(int modifierKey) override;
|
||||
bool button1Released() override;
|
||||
bool drag1(int modifierKey) override;
|
||||
CursorShape getCursor(int objectID) const override;
|
||||
CursorShape getCursor(int objectID, int xPos, int yPos) const override;
|
||||
|
||||
|
||||
};
|
||||
|
@ -173,10 +173,10 @@ void EditDataProvider::setPipetteVal3(float newVal)
|
||||
pipetteVal3 = newVal;
|
||||
}
|
||||
|
||||
CursorShape EditDataProvider::getCursor(int objectID) const
|
||||
CursorShape EditDataProvider::getCursor(int objectID, int xPos, int yPos) const
|
||||
{
|
||||
if (currSubscriber) {
|
||||
currSubscriber->getCursor(objectID);
|
||||
currSubscriber->getCursor(objectID, xPos, yPos);
|
||||
}
|
||||
|
||||
return CSHandOpen;
|
||||
@ -187,12 +187,12 @@ EditSubscriber* EditDataProvider::getCurrSubscriber() const
|
||||
return currSubscriber;
|
||||
}
|
||||
|
||||
EditDataProvider* EditSubscriber::getEditProvider()
|
||||
EditDataProvider* EditSubscriber::getEditProvider() const
|
||||
{
|
||||
return provider;
|
||||
}
|
||||
|
||||
CursorShape EditSubscriber::getCursor(int objectID) const
|
||||
CursorShape EditSubscriber::getCursor(int objectID, int xPos, int yPos) const
|
||||
{
|
||||
return CSHandOpen;
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public:
|
||||
virtual ~EditSubscriber () = default;
|
||||
|
||||
void setEditProvider(EditDataProvider *provider);
|
||||
EditDataProvider* getEditProvider ();
|
||||
EditDataProvider* getEditProvider () const;
|
||||
void setEditID(EditUniqueID ID, BufferType buffType);
|
||||
bool isCurrentSubscriber() const;
|
||||
virtual void subscribe();
|
||||
@ -70,8 +70,10 @@ public:
|
||||
bool isPicking() const; /// Returns true if something is being picked
|
||||
|
||||
/** @brief Get the cursor to be displayed when above handles
|
||||
@param objectID object currently "hovered" */
|
||||
virtual CursorShape getCursor (int objectID) const;
|
||||
@param objectID object currently "hovered"
|
||||
@param xPos X cursor position in image space
|
||||
@param yPos Y cursor position in image space */
|
||||
virtual CursorShape getCursor (int objectID, int xPos, int yPos) const;
|
||||
|
||||
/** @brief Triggered when the mouse is moving over an object
|
||||
This method is also triggered when the cursor is moving over the image in ET_PIPETTE mode
|
||||
@ -189,7 +191,7 @@ public:
|
||||
void setPipetteVal1(float newVal);
|
||||
void setPipetteVal2(float newVal);
|
||||
void setPipetteVal3(float newVal);
|
||||
virtual CursorShape getCursor(int objectID) const;
|
||||
virtual CursorShape getCursor(int objectID, int xPos, int yPos) const;
|
||||
int getPipetteRectSize () const;
|
||||
EditSubscriber* getCurrSubscriber() const;
|
||||
virtual void getImageSize (int &w, int&h) = 0;
|
||||
|
@ -22,7 +22,11 @@
|
||||
#include "editbuffer.h"
|
||||
#include "editcallbacks.h"
|
||||
#include "rtsurface.h"
|
||||
#include "../rtengine/rt_math.h"
|
||||
|
||||
const std::vector<double> Geometry::dash = {3., 1.5};
|
||||
|
||||
#define INNERGEOM_OPACITY 1.
|
||||
#define OUTERGEOM_OPACITY 0.7
|
||||
|
||||
RGBColor Geometry::getInnerLineColor ()
|
||||
{
|
||||
@ -67,7 +71,8 @@ RGBColor Geometry::getOuterLineColor ()
|
||||
|
||||
void Circle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
|
||||
{
|
||||
if ((flags & F_VISIBLE) && state != INSENSITIVE) {
|
||||
double lineWidth = getOuterLineWidth();
|
||||
if ((flags & F_VISIBLE) && state != INSENSITIVE && lineWidth > 0. && innerLineWidth > 0.) {
|
||||
RGBColor color;
|
||||
|
||||
if (flags & F_AUTO_COLOR) {
|
||||
@ -76,8 +81,9 @@ void Circle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
|
||||
color = outerLineColor;
|
||||
}
|
||||
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
|
||||
cr->set_line_width( getOuterLineWidth() );
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
|
||||
cr->set_line_width (lineWidth);
|
||||
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
|
||||
|
||||
rtengine::Coord center_ = center;
|
||||
double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius);
|
||||
@ -107,10 +113,11 @@ void Circle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
|
||||
color = innerLineColor;
|
||||
}
|
||||
|
||||
cr->set_source_rgba(color.getR(), color.getG(), color.getB(), opacity / 100.);
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY);
|
||||
}
|
||||
|
||||
cr->set_line_width( innerLineWidth );
|
||||
cr->set_line_width(innerLineWidth);
|
||||
cr->set_line_cap(flags & F_DASHED ? Cairo::LINE_CAP_BUTT : Cairo::LINE_CAP_ROUND);
|
||||
|
||||
rtengine::Coord center_ = center;
|
||||
double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius);
|
||||
@ -123,9 +130,12 @@ void Circle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
|
||||
center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen;
|
||||
}
|
||||
|
||||
if (filled && state != INSENSITIVE) {
|
||||
cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI);
|
||||
if (flags & F_DASHED) {
|
||||
cr->set_dash(dash, 0.);
|
||||
}
|
||||
|
||||
if (filled) {
|
||||
cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI);
|
||||
if (innerLineWidth > 0.f) {
|
||||
cr->fill_preserve();
|
||||
cr->stroke();
|
||||
@ -134,28 +144,20 @@ void Circle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer
|
||||
}
|
||||
} else if (innerLineWidth > 0.f) {
|
||||
cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI);
|
||||
|
||||
if (state == INSENSITIVE) {
|
||||
std::valarray<double> ds(1);
|
||||
ds[0] = 4;
|
||||
cr->set_source_rgba(1.0, 1.0, 1.0, 0.618);
|
||||
cr->stroke_preserve();
|
||||
cr->set_source_rgba(0.0, 0.0, 0.0, 0.618);
|
||||
cr->set_dash(ds, 0);
|
||||
cr->stroke();
|
||||
ds.resize(0);
|
||||
cr->set_dash(ds, 0);
|
||||
} else {
|
||||
cr->stroke();
|
||||
}
|
||||
cr->stroke();
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & F_DASHED) {
|
||||
cr->unset_dash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Circle::drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
|
||||
{
|
||||
if (flags & F_HOVERABLE) {
|
||||
cr->set_line_width( getMouseOverLineWidth() );
|
||||
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
|
||||
rtengine::Coord center_ = center;
|
||||
double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius);
|
||||
|
||||
@ -190,7 +192,8 @@ void Circle::drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short
|
||||
|
||||
void Line::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
|
||||
{
|
||||
if ((flags & F_VISIBLE) && state != INSENSITIVE) {
|
||||
double lineWidth = getOuterLineWidth();
|
||||
if ((flags & F_VISIBLE) && state != INSENSITIVE && lineWidth > 0. && innerLineWidth > 0.) {
|
||||
RGBColor color;
|
||||
|
||||
if (flags & F_AUTO_COLOR) {
|
||||
@ -199,8 +202,9 @@ void Line::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *
|
||||
color = outerLineColor;
|
||||
}
|
||||
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.0);
|
||||
cr->set_line_width( getOuterLineWidth() );
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
|
||||
cr->set_line_width (lineWidth);
|
||||
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
|
||||
|
||||
rtengine::Coord begin_ = begin;
|
||||
rtengine::Coord end_ = end;
|
||||
@ -234,10 +238,11 @@ void Line::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *
|
||||
color = innerLineColor;
|
||||
}
|
||||
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY);
|
||||
}
|
||||
|
||||
cr->set_line_width(innerLineWidth);
|
||||
cr->set_line_cap(flags & F_DASHED ? Cairo::LINE_CAP_BUTT : Cairo::LINE_CAP_ROUND);
|
||||
|
||||
rtengine::Coord begin_ = begin;
|
||||
rtengine::Coord end_ = end;
|
||||
@ -253,21 +258,16 @@ void Line::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *
|
||||
end_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen;
|
||||
}
|
||||
|
||||
if (flags & F_DASHED) {
|
||||
cr->set_dash(dash, 0.);
|
||||
}
|
||||
|
||||
cr->move_to(begin_.x + 0.5, begin_.y + 0.5);
|
||||
cr->line_to(end_.x + 0.5, end_.y + 0.5);
|
||||
cr->stroke();
|
||||
|
||||
if (state == INSENSITIVE) {
|
||||
std::valarray<double> ds(1);
|
||||
ds[0] = 4;
|
||||
cr->set_source_rgba(1.0, 1.0, 1.0, 0.618);
|
||||
cr->stroke_preserve();
|
||||
cr->set_source_rgba(0.0, 0.0, 0.0, 0.618);
|
||||
cr->set_dash(ds, 0);
|
||||
cr->stroke();
|
||||
ds.resize(0);
|
||||
cr->set_dash(ds, 0);
|
||||
} else {
|
||||
cr->stroke();
|
||||
if (flags & F_DASHED) {
|
||||
cr->unset_dash();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -276,6 +276,7 @@ void Line::drawToMOChannel(Cairo::RefPtr<Cairo::Context> &cr, unsigned short id,
|
||||
{
|
||||
if (flags & F_HOVERABLE) {
|
||||
cr->set_line_width( getMouseOverLineWidth() );
|
||||
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
|
||||
rtengine::Coord begin_ = begin;
|
||||
rtengine::Coord end_ = end;
|
||||
|
||||
@ -304,7 +305,8 @@ void Line::drawToMOChannel(Cairo::RefPtr<Cairo::Context> &cr, unsigned short id,
|
||||
|
||||
void Polyline::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
|
||||
{
|
||||
if ((flags & F_VISIBLE) && state != INSENSITIVE && points.size() > 1) {
|
||||
double lineWidth = getOuterLineWidth();
|
||||
if ((flags & F_VISIBLE) && state != INSENSITIVE && points.size() > 1 && lineWidth > 0.) {
|
||||
RGBColor color;
|
||||
|
||||
if (flags & F_AUTO_COLOR) {
|
||||
@ -313,8 +315,10 @@ void Polyline::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuff
|
||||
color = outerLineColor;
|
||||
}
|
||||
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
|
||||
cr->set_line_width( getOuterLineWidth() );
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
|
||||
cr->set_line_width (lineWidth);
|
||||
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
|
||||
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
|
||||
|
||||
rtengine::Coord currPos;
|
||||
|
||||
@ -357,10 +361,16 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuff
|
||||
color = innerLineColor;
|
||||
}
|
||||
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY);
|
||||
}
|
||||
|
||||
cr->set_line_width( innerLineWidth );
|
||||
cr->set_line_width(innerLineWidth);
|
||||
cr->set_line_cap(flags & F_DASHED ? Cairo::LINE_CAP_BUTT : Cairo::LINE_CAP_ROUND);
|
||||
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
|
||||
|
||||
if (flags & F_DASHED) {
|
||||
cr->set_dash(dash, 0.);
|
||||
}
|
||||
|
||||
if (filled && state != INSENSITIVE) {
|
||||
rtengine::Coord currPos;
|
||||
@ -409,20 +419,11 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuff
|
||||
cr->line_to(currPos.x + 0.5, currPos.y + 0.5);
|
||||
}
|
||||
}
|
||||
cr->stroke();
|
||||
}
|
||||
|
||||
if (state == INSENSITIVE) {
|
||||
std::valarray<double> ds(1);
|
||||
ds[0] = 4;
|
||||
cr->set_source_rgba(1.0, 1.0, 1.0, 0.618);
|
||||
cr->stroke_preserve();
|
||||
cr->set_source_rgba(0.0, 0.0, 0.0, 0.618);
|
||||
cr->set_dash(ds, 0);
|
||||
cr->stroke();
|
||||
ds.resize(0);
|
||||
cr->set_dash(ds, 0);
|
||||
} else {
|
||||
cr->stroke();
|
||||
}
|
||||
if (flags & F_DASHED) {
|
||||
cr->unset_dash();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -439,8 +440,11 @@ void Polyline::drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned shor
|
||||
cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.);
|
||||
}
|
||||
|
||||
cr->set_line_width( getMouseOverLineWidth() );
|
||||
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
|
||||
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
|
||||
|
||||
for (unsigned int i = 0; i < points.size(); ++i) {
|
||||
cr->set_line_width( getMouseOverLineWidth() );
|
||||
currPos = points.at(i);
|
||||
|
||||
if (datum == IMAGE) {
|
||||
@ -497,7 +501,8 @@ void Rectangle::setXYXY(rtengine::Coord topLeft, rtengine::Coord bottomRight)
|
||||
|
||||
void Rectangle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem)
|
||||
{
|
||||
if ((flags & F_VISIBLE) && state != INSENSITIVE) {
|
||||
double lineWidth = getOuterLineWidth();
|
||||
if ((flags & F_VISIBLE) && state != INSENSITIVE && lineWidth > 0. && innerLineWidth > 0.) {
|
||||
RGBColor color;
|
||||
|
||||
if (flags & F_AUTO_COLOR) {
|
||||
@ -506,8 +511,9 @@ void Rectangle::drawOuterGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
|
||||
color = outerLineColor;
|
||||
}
|
||||
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
|
||||
cr->set_line_width( getOuterLineWidth() );
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), OUTERGEOM_OPACITY * rtengine::min(innerLineWidth / 2.f, 1.f));
|
||||
cr->set_line_width (lineWidth);
|
||||
cr->set_line_join(Cairo::LINE_JOIN_BEVEL);
|
||||
|
||||
rtengine::Coord tl, br;
|
||||
|
||||
@ -550,10 +556,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
|
||||
color = innerLineColor;
|
||||
}
|
||||
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.);
|
||||
cr->set_source_rgba (color.getR(), color.getG(), color.getB(), INNERGEOM_OPACITY);
|
||||
}
|
||||
|
||||
cr->set_line_width( innerLineWidth );
|
||||
cr->set_line_width(innerLineWidth);
|
||||
cr->set_line_join(Cairo::LINE_JOIN_BEVEL);
|
||||
|
||||
rtengine::Coord tl, br;
|
||||
|
||||
@ -573,7 +580,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
|
||||
br = bottomRight + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen;
|
||||
}
|
||||
|
||||
if (filled && state != INSENSITIVE) {
|
||||
if (flags & F_DASHED) {
|
||||
cr->set_dash(dash, 0.);
|
||||
}
|
||||
|
||||
if (filled) {
|
||||
cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y);
|
||||
|
||||
if (innerLineWidth > 0.f) {
|
||||
@ -584,20 +595,11 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuf
|
||||
}
|
||||
} else if (innerLineWidth > 0.f) {
|
||||
cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y);
|
||||
cr->stroke();
|
||||
}
|
||||
|
||||
if (state == INSENSITIVE) {
|
||||
std::valarray<double> ds(1);
|
||||
ds[0] = 4;
|
||||
cr->set_source_rgba(1.0, 1.0, 1.0, 0.618);
|
||||
cr->stroke_preserve();
|
||||
cr->set_source_rgba(0.0, 0.0, 0.0, 0.618);
|
||||
cr->set_dash(ds, 0);
|
||||
cr->stroke();
|
||||
ds.resize(0);
|
||||
cr->set_dash(ds, 0);
|
||||
} else {
|
||||
cr->stroke();
|
||||
}
|
||||
if (flags & F_DASHED) {
|
||||
cr->unset_dash();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -606,6 +608,7 @@ void Rectangle::drawToMOChannel(Cairo::RefPtr<Cairo::Context> &cr, unsigned shor
|
||||
{
|
||||
if (flags & F_HOVERABLE) {
|
||||
cr->set_line_width( getMouseOverLineWidth() );
|
||||
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
|
||||
|
||||
rtengine::Coord tl, br;
|
||||
|
||||
|
@ -24,10 +24,11 @@
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
#include "editcoordsys.h"
|
||||
#include "rtsurface.h"
|
||||
#include "../rtengine/coord.h"
|
||||
#include "../rtengine/rt_math.h"
|
||||
|
||||
class ObjectMOBuffer;
|
||||
class RTSurface;
|
||||
|
||||
/** @file
|
||||
*
|
||||
@ -210,6 +211,8 @@ public:
|
||||
F_VISIBLE = 1 << 0, /// true if the geometry have to be drawn on the visible layer
|
||||
F_HOVERABLE = 1 << 1, /// true if the geometry have to be drawn on the "mouse over" layer
|
||||
F_AUTO_COLOR = 1 << 2, /// true if the color depend on the state value, not the color field above
|
||||
F_DASHED = 1 << 3, /// true if the geometry have to be drawn as a dash line
|
||||
// (TODO: add a F_LARGE_DASH to have two different dash size ?)
|
||||
};
|
||||
|
||||
/// @brief Key point of the image's rectangle that is used to locate the icon copy to the target point:
|
||||
@ -226,6 +229,7 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
static const std::vector<double> dash;
|
||||
RGBColor innerLineColor;
|
||||
RGBColor outerLineColor;
|
||||
short flags;
|
||||
@ -252,6 +256,8 @@ public:
|
||||
void setVisible (bool visible);
|
||||
bool isHoverable ();
|
||||
void setHoverable (bool visible);
|
||||
bool isDashed ();
|
||||
void setDashed (bool dashed);
|
||||
|
||||
|
||||
// setActive will enable/disable the visible and hoverable flags in one shot!
|
||||
@ -449,7 +455,7 @@ inline void Geometry::setOuterLineColor (char r, char g, char b) {
|
||||
}
|
||||
|
||||
inline double Geometry::getMouseOverLineWidth () {
|
||||
return getOuterLineWidth () + 2.;
|
||||
return rtengine::max(double(innerLineWidth), 1.) + 2.;
|
||||
}
|
||||
|
||||
inline void Geometry::setAutoColor (bool aColor) {
|
||||
@ -484,6 +490,18 @@ inline void Geometry::setHoverable (bool hoverable) {
|
||||
}
|
||||
}
|
||||
|
||||
inline bool Geometry::isDashed () {
|
||||
return flags & F_DASHED;
|
||||
}
|
||||
|
||||
inline void Geometry::setDashed (bool dashed) {
|
||||
if (dashed) {
|
||||
flags |= F_DASHED;
|
||||
} else {
|
||||
flags &= ~F_DASHED;
|
||||
}
|
||||
}
|
||||
|
||||
inline void Geometry::setActive (bool active) {
|
||||
if (active) {
|
||||
flags |= (F_VISIBLE | F_HOVERABLE);
|
||||
|
@ -540,7 +540,7 @@ void FilmNegative::setEditProvider(EditDataProvider* provider)
|
||||
EditSubscriber::setEditProvider(provider);
|
||||
}
|
||||
|
||||
CursorShape FilmNegative::getCursor(int objectID) const
|
||||
CursorShape FilmNegative::getCursor(int objectID, int xPos, int yPos) const
|
||||
{
|
||||
return CSSpotWB;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ public:
|
||||
void setEditProvider(EditDataProvider* provider) override;
|
||||
|
||||
// EditSubscriber interface
|
||||
CursorShape getCursor(int objectID) const override;
|
||||
CursorShape getCursor(int objectID, int xPos, int yPos) const override;
|
||||
bool mouseOver(int modifierKey) override;
|
||||
bool button1Pressed(int modifierKey) override;
|
||||
bool button1Released() override;
|
||||
|
@ -329,7 +329,7 @@ void Gradient::editToggled ()
|
||||
}
|
||||
}
|
||||
|
||||
CursorShape Gradient::getCursor(int objectID) const
|
||||
CursorShape Gradient::getCursor(int objectID, int xPos, int yPos) const
|
||||
{
|
||||
switch (objectID) {
|
||||
case (0):
|
||||
|
@ -55,7 +55,7 @@ public:
|
||||
void setEditProvider (EditDataProvider* provider) override;
|
||||
|
||||
// EditSubscriber interface
|
||||
CursorShape getCursor(int objectID) const override;
|
||||
CursorShape getCursor(int objectID, int xPos, int yPos) const override;
|
||||
bool mouseOver(int modifierKey) override;
|
||||
bool button1Pressed(int modifierKey) override;
|
||||
bool button1Released() override;
|
||||
|
@ -426,6 +426,10 @@ void ParamsEdited::set(bool v)
|
||||
resize.width = v;
|
||||
resize.height = v;
|
||||
resize.enabled = v;
|
||||
|
||||
spot.enabled = v;
|
||||
spot.entries = v;
|
||||
|
||||
resize.allowUpscaling = v;
|
||||
icm.inputProfile = v;
|
||||
icm.toneCurve = v;
|
||||
@ -1715,6 +1719,8 @@ void ParamsEdited::initFrom(const std::vector<rtengine::procparams::ProcParams>&
|
||||
resize.width = resize.width && p.resize.width == other.resize.width;
|
||||
resize.height = resize.height && p.resize.height == other.resize.height;
|
||||
resize.enabled = resize.enabled && p.resize.enabled == other.resize.enabled;
|
||||
spot.enabled = spot.enabled && p.spot.enabled == other.spot.enabled;
|
||||
spot.entries = spot.entries && p.spot.entries == other.spot.entries;
|
||||
resize.allowUpscaling = resize.allowUpscaling && p.resize.allowUpscaling == other.resize.allowUpscaling;
|
||||
icm.inputProfile = icm.inputProfile && p.icm.inputProfile == other.icm.inputProfile;
|
||||
icm.toneCurve = icm.toneCurve && p.icm.toneCurve == other.icm.toneCurve;
|
||||
@ -5526,6 +5532,14 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
|
||||
|
||||
}
|
||||
|
||||
if (spot.enabled) {
|
||||
toEdit.spot.enabled = mods.spot.enabled;
|
||||
}
|
||||
|
||||
if (spot.entries) {
|
||||
toEdit.spot.entries = mods.spot.entries;
|
||||
}
|
||||
|
||||
if (pcvignette.enabled) {
|
||||
toEdit.pcvignette.enabled = mods.pcvignette.enabled;
|
||||
}
|
||||
|
@ -1087,6 +1087,13 @@ struct ResizeParamsEdited {
|
||||
bool allowUpscaling;
|
||||
};
|
||||
|
||||
class SpotParamsEdited
|
||||
{
|
||||
public:
|
||||
bool enabled;
|
||||
bool entries;
|
||||
};
|
||||
|
||||
struct ColorManagementParamsEdited {
|
||||
bool inputProfile;
|
||||
bool toneCurve;
|
||||
@ -1437,6 +1444,7 @@ struct ParamsEdited {
|
||||
ChannelMixerParamsEdited chmixer;
|
||||
BlackWhiteParamsEdited blackwhite;
|
||||
ResizeParamsEdited resize;
|
||||
SpotParamsEdited spot;
|
||||
ColorManagementParamsEdited icm;
|
||||
RAWParamsEdited raw;
|
||||
DirPyrEqualizerParamsEdited dirpyrequalizer;
|
||||
|
@ -226,6 +226,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
|
||||
labcurve = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_LABCURVE")));
|
||||
|
||||
// Detail Settings:
|
||||
spot = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SPOT")));
|
||||
sharpen = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SHARPENING")));
|
||||
localcontrast = Gtk::manage(new Gtk::CheckButton(M("PARTIALPASTE_LOCALCONTRAST")));
|
||||
sharpenedge = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_SHARPENEDGE")));
|
||||
@ -339,6 +340,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
|
||||
//DETAIL
|
||||
vboxes[1]->pack_start (*detail, Gtk::PACK_SHRINK, 2);
|
||||
vboxes[1]->pack_start (*hseps[1], Gtk::PACK_SHRINK, 2);
|
||||
vboxes[1]->pack_start (*spot, Gtk::PACK_SHRINK, 2);
|
||||
vboxes[1]->pack_start (*sharpen, Gtk::PACK_SHRINK, 2);
|
||||
vboxes[1]->pack_start (*localcontrast, Gtk::PACK_SHRINK, 2);
|
||||
vboxes[1]->pack_start (*sharpenedge, Gtk::PACK_SHRINK, 2);
|
||||
@ -501,6 +503,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
|
||||
labcurveConn = labcurve->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true));
|
||||
|
||||
// Detail Settings:
|
||||
spotConn = spot->signal_toggled().connect (sigc::bind (sigc::mem_fun(*detail, &Gtk::CheckButton::set_inconsistent), true));
|
||||
sharpenConn = sharpen->signal_toggled().connect (sigc::bind (sigc::mem_fun(*detail, &Gtk::CheckButton::set_inconsistent), true));
|
||||
localcontrastConn = localcontrast->signal_toggled().connect (sigc::bind (sigc::mem_fun(*detail, &Gtk::CheckButton::set_inconsistent), true));
|
||||
gradsharpenConn = sharpenedge->signal_toggled().connect (sigc::bind (sigc::mem_fun(*detail, &Gtk::CheckButton::set_inconsistent), true));
|
||||
@ -719,6 +722,7 @@ void PartialPasteDlg::basicToggled ()
|
||||
void PartialPasteDlg::detailToggled ()
|
||||
{
|
||||
|
||||
ConnectionBlocker spotBlocker(spotConn);
|
||||
ConnectionBlocker sharpenBlocker(sharpenConn);
|
||||
ConnectionBlocker localcontrastBlocker(localcontrastConn);
|
||||
ConnectionBlocker gradsharpenBlocker(gradsharpenConn);
|
||||
@ -731,6 +735,7 @@ void PartialPasteDlg::detailToggled ()
|
||||
|
||||
detail->set_inconsistent (false);
|
||||
|
||||
spot->set_active (detail->get_active ());
|
||||
sharpen->set_active (detail->get_active ());
|
||||
localcontrast->set_active(detail->get_active());
|
||||
sharpenedge->set_active (detail->get_active ());
|
||||
@ -912,6 +917,10 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param
|
||||
filterPE.colorappearance = falsePE.colorappearance;
|
||||
}
|
||||
|
||||
if (!spot->get_active ()) {
|
||||
filterPE.spot = falsePE.spot;
|
||||
}
|
||||
|
||||
if (!sharpen->get_active ()) {
|
||||
filterPE.sharpening = falsePE.sharpening;
|
||||
}
|
||||
|
@ -142,6 +142,7 @@ public:
|
||||
Gtk::CheckButton* colorappearance;
|
||||
|
||||
// options in detail:
|
||||
Gtk::CheckButton* spot;
|
||||
Gtk::CheckButton* sharpen;
|
||||
Gtk::CheckButton* sharpenedge;
|
||||
Gtk::CheckButton* sharpenmicro;
|
||||
@ -224,7 +225,7 @@ public:
|
||||
sigc::connection everythingConn, basicConn, detailConn, colorConn, lensConn, compositionConn, metaConn, rawConn, advancedConn;
|
||||
sigc::connection locallabConn;
|
||||
sigc::connection wbConn, exposureConn, localcontrastConn, shConn, pcvignetteConn, gradientConn, labcurveConn, colorappearanceConn;
|
||||
sigc::connection sharpenConn, gradsharpenConn, microcontrastConn, impdenConn, dirpyrdenConn, defringeConn, epdConn, fattalConn, dirpyreqConn, waveletConn, retinexConn, dehazeConn;
|
||||
sigc::connection spotConn, sharpenConn, gradsharpenConn, microcontrastConn, impdenConn, dirpyrdenConn, defringeConn, epdConn, fattalConn, dirpyreqConn, waveletConn, retinexConn, dehazeConn;
|
||||
sigc::connection vibranceConn, chmixerConn, hsveqConn, rgbcurvesConn, chmixerbwConn, colortoningConn, filmSimulationConn, softlightConn;
|
||||
sigc::connection distortionConn, cacorrConn, vignettingConn, lcpConn;
|
||||
sigc::connection coarserotConn, finerotConn, cropConn, resizeConn, prsharpeningConn, perspectiveConn, commonTransConn;
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
#include "guiutils.h"
|
||||
#include "options.h"
|
||||
|
||||
#include "../rtengine/procparams.h"
|
||||
|
||||
using namespace rtengine;
|
||||
|
817
rtgui/spot.cc
Normal file
817
rtgui/spot.cc
Normal 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
135
rtgui/spot.h
Normal 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
|
@ -47,8 +47,8 @@ public:
|
||||
Job(ThumbBrowserEntryBase* tbe, bool* priority, bool upgrade,
|
||||
ThumbImageUpdateListener* listener):
|
||||
tbe_(tbe),
|
||||
/*pparams_(pparams),
|
||||
height_(height), */
|
||||
/*pparams_(pparams),
|
||||
height_(height), */
|
||||
priority_(priority),
|
||||
upgrade_(upgrade),
|
||||
listener_(listener)
|
||||
|
@ -39,12 +39,21 @@ namespace procparams
|
||||
class ProcParams;
|
||||
}
|
||||
}
|
||||
class EditDataProvider;
|
||||
|
||||
class ToolPanelListener
|
||||
{
|
||||
public:
|
||||
virtual ~ToolPanelListener() = default;
|
||||
|
||||
/// @brief Ask to refresh the preview not triggered by a parameter change (e.g. 'On Preview' editing).
|
||||
virtual void refreshPreview(const rtengine::ProcEvent& event) = 0;
|
||||
/// @brief Used to notify all listeners that a parameters has been effectively changed
|
||||
virtual void panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr) = 0;
|
||||
/// @brief Set the TweakOperator to the StagedImageProcessor, to let some tool enter into special modes
|
||||
virtual void setTweakOperator (rtengine::TweakOperator *tOperator) = 0;
|
||||
/// @brief Unset the TweakOperator to the StagedImageProcessor
|
||||
virtual void unsetTweakOperator (rtengine::TweakOperator *tOperator) = 0;
|
||||
};
|
||||
|
||||
/// @brief This class control the space around the group of tools inside a tab, as well as the space separating each tool. */
|
||||
|
@ -49,6 +49,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
|
||||
shadowshighlights = Gtk::manage (new ShadowsHighlights ());
|
||||
impulsedenoise = Gtk::manage (new ImpulseDenoise ());
|
||||
defringe = Gtk::manage (new Defringe ());
|
||||
spot = Gtk::manage (new Spot ());
|
||||
dirpyrdenoise = Gtk::manage (new DirPyrDenoise ());
|
||||
epd = Gtk::manage (new EdgePreservingDecompositionUI ());
|
||||
sharpening = Gtk::manage (new Sharpening ());
|
||||
@ -117,6 +118,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
|
||||
addfavoritePanel (colorPanel, chmixer);
|
||||
addfavoritePanel (colorPanel, blackwhite);
|
||||
addfavoritePanel (exposurePanel, shadowshighlights);
|
||||
addfavoritePanel (detailsPanel, spot);
|
||||
addfavoritePanel (detailsPanel, sharpening);
|
||||
addfavoritePanel (detailsPanel, localContrast);
|
||||
addfavoritePanel (detailsPanel, sharpenEdge);
|
||||
@ -451,6 +453,33 @@ void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtr
|
||||
|
||||
}
|
||||
|
||||
void ToolPanelCoordinator::setTweakOperator (rtengine::TweakOperator *tOperator)
|
||||
{
|
||||
if (ipc && tOperator) {
|
||||
ipc->setTweakOperator(tOperator);
|
||||
}
|
||||
}
|
||||
|
||||
void ToolPanelCoordinator::unsetTweakOperator (rtengine::TweakOperator *tOperator)
|
||||
{
|
||||
if (ipc && tOperator) {
|
||||
ipc->unsetTweakOperator(tOperator);
|
||||
}
|
||||
}
|
||||
|
||||
void ToolPanelCoordinator::refreshPreview (const rtengine::ProcEvent& event)
|
||||
{
|
||||
if (!ipc) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProcParams* params = ipc->beginUpdateParams ();
|
||||
for (auto toolPanel : toolPanels) {
|
||||
toolPanel->write (params);
|
||||
}
|
||||
|
||||
ipc->endUpdateParams (event); // starts the IPC processing
|
||||
}
|
||||
|
||||
void ToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr)
|
||||
{
|
||||
|
@ -76,6 +76,7 @@
|
||||
#include "sharpening.h"
|
||||
#include "sharpenmicro.h"
|
||||
#include "softlight.h"
|
||||
#include "spot.h"
|
||||
#include "tonecurve.h"
|
||||
#include "toolbar.h"
|
||||
#include "toolpanel.h"
|
||||
@ -133,6 +134,7 @@ protected:
|
||||
ToneCurve* toneCurve;
|
||||
ShadowsHighlights* shadowshighlights;
|
||||
LocalContrast *localContrast;
|
||||
Spot* spot;
|
||||
Defringe* defringe;
|
||||
ImpulseDenoise* impulsedenoise;
|
||||
DirPyrDenoise* dirpyrdenoise;
|
||||
@ -252,8 +254,12 @@ public:
|
||||
}
|
||||
|
||||
// toolpanellistener interface
|
||||
void refreshPreview(const rtengine::ProcEvent& event) override;
|
||||
void panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr) override;
|
||||
void setTweakOperator (rtengine::TweakOperator *tOperator) override;
|
||||
void unsetTweakOperator (rtengine::TweakOperator *tOperator) override;
|
||||
|
||||
// FilmNegProvider interface
|
||||
void imageTypeChanged (bool isRaw, bool isBayer, bool isXtrans, bool isMono = false) override;
|
||||
|
||||
// profilechangelistener interface
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
#include "guiutils.h"
|
||||
#include "options.h"
|
||||
|
||||
#include "../rtengine/procparams.h"
|
||||
|
||||
using namespace rtengine;
|
||||
|
Loading…
x
Reference in New Issue
Block a user