rawTherapee/rtgui/perspective.cc
Lawrence Lee 1b06cb9999 Limit preview refreshing in control line edit mode
Previously, changing values in the camera-based perspective correction
tool while in control line edit mode resulted in the preview being
refreshed. This is unnecessary because all perspective transformations
are disabled in edit mode. This commit disables preview refreshing for
camera-based perspective adjustments in edit mode.
2020-06-24 17:06:21 -07:00

1180 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 "eventmapper.h"
#include "perspective.h"
#include "rtimage.h"
#include "rtsurface.h"
#include "../rtengine/procparams.h"
using namespace rtengine;
using namespace rtengine::procparams;
PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M("TP_PERSPECTIVE_LABEL"))
{
auto mapper = ProcEventMapper::getInstance();
// Normal events.
EvPerspCamAngle = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_CAM_ANGLE");
EvPerspCamFocalLength = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_CAM_FL");
EvPerspCamShift = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_CAM_SHIFT");
EvPerspMethod = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_METHOD");
EvPerspProjAngle = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_PROJ_ANGLE");
EvPerspProjRotate = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_PROJ_ROTATE");
EvPerspProjShift = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_PROJ_SHIFT");
EvPerspRender = mapper->newEvent(TRANSFORM);
// Void events.
EvPerspCamAngleVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_CAM_ANGLE");
EvPerspCamFocalLengthVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_CAM_FL");
EvPerspCamShiftVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_CAM_SHIFT");
EvPerspProjAngleVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_PROJ_ANGLE");
EvPerspProjRotateVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_PROJ_ROTATE");
EvPerspProjShiftVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_PROJ_SHIFT");
setCamBasedEventsActive();
lens_geom_listener = nullptr;
Gtk::Image* ipers_draw_horiz = Gtk::manage (new RTImage ("draw-horizontal.png"));
Gtk::Image* ipers_draw_vert = Gtk::manage (new RTImage ("draw-vertical.png"));
Gtk::Image* ipers_draw = new RTImage ("draw.png");
Gtk::Image* ipers_trash = Gtk::manage (new RTImage ("trash-empty.png"));
Gtk::Image* ipersHL = Gtk::manage (new RTImage ("perspective-horizontal-left-small.png"));
Gtk::Image* ipersHR = Gtk::manage (new RTImage ("perspective-horizontal-right-small.png"));
Gtk::Image* ipersVL = Gtk::manage (new RTImage ("perspective-vertical-bottom-small.png"));
Gtk::Image* ipersVR = Gtk::manage (new RTImage ("perspective-vertical-top-small.png"));
Gtk::Image* ipers_auto_pitch = Gtk::manage (new RTImage ("perspective-vertical-bottom.png"));
Gtk::Image* ipers_auto_yaw = Gtk::manage (new RTImage ("perspective-horizontal-left.png"));
Gtk::Image* ipers_auto_pitch_yaw = Gtk::manage (new RTImage ("perspective-horizontal-vertical.png"));
Gtk::Image* ipers_cam_yaw_left = Gtk::manage (new RTImage ("perspective-horizontal-left-small.png"));
Gtk::Image* ipers_cam_yaw_right = Gtk::manage (new RTImage ("perspective-horizontal-right-small.png"));
Gtk::Image* ipers_cam_pitch_left = Gtk::manage (new RTImage ("perspective-vertical-bottom-small.png"));
Gtk::Image* ipers_cam_pitch_right = Gtk::manage (new RTImage ("perspective-vertical-top-small.png"));
Gtk::Image* ipers_proj_yaw_left = Gtk::manage (new RTImage ("perspective-horizontal-left-small.png"));
Gtk::Image* ipers_proj_yaw_right = Gtk::manage (new RTImage ("perspective-horizontal-right-small.png"));
Gtk::Image* ipers_proj_pitch_left = Gtk::manage (new RTImage ("perspective-vertical-bottom-small.png"));
Gtk::Image* ipers_proj_pitch_right = Gtk::manage (new RTImage ("perspective-vertical-top-small.png"));
Gtk::Image* ipers_rotate_left = Gtk::manage(new RTImage("rotate-right-small.png"));
Gtk::Image* ipers_rotate_right = Gtk::manage(new RTImage("rotate-left-small.png"));
Gtk::HBox* method_hbox = Gtk::manage (new Gtk::HBox());
Gtk::Label* method_label = Gtk::manage (new Gtk::Label (M("TP_PERSPECTIVE_METHOD") + ": "));
method = Gtk::manage (new MyComboBoxText ());
method->append (M("TP_PERSPECTIVE_METHOD_SIMPLE"));
method->append (M("TP_PERSPECTIVE_METHOD_CAMERA_BASED"));
method_hbox->pack_start(*method_label, Gtk::PACK_SHRINK);
method_hbox->pack_start(*method);
pack_start(*method_hbox);
simple = Gtk::manage( new Gtk::VBox() );
vert = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_VERTICAL"), -100, 100, 0.1, 0, ipersVL, ipersVR));
vert->setAdjusterListener (this);
horiz = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_HORIZONTAL"), -100, 100, 0.1, 0, ipersHL, ipersHR));
horiz->setAdjusterListener (this);
camera_based = Gtk::manage( new Gtk::VBox() );
Gtk::Frame* camera_frame = Gtk::manage (new Gtk::Frame
(M("TP_PERSPECTIVE_CAMERA_FRAME")));
camera_frame->set_label_align(0.025, 0.5);
Gtk::VBox* camera_vbox = Gtk::manage (new Gtk::VBox());
camera_focal_length = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_FOCAL_LENGTH"), 0.5, 2000, 0.01, 24));
camera_focal_length->setAdjusterListener (this);
camera_crop_factor = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_CROP_FACTOR"), 0.1, 30, 0.01, 1));
camera_crop_factor->setAdjusterListener (this);
camera_shift_horiz = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL"), -50, 50, 0.01, 0));
camera_shift_horiz->setAdjusterListener (this);
camera_shift_vert = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL"), -50, 50, 0.01, 0));
camera_shift_vert->setAdjusterListener (this);
camera_roll = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_ROLL"), -45, 45, 0.01, 0));
camera_roll->setAdjusterListener (this);
camera_pitch = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_PITCH"),
-60, 60, 0.1, 0, ipers_cam_pitch_left, ipers_cam_pitch_right));
camera_pitch->setAdjusterListener (this);
camera_yaw = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_YAW"),
-60, 60, 0.1, 0, ipers_cam_yaw_left, ipers_cam_yaw_right));
camera_yaw->setAdjusterListener (this);
// Begin control lines interface.
lines_button_h = Gtk::manage (new Gtk::ToggleButton());
lines_button_h->set_image(*ipers_draw_horiz);
lines_button_h->signal_toggled().connect(sigc::bind(sigc::mem_fun(
*this, &::PerspCorrection::linesButtonPressed), lines_button_h));
lines_button_v = Gtk::manage (new Gtk::ToggleButton());
lines_button_v->set_image(*ipers_draw_vert);
lines_button_v->signal_toggled().connect(sigc::bind(sigc::mem_fun(
*this, &::PerspCorrection::linesButtonPressed), lines_button_v));
lines_button_edit = Gtk::manage (new Gtk::ToggleButton());
lines_button_edit->set_image(*ipers_draw);
lines_button_edit->signal_toggled().connect(sigc::mem_fun(
*this, &::PerspCorrection::linesEditButtonPressed));
lines_button_erase = Gtk::manage (new Gtk::Button());
lines_button_erase->set_image(*ipers_trash);
lines_button_erase->set_sensitive(false);
lines_button_erase->signal_pressed().connect(sigc::mem_fun(
*this, &::PerspCorrection::linesEraseButtonPressed));
lines = new ControlLineManager();
lines->callbacks = new LinesCallbacks(this, lines);
img_ctrl_lines_apply = new RTImage ("tick.png");
img_ctrl_lines_edit = ipers_draw;
Gtk::HBox* control_lines_box = Gtk::manage (new Gtk::HBox());
control_lines_box->set_tooltip_text( M("TP_PERSPECTIVE_CONTROL_LINES_TOOLTIP") );
Gtk::Label* control_lines_label = Gtk::manage (new Gtk::Label (M("TP_PERSPECTIVE_CONTROL_LINES") + ": "));
control_lines_box->pack_start(*control_lines_label, Gtk::PACK_SHRINK);
control_lines_box->pack_start(*lines_button_v);
control_lines_box->pack_start(*lines_button_h);
control_lines_box->pack_start(*lines_button_edit);
control_lines_box->pack_start(*lines_button_erase);
// End control lines interface.
auto_pitch = Gtk::manage (new Gtk::Button ());
auto_pitch->set_image(*ipers_auto_pitch);
auto_pitch->signal_pressed().connect( sigc::bind(sigc::mem_fun(*this, &PerspCorrection::autoCorrectionPressed), auto_pitch) );
auto_yaw = Gtk::manage (new Gtk::Button ());
auto_yaw->set_image(*ipers_auto_yaw);
auto_yaw->signal_pressed().connect( sigc::bind(sigc::mem_fun(*this, &PerspCorrection::autoCorrectionPressed), auto_yaw) );
auto_pitch_yaw = Gtk::manage (new Gtk::Button ());
auto_pitch_yaw->set_image(*ipers_auto_pitch_yaw);
auto_pitch_yaw->signal_pressed().connect( sigc::bind(sigc::mem_fun(*this, &PerspCorrection::autoCorrectionPressed), auto_pitch_yaw) );
Gtk::HBox* auto_hbox = Gtk::manage (new Gtk::HBox());
Gtk::Label* auto_label = Gtk::manage (new Gtk::Label (M("GENERAL_AUTO") + ": "));
auto_hbox->pack_start(*auto_label, Gtk::PACK_SHRINK);
Gtk::Frame* pca_frame = Gtk::manage (new Gtk::Frame
(M("TP_PERSPECTIVE_POST_CORRECTION_ADJUSTMENT_FRAME")));
pca_frame->set_label_align(0.025, 0.5);
Gtk::VBox* pca_vbox = Gtk::manage (new Gtk::VBox());
projection_shift_horiz = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_PROJECTION_SHIFT_HORIZONTAL"), -100, 100, 0.01, 0));
projection_shift_horiz->setAdjusterListener (this);
projection_shift_vert = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_PROJECTION_SHIFT_VERTICAL"), -100, 100, 0.01, 0));
projection_shift_vert->setAdjusterListener (this);
projection_rotate = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_PROJECTION_ROTATE"), -45, 45, 0.01, 0, ipers_rotate_left, ipers_rotate_right));
projection_rotate->setAdjusterListener (this);
Gtk::Frame* recovery_frame = Gtk::manage (new Gtk::Frame
(M("TP_PERSPECTIVE_RECOVERY_FRAME")));
recovery_frame->set_label_align(0.025, 0.5);
Gtk::VBox* recovery_vbox = Gtk::manage (new Gtk::VBox());
projection_pitch = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_PROJECTION_PITCH"), -60, 60, 0.1, 0, ipers_proj_pitch_left, ipers_proj_pitch_right));
projection_pitch->setAdjusterListener (this);
projection_yaw = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_PROJECTION_YAW"), -60, 60, 0.1, 0, ipers_proj_yaw_left, ipers_proj_yaw_right));
projection_yaw->setAdjusterListener (this);
simple->pack_start (*horiz);
simple->pack_start (*vert);
auto_hbox->pack_start (*auto_pitch);
auto_hbox->pack_start (*auto_yaw);
auto_hbox->pack_start (*auto_pitch_yaw);
camera_vbox->pack_start (*camera_focal_length);
camera_vbox->pack_start (*camera_crop_factor);
camera_vbox->pack_start (*camera_shift_horiz);
camera_vbox->pack_start (*camera_shift_vert);
camera_vbox->pack_start (*camera_roll);
camera_vbox->pack_start (*camera_pitch);
camera_vbox->pack_start (*camera_yaw);
camera_vbox->pack_start (*Gtk::manage (new Gtk::HSeparator()));
camera_vbox->pack_start (*control_lines_box);
camera_vbox->pack_start (*Gtk::manage (new Gtk::HSeparator()));
camera_vbox->pack_start (*auto_hbox);
camera_frame->add(*camera_vbox);
camera_based->pack_start(*camera_frame);
pca_vbox->pack_start (*projection_shift_horiz);
pca_vbox->pack_start (*projection_shift_vert);
pca_vbox->pack_start (*projection_rotate);
pca_frame->add(*pca_vbox);
camera_based->pack_start(*pca_frame);
recovery_vbox->pack_start (*projection_yaw);
recovery_vbox->pack_start (*projection_pitch);
recovery_frame->add(*recovery_vbox);
camera_based->pack_start(*recovery_frame);
pack_start(*simple);
pack_start(*camera_based);
horiz->setLogScale(2, 0);
vert->setLogScale(2, 0);
camera_focal_length->setLogScale(4000, 0.5);
camera_crop_factor->setLogScale(300, 0.1);
method->signal_changed().connect(sigc::mem_fun(*this, &PerspCorrection::methodChanged));
show_all();
}
PerspCorrection::~PerspCorrection()
{
delete lines->callbacks;
delete lines;
delete img_ctrl_lines_apply;
delete img_ctrl_lines_edit;
}
void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited)
{
disableListener ();
if (pedited) {
horiz->setEditedState (pedited->perspective.horizontal ? Edited : UnEdited);
vert->setEditedState (pedited->perspective.vertical ? Edited : UnEdited);
camera_crop_factor->setEditedState (pedited->perspective.camera_crop_factor ? Edited : UnEdited);
camera_focal_length->setEditedState (pedited->perspective.camera_focal_length ? Edited : UnEdited);
camera_pitch->setEditedState (pedited->perspective.camera_pitch ? Edited : UnEdited);
camera_roll->setEditedState (pedited->perspective.camera_roll ? Edited : UnEdited);
camera_shift_horiz->setEditedState (pedited->perspective.camera_shift_horiz ? Edited : UnEdited);
camera_shift_vert->setEditedState (pedited->perspective.camera_shift_vert ? Edited : UnEdited);
camera_yaw->setEditedState (pedited->perspective.camera_yaw ? Edited : UnEdited);
projection_pitch->setEditedState (pedited->perspective.projection_pitch ? Edited : UnEdited);
projection_rotate->setEditedState (pedited->perspective.projection_rotate ? Edited : UnEdited);
projection_shift_horiz->setEditedState (pedited->perspective.projection_shift_horiz ? Edited : UnEdited);
projection_shift_vert->setEditedState (pedited->perspective.projection_shift_vert ? Edited : UnEdited);
projection_yaw->setEditedState (pedited->perspective.projection_yaw ? Edited : UnEdited);
}
horiz->setValue (pp->perspective.horizontal);
vert->setValue (pp->perspective.vertical);
setFocalLengthValue (pp, metadata);
camera_pitch->setValue (pp->perspective.camera_pitch);
camera_roll->setValue (pp->perspective.camera_roll);
camera_shift_horiz->setValue (pp->perspective.camera_shift_horiz);
camera_shift_vert->setValue (pp->perspective.camera_shift_vert);
camera_yaw->setValue (pp->perspective.camera_yaw);
projection_pitch->setValue (pp->perspective.projection_pitch);
projection_rotate->setValue (pp->perspective.projection_rotate);
projection_shift_horiz->setValue (pp->perspective.projection_shift_horiz);
projection_shift_vert->setValue (pp->perspective.projection_shift_vert);
projection_yaw->setValue (pp->perspective.projection_yaw);
if (pedited && !pedited->perspective.method) {
method->set_active (2);
} else if (pp->perspective.method == "simple") {
method->set_active (0);
} else if (pp->perspective.method == "camera_based") {
method->set_active (1);
}
enableListener ();
}
void PerspCorrection::write (ProcParams* pp, ParamsEdited* pedited)
{
pp->perspective.render = render;
pp->perspective.horizontal = horiz->getValue ();
pp->perspective.vertical = vert->getValue ();
pp->perspective.camera_crop_factor= camera_crop_factor->getValue ();
pp->perspective.camera_focal_length = camera_focal_length->getValue ();
pp->perspective.camera_pitch = camera_pitch->getValue ();
pp->perspective.camera_roll = camera_roll->getValue ();
pp->perspective.camera_shift_horiz = camera_shift_horiz->getValue ();
pp->perspective.camera_shift_vert = camera_shift_vert->getValue ();
pp->perspective.camera_yaw = camera_yaw->getValue ();
pp->perspective.projection_pitch = projection_pitch->getValue ();
pp->perspective.projection_rotate = projection_rotate->getValue ();
pp->perspective.projection_shift_horiz = projection_shift_horiz->getValue ();
pp->perspective.projection_shift_vert = projection_shift_vert->getValue ();
pp->perspective.projection_yaw = projection_yaw->getValue ();
if (method->get_active_row_number() == 0) {
pp->perspective.method = "simple";
} else if (method->get_active_row_number() == 1) {
pp->perspective.method = "camera_based";
}
if (pedited) {
pedited->perspective.method = method->get_active_row_number() != 2;
pedited->perspective.horizontal = horiz->getEditedState ();
pedited->perspective.vertical = vert->getEditedState ();
pedited->perspective.camera_crop_factor= camera_crop_factor->getEditedState ();
pedited->perspective.camera_focal_length = camera_focal_length->getEditedState ();
pedited->perspective.camera_pitch = camera_pitch->getEditedState();
pedited->perspective.camera_roll = camera_roll->getEditedState();
pedited->perspective.camera_shift_horiz = camera_shift_horiz->getEditedState();
pedited->perspective.camera_shift_vert = camera_shift_vert->getEditedState();
pedited->perspective.camera_yaw = camera_yaw->getEditedState();
pedited->perspective.projection_pitch = projection_pitch->getEditedState();
pedited->perspective.projection_rotate = projection_rotate->getEditedState();
pedited->perspective.projection_shift_horiz = projection_shift_horiz->getEditedState();
pedited->perspective.projection_shift_vert = projection_shift_vert->getEditedState();
pedited->perspective.projection_yaw = projection_yaw->getEditedState();
}
}
void PerspCorrection::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited)
{
horiz->setDefault (defParams->perspective.horizontal);
vert->setDefault (defParams->perspective.vertical);
camera_crop_factor->setDefault (defParams->perspective.camera_crop_factor);
camera_focal_length->setDefault (defParams->perspective.camera_focal_length);
camera_pitch->setDefault (defParams->perspective.camera_pitch);
camera_roll->setDefault (defParams->perspective.camera_roll);
camera_shift_horiz->setDefault (defParams->perspective.camera_shift_horiz);
camera_shift_vert->setDefault (defParams->perspective.camera_shift_vert);
camera_yaw->setDefault (defParams->perspective.camera_yaw);
projection_pitch->setDefault (defParams->perspective.projection_pitch);
projection_rotate->setDefault (defParams->perspective.projection_rotate);
projection_shift_horiz->setDefault (defParams->perspective.projection_shift_horiz);
projection_shift_vert->setDefault (defParams->perspective.projection_shift_vert);
projection_yaw->setDefault (defParams->perspective.projection_yaw);
if (pedited) {
horiz->setDefaultEditedState (pedited->perspective.horizontal ? Edited : UnEdited);
vert->setDefaultEditedState (pedited->perspective.vertical ? Edited : UnEdited);
camera_crop_factor->setDefaultEditedState (pedited->perspective.camera_crop_factor ? Edited : UnEdited);
camera_focal_length->setDefaultEditedState (pedited->perspective.camera_focal_length ? Edited : UnEdited);
camera_pitch->setDefaultEditedState (pedited->perspective.camera_pitch ? Edited : UnEdited);
camera_roll->setDefaultEditedState (pedited->perspective.camera_roll ? Edited : UnEdited);
camera_shift_horiz->setDefaultEditedState (pedited->perspective.camera_shift_horiz ? Edited : UnEdited);
camera_shift_vert->setDefaultEditedState (pedited->perspective.camera_shift_vert ? Edited : UnEdited);
camera_yaw->setDefaultEditedState (pedited->perspective.camera_yaw ? Edited : UnEdited);
projection_pitch->setDefaultEditedState (pedited->perspective.projection_pitch ? Edited : UnEdited);
projection_rotate->setDefaultEditedState (pedited->perspective.projection_rotate ? Edited : UnEdited);
projection_shift_horiz->setDefaultEditedState (pedited->perspective.projection_shift_horiz ? Edited : UnEdited);
projection_shift_vert->setDefaultEditedState (pedited->perspective.projection_shift_vert ? Edited : UnEdited);
projection_yaw->setDefaultEditedState (pedited->perspective.projection_yaw ? Edited : UnEdited);
} else {
horiz->setDefaultEditedState (Irrelevant);
vert->setDefaultEditedState (Irrelevant);
camera_crop_factor->setDefaultEditedState (Irrelevant);
camera_focal_length->setDefaultEditedState (Irrelevant);
camera_pitch->setDefaultEditedState (Irrelevant);
camera_roll->setDefaultEditedState (Irrelevant);
camera_shift_horiz->setDefaultEditedState (Irrelevant);
camera_shift_vert->setDefaultEditedState (Irrelevant);
camera_yaw->setDefaultEditedState (Irrelevant);
projection_pitch->setDefaultEditedState (Irrelevant);
projection_rotate->setDefaultEditedState (Irrelevant);
projection_shift_horiz->setDefaultEditedState (Irrelevant);
projection_shift_vert->setDefaultEditedState (Irrelevant);
projection_yaw->setDefaultEditedState (Irrelevant);
}
}
void PerspCorrection::adjusterChanged(Adjuster* a, double newval)
{
if (listener) {
if (a == horiz || a == vert) {
listener->panelChanged (EvPerspCorr,
Glib::ustring::compose("%1=%2\n%3=%4",
M("TP_PERSPECTIVE_HORIZONTAL"),
horiz->getValue(),
M("TP_PERSPECTIVE_VERTICAL"),
vert->getValue()));
} else if (a == camera_focal_length || a == camera_crop_factor) {
listener->panelChanged (*event_persp_cam_focal_length,
Glib::ustring::compose("%1=%2\n%3=%4",
M("TP_PERSPECTIVE_CAMERA_FOCAL_LENGTH"),
camera_focal_length->getValue(),
M("TP_PERSPECTIVE_CAMERA_CROP_FACTOR"),
camera_crop_factor->getValue()));
} else if (a == camera_shift_horiz || a == camera_shift_vert) {
listener->panelChanged (*event_persp_cam_shift,
Glib::ustring::compose("%1=%2\n%3=%4",
M("TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL"),
camera_shift_horiz->getValue(),
M("TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL"),
camera_shift_vert->getValue()));
} else if (a == camera_pitch || a == camera_roll|| a == camera_yaw) {
listener->panelChanged (*event_persp_cam_angle,
Glib::ustring::compose("%1=%2\n%3=%4\n%5=%6",
M("TP_PERSPECTIVE_CAMERA_ROLL"),
camera_roll->getValue(),
M("TP_PERSPECTIVE_CAMERA_YAW"),
camera_yaw->getValue(),
M("TP_PERSPECTIVE_CAMERA_PITCH"),
camera_pitch->getValue()));
} else if (a == projection_shift_horiz || a == projection_shift_vert) {
listener->panelChanged (*event_persp_proj_shift,
Glib::ustring::compose("%1=%2\n%3=%4",
M("TP_PERSPECTIVE_PROJECTION_SHIFT_HORIZONTAL"),
projection_shift_horiz->getValue(),
M("TP_PERSPECTIVE_PROJECTION_SHIFT_VERTICAL"),
projection_shift_vert->getValue()));
} else if (a == projection_rotate) {
listener->panelChanged (*event_persp_proj_rotate,
Glib::ustring::format(projection_rotate->getValue()));
} else if (a == projection_pitch || a == projection_yaw) {
listener->panelChanged (*event_persp_proj_angle,
Glib::ustring::compose("%1=%2\n%3=%4",
M("TP_PERSPECTIVE_PROJECTION_PITCH"),
projection_pitch->getValue(),
M("TP_PERSPECTIVE_PROJECTION_YAW"),
projection_yaw->getValue()));
}
}
}
void PerspCorrection::applyControlLines(void)
{
if (!lens_geom_listener) {
return;
}
auto control_lines = lines->toControlLines();
int h_count = 0, v_count = 0;
double rot = camera_roll->getValue();
double pitch = camera_pitch->getValue();
double yaw = camera_yaw->getValue();
for (unsigned int i = 0; i < lines->size(); i++) {
if (control_lines[i].type == rtengine::ControlLine::HORIZONTAL) {
h_count++;
} else if (control_lines[i].type == rtengine::ControlLine::VERTICAL) {
v_count++;
}
}
lens_geom_listener->autoPerspRequested(v_count > 1, h_count > 1, rot, pitch,
yaw, control_lines, lines->size());
free(control_lines);
disableListener();
camera_pitch->setValue(pitch);
camera_roll->setValue(rot);
camera_yaw->setValue(yaw);
enableListener();
adjusterChanged(camera_pitch, pitch);
}
void PerspCorrection::autoCorrectionPressed(Gtk::Button* b)
{
if (!lens_geom_listener) {
return;
}
double rot = 0;
double pitch = 0;
double yaw = 0;
if (b == auto_pitch) {
lens_geom_listener->autoPerspRequested(true, false, rot, pitch, yaw);
} else if (b == auto_yaw) {
lens_geom_listener->autoPerspRequested(false, true, rot, pitch, yaw);
} else if (b == auto_pitch_yaw) {
lens_geom_listener->autoPerspRequested(true, true, rot, pitch, yaw);
}
disableListener();
camera_pitch->setValue(pitch);
camera_roll->setValue(rot);
camera_yaw->setValue(yaw);
enableListener();
adjusterChanged(camera_pitch, pitch);
}
void PerspCorrection::methodChanged (void)
{
if (!batchMode) {
removeIfThere (this, simple, false);
removeIfThere (this, camera_based, false);
if (method->get_active_row_number() == 0) {
pack_start (*simple);
} else if (method->get_active_row_number() == 1) {
pack_start (*camera_based);
}
// If no longer in camera-based mode and control lines are being edited.
if (method->get_active_row_number() != 1 && lines_button_edit->get_active()) {
lines_button_edit->set_active(false);
}
}
if (listener) {
listener->panelChanged (EvPerspMethod, method->get_active_text ());
}
}
void PerspCorrection::setAdjusterBehavior (bool badd, bool camera_focal_length_add, bool camera_shift_add, bool camera_angle_add, bool projection_angle_add, bool projection_shift_add, bool projection_rotate_add)
{
horiz->setAddMode(badd);
vert->setAddMode(badd);
camera_crop_factor->setAddMode(camera_focal_length_add);
camera_focal_length->setAddMode(camera_focal_length_add);
camera_pitch->setAddMode(camera_angle_add);
camera_roll->setAddMode(camera_angle_add);
camera_shift_horiz->setAddMode(camera_shift_add);
camera_shift_vert->setAddMode(camera_shift_add);
camera_yaw->setAddMode(camera_angle_add);
projection_pitch->setAddMode(projection_angle_add);
projection_rotate->setAddMode(projection_rotate_add);
projection_shift_horiz->setAddMode(projection_shift_add);
projection_shift_vert->setAddMode(projection_shift_add);
projection_yaw->setAddMode(projection_angle_add);
}
void PerspCorrection::setMetadata (const rtengine::FramesMetaData* metadata)
{
this->metadata = metadata;
}
void PerspCorrection::trimValues (rtengine::procparams::ProcParams* pp)
{
horiz->trimValue(pp->perspective.horizontal);
vert->trimValue(pp->perspective.vertical);
camera_crop_factor->trimValue(pp->perspective.camera_crop_factor);
camera_focal_length->trimValue(pp->perspective.camera_focal_length);
camera_pitch->trimValue(pp->perspective.camera_pitch);
camera_roll->trimValue(pp->perspective.camera_roll);
camera_shift_horiz->trimValue(pp->perspective.camera_shift_horiz);
camera_shift_vert->trimValue(pp->perspective.camera_shift_vert);
camera_yaw->trimValue(pp->perspective.camera_yaw);
projection_pitch->trimValue(pp->perspective.projection_pitch);
projection_rotate->trimValue(pp->perspective.projection_rotate);
projection_shift_horiz->trimValue(pp->perspective.projection_shift_horiz);
projection_shift_vert->trimValue(pp->perspective.projection_shift_vert);
projection_yaw->trimValue(pp->perspective.projection_yaw);
}
void PerspCorrection::setBatchMode (bool batchMode)
{
ToolPanel::setBatchMode (batchMode);
horiz->showEditedCB ();
vert->showEditedCB ();
camera_crop_factor->showEditedCB ();
camera_focal_length->showEditedCB ();
camera_pitch->showEditedCB ();
camera_roll->showEditedCB ();
camera_shift_horiz->showEditedCB ();
camera_shift_vert->showEditedCB ();
camera_yaw->showEditedCB ();
projection_pitch->showEditedCB ();
projection_rotate->showEditedCB ();
projection_shift_horiz->showEditedCB ();
projection_shift_vert->showEditedCB ();
projection_yaw->showEditedCB ();
lines_button_h->set_sensitive(false);
lines_button_v->set_sensitive(false);
lines_button_edit->set_sensitive(false);
auto_pitch->set_sensitive(false);
auto_yaw->set_sensitive(false);
auto_pitch_yaw->set_sensitive(false);
method->append (M("GENERAL_UNCHANGED"));
}
void PerspCorrection::setFocalLengthValue (const ProcParams* pparams, const FramesMetaData* metadata)
{
const double pp_crop_factor = pparams->perspective.camera_crop_factor;
const double pp_focal_length = pparams->perspective.camera_focal_length;
double default_crop_factor = 1.0;
double default_focal_length = 24.0;
// Defaults from metadata.
if (metadata && (pp_crop_factor <= 0 || pp_focal_length <= 0)) {
const double fl = metadata->getFocalLen();
const double fl35 = metadata->getFocalLen35mm();
if (fl <= 0) {
if (fl35 <= 0) {
// No focal length data.
} else {
// 35mm focal length available.
default_focal_length = fl35;
}
} else {
if (fl35 <= 0) {
// Focal length available.
default_focal_length = fl;
} else {
// Focal length and 35mm equivalent available.
default_focal_length = fl;
default_crop_factor = fl35 / fl;
}
}
}
// Change value if those from the ProcParams are invalid.
if (pp_crop_factor > 0) {
camera_crop_factor->setValue(pparams->perspective.camera_crop_factor);
} else {
camera_crop_factor->setDefault(default_crop_factor);
camera_crop_factor->setValue(default_crop_factor);
}
if (pp_focal_length > 0) {
camera_focal_length->setValue(pparams->perspective.camera_focal_length);
} else {
camera_focal_length->setDefault(default_focal_length);
camera_focal_length->setValue(default_focal_length);
}
}
void PerspCorrection::switchOffEditMode(ControlLineManager* lines)
{
lines_button_h->set_active(false);
lines_button_v->set_active(false);
lines_button_edit->set_active(false);
}
void PerspCorrection::setEditProvider(EditDataProvider* provider)
{
lines->setEditProvider(provider);
}
void PerspCorrection::linesButtonPressed(Gtk::ToggleButton* button)
{
lines->setLinesState(lines_button_h->get_active(), lines_button_v->get_active());
if (!button->get_active()) {
return;
}
if (button == lines_button_h) {
lines->draw_line_type = rtengine::ControlLine::HORIZONTAL;
if (lines_button_v->get_active()) {
lines_button_v->set_active(false);
}
} else if (button == lines_button_v) {
lines->draw_line_type = rtengine::ControlLine::VERTICAL;
if (lines_button_h->get_active()) {
lines_button_h->set_active(false);
}
}
if (!lines_button_edit->get_active()) {
lines_button_edit->set_active(true);
}
lines->setDrawMode(true);
}
void PerspCorrection::linesEditButtonPressed(void)
{
if (lines_button_edit->get_active()) { // Enter edit mode.
lines->setActive(true);
if (img_ctrl_lines_apply) {
lines_button_edit->set_image(*img_ctrl_lines_apply);
}
render = false;
lines->setLinesState(lines_button_h->get_active(), lines_button_v->get_active());
if (lens_geom_listener) {
lens_geom_listener->updateTransformPreviewRequested(EvPerspRender, false);
}
lines_button_erase->set_sensitive(true);
setCamBasedEventsActive(false);
} else { // Leave edit mode.
setCamBasedEventsActive(true);
lines_button_erase->set_sensitive(false);
render = true;
lines->setDrawMode(false);
lines->setActive(false);
if (img_ctrl_lines_edit) {
lines_button_edit->set_image(*img_ctrl_lines_edit);
}
lines_button_h->set_active(false);
lines_button_v->set_active(false);
if (method->get_active_row_number() == 1) {
// Calculate perspective distortion if in camera-based mode.
applyControlLines();
}
}
}
void PerspCorrection::linesEraseButtonPressed(void)
{
lines->removeAll();
}
void PerspCorrection::setCamBasedEventsActive(bool active)
{
if (active) {
event_persp_cam_focal_length = &EvPerspCamFocalLength;
event_persp_cam_shift = &EvPerspCamShift;
event_persp_cam_angle = &EvPerspCamAngle;
event_persp_proj_shift = &EvPerspProjShift;
event_persp_proj_rotate = &EvPerspProjRotate;
event_persp_proj_angle = &EvPerspProjAngle;
} else {
event_persp_cam_focal_length = &EvPerspCamFocalLengthVoid;
event_persp_cam_shift = &EvPerspCamShiftVoid;
event_persp_cam_angle = &EvPerspCamAngleVoid;
event_persp_proj_shift = &EvPerspProjShiftVoid;
event_persp_proj_rotate = &EvPerspProjRotateVoid;
event_persp_proj_angle = &EvPerspProjAngleVoid;
}
}
ControlLineManager::ControlLineManager():
EditSubscriber(ET_OBJECTS),
cursor(CSCrosshair),
prev_obj(-1),
selected_object(-1)
{
canvas_area = new Rectangle();
canvas_area->filled = true;
canvas_area->topLeft = Coord(0, 0);
mouseOverGeometry.push_back(canvas_area);
line_icon_h = Cairo::RefPtr<RTSurface>(new RTSurface("bidirectional-arrow-horizontal-hicontrast.png"));
line_icon_v = Cairo::RefPtr<RTSurface>(new RTSurface("bidirectional-arrow-vertical-hicontrast.png"));
line_icon_h_prelight = Cairo::RefPtr<RTSurface>(new RTSurface("bidirectional-arrow-horizontal-prelight.png"));
line_icon_v_prelight = Cairo::RefPtr<RTSurface>(new RTSurface("bidirectional-arrow-vertical-prelight.png"));
}
ControlLineManager::~ControlLineManager()
{
delete canvas_area;
for (auto i = control_lines.begin(); i != control_lines.end(); i++) {
delete (*i)->begin;
delete (*i)->end;
delete (*i)->line;
delete (*i)->icon_h;
delete (*i)->icon_v;
delete *i;
}
}
Geometry::State ControlLineManager::calcLineState(const ::ControlLine& line) const
{
if (line.type == rtengine::ControlLine::HORIZONTAL && active_h) {
return Geometry::NORMAL;
} else if (line.type == rtengine::ControlLine::VERTICAL && active_v) {
return Geometry::NORMAL;
}
return Geometry::INSENSITIVE;
}
void ControlLineManager::setActive(bool active)
{
EditDataProvider* provider = getEditProvider();
if (!provider || (this == provider->getCurrSubscriber()) == active) {
return;
}
if (active) {
subscribe();
int ih, iw;
provider->getImageSize(iw, ih);
canvas_area->bottomRight = Coord(iw, ih);
} else {
unsubscribe();
}
}
void ControlLineManager::setDrawMode(bool draw)
{
draw_mode = draw;
}
void ControlLineManager::setLinesState(bool horiz_active, bool vert_active)
{
active_h = horiz_active;
active_v = vert_active;
for (auto line = control_lines.begin(); line != control_lines.end(); line++) {
auto state = calcLineState(**line);
(*line)->begin->state = state;
(*line)->end->state = state;
(*line)->line->state = state;
}
}
size_t ControlLineManager::size(void) const
{
return control_lines.size();
}
bool ControlLineManager::button1Pressed(int modifierKey)
{
EditDataProvider* dataProvider = getEditProvider();
if (!dataProvider) {
return false;
}
drag_delta = Coord(0, 0);
const int object = dataProvider->getObject();
if (object > 0) { // A control line.
if (object % ::ControlLine::OBJ_COUNT == 2) { // Icon.
action = Action::PICKING;
} else {
selected_object = object;
action = Action::DRAGGING;
}
} else if (draw_mode && (modifierKey & GDK_CONTROL_MASK)) { // Add new line.
addLine(dataProvider->posImage, dataProvider->posImage);
selected_object = mouseOverGeometry.size() - 1; // Select endpoint.
action = Action::DRAGGING;
}
return true;
}
bool ControlLineManager::button1Released(void)
{
action = Action::NONE;
if (selected_object > 0) {
mouseOverGeometry[selected_object]->state = Geometry::NORMAL;
}
selected_object = -1;
return false;
}
bool ControlLineManager::button3Pressed(int modifierKey)
{
EditDataProvider* provider = getEditProvider();
action = Action::NONE;
if (!provider || provider->getObject() < 1) {
return false;
}
action = Action::PICKING;
return false;
}
bool ControlLineManager::pick1(bool picked)
{
action = Action::NONE;
if (!picked) {
return false;
}
EditDataProvider* provider = getEditProvider();
if (!provider || provider->getObject() % ::ControlLine::OBJ_COUNT != 2) {
return false;
}
// Change line type.
int object_id = provider->getObject();
auto line = control_lines[(object_id - 1) / ::ControlLine::OBJ_COUNT];
if (line->type == rtengine::ControlLine::HORIZONTAL) {
line->icon = line->icon_v;
line->type = rtengine::ControlLine::VERTICAL;
} else if (line->type == rtengine::ControlLine::VERTICAL) {
line->icon = line->icon_h;
line->type = rtengine::ControlLine::HORIZONTAL;
}
visibleGeometry[object_id - 1] = line->icon;
auto state = calcLineState(*line);
line->begin->state = state;
line->end->state = state;
line->line->state = state;
return true;
}
bool ControlLineManager::pick3(bool picked)
{
action = Action::NONE;
if (!picked) {
return false;
}
EditDataProvider* provider = getEditProvider();
if (!provider) {
return false;
}
removeLine((provider->getObject() - 1) / ::ControlLine::OBJ_COUNT);
return false;
}
bool ControlLineManager::drag1(int modifierKey)
{
EditDataProvider* provider = getEditProvider();
if (!provider || selected_object < 1) {
return false;
}
::ControlLine* control_line = control_lines[(selected_object - 1) / ::ControlLine::OBJ_COUNT];
int component = selected_object % ::ControlLine::OBJ_COUNT; // 0 == end, 1 == line, 2 == icon, 3 == begin
Coord mouse = provider->posImage + provider->deltaImage;
Coord delta = provider->deltaImage - drag_delta;
int ih, iw;
provider->getImageSize(iw, ih);
switch (component) {
case (0): // end
control_line->end->center = mouse;
control_line->end->center.clip(iw, ih);
control_line->line->end = control_line->end->center;
control_line->end->state = Geometry::DRAGGED;
break;
case (1): { // line
// Constrain delta so the end stays above the image.
Coord new_delta = control_line->end->center + delta;
new_delta.clip(iw, ih);
new_delta -= control_line->end->center;
// Constrain delta so the beginning stays above the image.
new_delta += control_line->begin->center;
new_delta.clip(iw, ih);
new_delta -= control_line->begin->center;
// Move all objects in the control line.
control_line->end->center += new_delta;
control_line->begin->center += new_delta;
control_line->line->end = control_line->end->center;
control_line->line->begin = control_line->begin->center;
drag_delta += new_delta;
control_line->line->state = Geometry::DRAGGED;
break;
}
case (3): // begin
control_line->begin->center = mouse;
control_line->begin->center.clip(iw, ih);
control_line->line->begin = control_line->begin->center;
control_line->begin->state = Geometry::DRAGGED;
break;
}
control_line->icon_h->position.x = (control_line->begin->center.x + control_line->end->center.x) / 2;
control_line->icon_h->position.y = (control_line->begin->center.y + control_line->end->center.y) / 2;
control_line->icon_v->position.x = control_line->icon_h->position.x;
control_line->icon_v->position.y = control_line->icon_h->position.y;
return false;
}
CursorShape ControlLineManager::getCursor(int objectID) const
{
return cursor;
}
bool ControlLineManager::mouseOver(int modifierKey)
{
EditDataProvider* provider = getEditProvider();
if (!provider) {
return false;
}
int cur_obj = provider->getObject();
if (cur_obj == 0) { // Canvas
if (draw_mode && modifierKey & GDK_CONTROL_MASK) {
cursor = CSPlus;
} else {
cursor = CSCrosshair;
}
} else if (cur_obj < 0) { // Nothing
cursor = CSArrow;
} else if (cur_obj % ::ControlLine::OBJ_COUNT == 2) { // Icon
visibleGeometry[cur_obj - 1]->state = Geometry::PRELIGHT;
cursor = CSArrow;
} else { // Object
visibleGeometry[cur_obj - 1]->state = Geometry::PRELIGHT;
cursor = CSMove2D;
}
if (prev_obj != cur_obj && prev_obj > 0) {
auto state = calcLineState(*control_lines[(prev_obj - 1) / ::ControlLine::OBJ_COUNT]);
visibleGeometry[prev_obj - 1]->state = state;
}
prev_obj = cur_obj;
return true;
}
void ControlLineManager::switchOffEditMode(void)
{
if (callbacks) {
callbacks->switchOffEditMode();
}
}
void ControlLineManager::setEditProvider(EditDataProvider* provider)
{
EditSubscriber::setEditProvider(provider);
}
void ControlLineManager::addLine(Coord begin, Coord end)
{
constexpr int line_width = 2;
constexpr int handle_radius = 6;
Line* line;
OPIcon *icon_h, *icon_v;
Circle *begin_c, *end_c;
line = new Line();
line->datum = Geometry::IMAGE;
line->innerLineWidth = line_width;
line->begin = begin;
line->end = end;
const Cairo::RefPtr<RTSurface> null_surface = Cairo::RefPtr<RTSurface>(nullptr);
icon_h = new OPIcon(line_icon_h, null_surface, line_icon_h_prelight,
null_surface, null_surface, Geometry::DP_CENTERCENTER);
icon_h->position = Coord((begin.x + end.x) / 2, (begin.y + end.y) / 2);
icon_v = new OPIcon(line_icon_v, null_surface, line_icon_v_prelight,
null_surface, null_surface, Geometry::DP_CENTERCENTER);
icon_v->position = Coord((begin.x + end.x) / 2, (begin.y + end.y) / 2);
begin_c = new Circle();
begin_c->datum = Geometry::IMAGE;
begin_c->filled = true;
begin_c->radius = handle_radius;
begin_c->center = begin;
end_c = new Circle();
end_c->datum = Geometry::IMAGE;
end_c->filled = true;
end_c->radius = handle_radius;
end_c->center = begin;
::ControlLine* control_line = new ::ControlLine();
control_line->begin = begin_c;
control_line->end = end_c;
control_line->icon_h = icon_h;
control_line->icon_v = icon_v;
if (draw_line_type == rtengine::ControlLine::HORIZONTAL) {
control_line->icon = icon_h;
} else if (draw_line_type == rtengine::ControlLine::VERTICAL) {
control_line->icon = icon_v;
}
control_line->line = line;
control_line->type = draw_line_type;
control_lines.push_back(control_line);
EditSubscriber::visibleGeometry.push_back(line);
EditSubscriber::visibleGeometry.push_back(control_line->icon);
EditSubscriber::visibleGeometry.push_back(begin_c);
EditSubscriber::visibleGeometry.push_back(end_c);
EditSubscriber::mouseOverGeometry.push_back(line);
EditSubscriber::mouseOverGeometry.push_back(control_line->icon);
EditSubscriber::mouseOverGeometry.push_back(begin_c);
EditSubscriber::mouseOverGeometry.push_back(end_c);
}
void ControlLineManager::removeAll(void)
{
for (unsigned int i = 0; i < control_lines.size(); i++) {
delete control_lines[i]->begin;
delete control_lines[i]->end;
delete control_lines[i]->line;
delete control_lines[i]->icon_h;
delete control_lines[i]->icon_v;
delete control_lines[i];
}
control_lines.clear();
visibleGeometry.clear();
mouseOverGeometry.erase(mouseOverGeometry.begin() + 1, mouseOverGeometry.end());
}
void ControlLineManager::removeLine(size_t line_id)
{
if (line_id >= control_lines.size()) {
return;
}
::ControlLine* line = control_lines[line_id];
delete line->begin;
delete line->end;
delete line->line;
delete line->icon_h;
delete line->icon_v;
delete line;
control_lines.erase(control_lines.begin() + line_id);
visibleGeometry.erase(visibleGeometry.begin() + ::ControlLine::OBJ_COUNT * line_id,
visibleGeometry.begin() + ::ControlLine::OBJ_COUNT * line_id + ::ControlLine::OBJ_COUNT);
mouseOverGeometry.erase(mouseOverGeometry.begin() + ::ControlLine::OBJ_COUNT * line_id + 1,
mouseOverGeometry.begin() + ::ControlLine::OBJ_COUNT * line_id + ::ControlLine::OBJ_COUNT + 1);
}
rtengine::ControlLine* ControlLineManager::toControlLines(void) const
{
auto retval = (rtengine::ControlLine*)malloc(control_lines.size() * sizeof(rtengine::ControlLine));
for (unsigned int i = 0; i < control_lines.size(); i++) {
retval[i].x1 = control_lines[i]->begin->center.x;
retval[i].y1 = control_lines[i]->begin->center.y;
retval[i].x2 = control_lines[i]->end->center.x;
retval[i].y2 = control_lines[i]->end->center.y;
retval[i].type = control_lines[i]->type;
}
return retval;
}
LinesCallbacks::LinesCallbacks(PerspCorrection* tool, ControlLineManager* lines):
lines(lines),
tool(tool)
{
}
LinesCallbacks::~LinesCallbacks()
{
}
void LinesCallbacks::switchOffEditMode(void)
{
if (tool) {
tool->switchOffEditMode(lines);
}
}