Spot Removal tool
It is not working yet, but the GUI is (almost) done. See issue #2239.
This commit is contained in:
@@ -6,7 +6,7 @@ set (BASESOURCEFILES
|
||||
cachemanager.cc cacheimagedata.cc shcselector.cc perspective.cc thresholdselector.cc thresholdadjuster.cc
|
||||
clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc coloredbar.cc edit.cc coordinateadjuster.cc
|
||||
coarsepanel.cc cacorrection.cc chmixer.cc blackwhite.cc
|
||||
resize.cc icmpanel.cc crop.cc shadowshighlights.cc
|
||||
resize.cc icmpanel.cc crop.cc shadowshighlights.cc spot.cc
|
||||
impulsedenoise.cc dirpyrdenoise.cc epd.cc
|
||||
exifpanel.cc toolpanel.cc lensprofile.cc
|
||||
sharpening.cc vibrance.cc rgbcurves.cc colortoning.cc
|
||||
|
||||
@@ -342,6 +342,10 @@ void ParamsEdited::set (bool v)
|
||||
resize.width = v;
|
||||
resize.height = v;
|
||||
resize.enabled = v;
|
||||
|
||||
spot.enabled = v;
|
||||
spot.entries = v;
|
||||
|
||||
icm.input = v;
|
||||
icm.toneCurve = v;
|
||||
icm.applyLookTable = v;
|
||||
@@ -835,6 +839,8 @@ void ParamsEdited::initFrom (const std::vector<rtengine::procparams::ProcParams>
|
||||
resize.width = resize.width && p.resize.width == other.resize.width;
|
||||
resize.height = resize.height && p.resize.height == other.resize.height;
|
||||
resize.enabled = resize.enabled && p.resize.enabled == other.resize.enabled;
|
||||
spot.enabled = spot.enabled && p.spot.enabled == other.spot.enabled;
|
||||
spot.entries = spot.entries && p.spot.entries == other.spot.entries;
|
||||
icm.input = icm.input && p.icm.input == other.icm.input;
|
||||
icm.toneCurve = icm.toneCurve && p.icm.toneCurve == other.icm.toneCurve;
|
||||
icm.applyLookTable = icm.applyLookTable && p.icm.applyLookTable == other.icm.applyLookTable;
|
||||
@@ -2164,6 +2170,14 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten
|
||||
toEdit.resize.enabled = mods.resize.enabled;
|
||||
}
|
||||
|
||||
if (spot.enabled) {
|
||||
toEdit.spot.enabled = mods.spot.enabled;
|
||||
}
|
||||
|
||||
if (spot.entries) {
|
||||
toEdit.spot.entries = mods.spot.entries;
|
||||
}
|
||||
|
||||
if (icm.input) {
|
||||
toEdit.icm.input = mods.icm.input;
|
||||
}
|
||||
|
||||
@@ -529,6 +529,13 @@ public:
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
class SpotParamsEdited
|
||||
{
|
||||
public:
|
||||
bool enabled;
|
||||
bool entries;
|
||||
};
|
||||
|
||||
class ColorManagementParamsEdited
|
||||
{
|
||||
|
||||
@@ -773,6 +780,7 @@ public:
|
||||
ChannelMixerParamsEdited chmixer;
|
||||
BlackWhiteParamsEdited blackwhite;
|
||||
ResizeParamsEdited resize;
|
||||
SpotParamsEdited spot;
|
||||
ColorManagementParamsEdited icm;
|
||||
RAWParamsEdited raw;
|
||||
DirPyrEqualizerParamsEdited dirpyrequalizer;
|
||||
|
||||
690
rtgui/spot.cc
Normal file
690
rtgui/spot.cc
Normal file
@@ -0,0 +1,690 @@
|
||||
/*
|
||||
* This file is part of RawTherapee.
|
||||
*/
|
||||
#include "spot.h"
|
||||
#include "rtimage.h"
|
||||
#include <iomanip>
|
||||
#include "../rtengine/rt_math.h"
|
||||
#include "guiutils.h"
|
||||
|
||||
using namespace rtengine;
|
||||
using namespace rtengine::procparams;
|
||||
|
||||
#define STATIC_VISIBLE_OBJ_NBR 6
|
||||
#define STATIC_MO_OBJ_NBR 6
|
||||
|
||||
Spot::Spot() : FoldableToolPanel (this, "spot", M ("TP_SPOT_LABEL"), true, true), EditSubscriber (ET_OBJECTS), lastObject (-1), activeSpot (-1),
|
||||
sourceIcon ("spot-normal.png", "spot-active.png", "spot-active.png", "spot-prelight.png", "", Geometry::DP_CENTERCENTER), editedCheckBox(NULL)
|
||||
{
|
||||
countLabel = Gtk::manage (new Gtk::Label (Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0)));
|
||||
|
||||
edit = Gtk::manage (new Gtk::ToggleButton());
|
||||
edit->add (*Gtk::manage (new RTImage ("editmodehand.png")));
|
||||
editConn = edit->signal_toggled().connect ( sigc::mem_fun (*this, &Spot::editToggled) );
|
||||
|
||||
reset = Gtk::manage (new Gtk::Button ());
|
||||
reset->add (*Gtk::manage (new RTImage ("gtk-undo-ltr-small.png", "gtk-undo-rtl-small.png")));
|
||||
reset->set_relief (Gtk::RELIEF_NONE);
|
||||
reset->set_border_width (0);
|
||||
reset->signal_clicked().connect ( sigc::mem_fun (*this, &Spot::resetPressed) );
|
||||
|
||||
labelBox = Gtk::manage (new Gtk::HBox());
|
||||
labelBox->set_spacing (2);
|
||||
labelBox->pack_start (*countLabel, false, false, 0);
|
||||
labelBox->pack_end (*edit, false, false, 0);
|
||||
labelBox->pack_end (*reset, false, false, 0);
|
||||
pack_start (*labelBox);
|
||||
|
||||
sourceIcon.datum = Geometry::IMAGE;
|
||||
sourceIcon.setActive (false);
|
||||
sourceIcon.state = Geometry::ACTIVE;
|
||||
sourceCircle.datum = Geometry::IMAGE;
|
||||
sourceCircle.setActive (false);
|
||||
sourceCircle.radiusInImageSpace = true;
|
||||
sourceMODisc.datum = Geometry::IMAGE;
|
||||
sourceMODisc.setActive (false);
|
||||
sourceMODisc.radiusInImageSpace = true;
|
||||
sourceMODisc.filled = true;
|
||||
sourceMODisc.innerLineWidth = 0.;
|
||||
targetCircle.datum = Geometry::IMAGE;
|
||||
targetCircle.setActive (false);
|
||||
targetCircle.radiusInImageSpace = true;
|
||||
targetMODisc.datum = Geometry::IMAGE;
|
||||
targetMODisc.setActive (false);
|
||||
targetMODisc.radiusInImageSpace = true;
|
||||
targetMODisc.filled = true;
|
||||
targetMODisc.innerLineWidth = 0.;
|
||||
sourceFeatherCircle.datum = Geometry::IMAGE;
|
||||
sourceFeatherCircle.setActive (false);
|
||||
sourceFeatherCircle.radiusInImageSpace = true;
|
||||
targetFeatherCircle.datum = Geometry::IMAGE;
|
||||
targetFeatherCircle.setActive (false);
|
||||
targetFeatherCircle.radiusInImageSpace = true;
|
||||
link.datum = Geometry::IMAGE;
|
||||
link.setActive (false);
|
||||
|
||||
show_all();
|
||||
}
|
||||
|
||||
Spot::~Spot()
|
||||
{
|
||||
// delete all dynamically allocated geometry
|
||||
if (EditSubscriber::visibleGeometry.size()) {
|
||||
for (size_t i = 0; i < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i) { // static visible geometry at the end if the list
|
||||
delete EditSubscriber::visibleGeometry.at (i);
|
||||
}
|
||||
}
|
||||
// We do not delete the mouseOverGeometry, because the referenced objects are either
|
||||
// shared with visibleGeometry or instantiated by the class's ctor
|
||||
}
|
||||
|
||||
void Spot::read (const ProcParams* pp, const ParamsEdited* pedited)
|
||||
{
|
||||
disableListener ();
|
||||
|
||||
size_t oldSize = spots.size();
|
||||
spots = pp->spot.entries;
|
||||
|
||||
if (pedited) {
|
||||
set_inconsistent (multiImage && !pedited->spot.enabled);
|
||||
}
|
||||
|
||||
setEnabled (pp->spot.enabled);
|
||||
lastEnabled = pp->spot.enabled;
|
||||
|
||||
if (spots.size() != oldSize) {
|
||||
createGeometry();
|
||||
}
|
||||
|
||||
updateGeometry();
|
||||
|
||||
enableListener ();
|
||||
}
|
||||
|
||||
void Spot::write (ProcParams* pp, ParamsEdited* pedited)
|
||||
{
|
||||
pp->spot.enabled = getEnabled();
|
||||
pp->spot.entries = spots;
|
||||
|
||||
if (pedited) {
|
||||
pedited->spot.enabled = !get_inconsistent();
|
||||
pedited->spot.entries = !editedCheckBox->get_active();
|
||||
}
|
||||
}
|
||||
|
||||
void Spot::resetPressed()
|
||||
{
|
||||
if (batchMode) {
|
||||
// no need to handle the Geometry in batch mode, since point editing is disabled
|
||||
spots.clear();
|
||||
editedConn.block (true);
|
||||
editedCheckBox->set_active (true);
|
||||
editedConn.block (false);
|
||||
|
||||
if (listener) {
|
||||
listener->panelChanged (EvSpotEntry, Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), spots.size()));
|
||||
}
|
||||
} else {
|
||||
if (!spots.empty()) {
|
||||
spots.clear();
|
||||
activeSpot = -1;
|
||||
lastObject = -1;
|
||||
createGeometry();
|
||||
updateGeometry();
|
||||
|
||||
if (listener) {
|
||||
listener->panelChanged (EvSpotEntry, Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spot::setBatchMode (bool batchMode)
|
||||
{
|
||||
ToolPanel::setBatchMode (batchMode);
|
||||
|
||||
if (batchMode) {
|
||||
removeIfThere (labelBox, edit, false);
|
||||
|
||||
if (!editedCheckBox) {
|
||||
removeIfThere (labelBox, countLabel, false);
|
||||
countLabel = NULL;
|
||||
editedCheckBox = Gtk::manage (new Gtk::CheckButton (Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0)));
|
||||
labelBox->pack_start (*editedCheckBox, Gtk::PACK_SHRINK, 2);
|
||||
labelBox->reorder_child (*editedCheckBox, 0);
|
||||
editedConn = editedCheckBox->signal_toggled().connect ( sigc::mem_fun (*this, &Spot::editedToggled) );
|
||||
editedCheckBox->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spot::editedToggled ()
|
||||
{
|
||||
if (listener) {
|
||||
listener->panelChanged (EvSpotEntry, !editedCheckBox->get_active() ? M ("GENERAL_UNCHANGED") : Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), spots.size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Spot::enabledChanged ()
|
||||
{
|
||||
if (listener) {
|
||||
if (get_inconsistent()) {
|
||||
listener->panelChanged (EvSpotEnabled, M("GENERAL_UNCHANGED"));
|
||||
} else if (getEnabled()) {
|
||||
listener->panelChanged (EvSpotEnabled, M("GENERAL_ENABLED"));
|
||||
} else {
|
||||
listener->panelChanged (EvSpotEnabled, M("GENERAL_DISABLED"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spot::setEditProvider (EditDataProvider* provider)
|
||||
{
|
||||
EditSubscriber::setEditProvider (provider);
|
||||
}
|
||||
|
||||
void Spot::editToggled ()
|
||||
{
|
||||
if (edit->get_active()) {
|
||||
subscribe();
|
||||
} else {
|
||||
unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
Geometry* Spot::getVisibleGeometryFromMO (int MOID)
|
||||
{
|
||||
if (MOID == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (MOID == 0) {
|
||||
return getActiveSpotIcon();
|
||||
}
|
||||
|
||||
if (MOID == 1) { // sourceMODisc
|
||||
return &sourceIcon;
|
||||
}
|
||||
|
||||
return EditSubscriber::mouseOverGeometry.at (MOID);
|
||||
}
|
||||
|
||||
void Spot::createGeometry ()
|
||||
{
|
||||
int nbrEntry = spots.size();
|
||||
|
||||
if (!batchMode) {
|
||||
countLabel->set_text(Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), nbrEntry));
|
||||
}
|
||||
|
||||
//printf("CreateGeometry(%d)\n", nbrEntry);
|
||||
// delete all dynamically allocated geometry
|
||||
if (EditSubscriber::visibleGeometry.size() > STATIC_VISIBLE_OBJ_NBR)
|
||||
for (size_t i = 0; i < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i) { // static visible geometry at the end if the list
|
||||
delete EditSubscriber::visibleGeometry.at (i);
|
||||
}
|
||||
|
||||
size_t i = 0, j = 0;
|
||||
EditSubscriber::mouseOverGeometry.resize (STATIC_MO_OBJ_NBR + nbrEntry);
|
||||
EditSubscriber::visibleGeometry.resize (nbrEntry + STATIC_VISIBLE_OBJ_NBR);
|
||||
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &targetMODisc; // STATIC_MO_OBJ_NBR + 0
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &sourceMODisc; // STATIC_MO_OBJ_NBR + 1
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &targetCircle; // STATIC_MO_OBJ_NBR + 2
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &sourceCircle; // STATIC_MO_OBJ_NBR + 3
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &targetFeatherCircle; // STATIC_MO_OBJ_NBR + 4
|
||||
EditSubscriber::mouseOverGeometry.at (i++) = &sourceFeatherCircle; // STATIC_MO_OBJ_NBR + 5
|
||||
|
||||
// recreate all spots geometry
|
||||
Cairo::RefPtr<Cairo::ImageSurface> normalImg = sourceIcon.getNormalImg();
|
||||
Cairo::RefPtr<Cairo::ImageSurface> prelightImg = sourceIcon.getPrelightImg();
|
||||
Cairo::RefPtr<Cairo::ImageSurface> activeImg = sourceIcon.getActiveImg();
|
||||
|
||||
for (; j < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i, ++j) {
|
||||
EditSubscriber::mouseOverGeometry.at (i) = EditSubscriber::visibleGeometry.at (j) = new OPIcon (normalImg, activeImg, prelightImg, Cairo::RefPtr<Cairo::ImageSurface>(NULL), Cairo::RefPtr<Cairo::ImageSurface>(NULL), Geometry::DP_CENTERCENTER);
|
||||
EditSubscriber::visibleGeometry.at (j)->setActive (true);
|
||||
EditSubscriber::visibleGeometry.at (j)->datum = Geometry::IMAGE;
|
||||
EditSubscriber::visibleGeometry.at (j)->state = Geometry::NORMAL;
|
||||
//printf("mouseOverGeometry.at(%d) = %p\n", (unsigned int)i, (void*)EditSubscriber::mouseOverGeometry.at(i));
|
||||
}
|
||||
|
||||
EditSubscriber::visibleGeometry.at (j++) = &sourceIcon; // STATIC_VISIBLE_OBJ_NBR + 0
|
||||
EditSubscriber::visibleGeometry.at (j++) = &sourceFeatherCircle; // STATIC_VISIBLE_OBJ_NBR + 1
|
||||
EditSubscriber::visibleGeometry.at (j++) = &link; // STATIC_VISIBLE_OBJ_NBR + 2
|
||||
EditSubscriber::visibleGeometry.at (j++) = &sourceCircle; // STATIC_VISIBLE_OBJ_NBR + 3
|
||||
EditSubscriber::visibleGeometry.at (j++) = &targetFeatherCircle; // STATIC_VISIBLE_OBJ_NBR + 4
|
||||
EditSubscriber::visibleGeometry.at (j++) = &targetCircle; // STATIC_VISIBLE_OBJ_NBR + 5
|
||||
}
|
||||
|
||||
void Spot::updateGeometry()
|
||||
{
|
||||
EditDataProvider* dataProvider = getEditProvider();
|
||||
|
||||
if (dataProvider) {
|
||||
int imW, imH;
|
||||
dataProvider->getImageSize (imW, imH);
|
||||
|
||||
if (activeSpot > -1) {
|
||||
// Target point circle
|
||||
targetCircle.center = spots.at (activeSpot).targetPos;
|
||||
targetCircle.radius = spots.at (activeSpot).radius;
|
||||
targetCircle.setActive (true);
|
||||
|
||||
// Target point Mouse Over disc
|
||||
targetMODisc.center = targetCircle.center;
|
||||
targetMODisc.radius = targetCircle.radius;
|
||||
targetMODisc.setActive (true);
|
||||
|
||||
// Source point Icon
|
||||
sourceIcon.position = spots.at (activeSpot).sourcePos;
|
||||
sourceIcon.setActive (true);
|
||||
|
||||
// Source point circle
|
||||
sourceCircle.center = spots.at (activeSpot).sourcePos;
|
||||
sourceCircle.radius = spots.at (activeSpot).radius;
|
||||
sourceCircle.setActive (true);
|
||||
|
||||
// Source point Mouse Over disc
|
||||
sourceMODisc.center = sourceCircle.center;
|
||||
sourceMODisc.radius = sourceCircle.radius;
|
||||
sourceMODisc.setActive (true);
|
||||
|
||||
// Target point feather circle
|
||||
targetFeatherCircle.center = spots.at (activeSpot).targetPos;
|
||||
targetFeatherCircle.radius = float (spots.at (activeSpot).radius) * (1.f + spots.at (activeSpot).feather);
|
||||
targetFeatherCircle.radiusInImageSpace = true;
|
||||
targetFeatherCircle.setActive (true);
|
||||
|
||||
// Source point feather circle
|
||||
sourceFeatherCircle.center = spots.at (activeSpot).sourcePos;
|
||||
sourceFeatherCircle.radius = targetFeatherCircle.radius;
|
||||
sourceFeatherCircle.setActive (true);
|
||||
|
||||
// Link line
|
||||
PolarCoord p;
|
||||
p = targetCircle.center - sourceCircle.center;
|
||||
|
||||
if (p.radius > sourceCircle.radius + targetCircle.radius) {
|
||||
PolarCoord p2 (sourceCircle.radius, p.angle);
|
||||
Coord p3;
|
||||
p3 = p2;
|
||||
link.begin = sourceCircle.center + p3;
|
||||
p2.set (targetCircle.radius, p.angle + 180);
|
||||
p3 = p2;
|
||||
link.end = targetCircle.center + p3;
|
||||
link.setActive (true);
|
||||
} else {
|
||||
link.setActive (false);
|
||||
}
|
||||
} else {
|
||||
targetCircle.setActive (false);
|
||||
targetMODisc.setActive (false);
|
||||
sourceIcon.setActive (false);
|
||||
sourceCircle.setActive (false);
|
||||
sourceMODisc.setActive (false);
|
||||
targetFeatherCircle.setActive (false);
|
||||
sourceFeatherCircle.setActive (false);
|
||||
link.setActive (false);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < spots.size(); ++i) {
|
||||
// Target point icon
|
||||
OPIcon* geom = static_cast<OPIcon*> (EditSubscriber::visibleGeometry.at (i));
|
||||
geom->position = spots.at (i).targetPos;
|
||||
geom->setActive (true);
|
||||
|
||||
if (int (i) == activeSpot) {
|
||||
geom->setHoverable (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OPIcon *Spot::getActiveSpotIcon()
|
||||
{
|
||||
if (activeSpot > -1) {
|
||||
return static_cast<OPIcon*> (EditSubscriber::visibleGeometry.at (activeSpot));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Spot::addNewEntry()
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
// we create a new entry
|
||||
SpotEntry se;
|
||||
se.targetPos = editProvider->posImage;
|
||||
se.sourcePos = se.targetPos;
|
||||
spots.push_back (se); // this make a copy of se ...
|
||||
activeSpot = spots.size() - 1;
|
||||
lastObject = 1;
|
||||
|
||||
//printf("ActiveSpot = %d\n", activeSpot);
|
||||
|
||||
createGeometry();
|
||||
updateGeometry();
|
||||
EditSubscriber::visibleGeometry.at (activeSpot)->state = Geometry::ACTIVE;
|
||||
sourceIcon.state = Geometry::DRAGGED;
|
||||
// TODO: find a way to disable the active spot's Mouse Over geometry but still displaying its location...
|
||||
|
||||
if (listener) {
|
||||
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
||||
}
|
||||
}
|
||||
|
||||
void Spot::deleteSelectedEntry()
|
||||
{
|
||||
// delete the activeSpot
|
||||
if (activeSpot > -1) {
|
||||
std::vector<rtengine::SpotEntry>::iterator i = spots.begin();
|
||||
for (int j = 0; j < activeSpot; ++j) {
|
||||
++i;
|
||||
}
|
||||
spots.erase (i);
|
||||
}
|
||||
|
||||
lastObject = -1;
|
||||
activeSpot = -1;
|
||||
|
||||
createGeometry();
|
||||
updateGeometry();
|
||||
|
||||
if (listener) {
|
||||
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
CursorShape Spot::getCursor (const int objectID)
|
||||
{
|
||||
return CSOpenHand;
|
||||
}
|
||||
|
||||
bool Spot::mouseOver (const int modifierKey)
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
|
||||
if (editProvider && editProvider->object != lastObject) {
|
||||
if (lastObject > -1) {
|
||||
if (EditSubscriber::mouseOverGeometry.at (lastObject) == &targetMODisc) {
|
||||
getVisibleGeometryFromMO (lastObject)->state = Geometry::ACTIVE;
|
||||
} else {
|
||||
getVisibleGeometryFromMO (lastObject)->state = Geometry::NORMAL;
|
||||
}
|
||||
|
||||
sourceIcon.state = Geometry::ACTIVE;
|
||||
}
|
||||
|
||||
if (editProvider->object > -1) {
|
||||
getVisibleGeometryFromMO (editProvider->object)->state = Geometry::PRELIGHT;
|
||||
|
||||
if (editProvider->object >= STATIC_MO_OBJ_NBR) {
|
||||
// a Spot is being edited
|
||||
int oldActiveSpot = activeSpot;
|
||||
activeSpot = editProvider->object - STATIC_MO_OBJ_NBR;
|
||||
|
||||
if (activeSpot != oldActiveSpot) {
|
||||
if (oldActiveSpot > -1) {
|
||||
EditSubscriber::visibleGeometry.at (oldActiveSpot)->state = Geometry::NORMAL;
|
||||
EditSubscriber::mouseOverGeometry.at (oldActiveSpot + STATIC_MO_OBJ_NBR)->state = Geometry::NORMAL;
|
||||
}
|
||||
|
||||
EditSubscriber::visibleGeometry.at (activeSpot)->state = Geometry::PRELIGHT;
|
||||
EditSubscriber::mouseOverGeometry.at (activeSpot + STATIC_MO_OBJ_NBR)->state = Geometry::PRELIGHT;
|
||||
//printf("ActiveSpot = %d (was %d before)\n", activeSpot, oldActiveSpot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastObject = editProvider->object;
|
||||
|
||||
if (lastObject > -1 && EditSubscriber::mouseOverGeometry.at (lastObject) == getActiveSpotIcon()) {
|
||||
lastObject = 0; // targetMODisc
|
||||
}
|
||||
|
||||
updateGeometry();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new Target and Source point or start the drag of a Target point under the cursor
|
||||
bool Spot::button1Pressed (const int modifierKey)
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
|
||||
if (editProvider) {
|
||||
if (lastObject == -1 && (modifierKey & GDK_CONTROL_MASK)) {
|
||||
addNewEntry();
|
||||
EditSubscriber::action = ES_ACTION_DRAGGING;
|
||||
return true;
|
||||
} else if (lastObject > -1) {
|
||||
getVisibleGeometryFromMO (lastObject)->state = Geometry::DRAGGED;
|
||||
EditSubscriber::action = ES_ACTION_DRAGGING;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// End the drag of a Target point
|
||||
bool Spot::button1Released()
|
||||
{
|
||||
Geometry *loGeom = getVisibleGeometryFromMO (lastObject);
|
||||
|
||||
if (!loGeom) {
|
||||
EditSubscriber::action = ES_ACTION_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
loGeom->state = Geometry::PRELIGHT;
|
||||
EditSubscriber::action = ES_ACTION_NONE;
|
||||
updateGeometry();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete a point
|
||||
bool Spot::button2Pressed (const int modifierKey)
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
|
||||
if (!editProvider || lastObject == -1 || activeSpot==-1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(modifierKey & (GDK_SHIFT_MASK|GDK_SHIFT_MASK))) {
|
||||
EditSubscriber::action = ES_ACTION_PICKING;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new Target and Source point or start the drag of a Target point under the cursor
|
||||
bool Spot::button3Pressed (const int modifierKey)
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
|
||||
if (!editProvider || lastObject == -1 || activeSpot==-1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((modifierKey & GDK_CONTROL_MASK) && (EditSubscriber::mouseOverGeometry.at (lastObject) == &targetMODisc || lastObject >= STATIC_MO_OBJ_NBR)) {
|
||||
lastObject = 1; // sourceMODisc
|
||||
sourceIcon.state = Geometry::DRAGGED;
|
||||
EditSubscriber::action = ES_ACTION_DRAGGING;
|
||||
return true;
|
||||
} else if (!(modifierKey & (GDK_SHIFT_MASK|GDK_SHIFT_MASK))) {
|
||||
EditSubscriber::action = ES_ACTION_PICKING;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spot::button3Released()
|
||||
{
|
||||
Geometry *loGeom = getVisibleGeometryFromMO (lastObject);
|
||||
|
||||
if (!loGeom) {
|
||||
EditSubscriber::action = ES_ACTION_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
lastObject = -1;
|
||||
sourceIcon.state = Geometry::ACTIVE;
|
||||
updateGeometry();
|
||||
EditSubscriber::action = ES_ACTION_NONE;
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Spot::drag1 (const int modifierKey)
|
||||
{
|
||||
EditDataProvider *editProvider = getEditProvider();
|
||||
int imW, imH;
|
||||
editProvider->getImageSize (imW, imH);
|
||||
bool modified = false;
|
||||
|
||||
//printf("Drag1 / LastObject=%d\n", lastObject);
|
||||
|
||||
Geometry *loGeom = EditSubscriber::mouseOverGeometry.at (lastObject);
|
||||
|
||||
if (loGeom == &sourceMODisc) {
|
||||
//printf("sourceMODisc / deltaPrevImage = %d / %d\n", editProvider->deltaPrevImage.x, editProvider->deltaPrevImage.y);
|
||||
rtengine::Coord currPos = spots.at (activeSpot).sourcePos;
|
||||
spots.at (activeSpot).sourcePos += editProvider->deltaPrevImage;
|
||||
spots.at (activeSpot).sourcePos.clip (imW, imH);
|
||||
|
||||
if (spots.at (activeSpot).sourcePos != currPos) {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
EditSubscriber::mouseOverGeometry.at (activeSpot + STATIC_MO_OBJ_NBR)->state = Geometry::DRAGGED;
|
||||
} else if (loGeom == &targetMODisc || lastObject >= STATIC_MO_OBJ_NBR) {
|
||||
//printf("targetMODisc / deltaPrevImage = %d / %d\n", editProvider->deltaPrevImage.x, editProvider->deltaPrevImage.y);
|
||||
rtengine::Coord currPos = spots.at (activeSpot).targetPos;
|
||||
spots.at (activeSpot).targetPos += editProvider->deltaPrevImage;
|
||||
spots.at (activeSpot).targetPos.clip (imW, imH);
|
||||
|
||||
if (spots.at (activeSpot).targetPos != currPos) {
|
||||
modified = true;
|
||||
}
|
||||
} else if (loGeom == &sourceCircle) {
|
||||
//printf("sourceCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
||||
int lastRadius = spots.at (activeSpot).radius;
|
||||
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
||||
rtengine::PolarCoord currPolar = currPos - spots.at (activeSpot).sourcePos;
|
||||
spots.at (activeSpot).radius = LIM<int> (int (currPolar.radius), SpotParams::minRadius, SpotParams::maxRadius);
|
||||
|
||||
if (spots.at (activeSpot).radius != lastRadius) {
|
||||
modified = true;
|
||||
}
|
||||
} else if (loGeom == &targetCircle) {
|
||||
//printf("targetCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
||||
int lastRadius = spots.at (activeSpot).radius;
|
||||
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
||||
rtengine::PolarCoord currPolar = currPos - spots.at (activeSpot).targetPos;
|
||||
spots.at (activeSpot).radius = LIM<int> (int (currPolar.radius), SpotParams::minRadius, SpotParams::maxRadius);
|
||||
|
||||
if (spots.at (activeSpot).radius != lastRadius) {
|
||||
modified = true;
|
||||
}
|
||||
} else if (loGeom == &sourceFeatherCircle) {
|
||||
//printf("sourceFeatherCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
||||
float currFeather = spots.at (activeSpot).feather;
|
||||
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
||||
rtengine::PolarCoord currPolar = currPos -spots.at (activeSpot).sourcePos;
|
||||
spots.at (activeSpot).feather = LIM01<float> ((currPolar.radius - double (spots.at (activeSpot).radius)) / double (spots.at (activeSpot).radius));
|
||||
|
||||
if (spots.at (activeSpot).feather != currFeather) {
|
||||
modified = true;
|
||||
}
|
||||
} else if (loGeom == &targetFeatherCircle) {
|
||||
//printf("targetFeatherCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
||||
float currFeather = spots.at (activeSpot).feather;
|
||||
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
||||
rtengine::PolarCoord currPolar = currPos - spots.at (activeSpot).targetPos;
|
||||
spots.at (activeSpot).feather = LIM01<float> ((currPolar.radius - double (spots.at (activeSpot).radius)) / double (spots.at (activeSpot).radius));
|
||||
|
||||
if (spots.at (activeSpot).feather != currFeather) {
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (listener && modified) {
|
||||
updateGeometry();
|
||||
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool Spot::drag3 (const int modifierKey)
|
||||
{
|
||||
EditDataProvider *editProvider = getEditProvider();
|
||||
int imW, imH;
|
||||
editProvider->getImageSize (imW, imH);
|
||||
bool modified = false;
|
||||
|
||||
Geometry *loGeom = EditSubscriber::mouseOverGeometry.at (lastObject);
|
||||
|
||||
if (loGeom == &sourceMODisc) {
|
||||
rtengine::Coord currPos = spots.at (activeSpot).sourcePos;
|
||||
spots.at (activeSpot).sourcePos += editProvider->deltaPrevImage;
|
||||
spots.at (activeSpot).sourcePos.clip (imW, imH);
|
||||
|
||||
if (spots.at (activeSpot).sourcePos != currPos) {
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (listener) {
|
||||
updateGeometry();
|
||||
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool Spot::pick2(const bool picked)
|
||||
{
|
||||
return pick3(picked);
|
||||
}
|
||||
|
||||
bool Spot::pick3 (const bool picked)
|
||||
{
|
||||
EditDataProvider* editProvider = getEditProvider();
|
||||
|
||||
if (!picked) {
|
||||
if (editProvider->object != lastObject) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Object is picked, we delete it
|
||||
deleteSelectedEntry();
|
||||
EditSubscriber::action = ES_ACTION_NONE;
|
||||
updateGeometry();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Spot::switchOffEditMode ()
|
||||
{
|
||||
if (edit->get_active()) {
|
||||
// switching off the toggle button
|
||||
bool wasBlocked = editConn.block (true);
|
||||
edit->set_active (false);
|
||||
|
||||
if (!wasBlocked) {
|
||||
editConn.block (false);
|
||||
}
|
||||
}
|
||||
|
||||
EditSubscriber::switchOffEditMode(); // disconnect
|
||||
}
|
||||
|
||||
101
rtgui/spot.h
Normal file
101
rtgui/spot.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* This file is part of RawTherapee.
|
||||
*/
|
||||
#ifndef _SPOT_H_
|
||||
#define _SPOT_H_
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include "toolpanel.h"
|
||||
#include "edit.h"
|
||||
|
||||
/**
|
||||
* @brief Let the user create/edit/delete points for Spot Removal tool
|
||||
*
|
||||
* User Interface:
|
||||
*
|
||||
* For the rest of this documentation, T represent a "target" point (where the image is edited) and
|
||||
* S represent the "source" location (where the edition takes its source data).
|
||||
*
|
||||
* When the edit button is active, all T points are shown by a small "dot". When the user
|
||||
* move the cursor over one of them, a circle is displayed to show the radius of the brush, as well
|
||||
* as a second circle representing the source data (S point). The user can then use the left mouse button
|
||||
* over the icon to drag the T point. The left mouse button can be used over the S circle or the right
|
||||
* mouse button can be used over the T point to move the S point.
|
||||
*
|
||||
* Using the left mouse button over the circle of the T point will let the user adjust its radius.
|
||||
*
|
||||
* Using the left mouse button over the feather circle will let the user adjust its radius by setting
|
||||
* a coefficient (0.0 = same radius than the inner circle ; 1.0 = 2 times the inner radius).
|
||||
*
|
||||
* To create a new point, just move over a free area, and press the left mouse button while holding
|
||||
* the CTRL key. This will create a new S and T pair of points. The CTRL key can be released, but keep
|
||||
* the left mouse button pressed and move away to position the S point.
|
||||
*
|
||||
* To delete a point, move your mouse over any of its geometry press the middle or right mouse button
|
||||
* (the point will be deleted on button release).
|
||||
*/
|
||||
class Spot : public ToolParamBlock, public FoldableToolPanel, public EditSubscriber
|
||||
{
|
||||
|
||||
private:
|
||||
int lastObject; // current object that is hovered
|
||||
int activeSpot; // currently active spot, being edited
|
||||
std::vector<rtengine::SpotEntry> spots; // list of edited spots
|
||||
OPIcon sourceIcon; // to show the source location
|
||||
Circle sourceCircle; // to show and change the Source radius
|
||||
Circle sourceMODisc; // to change the Source position
|
||||
Circle targetCircle; // to show and change the Target radius
|
||||
Circle targetMODisc; // to change the Target position
|
||||
Circle sourceFeatherCircle; // to show the Feather radius at the Source position
|
||||
Circle targetFeatherCircle; // to show the Feather radius at the Target position
|
||||
Line link; // to show the link between the Source and Target position
|
||||
|
||||
OPIcon *getActiveSpotIcon ();
|
||||
void updateGeometry ();
|
||||
void createGeometry ();
|
||||
void addNewEntry ();
|
||||
void deleteSelectedEntry ();
|
||||
void resetPressed ();
|
||||
|
||||
protected:
|
||||
Gtk::HBox* labelBox;
|
||||
Gtk::CheckButton* editedCheckBox;
|
||||
Gtk::Label* countLabel;
|
||||
Gtk::ToggleButton* edit;
|
||||
Gtk::Button* reset;
|
||||
sigc::connection editConn, editedConn;
|
||||
|
||||
void editToggled ();
|
||||
void editedToggled ();
|
||||
Geometry* getVisibleGeometryFromMO (int MOID);
|
||||
|
||||
public:
|
||||
|
||||
Spot ();
|
||||
~Spot ();
|
||||
|
||||
void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = NULL);
|
||||
void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = NULL);
|
||||
|
||||
void enabledChanged ();
|
||||
|
||||
void setEditProvider (EditDataProvider* provider);
|
||||
|
||||
void setBatchMode (bool batchMode);
|
||||
|
||||
// EditSubscriber interface
|
||||
CursorShape getCursor (const int objectID);
|
||||
bool mouseOver (const int modifierKey);
|
||||
bool button1Pressed (const int modifierKey);
|
||||
bool button1Released ();
|
||||
bool button2Pressed (const int modifierKey);
|
||||
bool button3Pressed (const int modifierKey);
|
||||
bool button3Released ();
|
||||
bool drag1 (const int modifierKey);
|
||||
bool drag3 (const int modifierKey);
|
||||
bool pick2 (const bool picked);
|
||||
bool pick3 (const bool picked);
|
||||
void switchOffEditMode ();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -39,8 +39,8 @@ public:
|
||||
Job(ThumbBrowserEntryBase* tbe, bool* priority, bool upgrade,
|
||||
ThumbImageUpdateListener* listener):
|
||||
tbe_(tbe),
|
||||
/*pparams_(pparams),
|
||||
height_(height), */
|
||||
/*pparams_(pparams),
|
||||
height_(height), */
|
||||
priority_(priority),
|
||||
upgrade_(upgrade),
|
||||
listener_(listener)
|
||||
|
||||
@@ -43,6 +43,7 @@ ToolPanelCoordinator::ToolPanelCoordinator () : ipc(nullptr), editDataProvider(n
|
||||
shadowshighlights = Gtk::manage (new ShadowsHighlights ());
|
||||
impulsedenoise = Gtk::manage (new ImpulseDenoise ());
|
||||
defringe = Gtk::manage (new Defringe ());
|
||||
spot = Gtk::manage (new Spot ());
|
||||
dirpyrdenoise = Gtk::manage (new DirPyrDenoise ());
|
||||
epd = Gtk::manage (new EdgePreservingDecompositionUI ());
|
||||
sharpening = Gtk::manage (new Sharpening ());
|
||||
@@ -110,6 +111,8 @@ ToolPanelCoordinator::ToolPanelCoordinator () : ipc(nullptr), editDataProvider(n
|
||||
toolPanels.push_back (blackwhite);
|
||||
addPanel (exposurePanel, shadowshighlights);
|
||||
toolPanels.push_back (shadowshighlights);
|
||||
addPanel (detailsPanel, spot);
|
||||
toolPanels.push_back (spot);
|
||||
addPanel (detailsPanel, sharpening);
|
||||
toolPanels.push_back (sharpening);
|
||||
addPanel (detailsPanel, sharpenEdge);
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "vignetting.h"
|
||||
#include "retinex.h"
|
||||
#include "gradient.h"
|
||||
#include "spot.h"
|
||||
#include "pcvignette.h"
|
||||
#include "toolbar.h"
|
||||
#include "lensgeom.h"
|
||||
@@ -118,6 +119,7 @@ protected:
|
||||
Crop* crop;
|
||||
ToneCurve* toneCurve;
|
||||
ShadowsHighlights* shadowshighlights;
|
||||
Spot* spot;
|
||||
Defringe* defringe;
|
||||
ImpulseDenoise* impulsedenoise;
|
||||
DirPyrDenoise* dirpyrdenoise;
|
||||
|
||||
Reference in New Issue
Block a user