1 /*
2 * This file is part of RawTherapee.
3 *
4 * Copyright (c) 2019 Jean-Christophe FRISCH <natureh.510@gmail.com>
5 *
6 * RawTherapee is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * RawTherapee is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "edit.h"
21 #include "spot.h"
22 #include "rtimage.h"
23 #include <iomanip>
24 #include "../rtengine/rt_math.h"
25 #include "guiutils.h"
26 #include "eventmapper.h"
27 #include "../rtengine/refreshmap.h"
28
29 using namespace rtengine;
30 using namespace rtengine::procparams;
31
32 namespace {
33
34 constexpr int STATIC_VISIBLE_OBJ_NBR = 6;
35 constexpr int STATIC_MO_OBJ_NBR = 7;
36
37 constexpr int SOURCE_DISC = 2;
38 constexpr int TARGET_DISC = 1;
39
40 } // namespace
41
42
Spot()43 Spot::Spot() :
44 FoldableToolPanel(this, "spot", M ("TP_SPOT_LABEL"), false, true, true),
45 EditSubscriber(ET_OBJECTS),
46 draggedSide(DraggedSide::NONE),
47 lastObject(-1),
48 activeSpot(-1),
49 sourceIcon("spot-normal.png", "spot-active.png", "spot-prelight.png", "", "", Geometry::DP_CENTERCENTER),
50 targetIcon("spot-normal-target.png", "spot-active-target.png", "spot-prelight-target.png", "", "", Geometry::DP_CENTERCENTER),
51 editedCheckBox(nullptr)
52 {
53 countLabel = Gtk::manage (new Gtk::Label (Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0)));
54
55 edit = Gtk::manage (new Gtk::ToggleButton());
56 edit->add (*Gtk::manage (new RTImage ("edit-point.png")));
57 editConn = edit->signal_toggled().connect ( sigc::mem_fun (*this, &Spot::editToggled) );
58 edit->set_tooltip_text(M("TP_SPOT_HINT"));
59
60 reset = Gtk::manage (new Gtk::Button ());
61 reset->add (*Gtk::manage (new RTImage ("undo-small.png")));
62 reset->set_relief (Gtk::RELIEF_NONE);
63 reset->set_border_width (0);
64 reset->signal_clicked().connect ( sigc::mem_fun (*this, &Spot::resetPressed) );
65
66 labelBox = Gtk::manage (new Gtk::HBox());
67 labelBox->set_spacing (2);
68 labelBox->pack_start (*countLabel, false, false, 0);
69 labelBox->pack_end (*edit, false, false, 0);
70 labelBox->pack_end (*reset, false, false, 0);
71 pack_start (*labelBox);
72
73 sourceIcon.datum = Geometry::IMAGE;
74 sourceIcon.setActive (false);
75 sourceIcon.state = Geometry::ACTIVE;
76 sourceCircle.datum = Geometry::IMAGE;
77 sourceCircle.setActive (false);
78 sourceCircle.radiusInImageSpace = true;
79 sourceCircle.setDashed(true);
80 sourceMODisc.datum = Geometry::IMAGE;
81 sourceMODisc.setActive (false);
82 sourceMODisc.radiusInImageSpace = true;
83 sourceMODisc.filled = true;
84 sourceMODisc.innerLineWidth = 0.;
85 targetCircle.datum = Geometry::IMAGE;
86 targetCircle.setActive (false);
87 targetCircle.radiusInImageSpace = true;
88 targetMODisc.datum = Geometry::IMAGE;
89 targetMODisc.setActive (false);
90 targetMODisc.radiusInImageSpace = true;
91 targetMODisc.filled = true;
92 targetMODisc.innerLineWidth = 0.;
93 sourceFeatherCircle.datum = Geometry::IMAGE;
94 sourceFeatherCircle.setActive (false);
95 sourceFeatherCircle.radiusInImageSpace = true;
96 sourceFeatherCircle.setDashed(true);
97 sourceFeatherCircle.innerLineWidth = 0.7;
98 targetFeatherCircle.datum = Geometry::IMAGE;
99 targetFeatherCircle.setActive (false);
100 targetFeatherCircle.radiusInImageSpace = true;
101 targetFeatherCircle.innerLineWidth = 0.7;
102 link.datum = Geometry::IMAGE;
103 link.setActive (false);
104
105 Rectangle *rect = new Rectangle();
106 whole_area_rectangle.reset(rect);
107 rect->filled = true;
108 rect->setActive(true);
109 rect->datum = Geometry::IMAGE;
110
111 auto m = ProcEventMapper::getInstance();
112 EvSpotEnabled = m->newEvent(ALLNORAW, "TP_SPOT_LABEL");
113 EvSpotEnabledOPA = m->newEvent(SPOTADJUST, "");
114 EvSpotEntry = m->newEvent(SPOTADJUST, "HISTORY_MSG_SPOT_ENTRY");
115 EvSpotEntryOPA = m->newEvent(SPOTADJUST, "HISTORY_MSG_SPOT_ENTRY");
116 EvToolReset.set_action(ALLNORAW);
117
118 spot_frame = Gtk::manage(new Gtk::Frame(M("TP_SPOT_CUR_SPOT_LABEL")));
119 Gtk::VBox *vb = Gtk::manage(new Gtk::VBox());
120 const auto mkadj =
121 [](const Glib::ustring &lbl, double vmin, double vmax, double vstep, double vdefault) -> Adjuster *
122 {
123 return Gtk::manage(new Adjuster(lbl, vmin, vmax, vstep, vdefault, nullptr, nullptr, nullptr, nullptr, false, true));
124 };
125 source_x = mkadj(M("TP_SPOT_SOURCE_X") + " ", 0, 10000, 1, 0);
126 source_y = mkadj(M("TP_SPOT_SOURCE_Y") + " ", 0, 10000, 1, 0);
127 target_y = mkadj(M("TP_SPOT_TARGET_Y") + " ", 0, 10000, 1, 0);
128 target_x = mkadj(M("TP_SPOT_TARGET_X") + " ", 0, 10000, 1, 0);
129 radius = mkadj(M("TP_SPOT_RADIUS") + " ", SpotParams::minRadius, SpotParams::maxRadius, 1, SpotParams::minRadius);
130 feather = mkadj(M("TP_SPOT_FEATHER") + " ", 0, 1, 0.01, 0);
131 opacity = mkadj(M("TP_SPOT_OPACITY") + " ", 0, 1, 0.01, 0);
132 detail = mkadj(M("TP_SPOT_DETAIL") + " ", 0, 5, 1, 0);
133
134 spot_adjusters = {
135 source_x,
136 source_y,
137 target_x,
138 target_y,
139 radius,
140 feather,
141 opacity,
142 detail
143 };
144
145 for (auto a : spot_adjusters) {
146 a->setAdjusterListener(this);
147 vb->pack_start(*a, Gtk::PACK_SHRINK, 2);
148 }
149
150 spot_frame->add(*vb);
151 pack_start(*spot_frame);
152
153 getExpander()->signal_button_release_event().connect_notify(sigc::mem_fun(this, &Spot::on_fold));
154 signal_unmap().connect(sigc::mem_fun(*this, &Spot::on_hide));
155
156 show_all();
157 }
158
~Spot()159 Spot::~Spot()
160 {
161 // delete all dynamically allocated geometry
162 if (EditSubscriber::visibleGeometry.size()) {
163 for (size_t i = 0; i < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i) { // static visible geometry at the end if the list
164 delete EditSubscriber::visibleGeometry.at (i);
165 }
166 }
167
168 // We do not delete the mouseOverGeometry, because the referenced objects are either
169 // shared with visibleGeometry or instantiated by the class's ctor
170 }
171
172
read(const ProcParams * pp)173 void Spot::read (const ProcParams *pp)
174 {
175 disableListener ();
176
177 spots = pp->spot.entries;
178 setEnabled (pp->spot.enabled);
179 lastEnabled = pp->spot.enabled;
180 activeSpot = -1;
181 lastObject = -1;
182
183 createGeometry();
184 updateGeometry();
185
186 enableListener ();
187 }
188
189
write(ProcParams * pp)190 void Spot::write(ProcParams *pp)
191 {
192 pp->spot.enabled = getEnabled();
193 pp->spot.entries = spots;
194 }
195
196
resetPressed()197 void Spot::resetPressed()
198 {
199 if (!spots.empty()) {
200 spots.clear();
201 activeSpot = -1;
202 lastObject = -1;
203 createGeometry();
204 updateGeometry();
205
206 if (listener) {
207 listener->panelChanged (edit->get_active() ? EvSpotEntryOPA : EvSpotEntry, Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0));
208 }
209 }
210 }
211
212
editedToggled()213 void Spot::editedToggled ()
214 {
215 if (listener) {
216 listener->panelChanged (EvSpotEntry, !editedCheckBox->get_active() ? M ("GENERAL_UNCHANGED") : Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), spots.size()));
217 }
218 }
219
enabledChanged()220 void Spot::enabledChanged ()
221 {
222 if (listener) {
223 if (get_inconsistent()) {
224 listener->panelChanged (edit->get_active() ? EvSpotEnabledOPA : EvSpotEnabled, M ("GENERAL_UNCHANGED"));
225 } else if (getEnabled()) {
226 listener->panelChanged (edit->get_active() ? EvSpotEnabledOPA : EvSpotEnabled, M ("GENERAL_ENABLED"));
227 } else {
228 listener->panelChanged (edit->get_active() ? EvSpotEnabledOPA : EvSpotEnabled, M ("GENERAL_DISABLED"));
229 }
230 }
231 }
232
setEditProvider(EditDataProvider * provider)233 void Spot::setEditProvider(EditDataProvider* provider)
234 {
235 EditSubscriber::setEditProvider(provider);
236 if (provider) {
237 int imW, imH;
238 provider->getImageSize(imW, imH);
239
240 static_cast<Rectangle *>(whole_area_rectangle.get())->bottomRight.x = imW;
241 static_cast<Rectangle *>(whole_area_rectangle.get())->bottomRight.y = imH;
242 }
243 }
244
editToggled()245 void Spot::editToggled ()
246 {
247 if (listener) {
248 if (edit->get_active()) {
249 listener->setTweakOperator(this);
250 listener->refreshPreview(EvSpotEnabledOPA); // reprocess the preview w/o creating History entry
251 subscribe();
252 } else {
253 reset_adjusters();
254 unsubscribe();
255 listener->unsetTweakOperator(this);
256 listener->refreshPreview(EvSpotEnabled); // reprocess the preview w/o creating History entry
257 }
258 }
259 }
260
getVisibleGeometryFromMO(int MOID)261 Geometry* Spot::getVisibleGeometryFromMO (int MOID)
262 {
263 if (MOID == -1) {
264 return nullptr;
265 }
266
267 if (MOID == 1) {
268 return getActiveSpotIcon();
269 }
270
271 if (MOID == SOURCE_DISC) { // sourceMODisc
272 return &sourceIcon;
273 }
274
275 if (MOID > STATIC_MO_OBJ_NBR) {
276 return EditSubscriber::visibleGeometry.at(MOID - STATIC_MO_OBJ_NBR);
277 }
278
279 return EditSubscriber::mouseOverGeometry.at(MOID);
280 }
281
createGeometry()282 void Spot::createGeometry ()
283 {
284 int nbrEntry = spots.size();
285 countLabel->set_text (Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), nbrEntry));
286
287 //printf("CreateGeometry(%d)\n", nbrEntry);
288 // delete all dynamically allocated geometry
289 if (EditSubscriber::visibleGeometry.size() > STATIC_VISIBLE_OBJ_NBR)
290 for (size_t i = 0; i < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i) { // static visible geometry at the end if the list
291 delete EditSubscriber::visibleGeometry.at (i);
292 }
293
294 // mouse over geometry starts with the static geometry, then the spot's icon geometry
295 EditSubscriber::mouseOverGeometry.resize (STATIC_MO_OBJ_NBR + nbrEntry);
296 // visible geometry starts with the spot's icon geometry, then the static geometry
297 EditSubscriber::visibleGeometry.resize (nbrEntry + STATIC_VISIBLE_OBJ_NBR);
298
299 size_t i = 0, j = 0;
300 mouseOverGeometry[i++] = whole_area_rectangle.get();
301 mouseOverGeometry[i++] = &targetMODisc;
302 mouseOverGeometry[i++] = &sourceMODisc;
303 mouseOverGeometry[i++] = &targetCircle;
304 mouseOverGeometry[i++] = &sourceCircle;
305 mouseOverGeometry[i++] = &targetFeatherCircle;
306 mouseOverGeometry[i++] = &sourceFeatherCircle;
307
308 // recreate all spots geometry
309 Cairo::RefPtr<RTSurface> normalImg = targetIcon.getNormalImg();
310 Cairo::RefPtr<RTSurface> prelightImg = targetIcon.getPrelightImg();
311 Cairo::RefPtr<RTSurface> activeImg = targetIcon.getActiveImg();
312
313 for (; j < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i, ++j) {
314 EditSubscriber::mouseOverGeometry.at (i) = EditSubscriber::visibleGeometry.at (j) = new OPIcon (normalImg, activeImg, prelightImg, Cairo::RefPtr<RTSurface> (nullptr), Cairo::RefPtr<RTSurface> (nullptr), Geometry::DP_CENTERCENTER);
315 EditSubscriber::visibleGeometry.at (j)->setActive (true);
316 EditSubscriber::visibleGeometry.at (j)->datum = Geometry::IMAGE;
317 EditSubscriber::visibleGeometry.at (j)->state = Geometry::NORMAL;
318 //printf("mouseOverGeometry.at(%d) = %p\n", (unsigned int)i, (void*)EditSubscriber::mouseOverGeometry.at(i));
319 }
320
321 visibleGeometry[j++] = &sourceIcon;
322 visibleGeometry[j++] = &sourceFeatherCircle;
323 visibleGeometry[j++] = &link;
324 visibleGeometry[j++] = &sourceCircle;
325 visibleGeometry[j++] = &targetFeatherCircle;
326 visibleGeometry[j++] = &targetCircle;
327 }
328
updateGeometry()329 void Spot::updateGeometry()
330 {
331 EditDataProvider* dataProvider = getEditProvider();
332
333 if (dataProvider) {
334 int imW, imH;
335 dataProvider->getImageSize (imW, imH);
336
337 static_cast<Rectangle *>(whole_area_rectangle.get())->bottomRight.x = imW;
338 static_cast<Rectangle *>(whole_area_rectangle.get())->bottomRight.y = imH;
339 source_x->setLimits(0, imW, 1, 0);
340 target_x->setLimits(0, imW, 1, 0);
341 source_y->setLimits(0, imH, 1, 0);
342 target_y->setLimits(0, imH, 1, 0);
343
344 if (activeSpot > -1) {
345 // Target point circle
346 targetCircle.center = spots.at (activeSpot).targetPos;
347 targetCircle.radius = spots.at (activeSpot).radius;
348 targetCircle.setActive (true);
349
350 // Target point Mouse Over disc
351 targetMODisc.center = targetCircle.center;
352 targetMODisc.radius = targetCircle.radius;
353 targetMODisc.setActive (true);
354
355 // Source point Icon
356 sourceIcon.position = spots.at (activeSpot).sourcePos;
357 sourceIcon.setActive (true);
358
359 // Source point circle
360 sourceCircle.center = spots.at (activeSpot).sourcePos;
361 sourceCircle.radius = spots.at (activeSpot).radius;
362 sourceCircle.setActive (true);
363
364 // Source point Mouse Over disc
365 sourceMODisc.center = sourceCircle.center;
366 sourceMODisc.radius = sourceCircle.radius;
367 sourceMODisc.setActive (true);
368
369 // Target point feather circle
370 targetFeatherCircle.center = spots.at (activeSpot).targetPos;
371 targetFeatherCircle.radius = float (spots.at (activeSpot).radius) * (1.f + spots.at (activeSpot).feather);
372 targetFeatherCircle.radiusInImageSpace = true;
373 targetFeatherCircle.setActive (true);
374
375 // Source point feather circle
376 sourceFeatherCircle.center = spots.at (activeSpot).sourcePos;
377 sourceFeatherCircle.radius = targetFeatherCircle.radius;
378 sourceFeatherCircle.setActive (true);
379
380 // Link line
381 PolarCoord p;
382 p = targetCircle.center - sourceCircle.center;
383
384 if (p.radius > sourceCircle.radius + targetCircle.radius) {
385 PolarCoord p2 (sourceCircle.radius, p.angle);
386 Coord p3;
387 p3 = p2;
388 link.begin = sourceCircle.center + p3;
389 p2.set (targetCircle.radius, p.angle + 180);
390 p3 = p2;
391 link.end = targetCircle.center + p3;
392 link.setActive (true);
393 } else {
394 link.setActive (false);
395 }
396
397 sourceCircle.setVisible(draggedSide != DraggedSide::SOURCE);
398 targetCircle.setVisible(draggedSide != DraggedSide::TARGET);
399 link.setVisible(draggedSide == DraggedSide::NONE);
400
401 {
402 auto &s = spots[activeSpot];
403 spot_frame->set_sensitive(true);
404 source_x->setValue(s.sourcePos.x);
405 source_y->setValue(s.sourcePos.y);
406 target_x->setValue(s.targetPos.x);
407 target_y->setValue(s.targetPos.y);
408 radius->setValue(s.radius);
409 feather->setValue(s.feather);
410 opacity->setValue(s.opacity);
411 detail->setValue(s.detail);
412 }
413 } else {
414 targetCircle.setActive (false);
415 targetMODisc.setActive (false);
416 sourceIcon.setActive (false);
417 sourceCircle.setActive (false);
418 sourceMODisc.setActive (false);
419 targetFeatherCircle.setActive (false);
420 sourceFeatherCircle.setActive (false);
421 link.setActive (false);
422
423 reset_adjusters();
424 }
425
426 for (size_t i = 0; i < spots.size(); ++i) {
427 // Target point icon
428 OPIcon* geom = static_cast<OPIcon*> (EditSubscriber::visibleGeometry.at (i));
429 geom->position = spots.at (i).targetPos;
430 geom->setActive (true);
431
432 if (int (i) == activeSpot) {
433 geom->setHoverable (false);
434 }
435 }
436 } else {
437 reset_adjusters();
438 }
439 }
440
441
getActiveSpotIcon()442 OPIcon *Spot::getActiveSpotIcon()
443 {
444 if (activeSpot > -1) {
445 return static_cast<OPIcon*> (EditSubscriber::visibleGeometry.at (activeSpot));
446 }
447
448 return nullptr;
449 }
450
addNewEntry()451 void Spot::addNewEntry()
452 {
453 EditDataProvider* editProvider = getEditProvider();
454 // we create a new entry
455 SpotEntry se;
456 se.targetPos = editProvider->posImage;
457 se.sourcePos = se.targetPos;
458 spots.push_back (se); // this make a copy of se ...
459 activeSpot = spots.size() - 1;
460 lastObject = 1;
461
462 //printf("ActiveSpot = %d\n", activeSpot);
463
464 createGeometry();
465 updateGeometry();
466 EditSubscriber::visibleGeometry.at (activeSpot)->state = Geometry::ACTIVE;
467 sourceIcon.state = Geometry::DRAGGED;
468 // TODO: find a way to disable the active spot's Mouse Over geometry but still displaying its location...
469
470 if (listener) {
471 listener->panelChanged (EvSpotEntryOPA, M ("TP_SPOT_ENTRYCHANGED"));
472 }
473 }
474
deleteSelectedEntry()475 void Spot::deleteSelectedEntry()
476 {
477 // delete the activeSpot
478 if (activeSpot > -1) {
479 std::vector<rtengine::procparams::SpotEntry>::iterator i = spots.begin();
480
481 for (int j = 0; j < activeSpot; ++j) {
482 ++i;
483 }
484
485 spots.erase (i);
486 }
487
488 lastObject = -1;
489 activeSpot = -1;
490
491 createGeometry();
492 updateGeometry();
493
494 if (listener) {
495 listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
496 }
497 }
498
getCursor(int objectID,int xPos,int yPos)499 CursorShape Spot::getCursor(int objectID, int xPos, int yPos)
500 {
501 EditDataProvider* editProvider = getEditProvider();
502 if (editProvider) {
503 if (draggedSide != DraggedSide::NONE) {
504 return CSEmpty;
505 }
506
507 if (objectID == TARGET_DISC || objectID == SOURCE_DISC) {
508 return CSMove2D;
509 }
510 if (objectID >= 3 && objectID <= 6 && activeSpot > -1) {
511 Coord delta(Coord(xPos, yPos) - ((objectID == 4 || objectID == 6) ? spots.at(activeSpot).sourcePos : spots.at(activeSpot).targetPos));
512 PolarCoord polarPos(delta);
513 if (polarPos.angle < 0.) {
514 polarPos.angle += 180.;
515 }
516 if (polarPos.angle < 22.5 || polarPos.angle >= 157.5) {
517 return CSMove1DH;
518 }
519 if (polarPos.angle < 67.5) {
520 return CSResizeBottomRight;
521 }
522 if (polarPos.angle < 112.5) {
523 return CSMove1DV;
524 }
525 return CSResizeBottomLeft;
526 }
527 }
528 return CSCrosshair;
529 }
530
mouseOver(int modifierKey)531 bool Spot::mouseOver (int modifierKey)
532 {
533 EditDataProvider* editProvider = getEditProvider();
534
535 if (editProvider && editProvider->getObject() != lastObject) {
536 if (lastObject > -1) {
537 if (EditSubscriber::mouseOverGeometry.at (lastObject) == &targetMODisc) {
538 getVisibleGeometryFromMO (lastObject)->state = Geometry::ACTIVE;
539 } else {
540 getVisibleGeometryFromMO (lastObject)->state = Geometry::NORMAL;
541 }
542
543 sourceIcon.state = Geometry::ACTIVE;
544 }
545
546 if (editProvider->getObject() > -1) {
547 getVisibleGeometryFromMO (editProvider->getObject())->state = Geometry::PRELIGHT;
548
549 if (editProvider->getObject() >= STATIC_MO_OBJ_NBR) {
550 // a Spot is being edited
551 int oldActiveSpot = activeSpot;
552 activeSpot = editProvider->getObject() - STATIC_MO_OBJ_NBR;
553
554 if (activeSpot != oldActiveSpot) {
555 if (oldActiveSpot > -1) {
556 EditSubscriber::visibleGeometry.at (oldActiveSpot)->state = Geometry::NORMAL;
557 EditSubscriber::mouseOverGeometry.at (oldActiveSpot + STATIC_MO_OBJ_NBR)->state = Geometry::NORMAL;
558 }
559
560 EditSubscriber::visibleGeometry.at (activeSpot)->state = Geometry::PRELIGHT;
561 EditSubscriber::mouseOverGeometry.at (activeSpot + STATIC_MO_OBJ_NBR)->state = Geometry::PRELIGHT;
562 //printf("ActiveSpot = %d (was %d before)\n", activeSpot, oldActiveSpot);
563 }
564 }
565 }
566
567 lastObject = editProvider->getObject();
568 if (lastObject == 0) {
569 lastObject = -1;
570 }
571
572 if (lastObject > -1 && EditSubscriber::mouseOverGeometry.at (lastObject) == getActiveSpotIcon()) {
573 lastObject = TARGET_DISC;
574 }
575
576 updateGeometry();
577 return true;
578 }
579
580 return false;
581 }
582
583 // Create a new Target and Source point or start the drag of a Target point under the cursor
button1Pressed(int modifierKey)584 bool Spot::button1Pressed (int modifierKey)
585 {
586 EditDataProvider* editProvider = getEditProvider();
587
588 if (editProvider) {
589 if (lastObject == -1 && (modifierKey & GDK_CONTROL_MASK)) {
590 draggedSide = DraggedSide::SOURCE;
591 addNewEntry();
592 EditSubscriber::action = ES_ACTION_DRAGGING;
593 return true;
594 } else if (lastObject > -1) {
595 draggedSide = lastObject == TARGET_DISC ? DraggedSide::TARGET : lastObject == SOURCE_DISC ? DraggedSide::SOURCE : DraggedSide::NONE;
596 getVisibleGeometryFromMO (lastObject)->state = Geometry::DRAGGED;
597 EditSubscriber::action = ES_ACTION_DRAGGING;
598 return true;
599 }
600 }
601
602 return false;
603 }
604
605 // End the drag of a Target point
button1Released()606 bool Spot::button1Released()
607 {
608 Geometry *loGeom = getVisibleGeometryFromMO (lastObject);
609
610 if (!loGeom) {
611 EditSubscriber::action = ES_ACTION_NONE;
612 return false;
613 }
614
615 loGeom->state = Geometry::PRELIGHT;
616 EditSubscriber::action = ES_ACTION_NONE;
617 draggedSide = DraggedSide::NONE;
618 updateGeometry();
619 return true;
620 }
621
622 // Delete a point
button2Pressed(int modifierKey)623 bool Spot::button2Pressed (int modifierKey)
624 {
625 EditDataProvider* editProvider = getEditProvider();
626
627 if (!editProvider || lastObject == -1 || activeSpot == -1) {
628 return false;
629 }
630
631 if (! (modifierKey & (GDK_SHIFT_MASK | GDK_SHIFT_MASK))) {
632 EditSubscriber::action = ES_ACTION_PICKING;
633 }
634
635 return false;
636 }
637
638 // Create a new Target and Source point or start the drag of a Target point under the cursor
button3Pressed(int modifierKey)639 bool Spot::button3Pressed (int modifierKey)
640 {
641 EditDataProvider* editProvider = getEditProvider();
642
643 if (!editProvider || lastObject == -1 || activeSpot == -1) {
644 return false;
645 }
646
647 if ((modifierKey & GDK_CONTROL_MASK) && (EditSubscriber::mouseOverGeometry.at (lastObject) == &targetMODisc || lastObject >= STATIC_MO_OBJ_NBR)) {
648 lastObject = SOURCE_DISC;
649 sourceIcon.state = Geometry::DRAGGED;
650 EditSubscriber::action = ES_ACTION_DRAGGING;
651 draggedSide = DraggedSide::SOURCE;
652 return true;
653 } else if (! (modifierKey & (GDK_SHIFT_MASK | GDK_SHIFT_MASK))) {
654 EditSubscriber::action = ES_ACTION_PICKING;
655 }
656
657 return false;
658 }
659
button3Released()660 bool Spot::button3Released()
661 {
662 Geometry *loGeom = getVisibleGeometryFromMO (lastObject);
663
664 if (!loGeom) {
665 EditSubscriber::action = ES_ACTION_NONE;
666 return false;
667 }
668
669 lastObject = -1;
670 sourceIcon.state = Geometry::ACTIVE;
671 draggedSide = DraggedSide::NONE;
672 updateGeometry();
673 EditSubscriber::action = ES_ACTION_NONE;
674 return true;
675
676 return false;
677 }
678
drag1(int modifierKey)679 bool Spot::drag1 (int modifierKey)
680 {
681 EditDataProvider *editProvider = getEditProvider();
682 int imW, imH;
683 editProvider->getImageSize (imW, imH);
684 bool modified = false;
685
686 //printf("Drag1 / LastObject=%d\n", lastObject);
687
688 Geometry *loGeom = EditSubscriber::mouseOverGeometry.at (lastObject);
689
690 if (loGeom == &sourceMODisc) {
691 //printf("sourceMODisc / deltaPrevImage = %d / %d\n", editProvider->deltaPrevImage.x, editProvider->deltaPrevImage.y);
692 rtengine::Coord currPos = spots.at (activeSpot).sourcePos;
693 spots.at (activeSpot).sourcePos += editProvider->deltaPrevImage;
694 spots.at (activeSpot).sourcePos.clip (imW, imH);
695
696 if (spots.at (activeSpot).sourcePos != currPos) {
697 modified = true;
698 }
699
700 EditSubscriber::mouseOverGeometry.at (activeSpot + STATIC_MO_OBJ_NBR)->state = Geometry::DRAGGED;
701 } else if (loGeom == &targetMODisc || lastObject >= STATIC_MO_OBJ_NBR) {
702 //printf("targetMODisc / deltaPrevImage = %d / %d\n", editProvider->deltaPrevImage.x, editProvider->deltaPrevImage.y);
703 rtengine::Coord currPos = spots.at (activeSpot).targetPos;
704 spots.at (activeSpot).targetPos += editProvider->deltaPrevImage;
705 spots.at (activeSpot).targetPos.clip (imW, imH);
706
707 if (spots.at (activeSpot).targetPos != currPos) {
708 modified = true;
709 }
710 } else if (loGeom == &sourceCircle) {
711 //printf("sourceCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
712 int lastRadius = spots.at (activeSpot).radius;
713 rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
714 rtengine::PolarCoord currPolar (currPos - spots.at (activeSpot).sourcePos);
715 spots.at (activeSpot).radius = LIM<int> (int (currPolar.radius), SpotParams::minRadius, SpotParams::maxRadius);
716
717 if (spots.at (activeSpot).radius != lastRadius) {
718 modified = true;
719 }
720 } else if (loGeom == &targetCircle) {
721 //printf("targetCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
722 int lastRadius = spots.at (activeSpot).radius;
723 rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
724 rtengine::PolarCoord currPolar (currPos - spots.at (activeSpot).targetPos);
725 spots.at (activeSpot).radius = LIM<int> (int (currPolar.radius), SpotParams::minRadius, SpotParams::maxRadius);
726
727 if (spots.at (activeSpot).radius != lastRadius) {
728 modified = true;
729 }
730 } else if (loGeom == &sourceFeatherCircle) {
731 //printf("sourceFeatherCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
732 float currFeather = spots.at (activeSpot).feather;
733 rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
734 rtengine::PolarCoord currPolar (currPos - spots.at (activeSpot).sourcePos);
735 spots.at (activeSpot).feather = LIM01<float> ((currPolar.radius - double (spots.at (activeSpot).radius)) / double (spots.at (activeSpot).radius));
736
737 if (spots.at (activeSpot).feather != currFeather) {
738 modified = true;
739 }
740 } else if (loGeom == &targetFeatherCircle) {
741 //printf("targetFeatherCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
742 float currFeather = spots.at (activeSpot).feather;
743 rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
744 rtengine::PolarCoord currPolar (currPos - spots.at (activeSpot).targetPos);
745 spots.at (activeSpot).feather = LIM01<float> ((currPolar.radius - double (spots.at (activeSpot).radius)) / double (spots.at (activeSpot).radius));
746
747 if (spots.at (activeSpot).feather != currFeather) {
748 modified = true;
749 }
750 }
751
752 if (listener && modified) {
753 updateGeometry();
754 listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
755 }
756
757 return modified;
758 }
759
drag3(int modifierKey)760 bool Spot::drag3 (int modifierKey)
761 {
762 EditDataProvider *editProvider = getEditProvider();
763 int imW, imH;
764 editProvider->getImageSize (imW, imH);
765 bool modified = false;
766
767 Geometry *loGeom = EditSubscriber::mouseOverGeometry.at (lastObject);
768
769 if (loGeom == &sourceMODisc) {
770 rtengine::Coord currPos = spots.at (activeSpot).sourcePos;
771 spots.at (activeSpot).sourcePos += editProvider->deltaPrevImage;
772 spots.at (activeSpot).sourcePos.clip (imW, imH);
773
774 if (spots.at (activeSpot).sourcePos != currPos) {
775 modified = true;
776 }
777 }
778
779 if (listener) {
780 updateGeometry();
781 listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
782 }
783
784 return modified;
785 }
786
pick2(bool picked)787 bool Spot::pick2 (bool picked)
788 {
789 return pick3 (picked);
790 }
791
pick3(bool picked)792 bool Spot::pick3 (bool picked)
793 {
794 EditDataProvider* editProvider = getEditProvider();
795
796 if (!picked) {
797 if (editProvider->getObject() != lastObject) {
798 return false;
799 }
800 }
801
802 // Object is picked, we delete it
803 deleteSelectedEntry();
804 EditSubscriber::action = ES_ACTION_NONE;
805 updateGeometry();
806 return true;
807 }
808
809
switchOffEditMode()810 void Spot::switchOffEditMode ()
811 {
812 if (edit->get_active()) {
813 // switching off the toggle button
814 bool wasBlocked = editConn.block (true);
815 edit->set_active (false);
816
817 if (!wasBlocked) {
818 editConn.block (false);
819 }
820 }
821
822 reset_adjusters();
823
824 EditSubscriber::switchOffEditMode(); // disconnect
825 listener->unsetTweakOperator(this);
826 listener->refreshPreview(EvSpotEnabled); // reprocess the preview w/o creating History entry
827 }
828
829
tweakParams(procparams::ProcParams & pparams)830 void Spot::tweakParams(procparams::ProcParams& pparams)
831 {
832 //params->raw.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::FAST);
833 //params->raw.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST);
834
835 // -> disabling all transform
836 //pparams.coarse = CoarseTransformParams();
837 pparams.lensProf = LensProfParams();
838 pparams.cacorrection = CACorrParams();
839 pparams.distortion = DistortionParams();
840 pparams.rotate = RotateParams();
841 pparams.perspective = PerspectiveParams();
842 pparams.vignetting = VignettingParams();
843
844 // -> disabling standard crop
845 pparams.crop.enabled = false;
846
847 // // -> disabling time consuming and unnecessary tool
848 // // pparams.sh.enabled = false;
849 // pparams.blackwhite.enabled = false;
850 // pparams.dehaze.enabled = false;
851 // // pparams.wavelet.enabled = false;
852 // pparams.filmSimulation.enabled = false;
853 // // pparams.sharpenEdge.enabled = false;
854 // // pparams.sharpenMicro.enabled = false;
855 // pparams.sharpening.enabled = false;
856 // pparams.softlight.enabled = false;
857 // pparams.gradient.enabled = false;
858 // pparams.pcvignette.enabled = false;
859 // // pparams.colorappearance.enabled = false;
860 }
861
862
adjusterChanged(Adjuster * a,double newval)863 void Spot::adjusterChanged(Adjuster *a, double newval)
864 {
865 if (activeSpot > -1) {
866 auto &s = spots[activeSpot];
867 s.sourcePos.x = source_x->getValue();
868 s.sourcePos.y = source_y->getValue();
869 s.targetPos.x = target_x->getValue();
870 s.targetPos.y = target_y->getValue();
871 s.radius = radius->getValue();
872 s.feather = feather->getValue();
873 s.opacity = opacity->getValue();
874 s.detail = detail->getValue();
875 }
876
877 if (listener && getEnabled()) {
878 disableListener();
879 updateGeometry();
880 enableListener();
881
882 listener->panelChanged(EvSpotEntry, M("TP_SPOT_ENTRYCHANGED"));
883 }
884 }
885
886
reset_adjusters()887 void Spot::reset_adjusters()
888 {
889 for (auto a : spot_adjusters) {
890 a->block(true);
891 a->resetValue(true);
892 a->block(false);
893 }
894 spot_frame->set_sensitive(false);
895 }
896
897
setDefaults(const ProcParams * def)898 void Spot::setDefaults(const ProcParams *def)
899 {
900 initial_params = def->spot;
901 }
902
903
toolReset(bool to_initial)904 void Spot::toolReset(bool to_initial)
905 {
906 ProcParams pp;
907 if (to_initial) {
908 pp.spot = initial_params;
909 }
910 pp.spot.enabled = getEnabled();
911 read(&pp);
912 }
913
914
on_fold(GdkEventButton * evt)915 void Spot::on_fold(GdkEventButton *evt)
916 {
917 if (isCurrentSubscriber()) {
918 switchOffEditMode();
919 }
920 }
921
922
on_hide()923 void Spot::on_hide()
924 {
925 if (isCurrentSubscriber()) {
926 switchOffEditMode();
927 }
928 }
929