rawTherapee/rtgui/toolpanelcoord.cc
rom9 e635030650
FilmNegative: non-raw file support and better accuracy (#5798)
* Added support for non-raw files to the film negative tool. The tool is now under the "Color" tab. Moved the entire filmneg code downstream, right after input profile conversion.
Usage changes a bit: White Balance must be set to the _backlight_ color temperature. Added two more sliders to color-balance the picture after negative inversion. Legacy inversion method kept for compatibility with processing profiles saved by RT v5.7 or 5.8 (only if Film Negative was enabled). Should be removed in a future version.
There is still an issue with DCP profiles that need the look table to be active to work properly. Using a simple matrix input profile (or just camera standard) is recommended for now.

* The user can now choose to perform inversion before or after input colorspace conversion.
Seamless backwards compatibility with previous processing profiles; upgrading from a previous version now gives an (almost) identical output as before.
Generalised the concept of film base values: the processing profile now contains a pair of RGB triplets, "reference input" and "reference output", which makes it much more straightforward to compute the output multipliers.

* Added support for `RGB` data type to putToKeyFile, removed the now unused `RGB::toVector()` method. Some cleanup.

* Spot balance picker now stays active indefinitely. Can be disabled by right-clicking, too.

* Removed film negative from `filterRawRefresh` condition, since the new version does not require raw rendering anymore.

* The Output Level slider is now exponential, so it should feel more familiar to use (similar to the exposure compensation slider).

* Removed old `RedBase`, `GreenBase`, `BlueBase` keys from the PP3 file after params upgrade. Keys are only kept if the file only undergoes batch edit (hence no params upgrade was done).

* Made the balance sliders exponential and centered at zero. Now they should be a bit smoother and possibly more user-friendly.

* Changed adjusters' step to more useful values, they were too fine-grained and hard to adjust using the +/- spinbutton.
Increased max ratio value from 3 to 5, as i found an old negative needing a very high blue channel exponent.

* Added an initial processing profile for film negative inversion, to be provided as a bundled profile.

* Removed Output Level and balance sliders when in batch mode: since the effect of these sliders is dependent on the reference input values, those values needed to be copied as well. And, touching any of the sliders needed to flag all three as dirty. All this felt more confusing than useful.
It should be sufficient (and clearer) to copy/paste the params, and then fine-tune the balance on individual pictures when needed.

* Set bayer demosaic method to RCD in the bundled "Film Negative" processing profile. RCD seems to play a bit better with Capture Sharpening in the presence of film grain, compared to Amaze.
This will favor new users starting with all-defaults settings, hence having Capture Sharpening enabled by default.

* Removed incorrect "contrast" term from the "Reference exponent" label. This parameter adjusts the image _gamma_, not its contrast.
2020-11-21 13:29:47 +01:00

1282 lines
46 KiB
C++

/*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "multilangmgr.h"
#include "toolpanelcoord.h"
#include "metadatapanel.h"
#include "options.h"
#include "rtimage.h"
#include "../rtengine/imagesource.h"
#include "../rtengine/dfmanager.h"
#include "../rtengine/ffmanager.h"
#include "../rtengine/improcfun.h"
#include "../rtengine/perspectivecorrection.h"
#include "../rtengine/procevents.h"
#include "../rtengine/refreshmap.h"
using namespace rtengine::procparams;
ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favoritePanelSW(nullptr), hasChanged (false), editDataProvider (nullptr), photoLoadedOnce(false)
{
favoritePanel = Gtk::manage (new ToolVBox ());
exposurePanel = Gtk::manage (new ToolVBox ());
detailsPanel = Gtk::manage (new ToolVBox ());
colorPanel = Gtk::manage (new ToolVBox ());
transformPanel = Gtk::manage (new ToolVBox ());
rawPanel = Gtk::manage (new ToolVBox ());
advancedPanel = Gtk::manage (new ToolVBox ());
locallabPanel = Gtk::manage(new ToolVBox());
coarse = Gtk::manage (new CoarsePanel ());
toneCurve = Gtk::manage (new ToneCurve ());
shadowshighlights = Gtk::manage (new ShadowsHighlights ());
impulsedenoise = Gtk::manage (new ImpulseDenoise ());
defringe = Gtk::manage (new Defringe ());
dirpyrdenoise = Gtk::manage (new DirPyrDenoise ());
epd = Gtk::manage (new EdgePreservingDecompositionUI ());
sharpening = Gtk::manage (new Sharpening ());
localContrast = Gtk::manage(new LocalContrast());
sharpenEdge = Gtk::manage(new SharpenEdge());
sharpenMicro = Gtk::manage(new SharpenMicro());
lcurve = Gtk::manage(new LCurve());
rgbcurves = Gtk::manage(new RGBCurves());
colortoning = Gtk::manage(new ColorToning());
lensgeom = Gtk::manage(new LensGeometry());
lensProf = Gtk::manage(new LensProfilePanel());
distortion = Gtk::manage(new Distortion());
rotate = Gtk::manage(new Rotate());
vibrance = Gtk::manage(new Vibrance());
colorappearance = Gtk::manage(new ColorAppearance());
whitebalance = Gtk::manage(new WhiteBalance());
vignetting = Gtk::manage(new Vignetting());
retinex = Gtk::manage(new Retinex());
gradient = Gtk::manage(new Gradient());
locallab = Gtk::manage(new Locallab());
pcvignette = Gtk::manage(new PCVignette());
perspective = Gtk::manage(new PerspCorrection());
cacorrection = Gtk::manage(new CACorrection());
chmixer = Gtk::manage(new ChMixer());
blackwhite = Gtk::manage(new BlackWhite());
resize = Gtk::manage(new Resize());
prsharpening = Gtk::manage(new PrSharpening());
crop = Gtk::manage(new Crop());
icm = Gtk::manage(new ICMPanel());
metadata = Gtk::manage(new MetaDataPanel());
wavelet = Gtk::manage(new Wavelet());
dirpyrequalizer = Gtk::manage(new DirPyrEqualizer());
hsvequalizer = Gtk::manage(new HSVEqualizer());
filmSimulation = Gtk::manage(new FilmSimulation());
softlight = Gtk::manage(new SoftLight());
dehaze = Gtk::manage(new Dehaze());
sensorbayer = Gtk::manage(new SensorBayer());
sensorxtrans = Gtk::manage(new SensorXTrans());
bayerprocess = Gtk::manage(new BayerProcess());
xtransprocess = Gtk::manage(new XTransProcess());
bayerpreprocess = Gtk::manage(new BayerPreProcess());
preprocess = Gtk::manage(new PreProcess());
darkframe = Gtk::manage(new DarkFrame());
flatfield = Gtk::manage(new FlatField());
rawcacorrection = Gtk::manage(new RAWCACorr());
rawexposure = Gtk::manage(new RAWExposure());
preprocessWB = Gtk::manage (new PreprocessWB ());
bayerrawexposure = Gtk::manage(new BayerRAWExposure());
xtransrawexposure = Gtk::manage(new XTransRAWExposure());
fattal = Gtk::manage(new FattalToneMapping());
filmNegative = Gtk::manage (new FilmNegative());
pdSharpening = Gtk::manage (new PdSharpening());
// So Demosaic, Line noise filter, Green Equilibration, Ca-Correction (garder le nom de section identique!) and Black-Level will be moved in a "Bayer sensor" tool,
// and a separate Demosaic and Black Level tool will be created in an "X-Trans sensor" tool
// X-Trans demozaic methods: "3-pass (best), 1-pass (medium), fast"
// Mettre jour les profils fournis pour inclure les nouvelles section Raw, notamment pour "Default High ISO"
// Valeurs par dfaut:
// Best -> low ISO
// Medium -> High ISO
favorites.resize(options.favorites.size(), nullptr);
addfavoritePanel (colorPanel, whitebalance);
addfavoritePanel (exposurePanel, toneCurve);
addfavoritePanel (colorPanel, vibrance);
addfavoritePanel (colorPanel, chmixer);
addfavoritePanel (colorPanel, blackwhite);
addfavoritePanel (exposurePanel, shadowshighlights);
addfavoritePanel (detailsPanel, sharpening);
addfavoritePanel (detailsPanel, localContrast);
addfavoritePanel (detailsPanel, sharpenEdge);
addfavoritePanel (detailsPanel, sharpenMicro);
addfavoritePanel (colorPanel, hsvequalizer);
addfavoritePanel (colorPanel, filmSimulation);
addfavoritePanel (colorPanel, filmNegative);
addfavoritePanel (colorPanel, softlight);
addfavoritePanel (colorPanel, rgbcurves);
addfavoritePanel (colorPanel, colortoning);
addfavoritePanel (exposurePanel, epd);
addfavoritePanel (exposurePanel, fattal);
addfavoritePanel (advancedPanel, retinex);
addfavoritePanel (exposurePanel, pcvignette);
addfavoritePanel (exposurePanel, gradient);
addfavoritePanel (exposurePanel, lcurve);
addfavoritePanel (advancedPanel, colorappearance);
addfavoritePanel (detailsPanel, impulsedenoise);
addfavoritePanel (detailsPanel, dirpyrdenoise);
addfavoritePanel (detailsPanel, defringe);
addfavoritePanel (detailsPanel, dirpyrequalizer);
addfavoritePanel (detailsPanel, dehaze);
addfavoritePanel (advancedPanel, wavelet);
addfavoritePanel(locallabPanel, locallab);
addfavoritePanel (transformPanel, crop);
addfavoritePanel (transformPanel, resize);
addPanel (resize->getPackBox(), prsharpening, 2);
addfavoritePanel (transformPanel, lensgeom);
addfavoritePanel (lensgeom->getPackBox(), rotate, 2);
addfavoritePanel (lensgeom->getPackBox(), perspective, 2);
addfavoritePanel (lensgeom->getPackBox(), lensProf, 2);
addfavoritePanel (lensgeom->getPackBox(), distortion, 2);
addfavoritePanel (lensgeom->getPackBox(), cacorrection, 2);
addfavoritePanel (lensgeom->getPackBox(), vignetting, 2);
addfavoritePanel (colorPanel, icm);
addfavoritePanel (rawPanel, sensorbayer);
addfavoritePanel (sensorbayer->getPackBox(), bayerprocess, 2);
addfavoritePanel (sensorbayer->getPackBox(), bayerrawexposure, 2);
addfavoritePanel (sensorbayer->getPackBox(), bayerpreprocess, 2);
addfavoritePanel (sensorbayer->getPackBox(), rawcacorrection, 2);
addfavoritePanel (rawPanel, sensorxtrans);
addfavoritePanel (sensorxtrans->getPackBox(), xtransprocess, 2);
addfavoritePanel (sensorxtrans->getPackBox(), xtransrawexposure, 2);
addfavoritePanel (rawPanel, rawexposure);
addfavoritePanel (rawPanel, preprocessWB);
addfavoritePanel (rawPanel, preprocess);
addfavoritePanel (rawPanel, darkframe);
addfavoritePanel (rawPanel, flatfield);
addfavoritePanel (rawPanel, pdSharpening);
int favoriteCount = 0;
for(auto it = favorites.begin(); it != favorites.end(); ++it) {
if (*it) {
addPanel(favoritePanel, *it);
++favoriteCount;
}
}
toolPanels.push_back (coarse);
toolPanels.push_back(metadata);
toolPanelNotebook = new Gtk::Notebook();
toolPanelNotebook->set_name("ToolPanelNotebook");
exposurePanelSW = Gtk::manage (new MyScrolledWindow ());
detailsPanelSW = Gtk::manage (new MyScrolledWindow ());
colorPanelSW = Gtk::manage (new MyScrolledWindow ());
transformPanelSW = Gtk::manage (new MyScrolledWindow ());
rawPanelSW = Gtk::manage (new MyScrolledWindow ());
advancedPanelSW = Gtk::manage (new MyScrolledWindow ());
locallabPanelSW = Gtk::manage(new MyScrolledWindow());
// load panel endings
for (int i = 0; i < 8; i++) {
vbPanelEnd[i] = Gtk::manage (new Gtk::VBox ());
imgPanelEnd[i] = Gtk::manage (new RTImage ("ornament1.png"));
imgPanelEnd[i]->show();
vbPanelEnd[i]->pack_start(*imgPanelEnd[i], Gtk::PACK_SHRINK);
vbPanelEnd[i]->show_all();
}
if(favoriteCount > 0) {
favoritePanelSW = Gtk::manage(new MyScrolledWindow());
favoritePanelSW->add(*favoritePanel);
favoritePanel->pack_start(*Gtk::manage(new Gtk::HSeparator), Gtk::PACK_SHRINK, 0);
favoritePanel->pack_start(*vbPanelEnd[0], Gtk::PACK_SHRINK, 4);
}
updateVScrollbars(options.hideTPVScrollbar);
exposurePanelSW->add (*exposurePanel);
exposurePanel->pack_start (*Gtk::manage (new Gtk::HSeparator), Gtk::PACK_SHRINK, 0);
exposurePanel->pack_start (*vbPanelEnd[1], Gtk::PACK_SHRINK, 4);
detailsPanelSW->add (*detailsPanel);
detailsPanel->pack_start (*Gtk::manage (new Gtk::HSeparator), Gtk::PACK_SHRINK, 0);
detailsPanel->pack_start (*vbPanelEnd[2], Gtk::PACK_SHRINK, 4);
colorPanelSW->add (*colorPanel);
colorPanel->pack_start (*Gtk::manage (new Gtk::HSeparator), Gtk::PACK_SHRINK, 0);
colorPanel->pack_start (*vbPanelEnd[3], Gtk::PACK_SHRINK, 4);
advancedPanelSW->add (*advancedPanel);
advancedPanel->pack_start (*Gtk::manage (new Gtk::HSeparator), Gtk::PACK_SHRINK, 0);
advancedPanel->pack_start (*vbPanelEnd[6], Gtk::PACK_SHRINK, 0);
locallabPanelSW->add(*locallabPanel);
locallabPanel->pack_start(*Gtk::manage(new Gtk::HSeparator), Gtk::PACK_SHRINK, 0);
locallabPanel->pack_start(*vbPanelEnd[7], Gtk::PACK_SHRINK, 4);
transformPanelSW->add (*transformPanel);
transformPanel->pack_start (*Gtk::manage (new Gtk::HSeparator), Gtk::PACK_SHRINK, 0);
transformPanel->pack_start (*vbPanelEnd[4], Gtk::PACK_SHRINK, 4);
rawPanelSW->add (*rawPanel);
rawPanel->pack_start (*Gtk::manage (new Gtk::HSeparator), Gtk::PACK_SHRINK, 0);
rawPanel->pack_start (*vbPanelEnd[5], Gtk::PACK_SHRINK, 0);
toiF = Gtk::manage (new TextOrIcon ("star.png", M ("MAIN_TAB_FAVORITES"), M ("MAIN_TAB_FAVORITES_TOOLTIP")));
toiE = Gtk::manage (new TextOrIcon ("exposure.png", M ("MAIN_TAB_EXPOSURE"), M ("MAIN_TAB_EXPOSURE_TOOLTIP")));
toiD = Gtk::manage (new TextOrIcon ("detail.png", M ("MAIN_TAB_DETAIL"), M ("MAIN_TAB_DETAIL_TOOLTIP")));
toiC = Gtk::manage (new TextOrIcon ("color-circles.png", M ("MAIN_TAB_COLOR"), M ("MAIN_TAB_COLOR_TOOLTIP")));
toiW = Gtk::manage (new TextOrIcon ("atom.png", M ("MAIN_TAB_ADVANCED"), M ("MAIN_TAB_ADVANCED_TOOLTIP")));
toiL = Gtk::manage(new TextOrIcon("hand-open.png", M("MAIN_TAB_LOCALLAB"), M("MAIN_TAB_LOCALLAB_TOOLTIP")));
toiT = Gtk::manage (new TextOrIcon ("transform.png", M ("MAIN_TAB_TRANSFORM"), M ("MAIN_TAB_TRANSFORM_TOOLTIP")));
toiR = Gtk::manage (new TextOrIcon ("bayer.png", M ("MAIN_TAB_RAW"), M ("MAIN_TAB_RAW_TOOLTIP")));
toiM = Gtk::manage (new TextOrIcon ("metadata.png", M ("MAIN_TAB_METADATA"), M ("MAIN_TAB_METADATA_TOOLTIP")));
if (favoritePanelSW) {
toolPanelNotebook->append_page (*favoritePanelSW, *toiF);
}
toolPanelNotebook->append_page (*exposurePanelSW, *toiE);
toolPanelNotebook->append_page (*detailsPanelSW, *toiD);
toolPanelNotebook->append_page (*colorPanelSW, *toiC);
toolPanelNotebook->append_page (*advancedPanelSW, *toiW);
// Locallab notebook is hidden in batch mode
if (!batch) {
toolPanelNotebook->append_page(*locallabPanelSW, *toiL);
}
toolPanelNotebook->append_page (*transformPanelSW, *toiT);
toolPanelNotebook->append_page (*rawPanelSW, *toiR);
toolPanelNotebook->append_page (*metadata, *toiM);
toolPanelNotebook->set_scrollable();
toolPanelNotebook->show_all();
notebookconn = toolPanelNotebook->signal_switch_page().connect(
sigc::mem_fun(*this, &ToolPanelCoordinator::notebookPageChanged));
// In batch mode, notebookPageChanged method is blocked because it's useless to display spots
if (batch) {
notebookconn.block(true);
}
for (auto toolPanel : toolPanels) {
toolPanel->setListener(this);
}
whitebalance->setWBProvider(this);
whitebalance->setSpotWBListener(this);
darkframe->setDFProvider(this);
flatfield->setFFProvider(this);
lensgeom->setLensGeomListener(this);
rotate->setLensGeomListener(this);
perspective->setLensGeomListener(this);
perspective->setPerspCorrectionPanelListener(this);
distortion->setLensGeomListener(this);
crop->setCropPanelListener(this);
icm->setICMPanelListener(this);
filmNegative->setFilmNegProvider(this);
toolBar = new ToolBar();
toolBar->setToolBarListener(this);
prevPage = toolPanelNotebook->get_nth_page(0);
}
void ToolPanelCoordinator::notebookPageChanged(Gtk::Widget* page, guint page_num)
{
// Locallab spot curves are set visible if at least one photo has been loaded (to avoid
// segfault) and locallab panel is active
if (photoLoadedOnce) {
if (page == locallabPanelSW) {
toolBar->blockEditDeactivation(); // Avoid edit tool deactivation when Locallab page is active (except if pressing other tools button)
locallab->subscribe();
}
if (prevPage == locallabPanelSW) { // To deactivate Locallab only when switching from Locallab page
toolBar->blockEditDeactivation(false);
locallab->unsubscribe();
}
prevPage = page;
}
}
void ToolPanelCoordinator::addPanel(Gtk::Box* where, FoldableToolPanel* panel, int level)
{
panel->setParent(where);
panel->setLevel(level);
expList.push_back(panel->getExpander());
where->pack_start(*panel->getExpander(), false, false);
toolPanels.push_back(panel);
}
void ToolPanelCoordinator::addfavoritePanel (Gtk::Box* where, FoldableToolPanel* panel, int level)
{
auto name = panel->getToolName();
auto it = std::find(options.favorites.begin(), options.favorites.end(), name);
if (it != options.favorites.end()) {
int index = std::distance(options.favorites.begin(), it);
favorites[index] = panel;
} else {
addPanel(where, panel, level);
}
}
ToolPanelCoordinator::~ToolPanelCoordinator ()
{
idle_register.destroy();
closeImage();
// When deleting toolPanelNotebook, pages removal activates notebookPageChanged function
// which is responsible of segfault if listener isn't deactivated before
notebookconn.block(true);
delete toolPanelNotebook;
delete toolBar;
}
void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtrans, bool isMono)
{
if (isRaw) {
if (isBayer) {
idle_register.add(
[this]() -> bool
{
rawPanelSW->set_sensitive(true);
sensorxtrans->FoldableToolPanel::hide();
xtransprocess->FoldableToolPanel::hide();
xtransrawexposure->FoldableToolPanel::hide();
sensorbayer->FoldableToolPanel::show();
bayerprocess->FoldableToolPanel::show();
bayerpreprocess->FoldableToolPanel::show();
rawcacorrection->FoldableToolPanel::show();
preprocessWB->FoldableToolPanel::show();
preprocess->FoldableToolPanel::show();
flatfield->FoldableToolPanel::show();
pdSharpening->FoldableToolPanel::show();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;
}
);
} else if (isXtrans) {
idle_register.add(
[this]() -> bool
{
rawPanelSW->set_sensitive(true);
sensorxtrans->FoldableToolPanel::show();
xtransprocess->FoldableToolPanel::show();
xtransrawexposure->FoldableToolPanel::show();
sensorbayer->FoldableToolPanel::hide();
bayerprocess->FoldableToolPanel::hide();
bayerpreprocess->FoldableToolPanel::hide();
rawcacorrection->FoldableToolPanel::hide();
preprocessWB->FoldableToolPanel::show();
preprocess->FoldableToolPanel::show();
flatfield->FoldableToolPanel::show();
pdSharpening->FoldableToolPanel::show();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;
}
);
} else if (isMono) {
idle_register.add(
[this]() -> bool
{
rawPanelSW->set_sensitive(true);
sensorbayer->FoldableToolPanel::hide();
bayerprocess->FoldableToolPanel::hide();
bayerpreprocess->FoldableToolPanel::hide();
rawcacorrection->FoldableToolPanel::hide();
sensorxtrans->FoldableToolPanel::hide();
xtransprocess->FoldableToolPanel::hide();
xtransrawexposure->FoldableToolPanel::hide();
preprocessWB->FoldableToolPanel::hide();
preprocess->FoldableToolPanel::hide();
flatfield->FoldableToolPanel::show();
pdSharpening->FoldableToolPanel::show();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;
}
);
} else {
idle_register.add(
[this]() -> bool
{
rawPanelSW->set_sensitive(true);
sensorbayer->FoldableToolPanel::hide();
bayerprocess->FoldableToolPanel::hide();
bayerpreprocess->FoldableToolPanel::hide();
rawcacorrection->FoldableToolPanel::hide();
sensorxtrans->FoldableToolPanel::hide();
xtransprocess->FoldableToolPanel::hide();
xtransrawexposure->FoldableToolPanel::hide();
preprocessWB->FoldableToolPanel::hide();
preprocess->FoldableToolPanel::hide();
flatfield->FoldableToolPanel::hide();
pdSharpening->FoldableToolPanel::hide();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;
}
);
}
} else {
idle_register.add(
[this]() -> bool
{
rawPanelSW->set_sensitive(false);
sensorbayer->FoldableToolPanel::hide();
bayerprocess->FoldableToolPanel::hide();
bayerpreprocess->FoldableToolPanel::hide();
rawcacorrection->FoldableToolPanel::hide();
sensorxtrans->FoldableToolPanel::hide();
xtransprocess->FoldableToolPanel::hide();
xtransrawexposure->FoldableToolPanel::hide();
preprocessWB->FoldableToolPanel::hide();
preprocess->FoldableToolPanel::hide();
flatfield->FoldableToolPanel::hide();
pdSharpening->FoldableToolPanel::hide();
retinex->FoldableToolPanel::setGrayedOut(true);
return false;
}
);
}
}
void ToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr)
{
if (!ipc) {
return;
}
int changeFlags = rtengine::RefreshMapper::getInstance()->getAction(event);
ProcParams* params = ipc->beginUpdateParams();
for (auto toolPanel : toolPanels) {
toolPanel->write(params);
}
// Compensate rotation on flip
if (event == rtengine::EvCTHFlip || event == rtengine::EvCTVFlip) {
if (fabs(params->rotate.degree) > 0.001) {
params->rotate.degree *= -1;
changeFlags |= rtengine::RefreshMapper::getInstance()->getAction(rtengine::EvROTDegree);
rotate->read(params);
}
}
int tr = TR_NONE;
if (params->coarse.rotate == 90) {
tr = TR_R90;
} else if (params->coarse.rotate == 180) {
tr = TR_R180;
} else if (params->coarse.rotate == 270) {
tr = TR_R270;
}
// Update "on preview" geometry
if (event == rtengine::EvPhotoLoaded || event == rtengine::EvProfileChanged || event == rtengine::EvHistoryBrowsed || event == rtengine::EvCTRotate) {
// updating the "on preview" geometry
int fw, fh;
ipc->getInitialImage()->getImageSource()->getFullSize(fw, fh, tr);
gradient->updateGeometry(params->gradient.centerX, params->gradient.centerY, params->gradient.feather, params->gradient.degree, fw, fh);
}
// some transformations make the crop change for convenience
if (event == rtengine::EvCTHFlip) {
crop->hFlipCrop();
crop->write(params);
} else if (event == rtengine::EvCTVFlip) {
crop->vFlipCrop();
crop->write(params);
} else if (event == rtengine::EvCTRotate) {
crop->rotateCrop(params->coarse.rotate, params->coarse.hflip, params->coarse.vflip);
crop->write(params);
resize->update(params->crop.enabled, params->crop.w, params->crop.h, ipc->getFullWidth(), ipc->getFullHeight());
resize->write(params);
} else if (event == rtengine::EvCrop) {
resize->update(params->crop.enabled, params->crop.w, params->crop.h);
resize->write(params);
}
/*
* Manage Locallab mask visibility:
* - Mask preview is updated when choosing a mask preview method
* - Mask preview is also updated when modifying (to avoid hiding a potentially visible mask combobox):
* - Color&Light invers
* - Exposure inversex
* - Shadow Highlight inverssh
* - Soft Light softMethod
* - Mask preview is stopped when creating, deleting or selecting a spot
* - Mask preview is also stopped when removing a spot or resetting all mask visibility
*/
if (event == rtengine::EvlocallabshowmaskMethod) {
const Locallab::llMaskVisibility maskStruc = locallab->getMaskVisibility();
ipc->setLocallabMaskVisibility(maskStruc.previewDeltaE, maskStruc.colorMask, maskStruc.colorMaskinv, maskStruc.expMask, maskStruc.expMaskinv,
maskStruc.SHMask, maskStruc.SHMaskinv, maskStruc.vibMask, maskStruc.softMask,
maskStruc.blMask, maskStruc.tmMask, maskStruc.retiMask, maskStruc.sharMask,
maskStruc.lcMask, maskStruc.cbMask, maskStruc.logMask, maskStruc.maskMask);
} else if (event == rtengine::EvLocallabSpotCreated || event == rtengine::EvLocallabSpotSelectedWithMask ||
event == rtengine::EvLocallabSpotDeleted || event == rtengine::Evlocallabshowreset ||
event == rtengine::EvlocallabToolRemovedWithRefresh) {
locallab->resetMaskVisibility();
ipc->setLocallabMaskVisibility(false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
ipc->endUpdateParams(changeFlags); // starts the IPC processing
hasChanged = true;
for (auto paramcListener : paramcListeners) {
paramcListener->procParamsChanged(params, event, descr);
}
// Locallab spot curves are set visible if at least one photo has been loaded (to avoid
// segfault) and locallab panel is active
// When a new photo is loaded, Locallab spot curves need to be set visible again
const auto func =
[this]() -> bool
{
if (photoLoadedOnce && (toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()) == locallabPanelSW)) {
locallab->subscribe();
}
return false;
};
if (event == rtengine::EvPhotoLoaded) {
idle_register.add(func);
}
photoLoadedOnce = true;
}
void ToolPanelCoordinator::profileChange(
const PartialProfile* nparams,
const rtengine::ProcEvent& event,
const Glib::ustring& descr,
const ParamsEdited* paramsEdited,
bool fromLastSave
)
{
int fw, fh, tr;
if (!ipc) {
return;
}
ProcParams *params = ipc->beginUpdateParams();
ProcParams *mergedParams = new ProcParams();
// Copy the current params as default values for the fusion
*mergedParams = *params;
// Reset IPTC values when switching procparams from the History
if (event == rtengine::EvHistoryBrowsed) {
mergedParams->iptc.clear();
mergedParams->exif.clear();
}
// And apply the partial profile nparams to mergedParams
nparams->applyTo(mergedParams, fromLastSave);
// Derive the effective changes, if it's a profile change, to prevent slow RAW rerendering if not necessary
bool filterRawRefresh = false;
if (event != rtengine::EvPhotoLoaded) {
ParamsEdited pe(true);
std::vector<rtengine::procparams::ProcParams> lParams(2);
lParams[0] = *params;
lParams[1] = *mergedParams;
pe.initFrom(lParams);
filterRawRefresh = pe.raw.isUnchanged() && pe.lensProf.isUnchanged() && pe.retinex.isUnchanged() && pe.pdsharpening.isUnchanged();
}
*params = *mergedParams;
delete mergedParams;
tr = TR_NONE;
if (params->coarse.rotate == 90) {
tr = TR_R90;
} else if (params->coarse.rotate == 180) {
tr = TR_R180;
} else if (params->coarse.rotate == 270) {
tr = TR_R270;
}
// trimming overflowing cropped area
ipc->getInitialImage()->getImageSource()->getFullSize(fw, fh, tr);
crop->trim(params, fw, fh);
// updating the GUI with updated values
for (auto toolPanel : toolPanels) {
toolPanel->read(params);
if (event == rtengine::EvPhotoLoaded || event == rtengine::EvProfileChanged) {
toolPanel->autoOpenCurve();
// For Locallab, reset tool expanders visibility only when a photo or profile is loaded
locallab->openAllTools();
}
}
if (event == rtengine::EvPhotoLoaded || event == rtengine::EvProfileChanged || event == rtengine::EvHistoryBrowsed || event == rtengine::EvCTRotate) {
// updating the "on preview" geometry
gradient->updateGeometry(params->gradient.centerX, params->gradient.centerY, params->gradient.feather, params->gradient.degree, fw, fh);
}
// Reset Locallab mask visibility
locallab->resetMaskVisibility();
ipc->setLocallabMaskVisibility(false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
// start the IPC processing
if (filterRawRefresh) {
ipc->endUpdateParams(rtengine::RefreshMapper::getInstance()->getAction(event) & ALLNORAW);
} else {
ipc->endUpdateParams(event);
}
hasChanged = event != rtengine::EvProfileChangeNotification;
for (auto paramcListener : paramcListeners) {
paramcListener->procParamsChanged(params, event, descr);
}
// Locallab spot curves are set visible if at least one photo has been loaded (to avoid
// segfault) and locallab panel is active
// When a new photo is loaded, Locallab spot curves need to be set visible again
const auto func =
[this]() -> bool
{
if (photoLoadedOnce && (toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()) == locallabPanelSW)) {
locallab->subscribe();
}
return false;
};
if (event == rtengine::EvPhotoLoaded) {
idle_register.add(func);
}
photoLoadedOnce = true;
}
void ToolPanelCoordinator::setDefaults(const ProcParams* defparams)
{
if (defparams) {
for (auto toolPanel : toolPanels) {
toolPanel->setDefaults(defparams);
}
}
}
CropGUIListener* ToolPanelCoordinator::getCropGUIListener()
{
return crop;
}
void ToolPanelCoordinator::initImage(rtengine::StagedImageProcessor* ipc_, bool raw)
{
ipc = ipc_;
toneCurve->disableListener();
toneCurve->enableAll();
toneCurve->enableListener();
if (ipc) {
const rtengine::FramesMetaData* pMetaData = ipc->getInitialImage()->getMetaData();
metadata->setImageData(pMetaData);
ipc->setAutoExpListener(toneCurve);
ipc->setAutoCamListener(colorappearance);
ipc->setAutoBWListener(blackwhite);
ipc->setFrameCountListener(bayerprocess);
ipc->setFlatFieldAutoClipListener (flatfield);
ipc->setBayerAutoContrastListener (bayerprocess);
ipc->setXtransAutoContrastListener (xtransprocess);
ipc->setpdSharpenAutoContrastListener (pdSharpening);
ipc->setpdSharpenAutoRadiusListener (pdSharpening);
ipc->setAutoWBListener(whitebalance);
ipc->setAutoColorTonListener(colortoning);
ipc->setAutoChromaListener(dirpyrdenoise);
ipc->setWaveletListener(wavelet);
ipc->setRetinexListener(retinex);
ipc->setSizeListener(crop);
ipc->setSizeListener(resize);
ipc->setLocallabListener(locallab);
ipc->setImageTypeListener(this);
ipc->setFilmNegListener(filmNegative);
flatfield->setShortcutPath(Glib::path_get_dirname(ipc->getInitialImage()->getFileName()));
icm->setRawMeta(raw, (const rtengine::FramesData*)pMetaData);
lensProf->setRawMeta(raw, pMetaData);
perspective->setMetadata(pMetaData);
}
toneCurve->setRaw(raw);
hasChanged = true;
}
void ToolPanelCoordinator::closeImage()
{
if (ipc) {
ipc->stopProcessing();
ipc = nullptr;
}
}
void ToolPanelCoordinator::closeAllTools()
{
for (size_t i = 0; i < options.tpOpen.size(); ++i) {
if (i < expList.size()) {
expList[i]->set_expanded(false);
}
}
}
void ToolPanelCoordinator::openAllTools()
{
for (size_t i = 0; i < options.tpOpen.size(); ++i) {
if (i < expList.size()) {
expList[i]->set_expanded(true);
}
}
}
void ToolPanelCoordinator::updateToolState()
{
if (options.tpOpen.empty()) {
for (auto expander : expList) {
expander->set_expanded(false);
}
wavelet->updateToolState({});
retinex->updateToolState({});
return;
}
for (size_t i = 0; i < options.tpOpen.size(); ++i) {
if (i < expList.size()) {
expList[i]->set_expanded(options.tpOpen[i]);
}
}
if (options.tpOpen.size() > expList.size()) {
const size_t sizeWavelet = options.tpOpen.size() - expList.size();
std::vector<int> temp;
for (size_t i = 0; i < sizeWavelet; ++i) {
temp.push_back(options.tpOpen[i + expList.size()]);
}
wavelet->updateToolState(temp);
retinex->updateToolState(temp);
}
}
void ToolPanelCoordinator::readOptions()
{
crop->readOptions();
}
void ToolPanelCoordinator::writeOptions()
{
crop->writeOptions();
if (options.autoSaveTpOpen) {
writeToolExpandedStatus(options.tpOpen);
}
}
void ToolPanelCoordinator::writeToolExpandedStatus(std::vector<int> &tpOpen)
{
tpOpen.clear();
for (size_t i = 0; i < expList.size(); i++) {
tpOpen.push_back(expList.at(i)->get_expanded());
}
wavelet->writeOptions(tpOpen);
retinex->writeOptions(tpOpen);
}
void ToolPanelCoordinator::updateShowtooltipVisibility (bool showtooltip)
{
locallab->updateShowtooltipVisibility(showtooltip);
}
void ToolPanelCoordinator::spotWBselected(int x, int y, Thumbnail* thm)
{
if (!ipc) {
return;
}
// toolBar->setTool (TOOL_HAND);
int rect = whitebalance->getSize();
int ww = ipc->getFullWidth();
int hh = ipc->getFullHeight();
if (x - rect > 0 && y - rect > 0 && x + rect < ww && y + rect < hh) {
double temp;
double green;
ipc->getSpotWB(x, y, rect, temp, green);
whitebalance->setWB(temp, green);
}
}
void ToolPanelCoordinator::sharpMaskSelected(bool sharpMask)
{
if (!ipc) {
return;
}
ipc->beginUpdateParams();
ipc->endUpdateParams (ipc->setSharpMask(sharpMask));
}
int ToolPanelCoordinator::getSpotWBRectSize() const
{
return whitebalance->getSize();
}
void ToolPanelCoordinator::cropSelectionReady()
{
toolBar->setTool (TMHand);
if (!ipc) {
return;
}
}
void ToolPanelCoordinator::rotateSelectionReady(double rotate_deg, Thumbnail* thm)
{
toolBar->setTool (TMHand);
if (!ipc) {
return;
}
if (rotate_deg != 0.0) {
rotate->straighten (rotate_deg);
}
}
ToolBar* ToolPanelCoordinator::getToolBar() const
{
return toolBar;
}
CropGUIListener* ToolPanelCoordinator::startCropEditing(Thumbnail* thm)
{
return crop;
}
void ToolPanelCoordinator::autoCropRequested()
{
if (!ipc) {
return;
}
int x1, y1, x2, y2, w, h;
ipc->getAutoCrop(crop->getRatio(), x1, y1, w, h);
x2 = x1 + w - 1;
y2 = y1 + h - 1;
crop->cropInit(x1, y1, w, h);
crop->cropResized(x1, y1, x2, y2);
crop->cropManipReady();
}
rtengine::RawImage* ToolPanelCoordinator::getDF()
{
if (!ipc) {
return nullptr;
}
const rtengine::FramesMetaData *imd = ipc->getInitialImage()->getMetaData();
if (imd) {
int iso = imd->getISOSpeed();
double shutter = imd->getShutterSpeed();
std::string maker(imd->getMake());
std::string model(imd->getModel());
time_t timestamp = imd->getDateTimeAsTS();
return rtengine::dfm.searchDarkFrame(maker, model, iso, shutter, timestamp);
}
return nullptr;
}
rtengine::RawImage* ToolPanelCoordinator::getFF()
{
if (!ipc) {
return nullptr;
}
const rtengine::FramesMetaData *imd = ipc->getInitialImage()->getMetaData();
if (imd) {
// int iso = imd->getISOSpeed(); temporarily removed because unused
// double shutter = imd->getShutterSpeed(); temporarily removed because unused
double aperture = imd->getFNumber();
double focallength = imd->getFocalLen();
std::string maker(imd->getMake());
std::string model(imd->getModel());
std::string lens(imd->getLens());
time_t timestamp = imd->getDateTimeAsTS();
return rtengine::ffm.searchFlatField(maker, model, lens, focallength, aperture, timestamp);
}
return nullptr;
}
Glib::ustring ToolPanelCoordinator::GetCurrentImageFilePath()
{
if (!ipc) {
return "";
}
return ipc->getInitialImage()->getFileName();
}
void ToolPanelCoordinator::straightenRequested()
{
if (!ipc) {
return;
}
toolBar->setTool(TMStraighten);
}
void ToolPanelCoordinator::autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const std::vector<rtengine::ControlLine> *lines)
{
if (!(ipc && (corr_pitch || corr_yaw))) {
return;
}
rtengine::ImageSource *src = dynamic_cast<rtengine::ImageSource *>(ipc->getInitialImage());
if (!src) {
return;
}
rtengine::procparams::ProcParams params;
ipc->getParams(&params);
auto res = rtengine::PerspectiveCorrection::autocompute(src, corr_pitch, corr_yaw, &params, src->getMetaData(), lines);
rot = res.angle;
pitch = res.pitch;
yaw = res.yaw;
}
double ToolPanelCoordinator::autoDistorRequested()
{
if (!ipc) {
return 0.0;
}
return rtengine::ImProcFunctions::getAutoDistor(ipc->getInitialImage()->getFileName(), 400);
}
void ToolPanelCoordinator::updateTransformPreviewRequested(rtengine::ProcEvent event, bool render_perspective)
{
if (!ipc) {
return;
}
ipc->beginUpdateParams()->perspective.render = render_perspective;
ipc->endUpdateParams(event);
}
void ToolPanelCoordinator::spotWBRequested(int size)
{
if (!ipc) {
return;
}
toolBar->setTool(TMSpotWB);
}
void ToolPanelCoordinator::cropSelectRequested()
{
if (!ipc) {
return;
}
toolBar->setTool(TMCropSelect);
}
void ToolPanelCoordinator::controlLineEditModeChanged(bool active)
{
if (!ipc) {
return;
}
if (active) {
toolBar->setTool(TMPerspective);
}
}
void ToolPanelCoordinator::saveInputICCReference(const Glib::ustring& fname, bool apply_wb)
{
if (ipc) {
ipc->saveInputICCReference(fname, apply_wb);
}
}
void ToolPanelCoordinator::updateCurveBackgroundHistogram(
const LUTu& histToneCurve,
const LUTu& histLCurve,
const LUTu& histCCurve,
const LUTu& histLCAM,
const LUTu& histCCAM,
const LUTu& histRed,
const LUTu& histGreen,
const LUTu& histBlue,
const LUTu& histLuma,
const LUTu& histLRETI
)
{
colorappearance->updateCurveBackgroundHistogram(histToneCurve, histLCurve, histCCurve, histLCAM, histCCAM, histRed, histGreen, histBlue, histLuma, histLRETI);
toneCurve->updateCurveBackgroundHistogram(histToneCurve, histLCurve, histCCurve,histLCAM, histCCAM, histRed, histGreen, histBlue, histLuma, histLRETI);
lcurve->updateCurveBackgroundHistogram(histToneCurve, histLCurve, histCCurve, histLCAM, histCCAM, histRed, histGreen, histBlue, histLuma, histLRETI);
rgbcurves->updateCurveBackgroundHistogram(histToneCurve, histLCurve, histCCurve, histLCAM, histCCAM, histRed, histGreen, histBlue, histLuma, histLRETI);
retinex->updateCurveBackgroundHistogram(histToneCurve, histLCurve, histCCurve, histLCAM, histCCAM, histRed, histGreen, histBlue, histLuma, histLRETI);
}
void ToolPanelCoordinator::foldAllButOne(Gtk::Box* parent, FoldableToolPanel* openedSection)
{
for (auto toolPanel : toolPanels) {
if (toolPanel->getParent() != nullptr) {
ToolPanel* currentTP = toolPanel;
if (currentTP->getParent() == parent) {
// Section in the same tab, we unfold it if it's not the one that has been clicked
if (currentTP != openedSection) {
currentTP->setExpanded(false);
} else {
if (!currentTP->getExpanded()) {
currentTP->setExpanded(true);
}
}
}
}
}
}
bool ToolPanelCoordinator::handleShortcutKey(GdkEventKey* event)
{
//bool ctrl = event->state & GDK_CONTROL_MASK; temporarily removed because unused
//bool shift = event->state & GDK_SHIFT_MASK; temporarily removed because unused
bool alt = event->state & GDK_MOD1_MASK;
if (alt) {
switch (event->keyval) {
case GDK_KEY_u:
if (favoritePanelSW) {
toolPanelNotebook->set_current_page (toolPanelNotebook->page_num (*favoritePanelSW));
}
return true;
case GDK_KEY_e:
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*exposurePanelSW));
return true;
case GDK_KEY_d:
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*detailsPanelSW));
return true;
case GDK_KEY_c:
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*colorPanelSW));
return true;
case GDK_KEY_t:
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*transformPanelSW));
return true;
case GDK_KEY_r:
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*rawPanelSW));
return true;
case GDK_KEY_a:
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*advancedPanelSW));
return true;
case GDK_KEY_o:
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*locallabPanelSW));
return true;
case GDK_KEY_m:
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*metadata));
return true;
}
}
return false;
}
void ToolPanelCoordinator::updateVScrollbars(bool hide)
{
GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected
Gtk::PolicyType policy = hide ? Gtk::POLICY_NEVER : Gtk::POLICY_AUTOMATIC;
if (favoritePanelSW) {
favoritePanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy);
}
exposurePanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy);
detailsPanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy);
colorPanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy);
transformPanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy);
rawPanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy);
advancedPanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy);
locallabPanelSW->set_policy(Gtk::POLICY_AUTOMATIC, policy);
for (auto currExp : expList) {
currExp->updateVScrollbars(hide);
}
}
void ToolPanelCoordinator::updateTPVScrollbar(bool hide)
{
updateVScrollbars(hide);
}
void ToolPanelCoordinator::toolDeselected(ToolMode tool)
{
if (tool == TMPerspective) {
perspective->requestApplyControlLines();
}
}
void ToolPanelCoordinator::toolSelected(ToolMode tool)
{
GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected
notebookconn.block(true); // "signal_switch_page" event is blocked to avoid unsubscribing Locallab (allows a correct behavior when switching to another tool using toolbar)
auto checkFavorite = [this](FoldableToolPanel* tool) {
for (auto fav : favorites) {
if (fav == tool) {
return true;
}
}
return false;
};
switch (tool) {
case TMCropSelect: {
toolBar->blockEditDeactivation(false); // To allow deactivating Locallab when switching to another tool using toolbar
crop->setExpanded(true);
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(checkFavorite(crop) ? *favoritePanelSW : *transformPanelSW));
prevPage = toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()); // Updating prevPage as "signal_switch_page" event
break;
}
case TMSpotWB: {
toolBar->blockEditDeactivation(false); // To allow deactivating Locallab when switching to another tool using toolbar
whitebalance->setExpanded(true);
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(checkFavorite(whitebalance) ? *favoritePanelSW : *colorPanelSW));
prevPage = toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()); // Updating prevPage as "signal_switch_page" event
break;
}
case TMStraighten: {
toolBar->blockEditDeactivation(false); // To allow deactivating Locallab when switching to another tool using toolbar
rotate->setExpanded(true);
bool isFavorite = checkFavorite(rotate);
if (!isFavorite) {
isFavorite = checkFavorite(lensgeom);
lensgeom->setExpanded(true);
}
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(isFavorite ? *favoritePanelSW : *transformPanelSW));
prevPage = toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()); // Updating prevPage as "signal_switch_page" event
break;
}
case TMPerspective: {
toolBar->blockEditDeactivation(false); // To allow deactivating Locallab when switching to another tool using toolbar
perspective->setControlLineEditMode(true);
perspective->setExpanded(true);
bool isFavorite = checkFavorite(perspective);
if (!isFavorite) {
isFavorite = checkFavorite(lensgeom);
lensgeom->setExpanded(true);
}
toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(isFavorite ? *favoritePanelSW : *transformPanelSW));
prevPage = toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()); // Updating prevPage as "signal_switch_page" event
break;
}
default:
break;
}
notebookconn.block(false);
}
void ToolPanelCoordinator::editModeSwitchedOff()
{
if (editDataProvider) {
editDataProvider->switchOffEditMode();
}
}
void ToolPanelCoordinator::dirSelected(const Glib::ustring& dirname, const Glib::ustring& openfile)
{
flatfield->setShortcutPath(dirname);
}
void ToolPanelCoordinator::setEditProvider(EditDataProvider *provider)
{
editDataProvider = provider;
for (size_t i = 0; i < toolPanels.size(); i++) {
toolPanels.at(i)->setEditProvider(provider);
}
}
bool ToolPanelCoordinator::getFilmNegativeSpot(rtengine::Coord spot, int spotSize, RGB &refInput, RGB &refOutput)
{
return ipc && ipc->getFilmNegativeSpot(spot.x, spot.y, spotSize, refInput, refOutput);
}