From 6b7c1871b9bd14e0bd6aa854953b46c032969033 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Mon, 8 Jun 2020 18:21:12 -0700 Subject: [PATCH 01/27] Implement basic perspective control lines (WIP) --- rtengine/iptransform.cc | 7 +- rtengine/perspectivecorrection.cc | 125 +++++--- rtengine/perspectivecorrection.h | 14 +- rtengine/procparams.cc | 2 + rtengine/procparams.h | 1 + rtgui/cropwindow.cc | 7 +- rtgui/editcallbacks.h | 6 +- rtgui/lensgeomlistener.h | 11 +- rtgui/perspective.cc | 464 ++++++++++++++++++++++++++++++ rtgui/perspective.h | 98 +++++++ rtgui/toolpanelcoord.cc | 16 +- rtgui/toolpanelcoord.h | 3 +- 12 files changed, 703 insertions(+), 51 deletions(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index eb823d711..aa22aac56 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -454,7 +454,7 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, double cost = cos (params->rotate.degree * rtengine::RT_PI / 180.0); double sint = sin (params->rotate.degree * rtengine::RT_PI / 180.0); - double ascale = ascaleDef > 0 ? ascaleDef : (params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0); + double ascale = ascaleDef > 0 ? ascaleDef : (params->commonTrans.autofill && params->perspective.render ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0); // auxiliary variables for perspective correction // Simple. @@ -1185,7 +1185,7 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I p_projection_rotate, p_projection_shift_horiz, p_projection_shift_vert, p_projection_scale); - const double ascale = params->commonTrans.autofill ? getTransformAutoFill(oW, oH, pLCPMap) : 1.0; + const double ascale = params->commonTrans.autofill && params->perspective.render ? getTransformAutoFill(oW, oH, pLCPMap) : 1.0; const bool darkening = (params->vignetting.amount <= 0.0); const bool useLog = params->commonTrans.method == "log" && highQuality; @@ -1477,7 +1477,8 @@ bool ImProcFunctions::needsPerspective () const { return ( (params->perspective.method == "simple") && (params->perspective.horizontal || params->perspective.vertical) ) - || ( (params->perspective.method == "camera_based") && ( + || ( (params->perspective.method == "camera_based") && + params->perspective.render && ( params->perspective.camera_pitch || params->perspective.camera_roll || params->perspective.camera_shift_horiz || diff --git a/rtengine/perspectivecorrection.cc b/rtengine/perspectivecorrection.cc index 5fa7b32f3..2c8d9e9f2 100644 --- a/rtengine/perspectivecorrection.cc +++ b/rtengine/perspectivecorrection.cc @@ -225,10 +225,44 @@ void get_view_size(int w, int h, const procparams::PerspectiveParams ¶ms, do } */ +/** + * Allocates a new array and populates it with ashift lines corresponding to the + * provided control lines. + */ +dt_iop_ashift_line_t* toAshiftLines(const ControlLine *lines, size_t count) +{ + auto retval = (dt_iop_ashift_line_t*)malloc(count * sizeof(dt_iop_ashift_line_t)); + + for (size_t i = 0; i < count; i++) { + const float x1 = lines[i].x1; + const float y1 = lines[i].y1; + const float x2 = lines[i].x2; + const float y2 = lines[i].y2; + retval[i].p1[0] = x1; + retval[i].p1[1] = y1; + retval[i].p1[2] = 1.0f; + retval[i].p2[0] = x2; + retval[i].p2[1] = y2; + retval[i].p2[2] = 1.0f; + retval[i].length = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + retval[i].width = 1.0f; + retval[i].weight = retval[i].length; + if (lines[i].type == ControlLine::HORIZONTAL) { + retval[i].type = ASHIFT_LINE_HORIZONTAL_SELECTED; + } else if (lines[i].type == ControlLine::VERTICAL) { + retval[i].type = ASHIFT_LINE_VERTICAL_SELECTED; + } else { + retval[i].type = ASHIFT_LINE_IRRELEVANT; + } + } + + return retval; +} + } // namespace -PerspectiveCorrection::Params PerspectiveCorrection::autocompute(ImageSource *src, bool corr_pitch, bool corr_yaw, const procparams::ProcParams *pparams, const FramesMetaData *metadata) +PerspectiveCorrection::Params PerspectiveCorrection::autocompute(ImageSource *src, bool corr_pitch, bool corr_yaw, const procparams::ProcParams *pparams, const FramesMetaData *metadata, const ControlLine *control_lines, size_t control_lines_count) { auto pcp = procparams::PerspectiveParams(pparams->perspective); procparams::PerspectiveParams dflt; @@ -252,49 +286,51 @@ PerspectiveCorrection::Params PerspectiveCorrection::autocompute(ImageSource *sr int tr = getCoarseBitMask(pparams->coarse); int fw, fh; src->getFullSize(fw, fh, tr); - int skip = max(float(max(fw, fh)) / 900.f + 0.5f, 1.f); - PreviewProps pp(0, 0, fw, fh, skip); - int w, h; - src->getSize(pp, w, h); - std::unique_ptr img(new Imagefloat(w, h)); + if (control_lines == nullptr) { + int skip = max(float(max(fw, fh)) / 900.f + 0.5f, 1.f); + PreviewProps pp(0, 0, fw, fh, skip); + int w, h; + src->getSize(pp, w, h); + std::unique_ptr img(new Imagefloat(w, h)); - ProcParams neutral; - neutral.raw.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::FAST); - neutral.raw.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST); - neutral.icm.outputProfile = ColorManagementParams::NoICMString; - src->getImage(src->getWB(), tr, img.get(), pp, neutral.toneCurve, neutral.raw); - src->convertColorSpace(img.get(), pparams->icm, src->getWB()); + ProcParams neutral; + neutral.raw.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::FAST); + neutral.raw.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST); + neutral.icm.outputProfile = ColorManagementParams::NoICMString; + src->getImage(src->getWB(), tr, img.get(), pp, neutral.toneCurve, neutral.raw); + src->convertColorSpace(img.get(), pparams->icm, src->getWB()); - neutral.commonTrans.autofill = false; // Ensures crop factor is correct. - // TODO: Ensure image borders of rotated image do not get detected as lines. - neutral.rotate = pparams->rotate; - neutral.distortion = pparams->distortion; - neutral.lensProf = pparams->lensProf; - ImProcFunctions ipf(&neutral, true); - if (ipf.needsTransform(w, h, src->getRotateDegree(), src->getMetaData())) { - Imagefloat *tmp = new Imagefloat(w, h); - ipf.transform(img.get(), tmp, 0, 0, 0, 0, w, h, w, h, - src->getMetaData(), src->getRotateDegree(), false); - img.reset(tmp); - } + neutral.commonTrans.autofill = false; // Ensures crop factor is correct. + // TODO: Ensure image borders of rotated image do not get detected as lines. + neutral.rotate = pparams->rotate; + neutral.distortion = pparams->distortion; + neutral.lensProf = pparams->lensProf; + ImProcFunctions ipf(&neutral, true); + if (ipf.needsTransform(w, h, src->getRotateDegree(), src->getMetaData())) { + Imagefloat *tmp = new Imagefloat(w, h); + ipf.transform(img.get(), tmp, 0, 0, 0, 0, w, h, w, h, + src->getMetaData(), src->getRotateDegree(), false); + img.reset(tmp); + } - // allocate the gui buffer - g.buf = static_cast(malloc(sizeof(float) * w * h * 4)); - g.buf_width = w; - g.buf_height = h; + // allocate the gui buffer + g.buf = static_cast(malloc(sizeof(float) * w * h * 4)); + g.buf_width = w; + g.buf_height = h; + + img->normalizeFloatTo1(); - img->normalizeFloatTo1(); - #ifdef _OPENMP # pragma omp parallel for #endif - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x) { - int i = (y * w + x) * 4; - g.buf[i] = img->r(y, x); - g.buf[i+1] = img->g(y, x); - g.buf[i+2] = img->b(y, x); - g.buf[i+3] = 1.f; + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + int i = (y * w + x) * 4; + g.buf[i] = img->r(y, x); + g.buf[i+1] = img->g(y, x); + g.buf[i+2] = img->b(y, x); + g.buf[i+3] = 1.f; + } } } @@ -311,7 +347,20 @@ PerspectiveCorrection::Params PerspectiveCorrection::autocompute(ImageSource *sr // internally! srand(1); - auto res = do_get_structure(&module, &p, ASHIFT_ENHANCE_EDGES) && do_fit(&module, &p, fitaxis); + bool res; + if (control_lines == nullptr) { + res = do_get_structure(&module, &p, ASHIFT_ENHANCE_EDGES) && do_fit(&module, &p, fitaxis); + } else { + dt_iop_ashift_gui_data_t *g = module.gui_data; + g->lines_count = control_lines_count; + g->lines = toAshiftLines(control_lines, control_lines_count); + g->lines_in_height = fh; + g->lines_in_width = fw; + // A hack. + g->horizontal_count = 4; + g->vertical_count = 4; + res = do_fit(&module, &p, fitaxis); + } Params retval = { .angle = p.rotation, .pitch = p.camera_pitch, diff --git a/rtengine/perspectivecorrection.h b/rtengine/perspectivecorrection.h index bf7cfa08d..94a63a42c 100644 --- a/rtengine/perspectivecorrection.h +++ b/rtengine/perspectivecorrection.h @@ -26,6 +26,18 @@ namespace rtengine { +class ControlLine +{ +public: + enum Type + { + HORIZONTAL, + VERTICAL + }; + float x1, y1, x2, y2; + Type type; +}; + class PerspectiveCorrection { public: struct Params @@ -35,7 +47,7 @@ public: double yaw; }; - static Params autocompute(ImageSource *src, bool corr_pitch, bool corr_yaw, const procparams::ProcParams *pparams, const FramesMetaData *metadata); + static Params autocompute(ImageSource *src, bool corr_pitch, bool corr_yaw, const procparams::ProcParams *pparams, const FramesMetaData *metadata, const ControlLine *control_lines = nullptr, size_t control_lines_count = 0); //static void autocrop(int width, int height, bool fixratio, const procparams::PerspectiveParams ¶ms, const FramesMetaData *metadata, int &x, int &y, int &w, int &h); }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index af48d027a..e00e50186 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1860,6 +1860,7 @@ LensProfParams::LcMode LensProfParams::getMethodNumber(const Glib::ustring& mode PerspectiveParams::PerspectiveParams() : method("simple"), + render(true), horizontal(0.0), vertical(0.0), camera_crop_factor(0.0), @@ -1881,6 +1882,7 @@ bool PerspectiveParams::operator ==(const PerspectiveParams& other) const { return method == other.method + && render == other.render && horizontal == other.horizontal && vertical == other.vertical && camera_focal_length == other.camera_focal_length diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 8a95032e8..b6d2b9c3a 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -911,6 +911,7 @@ struct LensProfParams { */ struct PerspectiveParams { Glib::ustring method; + bool render; double horizontal; double vertical; double camera_crop_factor; diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index d87876cec..975bac15c 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -429,6 +429,8 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) state = SEditPick1; pickedObject = iarea->getObject(); pickModifierKey = bstate; + } else { + state = SCropImgMove; } press_x = x; press_y = y; @@ -764,7 +766,10 @@ void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y) iarea->setObject(ObjectMOBuffer::getObjectID(cropPos)); - bool elemPicked = iarea->getObject() == pickedObject && bstate == pickModifierKey; + int buttonMask = ((state == SEditPick1) ? GDK_BUTTON1_MASK : 0) + | ((state == SEditPick2) ? GDK_BUTTON2_MASK : 0) + | ((state == SEditPick3) ? GDK_BUTTON3_MASK : 0); + bool elemPicked = iarea->getObject() == pickedObject && bstate == (pickModifierKey | buttonMask); if (state == SEditPick1) { needRedraw = editSubscriber->pick1 (elemPicked); diff --git a/rtgui/editcallbacks.h b/rtgui/editcallbacks.h index ee357c2f7..c2efcf53e 100644 --- a/rtgui/editcallbacks.h +++ b/rtgui/editcallbacks.h @@ -129,19 +129,19 @@ public: @param picked True if the cursor is still above the the same object than on button pressed and with the same modifier keys. If false, the user moved the cursor away or the modifier key is different, so the element is considered as NOT selected. @return true if the preview has to be redrawn, false otherwise */ - bool pick1 (bool picked); + virtual bool pick1 (bool picked); /** @brief Triggered when the user is releasing mouse button 2 while in action==ES_ACTION_PICKING mode @param picked True if the cursor is still above the the same object than on button pressed and with the same modifier keys. If false, the user moved the cursor away or the modifier key is different, so the element is considered as NOT selected. @return true if the preview has to be redrawn, false otherwise */ - bool pick2 (bool picked); + virtual bool pick2 (bool picked); /** @brief Triggered when the user is releasing mouse button 3 while in action==ES_ACTION_PICKING mode @param picked True if the cursor is still above the the same object than on button pressed and with the same modifier keys. If false, the user moved the cursor away or the modifier key is different, so the element is considered as NOT selected. @return true if the preview has to be redrawn, false otherwise */ - bool pick3 (bool picked); + virtual bool pick3 (bool picked); /** @brief Get the geometry to be shown to the user */ const std::vector& getVisibleGeometry (); diff --git a/rtgui/lensgeomlistener.h b/rtgui/lensgeomlistener.h index 810b7ed98..9bbaf125a 100644 --- a/rtgui/lensgeomlistener.h +++ b/rtgui/lensgeomlistener.h @@ -18,6 +18,14 @@ */ #pragma once +#include + +namespace rtengine +{ +class ControlLine; +class ProcEvent; +} + class LensGeomListener { public: @@ -25,5 +33,6 @@ public: virtual void straightenRequested () = 0; virtual void autoCropRequested () = 0; virtual double autoDistorRequested () = 0; - virtual void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw) = 0; + virtual void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const rtengine::ControlLine *lines = nullptr, size_t line_count = 0) = 0; + virtual void updateTransformPreviewRequested (rtengine::ProcEvent event, bool render_perspective) = 0; }; diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 0607a7604..1b1672914 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -37,6 +37,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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); lens_geom_listener = nullptr; Gtk::Image* ipersHL = Gtk::manage (new RTImage ("perspective-horizontal-left-small.png")); @@ -107,6 +108,31 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" -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->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->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->signal_toggled().connect(sigc::mem_fun( + *this, &::PerspCorrection::linesEditButtonPressed)); + + lines = new ControlLineManager(); + lines->callbacks = new LinesCallbacks(this, lines); + + img_ctrl_lines_apply = NULL; + img_ctrl_lines_edit = NULL; + + Gtk::HBox* control_lines_box = Gtk::manage (new Gtk::HBox()); + control_lines_box->pack_start(*lines_button_v); + control_lines_box->pack_start(*lines_button_h); + control_lines_box->pack_start(*lines_button_edit); + // 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) ); @@ -164,6 +190,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" camera_vbox->pack_start (*camera_roll); camera_vbox->pack_start (*camera_pitch); camera_vbox->pack_start (*camera_yaw); + camera_vbox->pack_start (*control_lines_box); camera_vbox->pack_start (*auto_hbox); camera_frame->add(*camera_vbox); camera_based->pack_start(*camera_frame); @@ -192,6 +219,18 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" show_all(); } +PerspCorrection::~PerspCorrection() +{ + delete lines->callbacks; + delete lines; + if (img_ctrl_lines_apply) { + delete img_ctrl_lines_apply; + } + if (img_ctrl_lines_edit) { + delete img_ctrl_lines_edit; + } +} + void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited) { @@ -242,6 +281,8 @@ void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited) 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 (); @@ -387,6 +428,39 @@ void PerspCorrection::adjusterChanged(Adjuster* a, double newval) } } +void PerspCorrection::applyControlLines(void) +{ + if (!lens_geom_listener) { + return; + } + + auto control_lines = lines->toControlLines(); + int h_count = 0, v_count = 0; + double rot = 0; + double pitch = 0; + double yaw = 0; + + 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) { @@ -548,3 +622,393 @@ void PerspCorrection::setFocalLengthValue (const ProcParams* pparams, const Fram 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); + } + } else { // Leave edit mode. + 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); + applyControlLines(); + } +} + +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); +} + +ControlLineManager::~ControlLineManager() +{ + for (auto i = mouseOverGeometry.begin(); i != mouseOverGeometry.end(); i++) { + delete *i; + } + for (auto i = control_lines.begin(); i != control_lines.end(); i++) { + 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. + 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 false; +} + +bool ControlLineManager::button1Released(void) +{ + action = Action::NONE; + 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::pick3(bool picked) +{ + if (!picked) { + return false; + } + + EditDataProvider* provider = getEditProvider(); + + if (!provider) { + return false; + } + + removeLine((provider->getObject() - 1) / 3); + 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) / 3]; + int component = selected_object % 3; // 0 == end, 1 == line, 2 == 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 (2): // 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; + } + + 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 { // 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) / 3]); + visibleGeometry[prev_obj - 1]->state = state; + } + + prev_obj = cur_obj; + + return false; +} + +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; + Circle *begin_c, *end_c; + + line = new Line(); + line->datum = Geometry::IMAGE; + line->innerLineWidth = line_width; + line->begin = begin; + line->end = end; + + 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; + + EditSubscriber::visibleGeometry.push_back(line); + EditSubscriber::visibleGeometry.push_back(begin_c); + EditSubscriber::visibleGeometry.push_back(end_c); + + EditSubscriber::mouseOverGeometry.push_back(line); + EditSubscriber::mouseOverGeometry.push_back(begin_c); + EditSubscriber::mouseOverGeometry.push_back(end_c); + + ::ControlLine* control_line = new ::ControlLine(); + control_line->begin = begin_c; + control_line->end = end_c; + control_line->line = line; + control_line->type = draw_line_type; + control_lines.push_back(control_line); +} + +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; + control_lines.erase(control_lines.begin() + line_id); + visibleGeometry.erase(visibleGeometry.begin() + 3 * line_id, + visibleGeometry.begin() + 3 * line_id + 3); + mouseOverGeometry.erase(mouseOverGeometry.begin() + 3 * line_id + 1, + mouseOverGeometry.begin() + 3 * line_id + 4); +} + +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); + } +} diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 1be392f7c..8a9933b19 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -21,8 +21,80 @@ #include #include "adjuster.h" +#include "editcallbacks.h" +#include "editwidgets.h" #include "lensgeomlistener.h" #include "toolpanel.h" +#include "../rtengine/coord.h" +#include "../rtengine/perspectivecorrection.h" + +struct ControlLine +{ + Line* line; + Circle *begin, *end; + rtengine::ControlLine::Type type; +}; + +class ControlLineManager: EditSubscriber +{ + +protected: + /** Determine how horizontal and vertical lines are displayed. */ + bool active_h, active_v; + /** Hidden object for capturing mouse events. */ + Rectangle* canvas_area; + rtengine::Coord drag_delta; + std::vector control_lines; + CursorShape cursor; + bool draw_mode; + int prev_obj; + int selected_object; + + void addLine (rtengine::Coord begin, rtengine::Coord end); + Geometry::State calcLineState(const ControlLine& line) const; + void removeLine (size_t line_id); + +public: + class Callbacks + { + public: + virtual ~Callbacks() {}; + /** Called when the EditSubscriber's switchOffEditMode is called. */ + virtual void switchOffEditMode (void) {}; + }; + + /** Callbacks to invoke. */ + Callbacks* callbacks; + /** Type of line for newly drawn lines. */ + rtengine::ControlLine::Type draw_line_type; + + ControlLineManager(); + ~ControlLineManager(); + + /** Sets whether or not the lines are visible and interact-able. */ + void setActive (bool active); + /** Set whether or not lines can be drawn and deleted. */ + void setDrawMode (bool draw); + void setEditProvider (EditDataProvider* provider); + /** Determines how each line type is displayed. */ + void setLinesState (bool horiz_active, bool vert_active); + /** Returns the number of lines. */ + size_t size (void) const; + /** + * Allocates a new array and populates it with copies of the control lines. + */ + rtengine::ControlLine* toControlLines (void) const; + + // EditSubscriber overrides + bool button1Pressed (int modifierKey) override; + bool button1Released (void) override; + bool button3Pressed (int modifierKey) override; + bool pick3 (bool picked) override; + bool drag1 (int modifierKey) override; + CursorShape getCursor (int objectID) const override; + bool mouseOver (int modifierKey) override; + void switchOffEditMode (void) override; +}; class PerspCorrection final : public ToolParamBlock, @@ -31,6 +103,7 @@ class PerspCorrection final : { protected: + bool render = true; MyComboBoxText* method; Gtk::VBox* simple; Adjuster* horiz; @@ -46,6 +119,12 @@ protected: Adjuster* camera_shift_horiz; Adjuster* camera_shift_vert; Adjuster* camera_yaw; + Gtk::Image* img_ctrl_lines_edit; + Gtk::Image* img_ctrl_lines_apply; + ControlLineManager* lines; + Gtk::ToggleButton* lines_button_edit; + Gtk::ToggleButton* lines_button_h; + Gtk::ToggleButton* lines_button_v; Adjuster* projection_pitch; Adjuster* projection_rotate; Adjuster* projection_shift_horiz; @@ -58,14 +137,17 @@ protected: rtengine::ProcEvent EvPerspProjShift; rtengine::ProcEvent EvPerspProjRotate; rtengine::ProcEvent EvPerspProjAngle; + rtengine::ProcEvent EvPerspRender; LensGeomListener* lens_geom_listener; const rtengine::FramesMetaData* metadata; + void applyControlLines (void); void setFocalLengthValue (const rtengine::procparams::ProcParams* pparams, const rtengine::FramesMetaData* metadata); public: PerspCorrection (); + ~PerspCorrection (); void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; @@ -74,12 +156,28 @@ public: void adjusterChanged (Adjuster* a, double newval) override; void autoCorrectionPressed (Gtk::Button* b); + void linesButtonPressed (Gtk::ToggleButton* button); + void linesEditButtonPressed (void); void methodChanged (void); void 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); + void setEditProvider (EditDataProvider* provider) override; void setLensGeomListener (LensGeomListener* listener) { lens_geom_listener = listener; } void setMetadata (const rtengine::FramesMetaData* metadata); + void switchOffEditMode (ControlLineManager* lines); void trimValues (rtengine::procparams::ProcParams* pp) override; }; + +class LinesCallbacks: public ControlLineManager::Callbacks +{ +protected: + ControlLineManager* lines; + PerspCorrection* tool; + +public: + LinesCallbacks(PerspCorrection* tool, ControlLineManager* lines); + ~LinesCallbacks(); + void switchOffEditMode (void) override; +}; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index dadc56d44..d02dd0237 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -860,7 +860,7 @@ void ToolPanelCoordinator::straightenRequested () toolBar->setTool (TMStraighten); } -void ToolPanelCoordinator::autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw) +void ToolPanelCoordinator::autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const rtengine::ControlLine *lines, size_t line_count) { if (!(ipc && (corr_pitch || corr_yaw))) { return; @@ -874,7 +874,7 @@ void ToolPanelCoordinator::autoPerspRequested (bool corr_pitch, bool corr_yaw, d rtengine::procparams::ProcParams params; ipc->getParams(¶ms); - auto res = rtengine::PerspectiveCorrection::autocompute(src, corr_pitch, corr_yaw, ¶ms, src->getMetaData()); + auto res = rtengine::PerspectiveCorrection::autocompute(src, corr_pitch, corr_yaw, ¶ms, src->getMetaData(), lines, line_count); rot = res.angle; pitch = res.pitch; yaw = res.yaw; @@ -889,6 +889,16 @@ double ToolPanelCoordinator::autoDistorRequested () return rtengine::ImProcFunctions::getAutoDistor (ipc->getInitialImage()->getFileName(), 400); } +void ToolPanelCoordinator::updateTransformPreviewRequested(rtengine::ProcEvent event, bool render_perspective) +{ + if (!ipc) { + return; + } + + ipc->beginUpdateParams()->perspective.render = render_perspective; + ipc->endUpdateParams(event); +} + void ToolPanelCoordinator::spotWBRequested (int size) { @@ -1101,4 +1111,4 @@ bool ToolPanelCoordinator::getFilmNegativeExponents(rtengine::Coord spotA, rteng bool ToolPanelCoordinator::getRawSpotValues(rtengine::Coord spot, int spotSize, std::array& rawValues) { return ipc && ipc->getRawSpotValues(spot.x, spot.y, spotSize, rawValues); -} \ No newline at end of file +} diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 3861052cd..fe65a0745 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -302,8 +302,9 @@ public: // rotatelistener interface void straightenRequested () override; void autoCropRequested () override; - void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw) override; + void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const rtengine::ControlLine *lines = nullptr, size_t line_count = 0) override; double autoDistorRequested () override; + void updateTransformPreviewRequested (rtengine::ProcEvent event, bool render_perspective) override; // spotwblistener interface void spotWBRequested (int size) override; From 5e5a86f513d61615d32cd7cf6ec50287e0231418 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Tue, 9 Jun 2020 16:29:57 -0700 Subject: [PATCH 02/27] Add icons for perspective control line buttons --- rtdata/images/svg/draw-horizontal.svg | 117 ++++++++++++++++++++++++++ rtdata/images/svg/draw-vertical.svg | 117 ++++++++++++++++++++++++++ rtdata/images/svg/draw.svg | 112 ++++++++++++++++++++++++ rtgui/perspective.cc | 11 ++- 4 files changed, 355 insertions(+), 2 deletions(-) create mode 100644 rtdata/images/svg/draw-horizontal.svg create mode 100644 rtdata/images/svg/draw-vertical.svg create mode 100644 rtdata/images/svg/draw.svg diff --git a/rtdata/images/svg/draw-horizontal.svg b/rtdata/images/svg/draw-horizontal.svg new file mode 100644 index 000000000..ae8fa8c44 --- /dev/null +++ b/rtdata/images/svg/draw-horizontal.svg @@ -0,0 +1,117 @@ + + + + + + + + + + image/svg+xml + + + + + Lawrence + + + + + + + + RawTherapee icon. + + + + + + + + + + + + + + + + + + diff --git a/rtdata/images/svg/draw-vertical.svg b/rtdata/images/svg/draw-vertical.svg new file mode 100644 index 000000000..4613d95c6 --- /dev/null +++ b/rtdata/images/svg/draw-vertical.svg @@ -0,0 +1,117 @@ + + + + + + + + + + image/svg+xml + + + + + Lawrence + + + + + + + + RawTherapee icon. + + + + + + + + + + + + + + + + + + diff --git a/rtdata/images/svg/draw.svg b/rtdata/images/svg/draw.svg new file mode 100644 index 000000000..308e15654 --- /dev/null +++ b/rtdata/images/svg/draw.svg @@ -0,0 +1,112 @@ + + + + + + + + + + image/svg+xml + + + + + Lawrence + + + + + + + + RawTherapee icon. + + + + + + + + + + + + + + + + + diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 1b1672914..1d72011fa 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -40,6 +40,10 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" EvPerspRender = mapper->newEvent(TRANSFORM); 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* 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")); @@ -110,22 +114,25 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" // 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 = new ControlLineManager(); lines->callbacks = new LinesCallbacks(this, lines); - img_ctrl_lines_apply = NULL; - img_ctrl_lines_edit = NULL; + 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->pack_start(*lines_button_v); From dd4364fab3e0a4699de133125a1d6bc8d1c619aa Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 10 Jun 2020 11:41:15 -0700 Subject: [PATCH 03/27] Add buttons for switching control line type --- ...irectional-arrow-horizontal-hicontrast.svg | 127 ++++++++++++++++++ ...idirectional-arrow-horizontal-prelight.svg | 127 ++++++++++++++++++ ...idirectional-arrow-vertical-hicontrast.svg | 127 ++++++++++++++++++ .../bidirectional-arrow-vertical-prelight.svg | 127 ++++++++++++++++++ rtgui/perspective.cc | 127 ++++++++++++++---- rtgui/perspective.h | 6 + 6 files changed, 617 insertions(+), 24 deletions(-) create mode 100644 rtdata/images/svg/bidirectional-arrow-horizontal-hicontrast.svg create mode 100644 rtdata/images/svg/bidirectional-arrow-horizontal-prelight.svg create mode 100644 rtdata/images/svg/bidirectional-arrow-vertical-hicontrast.svg create mode 100644 rtdata/images/svg/bidirectional-arrow-vertical-prelight.svg diff --git a/rtdata/images/svg/bidirectional-arrow-horizontal-hicontrast.svg b/rtdata/images/svg/bidirectional-arrow-horizontal-hicontrast.svg new file mode 100644 index 000000000..b41979c6a --- /dev/null +++ b/rtdata/images/svg/bidirectional-arrow-horizontal-hicontrast.svg @@ -0,0 +1,127 @@ + + + + + + + + + + image/svg+xml + + + + + Lawrence + + + + + + + + RawTherapee icon. + + + + + + + + + + + + + + + + + + diff --git a/rtdata/images/svg/bidirectional-arrow-horizontal-prelight.svg b/rtdata/images/svg/bidirectional-arrow-horizontal-prelight.svg new file mode 100644 index 000000000..0ad40b970 --- /dev/null +++ b/rtdata/images/svg/bidirectional-arrow-horizontal-prelight.svg @@ -0,0 +1,127 @@ + + + + + + + + + + image/svg+xml + + + + + Lawrence + + + + + + + + RawTherapee icon. + + + + + + + + + + + + + + + + + + diff --git a/rtdata/images/svg/bidirectional-arrow-vertical-hicontrast.svg b/rtdata/images/svg/bidirectional-arrow-vertical-hicontrast.svg new file mode 100644 index 000000000..18d041279 --- /dev/null +++ b/rtdata/images/svg/bidirectional-arrow-vertical-hicontrast.svg @@ -0,0 +1,127 @@ + + + + + + + + + + image/svg+xml + + + + + Lawrence + + + + + + + + RawTherapee icon. + + + + + + + + + + + + + + + + + + diff --git a/rtdata/images/svg/bidirectional-arrow-vertical-prelight.svg b/rtdata/images/svg/bidirectional-arrow-vertical-prelight.svg new file mode 100644 index 000000000..3dc5034f4 --- /dev/null +++ b/rtdata/images/svg/bidirectional-arrow-vertical-prelight.svg @@ -0,0 +1,127 @@ + + + + + + + + + + image/svg+xml + + + + + Lawrence + + + + + + + + RawTherapee icon. + + + + + + + + + + + + + + + + + + diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 1d72011fa..777278419 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -20,6 +20,7 @@ #include "perspective.h" #include "rtimage.h" +#include "rtsurface.h" #include "../rtengine/procparams.h" @@ -704,14 +705,22 @@ ControlLineManager::ControlLineManager(): canvas_area->filled = true; canvas_area->topLeft = Coord(0, 0); mouseOverGeometry.push_back(canvas_area); + + line_icon_h = Cairo::RefPtr(new RTSurface("bidirectional-arrow-horizontal-hicontrast.png")); + line_icon_v = Cairo::RefPtr(new RTSurface("bidirectional-arrow-vertical-hicontrast.png")); + line_icon_h_prelight = Cairo::RefPtr(new RTSurface("bidirectional-arrow-horizontal-prelight.png")); + line_icon_v_prelight = Cairo::RefPtr(new RTSurface("bidirectional-arrow-vertical-prelight.png")); } ControlLineManager::~ControlLineManager() { - for (auto i = mouseOverGeometry.begin(); i != mouseOverGeometry.end(); i++) { - delete *i; - } + 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; } } @@ -780,15 +789,19 @@ bool ControlLineManager::button1Pressed(int modifierKey) const int object = dataProvider->getObject(); if (object > 0) { // A control line. - selected_object = object; - action = Action::DRAGGING; + 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 false; + return true; } bool ControlLineManager::button1Released(void) @@ -812,6 +825,42 @@ bool ControlLineManager::button3Pressed(int modifierKey) 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) { if (!picked) { @@ -824,7 +873,7 @@ bool ControlLineManager::pick3(bool picked) return false; } - removeLine((provider->getObject() - 1) / 3); + removeLine((provider->getObject() - 1) / ::ControlLine::OBJ_COUNT); return false; } @@ -836,8 +885,8 @@ bool ControlLineManager::drag1(int modifierKey) return false; } - ::ControlLine* control_line = control_lines[(selected_object - 1) / 3]; - int component = selected_object % 3; // 0 == end, 1 == line, 2 == begin + ::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; @@ -850,7 +899,8 @@ bool ControlLineManager::drag1(int modifierKey) control_line->line->end = control_line->end->center; control_line->end->state = Geometry::DRAGGED; break; - case (1): { // line + case (1): + case (2): { // line // Constrain delta so the end stays above the image. Coord new_delta = control_line->end->center + delta; new_delta.clip(iw, ih); @@ -868,7 +918,7 @@ bool ControlLineManager::drag1(int modifierKey) control_line->line->state = Geometry::DRAGGED; break; } - case (2): // begin + case (3): // begin control_line->begin->center = mouse; control_line->begin->center.clip(iw, ih); control_line->line->begin = control_line->begin->center; @@ -876,6 +926,12 @@ bool ControlLineManager::drag1(int modifierKey) 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; } @@ -902,19 +958,22 @@ bool ControlLineManager::mouseOver(int modifierKey) } } 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) / 3]); + auto state = calcLineState(*control_lines[(prev_obj - 1) / ::ControlLine::OBJ_COUNT]); visibleGeometry[prev_obj - 1]->state = state; } prev_obj = cur_obj; - return false; + return true; } void ControlLineManager::switchOffEditMode(void) @@ -934,6 +993,7 @@ 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(); @@ -942,6 +1002,16 @@ void ControlLineManager::addLine(Coord begin, Coord end) line->begin = begin; line->end = end; + const Cairo::RefPtr null_surface = Cairo::RefPtr(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; @@ -954,20 +1024,29 @@ void ControlLineManager::addLine(Coord begin, Coord end) 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); - - ::ControlLine* control_line = new ::ControlLine(); - control_line->begin = begin_c; - control_line->end = end_c; - control_line->line = line; - control_line->type = draw_line_type; - control_lines.push_back(control_line); } void ControlLineManager::removeLine(size_t line_id) @@ -982,10 +1061,10 @@ void ControlLineManager::removeLine(size_t line_id) delete line->line; delete line; control_lines.erase(control_lines.begin() + line_id); - visibleGeometry.erase(visibleGeometry.begin() + 3 * line_id, - visibleGeometry.begin() + 3 * line_id + 3); - mouseOverGeometry.erase(mouseOverGeometry.begin() + 3 * line_id + 1, - mouseOverGeometry.begin() + 3 * line_id + 4); + 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 diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 8a9933b19..5956c20c8 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -30,7 +30,10 @@ struct ControlLine { + static constexpr int OBJ_COUNT = 4; Line* line; + OPIcon* icon; + OPIcon *icon_h, *icon_v; Circle *begin, *end; rtengine::ControlLine::Type type; }; @@ -47,6 +50,8 @@ protected: std::vector control_lines; CursorShape cursor; bool draw_mode; + Cairo::RefPtr line_icon_h, line_icon_v; + Cairo::RefPtr line_icon_h_prelight, line_icon_v_prelight; int prev_obj; int selected_object; @@ -89,6 +94,7 @@ public: bool button1Pressed (int modifierKey) override; bool button1Released (void) override; bool button3Pressed (int modifierKey) override; + bool pick1 (bool picked) override; bool pick3 (bool picked) override; bool drag1 (int modifierKey) override; CursorShape getCursor (int objectID) const override; From 72b69b3249d86909e1e254d7b3908fc1558b1f09 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 10 Jun 2020 12:35:53 -0700 Subject: [PATCH 04/27] Fix a few small control lines bugs --- rtgui/perspective.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 777278419..d141cb07a 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -807,6 +807,9 @@ bool ControlLineManager::button1Pressed(int modifierKey) bool ControlLineManager::button1Released(void) { action = Action::NONE; + if (selected_object > 0) { + mouseOverGeometry[selected_object]->state = Geometry::NORMAL; + } selected_object = -1; return false; } @@ -863,6 +866,8 @@ bool ControlLineManager::pick1(bool picked) bool ControlLineManager::pick3(bool picked) { + action = Action::NONE; + if (!picked) { return false; } From 11459a8bb1a94eaaae118539bf1c332f4af7313f Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 10 Jun 2020 12:50:14 -0700 Subject: [PATCH 05/27] Add label for control lines buttons --- rtdata/languages/default | 1 + rtgui/perspective.cc | 2 ++ 2 files changed, 3 insertions(+) diff --git a/rtdata/languages/default b/rtdata/languages/default index e1b4399f6..a0eccd167 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1868,6 +1868,7 @@ TP_PERSPECTIVE_CAMERA_ROLL;Rotation TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL;Horizontal shift TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL;Vertical shift TP_PERSPECTIVE_CAMERA_YAW;Horizontal +TP_PERSPECTIVE_CONTROL_LINES;Control lines TP_PERSPECTIVE_HORIZONTAL;Horizontal TP_PERSPECTIVE_LABEL;Perspective TP_PERSPECTIVE_METHOD;Method diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index d141cb07a..aade62911 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -136,6 +136,8 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" img_ctrl_lines_edit = ipers_draw; Gtk::HBox* control_lines_box = Gtk::manage (new Gtk::HBox()); + 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); From 976cf4c10388ddceba931f4369f9a79cdcbf26b4 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 10 Jun 2020 12:57:08 -0700 Subject: [PATCH 06/27] Make control lines buttons disabled in batch mode --- rtgui/perspective.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index aade62911..00d6cfa49 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -580,6 +580,9 @@ void PerspCorrection::setBatchMode (bool batchMode) 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); From a120b304f1bf2f24de1e5ff2adf014e5beb084c0 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 10 Jun 2020 13:13:18 -0700 Subject: [PATCH 07/27] Add tooltip for control lines --- rtdata/languages/default | 1 + rtgui/perspective.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/rtdata/languages/default b/rtdata/languages/default index a0eccd167..6589c3a43 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1869,6 +1869,7 @@ TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL;Horizontal shift TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL;Vertical shift TP_PERSPECTIVE_CAMERA_YAW;Horizontal TP_PERSPECTIVE_CONTROL_LINES;Control lines +TP_PERSPECTIVE_CONTROL_LINES_TOOLTIP;Correct perspective distortion by drawing control lines.\n\nTo draw a line, activate either the vertical or horizontal drawing button. Hold down the control key and click-and-drag to draw a line. At least two vertical lines are required for vertical perspective correction. The same is true for horizontal correction. Click the tick mark button to apply perspective correction. TP_PERSPECTIVE_HORIZONTAL;Horizontal TP_PERSPECTIVE_LABEL;Perspective TP_PERSPECTIVE_METHOD;Method diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 00d6cfa49..d84e1747e 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -136,6 +136,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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); From 72d390e99a158ce0d1037731281f6ce1fa1b646b Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 10 Jun 2020 16:16:40 -0700 Subject: [PATCH 08/27] Add button for deleting all control lines --- rtgui/perspective.cc | 28 ++++++++++++++++++++++++++++ rtgui/perspective.h | 3 +++ 2 files changed, 31 insertions(+) diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index d84e1747e..bffc2f301 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -44,6 +44,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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 = 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")); @@ -129,6 +130,12 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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); @@ -142,6 +149,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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 ()); @@ -688,7 +696,9 @@ void PerspCorrection::linesEditButtonPressed(void) if (lens_geom_listener) { lens_geom_listener->updateTransformPreviewRequested(EvPerspRender, false); } + lines_button_erase->set_sensitive(true); } else { // Leave edit mode. + lines_button_erase->set_sensitive(false); render = true; lines->setDrawMode(false); lines->setActive(false); @@ -701,6 +711,11 @@ void PerspCorrection::linesEditButtonPressed(void) } } +void PerspCorrection::linesEraseButtonPressed(void) +{ + lines->removeAll(); +} + ControlLineManager::ControlLineManager(): EditSubscriber(ET_OBJECTS), cursor(CSCrosshair), @@ -1060,6 +1075,19 @@ void ControlLineManager::addLine(Coord begin, Coord end) 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]; + } + 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()) { diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 5956c20c8..8ddc2c1b7 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -76,6 +76,7 @@ public: ControlLineManager(); ~ControlLineManager(); + void removeAll (void); /** Sets whether or not the lines are visible and interact-able. */ void setActive (bool active); /** Set whether or not lines can be drawn and deleted. */ @@ -129,6 +130,7 @@ protected: Gtk::Image* img_ctrl_lines_apply; ControlLineManager* lines; Gtk::ToggleButton* lines_button_edit; + Gtk::Button* lines_button_erase; Gtk::ToggleButton* lines_button_h; Gtk::ToggleButton* lines_button_v; Adjuster* projection_pitch; @@ -164,6 +166,7 @@ public: void autoCorrectionPressed (Gtk::Button* b); void linesButtonPressed (Gtk::ToggleButton* button); void linesEditButtonPressed (void); + void linesEraseButtonPressed (void); void methodChanged (void); void 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); void setEditProvider (EditDataProvider* provider) override; From 24681f324c70fedc6fcda81fe7150a19a873770c Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 10 Jun 2020 16:53:33 -0700 Subject: [PATCH 09/27] Remove unneeded code --- rtgui/perspective.cc | 3 +-- rtgui/perspective.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index bffc2f301..cf366a766 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -925,8 +925,7 @@ bool ControlLineManager::drag1(int modifierKey) control_line->line->end = control_line->end->center; control_line->end->state = Geometry::DRAGGED; break; - case (1): - case (2): { // line + 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); diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 8ddc2c1b7..89a743a50 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -25,7 +25,6 @@ #include "editwidgets.h" #include "lensgeomlistener.h" #include "toolpanel.h" -#include "../rtengine/coord.h" #include "../rtengine/perspectivecorrection.h" struct ControlLine From 6c59f0586fa930149fb0c2e069551f5451639070 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Thu, 11 Jun 2020 12:41:43 -0700 Subject: [PATCH 10/27] Fix minor memory leaks --- rtengine/perspectivecorrection.cc | 2 +- rtgui/perspective.cc | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rtengine/perspectivecorrection.cc b/rtengine/perspectivecorrection.cc index 2c8d9e9f2..5744aef50 100644 --- a/rtengine/perspectivecorrection.cc +++ b/rtengine/perspectivecorrection.cc @@ -371,7 +371,7 @@ PerspectiveCorrection::Params PerspectiveCorrection::autocompute(ImageSource *sr if (g.lines) free(g.lines); if (g.points) free(g.points); if (g.points_idx) free(g.points_idx); - free(g.buf); + if (g.buf) free(g.buf); if (!res) { retval.angle = pparams->perspective.camera_roll; diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index cf366a766..bd9027f33 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -44,7 +44,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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 = new RTImage ("trash-empty.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")); @@ -242,12 +242,8 @@ PerspCorrection::~PerspCorrection() { delete lines->callbacks; delete lines; - if (img_ctrl_lines_apply) { - delete img_ctrl_lines_apply; - } - if (img_ctrl_lines_edit) { - delete img_ctrl_lines_edit; - } + delete img_ctrl_lines_apply; + delete img_ctrl_lines_edit; } void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited) @@ -1080,6 +1076,8 @@ void ControlLineManager::removeAll(void) 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(); @@ -1097,6 +1095,8 @@ void ControlLineManager::removeLine(size_t 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, From 9a40c1485879454930496e3742212fa491f960a1 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Tue, 23 Jun 2020 21:57:10 -0700 Subject: [PATCH 11/27] Make proper use of minimum control line count When using control lines for perspective correction, set the number of vertical and horizontal lines to the proper values instead of hard-coding them to 4. The minimum line count is set to 2 when using control lines, and defaults to 4 when using fully-automatic correction. --- rtengine/ashift_dt.c | 17 +++++++++++------ rtengine/perspectivecorrection.cc | 6 ++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/rtengine/ashift_dt.c b/rtengine/ashift_dt.c index 3894a1c1b..ce19b6808 100644 --- a/rtengine/ashift_dt.c +++ b/rtengine/ashift_dt.c @@ -2111,7 +2111,7 @@ static double model_fitness(double *params, void *data) } // setup all data structures for fitting and call NM simplex -static dt_iop_ashift_nmsresult_t nmsfit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir) +static dt_iop_ashift_nmsresult_t nmsfit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir, int min_line_count) { dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; @@ -2227,7 +2227,7 @@ static dt_iop_ashift_nmsresult_t nmsfit(dt_iop_module_t *module, dt_iop_ashift_p // we use vertical lines for fitting fit.linetype |= ASHIFT_LINE_DIRVERT; fit.weight += g->vertical_weight; - enough_lines = enough_lines && (g->vertical_count >= MINIMUM_FITLINES); + enough_lines = enough_lines && (g->vertical_count >= min_line_count); } if(mdir & ASHIFT_FIT_LINES_HOR) @@ -2235,7 +2235,7 @@ static dt_iop_ashift_nmsresult_t nmsfit(dt_iop_module_t *module, dt_iop_ashift_p // we use horizontal lines for fitting fit.linetype |= 0; fit.weight += g->horizontal_weight; - enough_lines = enough_lines && (g->horizontal_count >= MINIMUM_FITLINES); + enough_lines = enough_lines && (g->horizontal_count >= min_line_count); } // this needs to come after ASHIFT_FIT_LINES_VERT and ASHIFT_FIT_LINES_HOR @@ -2905,7 +2905,7 @@ static int do_clean_structure(dt_iop_module_t *module, dt_iop_ashift_params_t *p //----------------------------------------------------------------------------- // helper function to start parameter fit and report about errors -static int do_fit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir) +static int do_fit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir, int min_line_count = MINIMUM_FITLINES) { dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; dt_iop_ashift_nmsresult_t res; @@ -2918,7 +2918,7 @@ static int do_fit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ash g->fitting = 1; - res = nmsfit(module, p, dir); + res = nmsfit(module, p, dir, min_line_count); switch(res) { @@ -3815,8 +3815,10 @@ void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, i cairo_restore(cr); } +#endif // if 0 +//----------------------------------------------------------------------------- -update the number of selected vertical and horizontal lines +// update the number of selected vertical and horizontal lines static void update_lines_count(const dt_iop_ashift_line_t *lines, const int lines_count, int *vertical_count, int *horizontal_count) { @@ -3835,6 +3837,9 @@ static void update_lines_count(const dt_iop_ashift_line_t *lines, const int line *horizontal_count = hlines; } +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which) { dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; diff --git a/rtengine/perspectivecorrection.cc b/rtengine/perspectivecorrection.cc index 5744aef50..9a1288aba 100644 --- a/rtengine/perspectivecorrection.cc +++ b/rtengine/perspectivecorrection.cc @@ -356,10 +356,8 @@ PerspectiveCorrection::Params PerspectiveCorrection::autocompute(ImageSource *sr g->lines = toAshiftLines(control_lines, control_lines_count); g->lines_in_height = fh; g->lines_in_width = fw; - // A hack. - g->horizontal_count = 4; - g->vertical_count = 4; - res = do_fit(&module, &p, fitaxis); + update_lines_count(g->lines, g->lines_count, &(g->vertical_count), &(g->horizontal_count)); + res = do_fit(&module, &p, fitaxis, 2); } Params retval = { .angle = p.rotation, From afc3edae26d98e2184a70754830e170dec479e84 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Tue, 23 Jun 2020 22:24:27 -0700 Subject: [PATCH 12/27] Make control line correction keep old values Perspective correction through control lines will no longer modify any parameters if there are not enough lines to do any correction. --- rtgui/perspective.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index bd9027f33..e58cec211 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -451,9 +451,9 @@ void PerspCorrection::applyControlLines(void) auto control_lines = lines->toControlLines(); int h_count = 0, v_count = 0; - double rot = 0; - double pitch = 0; - double yaw = 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) { From 8d7a5546e182d4c3c611a5a251ba5a4db713a172 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Tue, 23 Jun 2020 22:33:08 -0700 Subject: [PATCH 13/27] Add separators in camera-based persp correction Horizontal lines to separate the sliders, control lines buttons, and automatic buttons. --- rtgui/perspective.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index e58cec211..7ce981548 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -209,7 +209,9 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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); From d76cf5cdaebee53cae907ef190819abd5a546919 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 24 Jun 2020 16:20:03 -0700 Subject: [PATCH 14/27] Make control lines hidden when switching mode The program now exits control line editing mode when the perspective correction method is switched out of camera-based mode. --- rtgui/perspective.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 7ce981548..7048b31d1 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -517,6 +517,11 @@ void PerspCorrection::methodChanged (void) } 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) { @@ -705,7 +710,10 @@ void PerspCorrection::linesEditButtonPressed(void) } lines_button_h->set_active(false); lines_button_v->set_active(false); - applyControlLines(); + if (method->get_active_row_number() == 1) { + // Calculate perspective distortion if in camera-based mode. + applyControlLines(); + } } } From 1b06cb999934f97e9373fee1ce9dccfb1c25a33c Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 24 Jun 2020 17:06:21 -0700 Subject: [PATCH 15/27] 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. --- rtgui/perspective.cc | 43 +++++++++++++++++++++++++++++++++++++------ rtgui/perspective.h | 13 +++++++++++++ 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 7048b31d1..bdcc40a8b 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -31,6 +31,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" { 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"); @@ -39,6 +40,15 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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")); @@ -402,21 +412,21 @@ void PerspCorrection::adjusterChanged(Adjuster* a, double newval) M("TP_PERSPECTIVE_VERTICAL"), vert->getValue())); } else if (a == camera_focal_length || a == camera_crop_factor) { - listener->panelChanged (EvPerspCamFocalLength, + 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 (EvPerspCamShift, + 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 (EvPerspCamAngle, + 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(), @@ -425,17 +435,17 @@ void PerspCorrection::adjusterChanged(Adjuster* a, double newval) M("TP_PERSPECTIVE_CAMERA_PITCH"), camera_pitch->getValue())); } else if (a == projection_shift_horiz || a == projection_shift_vert) { - listener->panelChanged (EvPerspProjShift, + 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 (EvPerspProjRotate, + listener->panelChanged (*event_persp_proj_rotate, Glib::ustring::format(projection_rotate->getValue())); } else if (a == projection_pitch || a == projection_yaw) { - listener->panelChanged (EvPerspProjAngle, + listener->panelChanged (*event_persp_proj_angle, Glib::ustring::compose("%1=%2\n%3=%4", M("TP_PERSPECTIVE_PROJECTION_PITCH"), projection_pitch->getValue(), @@ -700,7 +710,9 @@ void PerspCorrection::linesEditButtonPressed(void) 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); @@ -722,6 +734,25 @@ 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), diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 89a743a50..dd9155c50 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -145,10 +145,23 @@ protected: rtengine::ProcEvent EvPerspProjRotate; rtengine::ProcEvent EvPerspProjAngle; rtengine::ProcEvent EvPerspRender; + rtengine::ProcEvent EvPerspCamFocalLengthVoid; + rtengine::ProcEvent EvPerspCamShiftVoid; + rtengine::ProcEvent EvPerspCamAngleVoid; + rtengine::ProcEvent EvPerspProjShiftVoid; + rtengine::ProcEvent EvPerspProjRotateVoid; + rtengine::ProcEvent EvPerspProjAngleVoid; + rtengine::ProcEvent* event_persp_cam_focal_length; + rtengine::ProcEvent* event_persp_cam_shift; + rtengine::ProcEvent* event_persp_cam_angle; + rtengine::ProcEvent* event_persp_proj_shift; + rtengine::ProcEvent* event_persp_proj_rotate; + rtengine::ProcEvent* event_persp_proj_angle; LensGeomListener* lens_geom_listener; const rtengine::FramesMetaData* metadata; void applyControlLines (void); + void setCamBasedEventsActive (bool active = true); void setFocalLengthValue (const rtengine::procparams::ProcParams* pparams, const rtengine::FramesMetaData* metadata); public: From 98678c1f48c822a105270b6d21b485c3cd051e98 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Sun, 5 Jul 2020 16:37:49 -0700 Subject: [PATCH 16/27] Use smart pointers --- rtengine/perspectivecorrection.cc | 26 ++-- rtengine/perspectivecorrection.h | 2 +- rtgui/lensgeomlistener.h | 3 +- rtgui/perspective.cc | 205 ++++++++++++------------------ rtgui/perspective.h | 30 ++--- rtgui/toolpanelcoord.cc | 4 +- rtgui/toolpanelcoord.h | 2 +- 7 files changed, 117 insertions(+), 155 deletions(-) diff --git a/rtengine/perspectivecorrection.cc b/rtengine/perspectivecorrection.cc index 9a1288aba..7a56ef5a8 100644 --- a/rtengine/perspectivecorrection.cc +++ b/rtengine/perspectivecorrection.cc @@ -229,15 +229,15 @@ void get_view_size(int w, int h, const procparams::PerspectiveParams ¶ms, do * Allocates a new array and populates it with ashift lines corresponding to the * provided control lines. */ -dt_iop_ashift_line_t* toAshiftLines(const ControlLine *lines, size_t count) +std::unique_ptr toAshiftLines(const std::vector *lines) { - auto retval = (dt_iop_ashift_line_t*)malloc(count * sizeof(dt_iop_ashift_line_t)); + std::unique_ptr retval(new dt_iop_ashift_line_t[lines->size()]); - for (size_t i = 0; i < count; i++) { - const float x1 = lines[i].x1; - const float y1 = lines[i].y1; - const float x2 = lines[i].x2; - const float y2 = lines[i].y2; + for (size_t i = 0; i < lines->size(); i++) { + const float x1 = (*lines)[i].x1; + const float y1 = (*lines)[i].y1; + const float x2 = (*lines)[i].x2; + const float y2 = (*lines)[i].y2; retval[i].p1[0] = x1; retval[i].p1[1] = y1; retval[i].p1[2] = 1.0f; @@ -247,9 +247,9 @@ dt_iop_ashift_line_t* toAshiftLines(const ControlLine *lines, size_t count) retval[i].length = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); retval[i].width = 1.0f; retval[i].weight = retval[i].length; - if (lines[i].type == ControlLine::HORIZONTAL) { + if ((*lines)[i].type == ControlLine::HORIZONTAL) { retval[i].type = ASHIFT_LINE_HORIZONTAL_SELECTED; - } else if (lines[i].type == ControlLine::VERTICAL) { + } else if ((*lines)[i].type == ControlLine::VERTICAL) { retval[i].type = ASHIFT_LINE_VERTICAL_SELECTED; } else { retval[i].type = ASHIFT_LINE_IRRELEVANT; @@ -262,7 +262,7 @@ dt_iop_ashift_line_t* toAshiftLines(const ControlLine *lines, size_t count) } // namespace -PerspectiveCorrection::Params PerspectiveCorrection::autocompute(ImageSource *src, bool corr_pitch, bool corr_yaw, const procparams::ProcParams *pparams, const FramesMetaData *metadata, const ControlLine *control_lines, size_t control_lines_count) +PerspectiveCorrection::Params PerspectiveCorrection::autocompute(ImageSource *src, bool corr_pitch, bool corr_yaw, const procparams::ProcParams *pparams, const FramesMetaData *metadata, const std::vector *control_lines) { auto pcp = procparams::PerspectiveParams(pparams->perspective); procparams::PerspectiveParams dflt; @@ -351,13 +351,15 @@ PerspectiveCorrection::Params PerspectiveCorrection::autocompute(ImageSource *sr if (control_lines == nullptr) { res = do_get_structure(&module, &p, ASHIFT_ENHANCE_EDGES) && do_fit(&module, &p, fitaxis); } else { + std::unique_ptr ashift_lines = toAshiftLines(control_lines); dt_iop_ashift_gui_data_t *g = module.gui_data; - g->lines_count = control_lines_count; - g->lines = toAshiftLines(control_lines, control_lines_count); + g->lines_count = control_lines->size(); + g->lines = ashift_lines.get(); g->lines_in_height = fh; g->lines_in_width = fw; update_lines_count(g->lines, g->lines_count, &(g->vertical_count), &(g->horizontal_count)); res = do_fit(&module, &p, fitaxis, 2); + g->lines = nullptr; } Params retval = { .angle = p.rotation, diff --git a/rtengine/perspectivecorrection.h b/rtengine/perspectivecorrection.h index 94a63a42c..c46c34147 100644 --- a/rtengine/perspectivecorrection.h +++ b/rtengine/perspectivecorrection.h @@ -47,7 +47,7 @@ public: double yaw; }; - static Params autocompute(ImageSource *src, bool corr_pitch, bool corr_yaw, const procparams::ProcParams *pparams, const FramesMetaData *metadata, const ControlLine *control_lines = nullptr, size_t control_lines_count = 0); + static Params autocompute(ImageSource *src, bool corr_pitch, bool corr_yaw, const procparams::ProcParams *pparams, const FramesMetaData *metadata, const std::vector *control_lines = nullptr); //static void autocrop(int width, int height, bool fixratio, const procparams::PerspectiveParams ¶ms, const FramesMetaData *metadata, int &x, int &y, int &w, int &h); }; diff --git a/rtgui/lensgeomlistener.h b/rtgui/lensgeomlistener.h index 9bbaf125a..b9979f9a2 100644 --- a/rtgui/lensgeomlistener.h +++ b/rtgui/lensgeomlistener.h @@ -19,6 +19,7 @@ #pragma once #include +#include namespace rtengine { @@ -33,6 +34,6 @@ public: virtual void straightenRequested () = 0; virtual void autoCropRequested () = 0; virtual double autoDistorRequested () = 0; - virtual void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const rtengine::ControlLine *lines = nullptr, size_t line_count = 0) = 0; + virtual void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const std::vector *lines = nullptr) = 0; virtual void updateTransformPreviewRequested (rtengine::ProcEvent event, bool render_perspective) = 0; }; diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 8031772fd..13fc170dc 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -54,7 +54,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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"); + std::unique_ptr 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")); @@ -147,11 +147,11 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" lines_button_erase->signal_pressed().connect(sigc::mem_fun( *this, &::PerspCorrection::linesEraseButtonPressed)); - lines = new ControlLineManager(); - lines->callbacks = new LinesCallbacks(this, lines); + lines = std::unique_ptr(new ControlLineManager()); + lines->callbacks = std::make_shared(this); - img_ctrl_lines_apply = new RTImage ("tick.png"); - img_ctrl_lines_edit = ipers_draw; + img_ctrl_lines_apply = std::unique_ptr(new RTImage ("tick.png")); + img_ctrl_lines_edit = std::move(ipers_draw); Gtk::HBox* control_lines_box = Gtk::manage (new Gtk::HBox()); control_lines_box->set_tooltip_text( M("TP_PERSPECTIVE_CONTROL_LINES_TOOLTIP") ); @@ -251,14 +251,6 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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) { @@ -462,12 +454,14 @@ void PerspCorrection::applyControlLines(void) return; } - auto control_lines = lines->toControlLines(); + std::vector control_lines; int h_count = 0, v_count = 0; double rot = camera_roll->getValue(); double pitch = camera_pitch->getValue(); double yaw = camera_yaw->getValue(); + lines->toControlLines(control_lines); + for (unsigned int i = 0; i < lines->size(); i++) { if (control_lines[i].type == rtengine::ControlLine::HORIZONTAL) { h_count++; @@ -476,9 +470,7 @@ void PerspCorrection::applyControlLines(void) } } lens_geom_listener->autoPerspRequested(v_count > 1, h_count > 1, rot, pitch, - yaw, control_lines, lines->size()); - - free(control_lines); + yaw, &control_lines); disableListener(); camera_pitch->setValue(pitch); @@ -659,7 +651,7 @@ void PerspCorrection::setFocalLengthValue (const ProcParams* pparams, const Fram } } -void PerspCorrection::switchOffEditMode(ControlLineManager* lines) +void PerspCorrection::switchOffEditMode(void) { lines_button_h->set_active(false); lines_button_v->set_active(false); @@ -760,10 +752,10 @@ ControlLineManager::ControlLineManager(): prev_obj(-1), selected_object(-1) { - canvas_area = new Rectangle(); + canvas_area = std::unique_ptr(new Rectangle()); canvas_area->filled = true; canvas_area->topLeft = Coord(0, 0); - mouseOverGeometry.push_back(canvas_area); + mouseOverGeometry.push_back(canvas_area.get()); line_icon_h = Cairo::RefPtr(new RTSurface("bidirectional-arrow-horizontal-hicontrast.png")); line_icon_v = Cairo::RefPtr(new RTSurface("bidirectional-arrow-vertical-hicontrast.png")); @@ -771,19 +763,6 @@ ControlLineManager::ControlLineManager(): line_icon_v_prelight = Cairo::RefPtr(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) { @@ -903,22 +882,22 @@ bool ControlLineManager::pick1(bool picked) // Change line type. int object_id = provider->getObject(); - auto line = control_lines[(object_id - 1) / ::ControlLine::OBJ_COUNT]; + ::ControlLine& 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; + 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; + visibleGeometry[object_id - 1] = line.icon.get(); - auto state = calcLineState(*line); - line->begin->state = state; - line->end->state = state; - line->line->state = state; + auto state = calcLineState(line); + line.begin->state = state; + line.end->state = state; + line.line->state = state; return true; } @@ -938,6 +917,8 @@ bool ControlLineManager::pick3(bool picked) } removeLine((provider->getObject() - 1) / ::ControlLine::OBJ_COUNT); + prev_obj = -1; + selected_object = -1; return false; } @@ -949,7 +930,7 @@ bool ControlLineManager::drag1(int modifierKey) return false; } - ::ControlLine* control_line = control_lines[(selected_object - 1) / ::ControlLine::OBJ_COUNT]; + ::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; @@ -958,41 +939,41 @@ bool ControlLineManager::drag1(int modifierKey) 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; + 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; + Coord new_delta = control_line.end->center + delta; new_delta.clip(iw, ih); - new_delta -= control_line->end->center; + new_delta -= control_line.end->center; // Constrain delta so the beginning stays above the image. - new_delta += control_line->begin->center; + new_delta += control_line.begin->center; new_delta.clip(iw, ih); - new_delta -= control_line->begin->center; + 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; + 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; + 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; + 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; + 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; @@ -1055,11 +1036,11 @@ 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; + std::unique_ptr line; + std::shared_ptr icon_h, icon_v; + std::unique_ptr begin_c, end_c; - line = new Line(); + line = std::unique_ptr(new Line()); line->datum = Geometry::IMAGE; line->innerLineWidth = line_width; line->begin = begin; @@ -1067,29 +1048,29 @@ void ControlLineManager::addLine(Coord begin, Coord end) const Cairo::RefPtr null_surface = Cairo::RefPtr(nullptr); - icon_h = new OPIcon(line_icon_h, null_surface, line_icon_h_prelight, + icon_h = std::make_shared(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, + icon_v = std::make_shared(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 = std::unique_ptr(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 = std::unique_ptr(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; + std::unique_ptr<::ControlLine> control_line(new ::ControlLine()); + control_line->begin = std::move(begin_c); + control_line->end = std::move(end_c); control_line->icon_h = icon_h; control_line->icon_v = icon_v; if (draw_line_type == rtengine::ControlLine::HORIZONTAL) { @@ -1097,34 +1078,29 @@ void ControlLineManager::addLine(Coord begin, Coord end) } else if (draw_line_type == rtengine::ControlLine::VERTICAL) { control_line->icon = icon_v; } - control_line->line = line; + control_line->line = std::move(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::visibleGeometry.push_back(control_line->line.get()); + EditSubscriber::visibleGeometry.push_back(control_line->icon.get()); + EditSubscriber::visibleGeometry.push_back(control_line->begin.get()); + EditSubscriber::visibleGeometry.push_back(control_line->end.get()); - EditSubscriber::mouseOverGeometry.push_back(line); - EditSubscriber::mouseOverGeometry.push_back(control_line->icon); - EditSubscriber::mouseOverGeometry.push_back(begin_c); - EditSubscriber::mouseOverGeometry.push_back(end_c); + EditSubscriber::mouseOverGeometry.push_back(control_line->line.get()); + EditSubscriber::mouseOverGeometry.push_back(control_line->icon.get()); + EditSubscriber::mouseOverGeometry.push_back(control_line->begin.get()); + EditSubscriber::mouseOverGeometry.push_back(control_line->end.get()); + + control_lines.push_back(std::move(control_line)); } 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()); + control_lines.clear(); + prev_obj = -1; + selected_object = -1; } void ControlLineManager::removeLine(size_t line_id) @@ -1133,48 +1109,35 @@ void ControlLineManager::removeLine(size_t line_id) 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); + control_lines.erase(control_lines.begin() + line_id); } -rtengine::ControlLine* ControlLineManager::toControlLines(void) const +void ControlLineManager::toControlLines(std::vector& converted) const { - auto retval = (rtengine::ControlLine*)malloc(control_lines.size() * sizeof(rtengine::ControlLine)); + converted.clear(); + converted.resize(control_lines.size()); 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; + converted[i].x1 = control_lines[i]->begin->center.x; + converted[i].y1 = control_lines[i]->begin->center.y; + converted[i].x2 = control_lines[i]->end->center.x; + converted[i].y2 = control_lines[i]->end->center.y; + converted[i].type = control_lines[i]->type; } - - return retval; } -LinesCallbacks::LinesCallbacks(PerspCorrection* tool, ControlLineManager* lines): - lines(lines), +LinesCallbacks::LinesCallbacks(PerspCorrection* tool): tool(tool) { } -LinesCallbacks::~LinesCallbacks() -{ -} - void LinesCallbacks::switchOffEditMode(void) { if (tool) { - tool->switchOffEditMode(lines); + tool->switchOffEditMode(); } } diff --git a/rtgui/perspective.h b/rtgui/perspective.h index dd9155c50..0c636373c 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -30,10 +30,10 @@ struct ControlLine { static constexpr int OBJ_COUNT = 4; - Line* line; - OPIcon* icon; - OPIcon *icon_h, *icon_v; - Circle *begin, *end; + std::unique_ptr line; + std::shared_ptr icon; + std::shared_ptr icon_h, icon_v; + std::unique_ptr begin, end; rtengine::ControlLine::Type type; }; @@ -44,9 +44,9 @@ protected: /** Determine how horizontal and vertical lines are displayed. */ bool active_h, active_v; /** Hidden object for capturing mouse events. */ - Rectangle* canvas_area; + std::unique_ptr canvas_area; rtengine::Coord drag_delta; - std::vector control_lines; + std::vector> control_lines; CursorShape cursor; bool draw_mode; Cairo::RefPtr line_icon_h, line_icon_v; @@ -68,12 +68,11 @@ public: }; /** Callbacks to invoke. */ - Callbacks* callbacks; + std::shared_ptr callbacks; /** Type of line for newly drawn lines. */ rtengine::ControlLine::Type draw_line_type; ControlLineManager(); - ~ControlLineManager(); void removeAll (void); /** Sets whether or not the lines are visible and interact-able. */ @@ -88,7 +87,7 @@ public: /** * Allocates a new array and populates it with copies of the control lines. */ - rtengine::ControlLine* toControlLines (void) const; + void toControlLines (std::vector& converted) const; // EditSubscriber overrides bool button1Pressed (int modifierKey) override; @@ -125,9 +124,9 @@ protected: Adjuster* camera_shift_horiz; Adjuster* camera_shift_vert; Adjuster* camera_yaw; - Gtk::Image* img_ctrl_lines_edit; - Gtk::Image* img_ctrl_lines_apply; - ControlLineManager* lines; + std::unique_ptr img_ctrl_lines_edit; + std::unique_ptr img_ctrl_lines_apply; + std::unique_ptr lines; Gtk::ToggleButton* lines_button_edit; Gtk::Button* lines_button_erase; Gtk::ToggleButton* lines_button_h; @@ -167,7 +166,6 @@ protected: public: PerspCorrection (); - ~PerspCorrection (); void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; @@ -187,18 +185,16 @@ public: lens_geom_listener = listener; } void setMetadata (const rtengine::FramesMetaData* metadata); - void switchOffEditMode (ControlLineManager* lines); + void switchOffEditMode (void); void trimValues (rtengine::procparams::ProcParams* pp) override; }; class LinesCallbacks: public ControlLineManager::Callbacks { protected: - ControlLineManager* lines; PerspCorrection* tool; public: - LinesCallbacks(PerspCorrection* tool, ControlLineManager* lines); - ~LinesCallbacks(); + LinesCallbacks(PerspCorrection* tool); void switchOffEditMode (void) override; }; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 857a4c57b..d25d6414d 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -986,7 +986,7 @@ void ToolPanelCoordinator::straightenRequested() toolBar->setTool(TMStraighten); } -void ToolPanelCoordinator::autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const rtengine::ControlLine *lines, size_t line_count) +void ToolPanelCoordinator::autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const std::vector *lines) { if (!(ipc && (corr_pitch || corr_yaw))) { return; @@ -1000,7 +1000,7 @@ void ToolPanelCoordinator::autoPerspRequested (bool corr_pitch, bool corr_yaw, d rtengine::procparams::ProcParams params; ipc->getParams(¶ms); - auto res = rtengine::PerspectiveCorrection::autocompute(src, corr_pitch, corr_yaw, ¶ms, src->getMetaData(), lines, line_count); + auto res = rtengine::PerspectiveCorrection::autocompute(src, corr_pitch, corr_yaw, ¶ms, src->getMetaData(), lines); rot = res.angle; pitch = res.pitch; yaw = res.yaw; diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 7ab17018b..3889e786c 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -313,7 +313,7 @@ public: // rotatelistener interface void straightenRequested () override; void autoCropRequested () override; - void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const rtengine::ControlLine *lines = nullptr, size_t line_count = 0) override; + void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const std::vector *lines = nullptr) override; double autoDistorRequested () override; void updateTransformPreviewRequested (rtengine::ProcEvent event, bool render_perspective) override; From a7f397541a425ea582def20683badaaf980deea5 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 8 Jul 2020 11:46:28 -0700 Subject: [PATCH 17/27] Make control line type automatically set Perspective control line type is automatically set when first drawn. The type is determined by the line's angle. Buttons for specifying which line type to draw are removed. --- rtdata/images/svg/draw-horizontal.svg | 117 ------------------- rtdata/images/svg/draw-vertical.svg | 117 ------------------- rtgui/perspective.cc | 154 ++++++++++---------------- rtgui/perspective.h | 23 ++-- 4 files changed, 71 insertions(+), 340 deletions(-) delete mode 100644 rtdata/images/svg/draw-horizontal.svg delete mode 100644 rtdata/images/svg/draw-vertical.svg diff --git a/rtdata/images/svg/draw-horizontal.svg b/rtdata/images/svg/draw-horizontal.svg deleted file mode 100644 index ae8fa8c44..000000000 --- a/rtdata/images/svg/draw-horizontal.svg +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - Lawrence - - - - - - - - RawTherapee icon. - - - - - - - - - - - - - - - - - - diff --git a/rtdata/images/svg/draw-vertical.svg b/rtdata/images/svg/draw-vertical.svg deleted file mode 100644 index 4613d95c6..000000000 --- a/rtdata/images/svg/draw-vertical.svg +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - Lawrence - - - - - - - - RawTherapee icon. - - - - - - - - - - - - - - - - - - diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 13fc170dc..02aea16f6 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -52,10 +52,9 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" lens_geom_listener = nullptr; metadata = 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")); - std::unique_ptr ipers_draw(new RTImage ("draw.png")); + Gtk::Image* ipers_draw(new RTImage ("draw.png")); Gtk::Image* ipers_trash = Gtk::manage (new RTImage ("trash-empty.png")); + Gtk::Image* ipers_apply = Gtk::manage (new RTImage ("tick.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")); @@ -126,15 +125,11 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" 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_apply = Gtk::manage (new Gtk::Button()); + lines_button_apply->set_image(*ipers_apply); + lines_button_apply->set_sensitive(false); + lines_button_apply->signal_pressed().connect(sigc::mem_fun( + *this, &::PerspCorrection::linesApplyButtonPressed)); lines_button_edit = Gtk::manage (new Gtk::ToggleButton()); lines_button_edit->set_image(*ipers_draw); @@ -150,16 +145,12 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" lines = std::unique_ptr(new ControlLineManager()); lines->callbacks = std::make_shared(this); - img_ctrl_lines_apply = std::unique_ptr(new RTImage ("tick.png")); - img_ctrl_lines_edit = std::move(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_apply); control_lines_box->pack_start(*lines_button_erase); // End control lines interface. @@ -595,8 +586,6 @@ void PerspCorrection::setBatchMode (bool batchMode) 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); @@ -653,8 +642,6 @@ void PerspCorrection::setFocalLengthValue (const ProcParams* pparams, const Fram void PerspCorrection::switchOffEditMode(void) { - lines_button_h->set_active(false); - lines_button_v->set_active(false); lines_button_edit->set_active(false); } @@ -663,62 +650,37 @@ void PerspCorrection::setEditProvider(EditDataProvider* provider) lines->setEditProvider(provider); } -void PerspCorrection::linesButtonPressed(Gtk::ToggleButton* button) +void PerspCorrection::linesApplyButtonPressed(void) { - lines->setLinesState(lines_button_h->get_active(), lines_button_v->get_active()); - - if (!button->get_active()) { - return; + if (method->get_active_row_number() == 1) { + // Calculate perspective distortion if in camera-based mode. + applyControlLines(); } - - 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); + lines_button_edit->set_active(false); } 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); - } + lines->setDrawMode(true); 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_apply->set_sensitive(true); lines_button_erase->set_sensitive(true); setCamBasedEventsActive(false); } else { // Leave edit mode. setCamBasedEventsActive(true); + lines_button_apply->set_sensitive(false); lines_button_erase->set_sensitive(false); render = true; + if (lens_geom_listener) { + lens_geom_listener->updateTransformPreviewRequested(EvPerspRender, 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(); - } } } @@ -763,16 +725,6 @@ ControlLineManager::ControlLineManager(): line_icon_v_prelight = Cairo::RefPtr(new RTSurface("bidirectional-arrow-vertical-prelight.png")); } -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(); @@ -797,19 +749,6 @@ 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(); @@ -835,6 +774,7 @@ bool ControlLineManager::button1Pressed(int modifierKey) } } else if (draw_mode && (modifierKey & GDK_CONTROL_MASK)) { // Add new line. addLine(dataProvider->posImage, dataProvider->posImage); + drawing_line = true; selected_object = mouseOverGeometry.size() - 1; // Select endpoint. action = Action::DRAGGING; } @@ -848,6 +788,7 @@ bool ControlLineManager::button1Released(void) if (selected_object > 0) { mouseOverGeometry[selected_object]->state = Geometry::NORMAL; } + drawing_line = false; selected_object = -1; return false; } @@ -894,11 +835,6 @@ bool ControlLineManager::pick1(bool picked) visibleGeometry[object_id - 1] = line.icon.get(); - auto state = calcLineState(line); - line.begin->state = state; - line.end->state = state; - line.line->state = state; - return true; } @@ -975,6 +911,9 @@ bool ControlLineManager::drag1(int modifierKey) control_line.icon_v->position.x = control_line.icon_h->position.x; control_line.icon_v->position.y = control_line.icon_h->position.y; + if (drawing_line) { + autoSetLineType(selected_object); + } return false; } @@ -1011,10 +950,8 @@ bool ControlLineManager::mouseOver(int modifierKey) } 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; + visibleGeometry[prev_obj - 1]->state = Geometry::NORMAL; } - prev_obj = cur_obj; return true; @@ -1073,13 +1010,9 @@ void ControlLineManager::addLine(Coord begin, Coord end) control_line->end = std::move(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->icon = icon_v; control_line->line = std::move(line); - control_line->type = draw_line_type; + control_line->type = rtengine::ControlLine::VERTICAL; EditSubscriber::visibleGeometry.push_back(control_line->line.get()); EditSubscriber::visibleGeometry.push_back(control_line->icon.get()); @@ -1094,6 +1027,39 @@ void ControlLineManager::addLine(Coord begin, Coord end) control_lines.push_back(std::move(control_line)); } +void ControlLineManager::autoSetLineType(int object_id) +{ + int line_id = (object_id - 1) / ::ControlLine::OBJ_COUNT; + ::ControlLine& line = *control_lines[line_id]; + + int dx = line.begin->center.x - line.end->center.x; + int dy = line.begin->center.y - line.end->center.y; + + if (dx < 0) { + dx = -dx; + } + if (dy < 0) { + dy = -dy; + } + + rtengine::ControlLine::Type type; + std::shared_ptr icon; + + if (dx > dy) { // More horizontal than vertical. + type = rtengine::ControlLine::HORIZONTAL; + icon = line.icon_h; + } else { + type = rtengine::ControlLine::VERTICAL; + icon = line.icon_v; + } + + if (type != line.type) { // Need to update line type. + line.type = type; + line.icon = icon; + visibleGeometry[line_id * ::ControlLine::OBJ_COUNT + 1] = line.icon.get(); + } +} + void ControlLineManager::removeAll(void) { visibleGeometry.clear(); diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 0c636373c..40c8d7e46 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -41,21 +41,27 @@ class ControlLineManager: EditSubscriber { protected: - /** Determine how horizontal and vertical lines are displayed. */ - bool active_h, active_v; /** Hidden object for capturing mouse events. */ std::unique_ptr canvas_area; rtengine::Coord drag_delta; std::vector> control_lines; CursorShape cursor; bool draw_mode; + bool drawing_line = false; Cairo::RefPtr line_icon_h, line_icon_v; Cairo::RefPtr line_icon_h_prelight, line_icon_v_prelight; int prev_obj; int selected_object; void addLine (rtengine::Coord begin, rtengine::Coord end); - Geometry::State calcLineState(const ControlLine& line) const; + /** + * Set the line type of the line containing the object according to the + * line's angle. + * + * If the line is within 45 degrees of a perfectly vertical + * line, inclusive, the line type is set to vertical. Otherwise, horizontal. + */ + void autoSetLineType(int object_id); void removeLine (size_t line_id); public: @@ -69,8 +75,6 @@ public: /** Callbacks to invoke. */ std::shared_ptr callbacks; - /** Type of line for newly drawn lines. */ - rtengine::ControlLine::Type draw_line_type; ControlLineManager(); @@ -80,8 +84,6 @@ public: /** Set whether or not lines can be drawn and deleted. */ void setDrawMode (bool draw); void setEditProvider (EditDataProvider* provider); - /** Determines how each line type is displayed. */ - void setLinesState (bool horiz_active, bool vert_active); /** Returns the number of lines. */ size_t size (void) const; /** @@ -124,13 +126,10 @@ protected: Adjuster* camera_shift_horiz; Adjuster* camera_shift_vert; Adjuster* camera_yaw; - std::unique_ptr img_ctrl_lines_edit; - std::unique_ptr img_ctrl_lines_apply; std::unique_ptr lines; + Gtk::Button* lines_button_apply; Gtk::ToggleButton* lines_button_edit; Gtk::Button* lines_button_erase; - Gtk::ToggleButton* lines_button_h; - Gtk::ToggleButton* lines_button_v; Adjuster* projection_pitch; Adjuster* projection_rotate; Adjuster* projection_shift_horiz; @@ -174,7 +173,7 @@ public: void adjusterChanged (Adjuster* a, double newval) override; void autoCorrectionPressed (Gtk::Button* b); - void linesButtonPressed (Gtk::ToggleButton* button); + void linesApplyButtonPressed (void); void linesEditButtonPressed (void); void linesEraseButtonPressed (void); void methodChanged (void); From 7f647d188c0db1f2bb6ebff7e8d16308e2dd5bb4 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Wed, 8 Jul 2020 11:57:04 -0700 Subject: [PATCH 18/27] Fix cppcheck warnings --- rtgui/perspective.cc | 4 +++- rtgui/perspective.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 02aea16f6..68e43278f 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -710,11 +710,13 @@ void PerspCorrection::setCamBasedEventsActive(bool active) ControlLineManager::ControlLineManager(): EditSubscriber(ET_OBJECTS), + canvas_area(new Rectangle()), cursor(CSCrosshair), + draw_mode(false), + drawing_line(false), prev_obj(-1), selected_object(-1) { - canvas_area = std::unique_ptr(new Rectangle()); canvas_area->filled = true; canvas_area->topLeft = Coord(0, 0); mouseOverGeometry.push_back(canvas_area.get()); diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 40c8d7e46..3538af279 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -47,7 +47,7 @@ protected: std::vector> control_lines; CursorShape cursor; bool draw_mode; - bool drawing_line = false; + bool drawing_line; Cairo::RefPtr line_icon_h, line_icon_v; Cairo::RefPtr line_icon_h_prelight, line_icon_v_prelight; int prev_obj; @@ -194,6 +194,6 @@ protected: PerspCorrection* tool; public: - LinesCallbacks(PerspCorrection* tool); + explicit LinesCallbacks(PerspCorrection* tool); void switchOffEditMode (void) override; }; From eb548f1aff48a90fc9a6d2a8c5bb11c0e1d1bdbf Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Thu, 9 Jul 2020 11:43:23 -0700 Subject: [PATCH 19/27] Make control lines part of processing parameters --- rtengine/procparams.cc | 16 +++++- rtengine/procparams.h | 4 ++ rtgui/paramsedited.cc | 7 +++ rtgui/paramsedited.h | 1 + rtgui/perspective.cc | 124 +++++++++++++++++++++++++++++++++++++++-- rtgui/perspective.h | 12 +++- 6 files changed, 158 insertions(+), 6 deletions(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index def39a5cd..0cd47f132 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1904,7 +1904,12 @@ bool PerspectiveParams::operator ==(const PerspectiveParams& other) const && projection_shift_vert == other.projection_shift_vert && projection_rotate == other.projection_rotate && projection_pitch == other.projection_pitch - && projection_yaw == other.projection_yaw; + && projection_yaw == other.projection_yaw + // Lines could still be equivalent if the vectors aren't, but this is + // rare and a small issue. Besides, a proper comparison requires lots + // more code which introduces clutter. + && control_line_values == other.control_line_values + && control_line_types == other.control_line_types; } bool PerspectiveParams::operator !=(const PerspectiveParams& other) const @@ -5210,6 +5215,8 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->perspective.projection_shift_horiz, "Perspective", "ProjectionShiftHorizontal", perspective.projection_shift_horiz, keyFile); saveToKeyfile(!pedited || pedited->perspective.projection_shift_vert, "Perspective", "ProjectionShiftVertical", perspective.projection_shift_vert, keyFile); saveToKeyfile(!pedited || pedited->perspective.projection_yaw, "Perspective", "ProjectionYaw", perspective.projection_yaw, keyFile); + saveToKeyfile(!pedited || pedited->perspective.control_lines, "Perspective", "ControlLineValues", perspective.control_line_values, keyFile); + saveToKeyfile(!pedited || pedited->perspective.control_lines, "Perspective", "ControlLineTypes", perspective.control_line_types, keyFile); // Gradient saveToKeyfile(!pedited || pedited->gradient.enabled, "Gradient", "Enabled", gradient.enabled, keyFile); @@ -6839,6 +6846,13 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Perspective", "ProjectionShiftHorizontal", pedited, perspective.projection_shift_horiz, pedited->perspective.projection_shift_horiz); assignFromKeyfile(keyFile, "Perspective", "ProjectionShiftVertical", pedited, perspective.projection_shift_vert, pedited->perspective.projection_shift_vert); assignFromKeyfile(keyFile, "Perspective", "ProjectionYaw", pedited, perspective.projection_yaw, pedited->perspective.projection_yaw); + if (keyFile.has_key("Perspective", "ControlLineValues") && keyFile.has_key("Perspective", "ControlLineTypes")) { + perspective.control_line_values = keyFile.get_integer_list("Perspective", "ControlLineValues"); + perspective.control_line_types = keyFile.get_integer_list("Perspective", "ControlLineTypes"); + if (pedited) { + pedited->perspective.control_lines = true; + } + } } if (keyFile.has_group("Gradient")) { diff --git a/rtengine/procparams.h b/rtengine/procparams.h index ae46174a2..c7139236c 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -945,6 +945,10 @@ struct PerspectiveParams { double projection_shift_horiz; double projection_shift_vert; double projection_yaw; + /** A line is stored as 4 integers in this order: x1, y1, x2, y2 */ + std::vector control_line_values; + /** 0 is vertical, 1 is horizontal, undefined otherwise. */ + std::vector control_line_types; PerspectiveParams(); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index bbfb7cc4e..53c514916 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -353,6 +353,7 @@ void ParamsEdited::set(bool v) perspective.projection_shift_horiz = v; perspective.projection_shift_vert = v; perspective.projection_yaw = v; + perspective.control_lines = v; gradient.enabled = v; gradient.degree = v; gradient.feather = v; @@ -1006,6 +1007,7 @@ void ParamsEdited::initFrom(const std::vector& perspective.projection_shift_horiz = perspective.projection_shift_horiz && p.perspective.projection_shift_horiz == other.perspective.projection_shift_horiz; perspective.projection_shift_vert = perspective.projection_shift_vert && p.perspective.projection_shift_vert == other.perspective.projection_shift_vert; perspective.projection_yaw = perspective.projection_yaw && p.perspective.projection_yaw == other.perspective.projection_yaw; + perspective.control_lines = perspective.control_lines && p.perspective.control_line_values == other.perspective.control_line_values && p.perspective.control_line_types == other.perspective.control_line_types; gradient.enabled = gradient.enabled && p.gradient.enabled == other.gradient.enabled; gradient.degree = gradient.degree && p.gradient.degree == other.gradient.degree; gradient.feather = gradient.feather && p.gradient.feather == other.gradient.feather; @@ -2986,6 +2988,11 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.perspective.projection_yaw = dontforceSet && options.baBehav[ADDSET_PERSP_PROJ_ANGLE] ? toEdit.perspective.projection_yaw + mods.perspective.projection_yaw : mods.perspective.projection_yaw; } + if (perspective.control_lines) { + toEdit.perspective.control_line_values = mods.perspective.control_line_values; + toEdit.perspective.control_line_types = mods.perspective.control_line_types; + } + if (gradient.enabled) { toEdit.gradient.enabled = mods.gradient.enabled; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 1bd2e045e..1c309e994 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -879,6 +879,7 @@ struct PerspectiveParamsEdited { bool projection_shift_horiz; bool projection_shift_vert; bool projection_yaw; + bool control_lines; }; struct GradientParamsEdited { diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 68e43278f..e652c21f2 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -27,6 +27,63 @@ using namespace rtengine; using namespace rtengine::procparams; +namespace +{ + +void controlLinesToValues(const std::vector& lines, + std::vector& values, std::vector& types) +{ + values.clear(); + types.clear(); + + for (auto&& line : lines) { + values.push_back(line.x1); + values.push_back(line.y1); + values.push_back(line.x2); + values.push_back(line.y2); + + int type = -1; + switch (line.type) { + case rtengine::ControlLine::VERTICAL: + type = 0; + break; + case rtengine::ControlLine::HORIZONTAL: + type = 1; + break; + } + types.push_back(type); + } +} + +std::vector valuesToControlLines( + const std::vector& values, const std::vector& types) +{ + int line_count = min(values.size() / 4, types.size()); + std::vector lines(line_count); + + auto values_iter = values.begin(); + auto types_iter = types.begin(); + for (auto&& line : lines) { + line.x1 = *(values_iter++); + line.y1 = *(values_iter++); + line.x2 = *(values_iter++); + line.y2 = *(values_iter++); + + switch (*(types_iter++)) { + case 0: + line.type = rtengine::ControlLine::VERTICAL; + break; + case 1: + line.type = rtengine::ControlLine::HORIZONTAL; + break; + } + } + + return lines; +} + +} + PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M("TP_PERSPECTIVE_LABEL")) { @@ -48,6 +105,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" EvPerspProjRotateVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_PROJ_ROTATE"); EvPerspProjShiftVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_PROJ_SHIFT"); setCamBasedEventsActive(); + EvPerspControlLines = mapper->newEvent(M_VOID); lens_geom_listener = nullptr; metadata = nullptr; @@ -262,6 +320,7 @@ void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited) 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); + lines->setEdited (pedited->perspective.control_lines); } horiz->setValue (pp->perspective.horizontal); @@ -277,6 +336,8 @@ void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited) 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); + lines->setLines(valuesToControlLines(pp->perspective.control_line_values, + pp->perspective.control_line_types)); if (pedited && !pedited->perspective.method) { method->set_active (2); @@ -309,6 +370,11 @@ void PerspCorrection::write (ProcParams* pp, ParamsEdited* pedited) pp->perspective.projection_shift_vert = projection_shift_vert->getValue (); pp->perspective.projection_yaw = projection_yaw->getValue (); + std::vector control_lines; + lines->toControlLines(control_lines); + controlLinesToValues(control_lines, pp->perspective.control_line_values, + pp->perspective.control_line_types); + if (method->get_active_row_number() == 0) { pp->perspective.method = "simple"; } else if (method->get_active_row_number() == 1) { @@ -331,6 +397,7 @@ void PerspCorrection::write (ProcParams* pp, ParamsEdited* pedited) 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(); + pedited->perspective.control_lines = lines->getEdited(); } } @@ -650,6 +717,13 @@ void PerspCorrection::setEditProvider(EditDataProvider* provider) lines->setEditProvider(provider); } +void PerspCorrection::lineChanged(void) +{ + if (listener) { + listener->panelChanged(EvPerspControlLines, ""); + } +} + void PerspCorrection::linesApplyButtonPressed(void) { if (method->get_active_row_number() == 1) { @@ -714,6 +788,7 @@ ControlLineManager::ControlLineManager(): cursor(CSCrosshair), draw_mode(false), drawing_line(false), + edited(false), prev_obj(-1), selected_object(-1) { @@ -790,6 +865,8 @@ bool ControlLineManager::button1Released(void) if (selected_object > 0) { mouseOverGeometry[selected_object]->state = Geometry::NORMAL; } + edited = true; + callbacks->lineChanged(); drawing_line = false; selected_object = -1; return false; @@ -837,6 +914,9 @@ bool ControlLineManager::pick1(bool picked) visibleGeometry[object_id - 1] = line.icon.get(); + edited = true; + callbacks->lineChanged(); + return true; } @@ -920,6 +1000,11 @@ bool ControlLineManager::drag1(int modifierKey) return false; } +bool ControlLineManager::getEdited(void) const +{ + return edited; +} + CursorShape ControlLineManager::getCursor(int objectID) const { return cursor; @@ -966,12 +1051,27 @@ void ControlLineManager::switchOffEditMode(void) } } +void ControlLineManager::setEdited(bool edited) +{ + this->edited = edited; +} + void ControlLineManager::setEditProvider(EditDataProvider* provider) { EditSubscriber::setEditProvider(provider); } -void ControlLineManager::addLine(Coord begin, Coord end) +void ControlLineManager::setLines(const std::vector& lines) +{ + removeAll(); + for (auto&& line : lines) { + Coord start(line.x1, line.y1); + Coord end(line.x2, line.y2); + addLine(start, end, line.type); + } +} + +void ControlLineManager::addLine(Coord begin, Coord end, rtengine::ControlLine::Type type) { constexpr int line_width = 2; constexpr int handle_radius = 6; @@ -1005,16 +1105,20 @@ void ControlLineManager::addLine(Coord begin, Coord end) end_c->datum = Geometry::IMAGE; end_c->filled = true; end_c->radius = handle_radius; - end_c->center = begin; + end_c->center = end; std::unique_ptr<::ControlLine> control_line(new ::ControlLine()); control_line->begin = std::move(begin_c); control_line->end = std::move(end_c); control_line->icon_h = icon_h; control_line->icon_v = icon_v; - control_line->icon = icon_v; + if (type == rtengine::ControlLine::HORIZONTAL) { + control_line->icon = icon_h; + } else { + control_line->icon = icon_v; + } control_line->line = std::move(line); - control_line->type = rtengine::ControlLine::VERTICAL; + control_line->type = type; EditSubscriber::visibleGeometry.push_back(control_line->line.get()); EditSubscriber::visibleGeometry.push_back(control_line->icon.get()); @@ -1069,6 +1173,8 @@ void ControlLineManager::removeAll(void) control_lines.clear(); prev_obj = -1; selected_object = -1; + edited = true; + callbacks->lineChanged(); } void ControlLineManager::removeLine(size_t line_id) @@ -1082,6 +1188,9 @@ void ControlLineManager::removeLine(size_t line_id) mouseOverGeometry.erase(mouseOverGeometry.begin() + ::ControlLine::OBJ_COUNT * line_id + 1, mouseOverGeometry.begin() + ::ControlLine::OBJ_COUNT * line_id + ::ControlLine::OBJ_COUNT + 1); control_lines.erase(control_lines.begin() + line_id); + + edited = true; + callbacks->lineChanged(); } void ControlLineManager::toControlLines(std::vector& converted) const @@ -1103,6 +1212,13 @@ LinesCallbacks::LinesCallbacks(PerspCorrection* tool): { } +void LinesCallbacks::lineChanged(void) +{ + if (tool) { + tool->lineChanged(); + } +} + void LinesCallbacks::switchOffEditMode(void) { if (tool) { diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 3538af279..c58a9420e 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -48,12 +48,14 @@ protected: CursorShape cursor; bool draw_mode; bool drawing_line; + bool edited; Cairo::RefPtr line_icon_h, line_icon_v; Cairo::RefPtr line_icon_h_prelight, line_icon_v_prelight; int prev_obj; int selected_object; - void addLine (rtengine::Coord begin, rtengine::Coord end); + void addLine (rtengine::Coord begin, rtengine::Coord end, + rtengine::ControlLine::Type type = rtengine::ControlLine::VERTICAL); /** * Set the line type of the line containing the object according to the * line's angle. @@ -69,6 +71,8 @@ public: { public: virtual ~Callbacks() {}; + /** Called when a line changed (added, removed, moved, etc.). */ + virtual void lineChanged (void) {}; /** Called when the EditSubscriber's switchOffEditMode is called. */ virtual void switchOffEditMode (void) {}; }; @@ -78,12 +82,15 @@ public: ControlLineManager(); + bool getEdited (void) const; void removeAll (void); /** Sets whether or not the lines are visible and interact-able. */ void setActive (bool active); /** Set whether or not lines can be drawn and deleted. */ void setDrawMode (bool draw); + void setEdited (bool edited); void setEditProvider (EditDataProvider* provider); + void setLines (const std::vector& lines); /** Returns the number of lines. */ size_t size (void) const; /** @@ -138,6 +145,7 @@ protected: rtengine::ProcEvent EvPerspCamFocalLength; rtengine::ProcEvent EvPerspCamShift; rtengine::ProcEvent EvPerspCamAngle; + rtengine::ProcEvent EvPerspControlLines; rtengine::ProcEvent EvPerspMethod; rtengine::ProcEvent EvPerspProjShift; rtengine::ProcEvent EvPerspProjRotate; @@ -173,6 +181,7 @@ public: void adjusterChanged (Adjuster* a, double newval) override; void autoCorrectionPressed (Gtk::Button* b); + void lineChanged (void); void linesApplyButtonPressed (void); void linesEditButtonPressed (void); void linesEraseButtonPressed (void); @@ -195,5 +204,6 @@ protected: public: explicit LinesCallbacks(PerspCorrection* tool); + void lineChanged (void) override; void switchOffEditMode (void) override; }; From 4f8aa2db2b726779228af5f8d1a7e4973a9f2cdd Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Sat, 11 Jul 2020 13:34:05 -0700 Subject: [PATCH 20/27] Add perspective control lines to history Allows undoing and redoing of changes to the control lines. --- rtdata/languages/default | 1 + rtgui/perspective.cc | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index f89c13cb5..9fd301eba 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1216,6 +1216,7 @@ HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;CS - Corner radius boost HISTORY_MSG_PERSP_CAM_ANGLE;Perspective - Camera HISTORY_MSG_PERSP_CAM_FL;Perspective - Camera HISTORY_MSG_PERSP_CAM_SHIFT;Perspective - Camera +HISTORY_MSG_PERSP_CTRL_LINE;Perspective - Control lines HISTORY_MSG_PERSP_METHOD;Perspective - Method HISTORY_MSG_PERSP_PROJ_ANGLE;Perspective - Recovery HISTORY_MSG_PERSP_PROJ_ROTATE;Perspective - PCA rotation diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index e652c21f2..a113a13f9 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -105,7 +105,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" EvPerspProjRotateVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_PROJ_ROTATE"); EvPerspProjShiftVoid = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_PROJ_SHIFT"); setCamBasedEventsActive(); - EvPerspControlLines = mapper->newEvent(M_VOID); + EvPerspControlLines = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_CTRL_LINE"); lens_geom_listener = nullptr; metadata = nullptr; @@ -720,7 +720,7 @@ void PerspCorrection::setEditProvider(EditDataProvider* provider) void PerspCorrection::lineChanged(void) { if (listener) { - listener->panelChanged(EvPerspControlLines, ""); + listener->panelChanged(EvPerspControlLines, M("HISTORY_CHANGED")); } } From cef97eefeab9864274a92862dbea568b2079eb8e Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Sun, 12 Jul 2020 11:36:14 -0700 Subject: [PATCH 21/27] Simplify tooltip for perspective control lines Only show how to draw new lines and delete a single line. Move the tooltip activation area to the label. --- rtdata/languages/default | 2 +- rtgui/perspective.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 9fd301eba..399d8f041 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2880,7 +2880,7 @@ TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL;Horizontal shift TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL;Vertical shift TP_PERSPECTIVE_CAMERA_YAW;Horizontal TP_PERSPECTIVE_CONTROL_LINES;Control lines -TP_PERSPECTIVE_CONTROL_LINES_TOOLTIP;Correct perspective distortion by drawing control lines.\n\nTo draw a line, activate either the vertical or horizontal drawing button. Hold down the control key and click-and-drag to draw a line. At least two vertical lines are required for vertical perspective correction. The same is true for horizontal correction. Click the tick mark button to apply perspective correction. +TP_PERSPECTIVE_CONTROL_LINES_TOOLTIP;Ctrl+drag: Draw new line\nRight-click: Delete line TP_PERSPECTIVE_HORIZONTAL;Horizontal TP_PERSPECTIVE_LABEL;Perspective TP_PERSPECTIVE_METHOD;Method diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index a113a13f9..a8a1f73d3 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -204,8 +204,8 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" lines->callbacks = std::make_shared(this); 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_label->set_tooltip_markup( M("TP_PERSPECTIVE_CONTROL_LINES_TOOLTIP") ); control_lines_box->pack_start(*control_lines_label, Gtk::PACK_SHRINK); control_lines_box->pack_start(*lines_button_edit); control_lines_box->pack_start(*lines_button_apply); From 06caf40598a55aeafeffd1cb4bf3b8cb97e6f0fb Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Sun, 12 Jul 2020 15:19:10 -0700 Subject: [PATCH 22/27] Move control lines code to new files --- rtengine/perspectivecorrection.h | 14 +- rtgui/CMakeLists.txt | 1 + rtgui/controllines.cc | 457 +++++++++++++++++++++++++++++++ rtgui/controllines.h | 116 ++++++++ rtgui/perspective.cc | 425 ---------------------------- rtgui/perspective.h | 87 +----- 6 files changed, 586 insertions(+), 514 deletions(-) create mode 100644 rtgui/controllines.cc create mode 100644 rtgui/controllines.h diff --git a/rtengine/perspectivecorrection.h b/rtengine/perspectivecorrection.h index c46c34147..f092f2c8c 100644 --- a/rtengine/perspectivecorrection.h +++ b/rtengine/perspectivecorrection.h @@ -20,12 +20,20 @@ #pragma once -#include "coord2d.h" -#include "procparams.h" -#include "imagesource.h" +#include namespace rtengine { +namespace procparams +{ + +class ProcParams; + +} + +class ImageSource; +class FramesMetaData; + class ControlLine { public: diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index cfadb7928..514204baf 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -34,6 +34,7 @@ set(NONCLISOURCEFILES colorappearance.cc coloredbar.cc colortoning.cc + controllines.cc controlspotpanel.cc coordinateadjuster.cc crop.cc diff --git a/rtgui/controllines.cc b/rtgui/controllines.cc new file mode 100644 index 000000000..8d39c9d59 --- /dev/null +++ b/rtgui/controllines.cc @@ -0,0 +1,457 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2020 Lawrence Lee + * + * 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 . + */ +#include + +#include "controllines.h" +#include "editcallbacks.h" +#include "editwidgets.h" +#include "rtsurface.h" + +#include "../rtengine/perspectivecorrection.h" + +using namespace rtengine; + +::ControlLine::~ControlLine() = default; + +ControlLineManager::ControlLineManager(): + EditSubscriber(ET_OBJECTS), + canvas_area(new Rectangle()), + cursor(CSCrosshair), + draw_mode(false), + drawing_line(false), + edited(false), + prev_obj(-1), + selected_object(-1) +{ + canvas_area->filled = true; + canvas_area->topLeft = Coord(0, 0); + mouseOverGeometry.push_back(canvas_area.get()); + + line_icon_h = Cairo::RefPtr(new RTSurface("bidirectional-arrow-horizontal-hicontrast.png")); + line_icon_v = Cairo::RefPtr(new RTSurface("bidirectional-arrow-vertical-hicontrast.png")); + line_icon_h_prelight = Cairo::RefPtr(new RTSurface("bidirectional-arrow-horizontal-prelight.png")); + line_icon_v_prelight = Cairo::RefPtr(new RTSurface("bidirectional-arrow-vertical-prelight.png")); +} + +ControlLineManager::~ControlLineManager() = default; + +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; +} + +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); + drawing_line = true; + 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; + } + edited = true; + callbacks->lineChanged(); + drawing_line = false; + 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(); + ::ControlLine& 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.get(); + + edited = true; + callbacks->lineChanged(); + + 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); + prev_obj = -1; + selected_object = -1; + 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; + + if (drawing_line) { + autoSetLineType(selected_object); + } + + return false; +} + +bool ControlLineManager::getEdited(void) const +{ + return edited; +} + +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) { + visibleGeometry[prev_obj - 1]->state = Geometry::NORMAL; + } + prev_obj = cur_obj; + + return true; +} + +void ControlLineManager::switchOffEditMode(void) +{ + if (callbacks) { + callbacks->switchOffEditMode(); + } +} + +void ControlLineManager::setEdited(bool edited) +{ + this->edited = edited; +} + +void ControlLineManager::setEditProvider(EditDataProvider* provider) +{ + EditSubscriber::setEditProvider(provider); +} + +void ControlLineManager::setLines(const std::vector& lines) +{ + removeAll(); + for (auto&& line : lines) { + Coord start(line.x1, line.y1); + Coord end(line.x2, line.y2); + addLine(start, end, line.type); + } +} + +void ControlLineManager::addLine(Coord begin, Coord end, rtengine::ControlLine::Type type) +{ + constexpr int line_width = 2; + constexpr int handle_radius = 6; + std::unique_ptr line; + std::shared_ptr icon_h, icon_v; + std::unique_ptr begin_c, end_c; + + line = std::unique_ptr(new Line()); + line->datum = Geometry::IMAGE; + line->innerLineWidth = line_width; + line->begin = begin; + line->end = end; + + const Cairo::RefPtr null_surface = Cairo::RefPtr(nullptr); + + icon_h = std::make_shared(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 = std::make_shared(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 = std::unique_ptr(new Circle()); + begin_c->datum = Geometry::IMAGE; + begin_c->filled = true; + begin_c->radius = handle_radius; + begin_c->center = begin; + + end_c = std::unique_ptr(new Circle()); + end_c->datum = Geometry::IMAGE; + end_c->filled = true; + end_c->radius = handle_radius; + end_c->center = end; + + std::unique_ptr<::ControlLine> control_line(new ::ControlLine()); + control_line->begin = std::move(begin_c); + control_line->end = std::move(end_c); + control_line->icon_h = icon_h; + control_line->icon_v = icon_v; + if (type == rtengine::ControlLine::HORIZONTAL) { + control_line->icon = icon_h; + } else { + control_line->icon = icon_v; + } + control_line->line = std::move(line); + control_line->type = type; + + EditSubscriber::visibleGeometry.push_back(control_line->line.get()); + EditSubscriber::visibleGeometry.push_back(control_line->icon.get()); + EditSubscriber::visibleGeometry.push_back(control_line->begin.get()); + EditSubscriber::visibleGeometry.push_back(control_line->end.get()); + + EditSubscriber::mouseOverGeometry.push_back(control_line->line.get()); + EditSubscriber::mouseOverGeometry.push_back(control_line->icon.get()); + EditSubscriber::mouseOverGeometry.push_back(control_line->begin.get()); + EditSubscriber::mouseOverGeometry.push_back(control_line->end.get()); + + control_lines.push_back(std::move(control_line)); +} + +void ControlLineManager::autoSetLineType(int object_id) +{ + int line_id = (object_id - 1) / ::ControlLine::OBJ_COUNT; + ::ControlLine& line = *control_lines[line_id]; + + int dx = line.begin->center.x - line.end->center.x; + int dy = line.begin->center.y - line.end->center.y; + + if (dx < 0) { + dx = -dx; + } + if (dy < 0) { + dy = -dy; + } + + rtengine::ControlLine::Type type; + std::shared_ptr icon; + + if (dx > dy) { // More horizontal than vertical. + type = rtengine::ControlLine::HORIZONTAL; + icon = line.icon_h; + } else { + type = rtengine::ControlLine::VERTICAL; + icon = line.icon_v; + } + + if (type != line.type) { // Need to update line type. + line.type = type; + line.icon = icon; + visibleGeometry[line_id * ::ControlLine::OBJ_COUNT + 1] = line.icon.get(); + } +} + +void ControlLineManager::removeAll(void) +{ + visibleGeometry.clear(); + mouseOverGeometry.erase(mouseOverGeometry.begin() + 1, mouseOverGeometry.end()); + control_lines.clear(); + prev_obj = -1; + selected_object = -1; + edited = true; + callbacks->lineChanged(); +} + +void ControlLineManager::removeLine(size_t line_id) +{ + if (line_id >= control_lines.size()) { + return; + } + + 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); + control_lines.erase(control_lines.begin() + line_id); + + edited = true; + callbacks->lineChanged(); +} + +void ControlLineManager::toControlLines(std::vector& converted) const +{ + converted.clear(); + converted.resize(control_lines.size()); + + for (unsigned int i = 0; i < control_lines.size(); i++) { + converted[i].x1 = control_lines[i]->begin->center.x; + converted[i].y1 = control_lines[i]->begin->center.y; + converted[i].x2 = control_lines[i]->end->center.x; + converted[i].y2 = control_lines[i]->end->center.y; + converted[i].type = control_lines[i]->type; + } +} diff --git a/rtgui/controllines.h b/rtgui/controllines.h new file mode 100644 index 000000000..3743ad5b0 --- /dev/null +++ b/rtgui/controllines.h @@ -0,0 +1,116 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2020 Lawrence Lee + * + * 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 . + */ +#pragma once + +#include + +#include "editcallbacks.h" +#include "../rtengine/perspectivecorrection.h" + +class Circle; +class Line; +class OPIcon; +class Rectangle; +class RTSurface; + +struct ControlLine +{ + static constexpr int OBJ_COUNT = 4; + std::unique_ptr line; + std::shared_ptr icon; + std::shared_ptr icon_h, icon_v; + std::unique_ptr begin, end; + rtengine::ControlLine::Type type; + + ~ControlLine(); +}; + +class ControlLineManager: EditSubscriber +{ + +protected: + /** Hidden object for capturing mouse events. */ + std::unique_ptr canvas_area; + rtengine::Coord drag_delta; + std::vector> control_lines; + CursorShape cursor; + bool draw_mode; + bool drawing_line; + bool edited; + Cairo::RefPtr line_icon_h, line_icon_v; + Cairo::RefPtr line_icon_h_prelight, line_icon_v_prelight; + int prev_obj; + int selected_object; + + void addLine (rtengine::Coord begin, rtengine::Coord end, + rtengine::ControlLine::Type type = rtengine::ControlLine::VERTICAL); + /** + * Set the line type of the line containing the object according to the + * line's angle. + * + * If the line is within 45 degrees of a perfectly vertical + * line, inclusive, the line type is set to vertical. Otherwise, horizontal. + */ + void autoSetLineType(int object_id); + void removeLine (size_t line_id); + +public: + class Callbacks + { + public: + virtual ~Callbacks() {}; + /** Called when a line changed (added, removed, moved, etc.). */ + virtual void lineChanged (void) {}; + /** Called when the EditSubscriber's switchOffEditMode is called. */ + virtual void switchOffEditMode (void) {}; + }; + + /** Callbacks to invoke. */ + std::shared_ptr callbacks; + + ControlLineManager(); + ~ControlLineManager(); + + bool getEdited (void) const; + void removeAll (void); + /** Sets whether or not the lines are visible and interact-able. */ + void setActive (bool active); + /** Set whether or not lines can be drawn and deleted. */ + void setDrawMode (bool draw); + void setEdited (bool edited); + void setEditProvider (EditDataProvider* provider); + void setLines (const std::vector& lines); + /** Returns the number of lines. */ + size_t size (void) const; + /** + * Allocates a new array and populates it with copies of the control lines. + */ + void toControlLines (std::vector& converted) const; + + // EditSubscriber overrides + bool button1Pressed (int modifierKey) override; + bool button1Released (void) override; + bool button3Pressed (int modifierKey) override; + bool pick1 (bool picked) override; + bool pick3 (bool picked) override; + bool drag1 (int modifierKey) override; + CursorShape getCursor (int objectID) const override; + bool mouseOver (int modifierKey) override; + void switchOffEditMode (void) override; +}; diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index a8a1f73d3..ec86da161 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -782,431 +782,6 @@ void PerspCorrection::setCamBasedEventsActive(bool active) } } -ControlLineManager::ControlLineManager(): - EditSubscriber(ET_OBJECTS), - canvas_area(new Rectangle()), - cursor(CSCrosshair), - draw_mode(false), - drawing_line(false), - edited(false), - prev_obj(-1), - selected_object(-1) -{ - canvas_area->filled = true; - canvas_area->topLeft = Coord(0, 0); - mouseOverGeometry.push_back(canvas_area.get()); - - line_icon_h = Cairo::RefPtr(new RTSurface("bidirectional-arrow-horizontal-hicontrast.png")); - line_icon_v = Cairo::RefPtr(new RTSurface("bidirectional-arrow-vertical-hicontrast.png")); - line_icon_h_prelight = Cairo::RefPtr(new RTSurface("bidirectional-arrow-horizontal-prelight.png")); - line_icon_v_prelight = Cairo::RefPtr(new RTSurface("bidirectional-arrow-vertical-prelight.png")); -} - -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; -} - -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); - drawing_line = true; - 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; - } - edited = true; - callbacks->lineChanged(); - drawing_line = false; - 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(); - ::ControlLine& 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.get(); - - edited = true; - callbacks->lineChanged(); - - 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); - prev_obj = -1; - selected_object = -1; - 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; - - if (drawing_line) { - autoSetLineType(selected_object); - } - - return false; -} - -bool ControlLineManager::getEdited(void) const -{ - return edited; -} - -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) { - visibleGeometry[prev_obj - 1]->state = Geometry::NORMAL; - } - prev_obj = cur_obj; - - return true; -} - -void ControlLineManager::switchOffEditMode(void) -{ - if (callbacks) { - callbacks->switchOffEditMode(); - } -} - -void ControlLineManager::setEdited(bool edited) -{ - this->edited = edited; -} - -void ControlLineManager::setEditProvider(EditDataProvider* provider) -{ - EditSubscriber::setEditProvider(provider); -} - -void ControlLineManager::setLines(const std::vector& lines) -{ - removeAll(); - for (auto&& line : lines) { - Coord start(line.x1, line.y1); - Coord end(line.x2, line.y2); - addLine(start, end, line.type); - } -} - -void ControlLineManager::addLine(Coord begin, Coord end, rtengine::ControlLine::Type type) -{ - constexpr int line_width = 2; - constexpr int handle_radius = 6; - std::unique_ptr line; - std::shared_ptr icon_h, icon_v; - std::unique_ptr begin_c, end_c; - - line = std::unique_ptr(new Line()); - line->datum = Geometry::IMAGE; - line->innerLineWidth = line_width; - line->begin = begin; - line->end = end; - - const Cairo::RefPtr null_surface = Cairo::RefPtr(nullptr); - - icon_h = std::make_shared(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 = std::make_shared(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 = std::unique_ptr(new Circle()); - begin_c->datum = Geometry::IMAGE; - begin_c->filled = true; - begin_c->radius = handle_radius; - begin_c->center = begin; - - end_c = std::unique_ptr(new Circle()); - end_c->datum = Geometry::IMAGE; - end_c->filled = true; - end_c->radius = handle_radius; - end_c->center = end; - - std::unique_ptr<::ControlLine> control_line(new ::ControlLine()); - control_line->begin = std::move(begin_c); - control_line->end = std::move(end_c); - control_line->icon_h = icon_h; - control_line->icon_v = icon_v; - if (type == rtengine::ControlLine::HORIZONTAL) { - control_line->icon = icon_h; - } else { - control_line->icon = icon_v; - } - control_line->line = std::move(line); - control_line->type = type; - - EditSubscriber::visibleGeometry.push_back(control_line->line.get()); - EditSubscriber::visibleGeometry.push_back(control_line->icon.get()); - EditSubscriber::visibleGeometry.push_back(control_line->begin.get()); - EditSubscriber::visibleGeometry.push_back(control_line->end.get()); - - EditSubscriber::mouseOverGeometry.push_back(control_line->line.get()); - EditSubscriber::mouseOverGeometry.push_back(control_line->icon.get()); - EditSubscriber::mouseOverGeometry.push_back(control_line->begin.get()); - EditSubscriber::mouseOverGeometry.push_back(control_line->end.get()); - - control_lines.push_back(std::move(control_line)); -} - -void ControlLineManager::autoSetLineType(int object_id) -{ - int line_id = (object_id - 1) / ::ControlLine::OBJ_COUNT; - ::ControlLine& line = *control_lines[line_id]; - - int dx = line.begin->center.x - line.end->center.x; - int dy = line.begin->center.y - line.end->center.y; - - if (dx < 0) { - dx = -dx; - } - if (dy < 0) { - dy = -dy; - } - - rtengine::ControlLine::Type type; - std::shared_ptr icon; - - if (dx > dy) { // More horizontal than vertical. - type = rtengine::ControlLine::HORIZONTAL; - icon = line.icon_h; - } else { - type = rtengine::ControlLine::VERTICAL; - icon = line.icon_v; - } - - if (type != line.type) { // Need to update line type. - line.type = type; - line.icon = icon; - visibleGeometry[line_id * ::ControlLine::OBJ_COUNT + 1] = line.icon.get(); - } -} - -void ControlLineManager::removeAll(void) -{ - visibleGeometry.clear(); - mouseOverGeometry.erase(mouseOverGeometry.begin() + 1, mouseOverGeometry.end()); - control_lines.clear(); - prev_obj = -1; - selected_object = -1; - edited = true; - callbacks->lineChanged(); -} - -void ControlLineManager::removeLine(size_t line_id) -{ - if (line_id >= control_lines.size()) { - return; - } - - 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); - control_lines.erase(control_lines.begin() + line_id); - - edited = true; - callbacks->lineChanged(); -} - -void ControlLineManager::toControlLines(std::vector& converted) const -{ - converted.clear(); - converted.resize(control_lines.size()); - - for (unsigned int i = 0; i < control_lines.size(); i++) { - converted[i].x1 = control_lines[i]->begin->center.x; - converted[i].y1 = control_lines[i]->begin->center.y; - converted[i].x2 = control_lines[i]->end->center.x; - converted[i].y2 = control_lines[i]->end->center.y; - converted[i].type = control_lines[i]->type; - } -} - LinesCallbacks::LinesCallbacks(PerspCorrection* tool): tool(tool) { diff --git a/rtgui/perspective.h b/rtgui/perspective.h index c58a9420e..0694ccb4e 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -21,94 +21,9 @@ #include #include "adjuster.h" -#include "editcallbacks.h" -#include "editwidgets.h" +#include "controllines.h" #include "lensgeomlistener.h" #include "toolpanel.h" -#include "../rtengine/perspectivecorrection.h" - -struct ControlLine -{ - static constexpr int OBJ_COUNT = 4; - std::unique_ptr line; - std::shared_ptr icon; - std::shared_ptr icon_h, icon_v; - std::unique_ptr begin, end; - rtengine::ControlLine::Type type; -}; - -class ControlLineManager: EditSubscriber -{ - -protected: - /** Hidden object for capturing mouse events. */ - std::unique_ptr canvas_area; - rtengine::Coord drag_delta; - std::vector> control_lines; - CursorShape cursor; - bool draw_mode; - bool drawing_line; - bool edited; - Cairo::RefPtr line_icon_h, line_icon_v; - Cairo::RefPtr line_icon_h_prelight, line_icon_v_prelight; - int prev_obj; - int selected_object; - - void addLine (rtengine::Coord begin, rtengine::Coord end, - rtengine::ControlLine::Type type = rtengine::ControlLine::VERTICAL); - /** - * Set the line type of the line containing the object according to the - * line's angle. - * - * If the line is within 45 degrees of a perfectly vertical - * line, inclusive, the line type is set to vertical. Otherwise, horizontal. - */ - void autoSetLineType(int object_id); - void removeLine (size_t line_id); - -public: - class Callbacks - { - public: - virtual ~Callbacks() {}; - /** Called when a line changed (added, removed, moved, etc.). */ - virtual void lineChanged (void) {}; - /** Called when the EditSubscriber's switchOffEditMode is called. */ - virtual void switchOffEditMode (void) {}; - }; - - /** Callbacks to invoke. */ - std::shared_ptr callbacks; - - ControlLineManager(); - - bool getEdited (void) const; - void removeAll (void); - /** Sets whether or not the lines are visible and interact-able. */ - void setActive (bool active); - /** Set whether or not lines can be drawn and deleted. */ - void setDrawMode (bool draw); - void setEdited (bool edited); - void setEditProvider (EditDataProvider* provider); - void setLines (const std::vector& lines); - /** Returns the number of lines. */ - size_t size (void) const; - /** - * Allocates a new array and populates it with copies of the control lines. - */ - void toControlLines (std::vector& converted) const; - - // EditSubscriber overrides - bool button1Pressed (int modifierKey) override; - bool button1Released (void) override; - bool button3Pressed (int modifierKey) override; - bool pick1 (bool picked) override; - bool pick3 (bool picked) override; - bool drag1 (int modifierKey) override; - CursorShape getCursor (int objectID) const override; - bool mouseOver (int modifierKey) override; - void switchOffEditMode (void) override; -}; class PerspCorrection final : public ToolParamBlock, From ef405eeb1dbe7f5d49a41e1b42346d4300fd20a7 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Sun, 12 Jul 2020 15:44:58 -0700 Subject: [PATCH 23/27] Apply astyle to rtgui/controllines.* --- rtgui/controllines.cc | 81 +++++++++++++++++++++++++++++++------------ rtgui/controllines.h | 49 +++++++++++++------------- 2 files changed, 82 insertions(+), 48 deletions(-) diff --git a/rtgui/controllines.cc b/rtgui/controllines.cc index 8d39c9d59..2fd446c56 100644 --- a/rtgui/controllines.cc +++ b/rtgui/controllines.cc @@ -43,10 +43,14 @@ ControlLineManager::ControlLineManager(): canvas_area->topLeft = Coord(0, 0); mouseOverGeometry.push_back(canvas_area.get()); - line_icon_h = Cairo::RefPtr(new RTSurface("bidirectional-arrow-horizontal-hicontrast.png")); - line_icon_v = Cairo::RefPtr(new RTSurface("bidirectional-arrow-vertical-hicontrast.png")); - line_icon_h_prelight = Cairo::RefPtr(new RTSurface("bidirectional-arrow-horizontal-prelight.png")); - line_icon_v_prelight = Cairo::RefPtr(new RTSurface("bidirectional-arrow-vertical-prelight.png")); + line_icon_h = Cairo::RefPtr(new RTSurface( + "bidirectional-arrow-horizontal-hicontrast.png")); + line_icon_v = Cairo::RefPtr(new RTSurface( + "bidirectional-arrow-vertical-hicontrast.png")); + line_icon_h_prelight = Cairo::RefPtr(new RTSurface( + "bidirectional-arrow-horizontal-prelight.png")); + line_icon_v_prelight = Cairo::RefPtr(new RTSurface( + "bidirectional-arrow-vertical-prelight.png")); } ControlLineManager::~ControlLineManager() = default; @@ -91,6 +95,7 @@ bool ControlLineManager::button1Pressed(int modifierKey) 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; @@ -111,9 +116,11 @@ bool ControlLineManager::button1Pressed(int modifierKey) bool ControlLineManager::button1Released(void) { action = Action::NONE; + if (selected_object > 0) { mouseOverGeometry[selected_object]->state = Geometry::NORMAL; } + edited = true; callbacks->lineChanged(); drawing_line = false; @@ -151,7 +158,8 @@ bool ControlLineManager::pick1(bool picked) // Change line type. int object_id = provider->getObject(); - ::ControlLine& line = *control_lines[(object_id - 1) / ::ControlLine::OBJ_COUNT]; + ::ControlLine& line = + *control_lines[(object_id - 1) / ::ControlLine::OBJ_COUNT]; if (line.type == rtengine::ControlLine::HORIZONTAL) { line.icon = line.icon_v; @@ -197,8 +205,10 @@ bool ControlLineManager::drag1(int modifierKey) 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 + ::ControlLine& control_line = + *control_lines[(selected_object - 1) / ::ControlLine::OBJ_COUNT]; + // 0 == end, 1 == line, 2 == icon, 3 == begin + int component = selected_object % ::ControlLine::OBJ_COUNT; Coord mouse = provider->posImage + provider->deltaImage; Coord delta = provider->deltaImage - drag_delta; int ih, iw; @@ -211,6 +221,7 @@ bool ControlLineManager::drag1(int modifierKey) 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; @@ -229,6 +240,7 @@ bool ControlLineManager::drag1(int modifierKey) control_line.line->state = Geometry::DRAGGED; break; } + case (3): // begin control_line.begin->center = mouse; control_line.begin->center.clip(iw, ih); @@ -237,8 +249,10 @@ bool ControlLineManager::drag1(int modifierKey) 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_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; @@ -288,6 +302,7 @@ bool ControlLineManager::mouseOver(int modifierKey) if (prev_obj != cur_obj && prev_obj > 0) { visibleGeometry[prev_obj - 1]->state = Geometry::NORMAL; } + prev_obj = cur_obj; return true; @@ -310,9 +325,11 @@ void ControlLineManager::setEditProvider(EditDataProvider* provider) EditSubscriber::setEditProvider(provider); } -void ControlLineManager::setLines(const std::vector& lines) +void ControlLineManager::setLines(const std::vector& + lines) { removeAll(); + for (auto&& line : lines) { Coord start(line.x1, line.y1); Coord end(line.x2, line.y2); @@ -320,7 +337,8 @@ void ControlLineManager::setLines(const std::vector& line } } -void ControlLineManager::addLine(Coord begin, Coord end, rtengine::ControlLine::Type type) +void ControlLineManager::addLine(Coord begin, Coord end, + rtengine::ControlLine::Type type) { constexpr int line_width = 2; constexpr int handle_radius = 6; @@ -334,14 +352,19 @@ void ControlLineManager::addLine(Coord begin, Coord end, rtengine::ControlLine:: line->begin = begin; line->end = end; - const Cairo::RefPtr null_surface = Cairo::RefPtr(nullptr); + const Cairo::RefPtr null_surface = + Cairo::RefPtr(nullptr); - icon_h = std::make_shared(line_icon_h, null_surface, line_icon_h_prelight, - null_surface, null_surface, Geometry::DP_CENTERCENTER); + icon_h = std::make_shared(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 = std::make_shared(line_icon_v, null_surface, line_icon_v_prelight, - null_surface, null_surface, Geometry::DP_CENTERCENTER); + icon_v = std::make_shared(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 = std::unique_ptr(new Circle()); @@ -361,11 +384,13 @@ void ControlLineManager::addLine(Coord begin, Coord end, rtengine::ControlLine:: control_line->end = std::move(end_c); control_line->icon_h = icon_h; control_line->icon_v = icon_v; + if (type == rtengine::ControlLine::HORIZONTAL) { control_line->icon = icon_h; } else { control_line->icon = icon_v; } + control_line->line = std::move(line); control_line->type = type; @@ -393,6 +418,7 @@ void ControlLineManager::autoSetLineType(int object_id) if (dx < 0) { dx = -dx; } + if (dy < 0) { dy = -dy; } @@ -411,14 +437,16 @@ void ControlLineManager::autoSetLineType(int object_id) if (type != line.type) { // Need to update line type. line.type = type; line.icon = icon; - visibleGeometry[line_id * ::ControlLine::OBJ_COUNT + 1] = line.icon.get(); + visibleGeometry[line_id * ::ControlLine::OBJ_COUNT + 1] = + line.icon.get(); } } void ControlLineManager::removeAll(void) { visibleGeometry.clear(); - mouseOverGeometry.erase(mouseOverGeometry.begin() + 1, mouseOverGeometry.end()); + mouseOverGeometry.erase(mouseOverGeometry.begin() + 1, + mouseOverGeometry.end()); control_lines.clear(); prev_obj = -1; selected_object = -1; @@ -432,17 +460,24 @@ void ControlLineManager::removeLine(size_t line_id) return; } - 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); + 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 + ); control_lines.erase(control_lines.begin() + line_id); edited = true; callbacks->lineChanged(); } -void ControlLineManager::toControlLines(std::vector& converted) const +void ControlLineManager::toControlLines(std::vector& + converted) const { converted.clear(); converted.resize(control_lines.size()); diff --git a/rtgui/controllines.h b/rtgui/controllines.h index 3743ad5b0..0ff449092 100644 --- a/rtgui/controllines.h +++ b/rtgui/controllines.h @@ -29,8 +29,7 @@ class OPIcon; class Rectangle; class RTSurface; -struct ControlLine -{ +struct ControlLine { static constexpr int OBJ_COUNT = 4; std::unique_ptr line; std::shared_ptr icon; @@ -58,8 +57,8 @@ protected: int prev_obj; int selected_object; - void addLine (rtengine::Coord begin, rtengine::Coord end, - rtengine::ControlLine::Type type = rtengine::ControlLine::VERTICAL); + void addLine(rtengine::Coord begin, rtengine::Coord end, + rtengine::ControlLine::Type type = rtengine::ControlLine::VERTICAL); /** * Set the line type of the line containing the object according to the * line's angle. @@ -68,7 +67,7 @@ protected: * line, inclusive, the line type is set to vertical. Otherwise, horizontal. */ void autoSetLineType(int object_id); - void removeLine (size_t line_id); + void removeLine(size_t line_id); public: class Callbacks @@ -76,9 +75,9 @@ public: public: virtual ~Callbacks() {}; /** Called when a line changed (added, removed, moved, etc.). */ - virtual void lineChanged (void) {}; + virtual void lineChanged(void) {}; /** Called when the EditSubscriber's switchOffEditMode is called. */ - virtual void switchOffEditMode (void) {}; + virtual void switchOffEditMode(void) {}; }; /** Callbacks to invoke. */ @@ -87,30 +86,30 @@ public: ControlLineManager(); ~ControlLineManager(); - bool getEdited (void) const; - void removeAll (void); + bool getEdited(void) const; + void removeAll(void); /** Sets whether or not the lines are visible and interact-able. */ - void setActive (bool active); + void setActive(bool active); /** Set whether or not lines can be drawn and deleted. */ - void setDrawMode (bool draw); - void setEdited (bool edited); - void setEditProvider (EditDataProvider* provider); - void setLines (const std::vector& lines); + void setDrawMode(bool draw); + void setEdited(bool edited); + void setEditProvider(EditDataProvider* provider); + void setLines(const std::vector& lines); /** Returns the number of lines. */ - size_t size (void) const; + size_t size(void) const; /** * Allocates a new array and populates it with copies of the control lines. */ - void toControlLines (std::vector& converted) const; + void toControlLines(std::vector& converted) const; // EditSubscriber overrides - bool button1Pressed (int modifierKey) override; - bool button1Released (void) override; - bool button3Pressed (int modifierKey) override; - bool pick1 (bool picked) override; - bool pick3 (bool picked) override; - bool drag1 (int modifierKey) override; - CursorShape getCursor (int objectID) const override; - bool mouseOver (int modifierKey) override; - void switchOffEditMode (void) override; + bool button1Pressed(int modifierKey) override; + bool button1Released(void) override; + bool button3Pressed(int modifierKey) override; + bool pick1(bool picked) override; + bool pick3(bool picked) override; + bool drag1(int modifierKey) override; + CursorShape getCursor(int objectID) const override; + bool mouseOver(int modifierKey) override; + void switchOffEditMode(void) override; }; From 95141a943c1a84d9d9a11cd73e568b498dddc63d Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Mon, 20 Jul 2020 15:07:20 -0700 Subject: [PATCH 24/27] Make perspective icons consistent with other icons Ensure a one pixel padding on all sides of the bidirectional arrows and draw icons. --- ...irectional-arrow-horizontal-hicontrast.svg | 124 +++++++++--------- ...idirectional-arrow-horizontal-prelight.svg | 124 +++++++++--------- ...idirectional-arrow-vertical-hicontrast.svg | 124 +++++++++--------- .../bidirectional-arrow-vertical-prelight.svg | 124 +++++++++--------- rtdata/images/svg/draw.svg | 98 +++++++------- 5 files changed, 295 insertions(+), 299 deletions(-) diff --git a/rtdata/images/svg/bidirectional-arrow-horizontal-hicontrast.svg b/rtdata/images/svg/bidirectional-arrow-horizontal-hicontrast.svg index b41979c6a..f2712cee9 100644 --- a/rtdata/images/svg/bidirectional-arrow-horizontal-hicontrast.svg +++ b/rtdata/images/svg/bidirectional-arrow-horizontal-hicontrast.svg @@ -7,61 +7,61 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - sodipodi:docname="bidirectional-arrow-horizontal-hicontrast.svg" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - inkscape:export-ydpi="96" - inkscape:export-xdpi="96" - inkscape:export-filename="/tmp/template.png" - id="SVGRoot" - version="1.1" - viewBox="0 0 24 24" + width="24px" height="24px" - width="24px"> + viewBox="0 0 24 24" + version="1.1" + id="SVGRoot" + inkscape:export-filename="/tmp/template.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + sodipodi:docname="bidirectional-arrow-horizontal-hicontrast.svg"> + bordercolor="#666768" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="29.288994" + inkscape:cx="11.864052" + inkscape:cy="11.292624" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:window-width="1920" + inkscape:window-height="1041" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:grid-bbox="true" + inkscape:pagecheckerboard="false" + inkscape:snap-bbox="true" + inkscape:bbox-nodes="true" + inkscape:snap-others="true" + inkscape:object-nodes="true" + inkscape:snap-grids="true" + inkscape:snap-bbox-midpoints="true" + inkscape:document-rotation="0" + inkscape:object-paths="true" + inkscape:snap-nodes="true" + inkscape:snap-smooth-nodes="true" + inkscape:bbox-paths="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:snap-intersection-paths="true" + inkscape:snap-midpoints="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-center="true" + inkscape:snap-text-baseline="true" + inkscape:snap-page="true"> + originx="1" + originy="1" + empspacing="11" + dotted="false" /> @@ -73,7 +73,7 @@ image/svg+xml - + Lawrence @@ -106,22 +106,22 @@ + inkscape:label="Layer 1"> + sodipodi:nodetypes="ccccc" /> + sodipodi:nodetypes="ccccc" /> + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1" + d="m 17.5,13.5 v 2.25 l 5,-3.75 -5,-3.75 v 2.25" + id="path855" /> diff --git a/rtdata/images/svg/bidirectional-arrow-horizontal-prelight.svg b/rtdata/images/svg/bidirectional-arrow-horizontal-prelight.svg index 0ad40b970..6382cc546 100644 --- a/rtdata/images/svg/bidirectional-arrow-horizontal-prelight.svg +++ b/rtdata/images/svg/bidirectional-arrow-horizontal-prelight.svg @@ -7,61 +7,61 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - sodipodi:docname="bidirectional-arrow-horizontal-prelight.svg" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - inkscape:export-ydpi="96" - inkscape:export-xdpi="96" - inkscape:export-filename="/tmp/template.png" - id="SVGRoot" - version="1.1" - viewBox="0 0 24 24" + width="24px" height="24px" - width="24px"> + viewBox="0 0 24 24" + version="1.1" + id="SVGRoot" + inkscape:export-filename="/tmp/template.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + sodipodi:docname="bidirectional-arrow-horizontal-prelight.svg"> + bordercolor="#666768" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="29.288994" + inkscape:cx="11.864052" + inkscape:cy="11.292624" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:window-width="1920" + inkscape:window-height="1041" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:grid-bbox="true" + inkscape:pagecheckerboard="false" + inkscape:snap-bbox="true" + inkscape:bbox-nodes="true" + inkscape:snap-others="true" + inkscape:object-nodes="true" + inkscape:snap-grids="true" + inkscape:snap-bbox-midpoints="true" + inkscape:document-rotation="0" + inkscape:object-paths="true" + inkscape:snap-nodes="true" + inkscape:snap-smooth-nodes="true" + inkscape:bbox-paths="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:snap-intersection-paths="true" + inkscape:snap-midpoints="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-center="true" + inkscape:snap-text-baseline="true" + inkscape:snap-page="true"> + originx="1" + originy="1" + empspacing="11" + dotted="false" /> @@ -73,7 +73,7 @@ image/svg+xml - + Lawrence @@ -106,22 +106,22 @@ + inkscape:label="Layer 1"> + sodipodi:nodetypes="ccccc" /> + sodipodi:nodetypes="ccccc" /> + style="fill:#ff6400;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1" + d="m 17.5,13.5 v 2.25 l 5,-3.75 -5,-3.75 v 2.25" + id="path855" /> diff --git a/rtdata/images/svg/bidirectional-arrow-vertical-hicontrast.svg b/rtdata/images/svg/bidirectional-arrow-vertical-hicontrast.svg index 18d041279..93cb21a43 100644 --- a/rtdata/images/svg/bidirectional-arrow-vertical-hicontrast.svg +++ b/rtdata/images/svg/bidirectional-arrow-vertical-hicontrast.svg @@ -7,61 +7,61 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - sodipodi:docname="bidirectional-arrow-vertical-hicontrast.svg" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - inkscape:export-ydpi="96" - inkscape:export-xdpi="96" - inkscape:export-filename="/tmp/template.png" - id="SVGRoot" - version="1.1" - viewBox="0 0 24 24" + width="24px" height="24px" - width="24px"> + viewBox="0 0 24 24" + version="1.1" + id="SVGRoot" + inkscape:export-filename="/tmp/template.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + sodipodi:docname="bidirectional-arrow-vertical-hicontrast.svg"> + bordercolor="#666768" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="29.288994" + inkscape:cx="11.864052" + inkscape:cy="11.292624" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:window-width="1920" + inkscape:window-height="1041" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:grid-bbox="true" + inkscape:pagecheckerboard="false" + inkscape:snap-bbox="true" + inkscape:bbox-nodes="true" + inkscape:snap-others="true" + inkscape:object-nodes="true" + inkscape:snap-grids="true" + inkscape:snap-bbox-midpoints="true" + inkscape:document-rotation="0" + inkscape:object-paths="true" + inkscape:snap-nodes="true" + inkscape:snap-smooth-nodes="true" + inkscape:bbox-paths="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:snap-intersection-paths="true" + inkscape:snap-midpoints="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-center="true" + inkscape:snap-text-baseline="true" + inkscape:snap-page="true"> + originx="1" + originy="1" + empspacing="11" + dotted="false" /> @@ -73,7 +73,7 @@ image/svg+xml - + Lawrence @@ -106,22 +106,22 @@ + inkscape:label="Layer 1"> + sodipodi:nodetypes="ccccc" /> + sodipodi:nodetypes="ccccc" /> + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1" + d="M 10.5,17.5 H 8.25 l 3.75,5 3.75,-5 H 13.5" + id="path855" /> diff --git a/rtdata/images/svg/bidirectional-arrow-vertical-prelight.svg b/rtdata/images/svg/bidirectional-arrow-vertical-prelight.svg index 3dc5034f4..76f4312cb 100644 --- a/rtdata/images/svg/bidirectional-arrow-vertical-prelight.svg +++ b/rtdata/images/svg/bidirectional-arrow-vertical-prelight.svg @@ -7,61 +7,61 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="24px" - height="24px" - viewBox="0 0 24 24" - version="1.1" - id="SVGRoot" - inkscape:export-filename="/tmp/template.png" - inkscape:export-xdpi="96" - inkscape:export-ydpi="96" + sodipodi:docname="bidirectional-arrow-vertical-prelight.svg" inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - sodipodi:docname="bidirectional-arrow-vertical-prelight.svg"> + inkscape:export-ydpi="96" + inkscape:export-xdpi="96" + inkscape:export-filename="/tmp/template.png" + id="SVGRoot" + version="1.1" + viewBox="0 0 24 24" + height="24px" + width="24px"> + inkscape:snap-center="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-midpoints="true" + inkscape:snap-intersection-paths="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:bbox-paths="true" + inkscape:snap-smooth-nodes="true" + inkscape:snap-nodes="true" + inkscape:object-paths="true" + inkscape:document-rotation="0" + inkscape:snap-bbox-midpoints="true" + inkscape:snap-grids="true" + inkscape:object-nodes="true" + inkscape:snap-others="true" + inkscape:bbox-nodes="true" + inkscape:snap-bbox="true" + inkscape:pagecheckerboard="false" + inkscape:grid-bbox="true" + inkscape:window-maximized="1" + inkscape:window-y="0" + inkscape:window-x="0" + inkscape:window-height="1041" + inkscape:window-width="1920" + showgrid="true" + inkscape:current-layer="layer1" + inkscape:document-units="px" + inkscape:cy="11.292624" + inkscape:cx="11.864052" + inkscape:zoom="29.288994" + inkscape:pageshadow="2" + inkscape:pageopacity="0" + borderopacity="1.0" + bordercolor="#666768" + pagecolor="#E0E1E2" + id="base"> + originy="1" + originx="1" + id="grid1374" + type="xygrid" /> @@ -73,7 +73,7 @@ image/svg+xml - + Lawrence @@ -106,22 +106,22 @@ + id="layer1"> + d="m 10.5,5.75 v 12.5 h 3 V 5.75 Z" + style="fill:#ff6400;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + d="m 10.49995,6.4999667 h -2.25 l 3.75,-5 3.75,5 h -2.25" + style="fill:#ff6400;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1" /> + id="path855" + d="M 10.5,17.5 H 8.25 l 3.75,5 3.75,-5 H 13.5" + style="fill:#ff6400;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1" /> diff --git a/rtdata/images/svg/draw.svg b/rtdata/images/svg/draw.svg index 308e15654..30ea04ea4 100644 --- a/rtdata/images/svg/draw.svg +++ b/rtdata/images/svg/draw.svg @@ -7,50 +7,50 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - sodipodi:docname="draw.svg" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)" - inkscape:export-ydpi="96" - inkscape:export-xdpi="96" - inkscape:export-filename="/tmp/template.png" - id="SVGRoot" - version="1.1" - viewBox="0 0 24 24" + width="24px" height="24px" - width="24px"> + viewBox="0 0 24 24" + version="1.1" + id="SVGRoot" + inkscape:export-filename="/tmp/template.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + sodipodi:docname="draw.svg"> + bordercolor="#666768" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="14.298358" + inkscape:cx="-1.7458856" + inkscape:cy="16.76612" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:window-width="1920" + inkscape:window-height="1041" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:grid-bbox="true" + inkscape:pagecheckerboard="false" + inkscape:snap-bbox="true" + inkscape:bbox-nodes="true" + inkscape:snap-others="true" + inkscape:object-nodes="false" + inkscape:snap-grids="true" + inkscape:snap-bbox-midpoints="false" + inkscape:document-rotation="0"> + originx="1" + originy="1" + empspacing="11" + dotted="false" /> @@ -62,7 +62,7 @@ image/svg+xml - + Lawrence @@ -95,18 +95,14 @@ + inkscape:label="Layer 1"> - + style="opacity:0.7;fill:#2a7fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00157;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 19.761765,1 c 1.135523,0 3.238095,2.1025723 3.238095,3.238095 0,1.1355228 -1.57952,1.57952 -1.57952,1.57952 L 18.182245,2.57952 c 0,0 0.443997,-1.57952 1.57952,-1.57952 z M 17.372721,3.3890438 20.610816,6.6271387 7.515719,19.722235 4.277625,16.484141 Z M 3.468101,17.293664 6.706195,20.531759 6.666665,20.571289 1,22.999859 3.42857,17.333193 Z" + id="path1658" + inkscape:connector-curvature="0" + sodipodi:nodetypes="zzcczccccccccccc" /> From a56c1fbb078e9ea25c74fd1ff694be6254e7bb77 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Mon, 20 Jul 2020 15:24:53 -0700 Subject: [PATCH 25/27] Add tooltips to control lines buttons --- rtdata/languages/default | 2 ++ rtgui/perspective.cc | 3 +++ 2 files changed, 5 insertions(+) diff --git a/rtdata/languages/default b/rtdata/languages/default index 399d8f041..9a3402f5f 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -220,8 +220,10 @@ GENERAL_BEFORE;Before GENERAL_CANCEL;Cancel GENERAL_CLOSE;Close GENERAL_CURRENT;Current +GENERAL_DELETE_ALL;Delete all GENERAL_DISABLE;Disable GENERAL_DISABLED;Disabled +GENERAL_EDIT;Edit GENERAL_ENABLE;Enable GENERAL_ENABLED;Enabled GENERAL_FILE;File diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index ec86da161..395729adb 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -185,17 +185,20 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" // Begin control lines interface. lines_button_apply = Gtk::manage (new Gtk::Button()); lines_button_apply->set_image(*ipers_apply); + lines_button_apply->set_tooltip_text(M("GENERAL_APPLY")); lines_button_apply->set_sensitive(false); lines_button_apply->signal_pressed().connect(sigc::mem_fun( *this, &::PerspCorrection::linesApplyButtonPressed)); lines_button_edit = Gtk::manage (new Gtk::ToggleButton()); lines_button_edit->set_image(*ipers_draw); + lines_button_edit->set_tooltip_text(M("GENERAL_EDIT")); 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_tooltip_text(M("GENERAL_DELETE_ALL")); lines_button_erase->set_sensitive(false); lines_button_erase->signal_pressed().connect(sigc::mem_fun( *this, &::PerspCorrection::linesEraseButtonPressed)); From 2804480585a371b21dd29de5455fea0cf217a852 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Mon, 20 Jul 2020 16:55:14 -0700 Subject: [PATCH 26/27] Change cursors used in control line edit mode Panning cursor is the open hand icon and draw cursor is the crosshair icon. --- rtgui/controllines.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtgui/controllines.cc b/rtgui/controllines.cc index 2fd446c56..573b3263f 100644 --- a/rtgui/controllines.cc +++ b/rtgui/controllines.cc @@ -32,7 +32,7 @@ using namespace rtengine; ControlLineManager::ControlLineManager(): EditSubscriber(ET_OBJECTS), canvas_area(new Rectangle()), - cursor(CSCrosshair), + cursor(CSHandOpen), draw_mode(false), drawing_line(false), edited(false), @@ -285,9 +285,9 @@ bool ControlLineManager::mouseOver(int modifierKey) if (cur_obj == 0) { // Canvas if (draw_mode && modifierKey & GDK_CONTROL_MASK) { - cursor = CSPlus; - } else { cursor = CSCrosshair; + } else { + cursor = CSHandOpen; } } else if (cur_obj < 0) { // Nothing cursor = CSArrow; From 1e73135d51938c5bc9731ab20ccc0a2124bfc523 Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Fri, 31 Jul 2020 21:58:00 -0700 Subject: [PATCH 27/27] Add perspective edit button to toolbar The perspective control lines edit mode no longer piggybacks on the hand tool button on the toolbar. --- rtdata/languages/default | 1 + rtgui/cropwindow.cc | 7 ++-- rtgui/perspective.cc | 25 +++++++++++++ rtgui/perspective.h | 15 ++++++++ rtgui/toolbar.cc | 77 +++++++++++++++++++++++++++++++++++++++- rtgui/toolbar.h | 5 +++ rtgui/toolenum.h | 2 +- rtgui/toolpanelcoord.cc | 33 +++++++++++++++++ rtgui/toolpanelcoord.h | 5 +++ 9 files changed, 165 insertions(+), 5 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 9a3402f5f..08c74cc7a 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1807,6 +1807,7 @@ THRESHOLDSELECTOR_TR;Top-right TOOLBAR_TOOLTIP_COLORPICKER;Lockable Color Picker\n\nWhen the tool is active:\n- Add a picker: left-click.\n- Drag a picker: left-click and drag.\n- Delete a picker: right-click.\n- Delete all pickers: Ctrl+Shift+right-click.\n- Revert to hand tool: right-click outside any picker. TOOLBAR_TOOLTIP_CROP;Crop selection.\nShortcut: c\nMove the crop using Shift+mouse drag. TOOLBAR_TOOLTIP_HAND;Hand tool.\nShortcut: h +TOOLBAR_TOOLTIP_PERSPECTIVE;Perspective Correction\n\nEdit control lines to correct perspective distortion. Click this button again to apply correction. TOOLBAR_TOOLTIP_STRAIGHTEN;Straighten / fine rotation.\nShortcut: s\n\nIndicate the vertical or horizontal by drawing a guide line over the image preview. Angle of rotation will be shown next to the guide line. Center of rotation is the geometrical center of the image. TOOLBAR_TOOLTIP_WB;Spot white balance.\nShortcut: w TP_BWMIX_ALGO;Algorithm OYCPM diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 975bac15c..a862ed122 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -414,7 +414,8 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) action_y = 0; needRedraw = true; } - } else if (iarea->getToolMode () == TMHand + } else if ((iarea->getToolMode () == TMHand + || iarea->getToolMode() == TMPerspective) && editSubscriber && cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) @@ -429,7 +430,7 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) state = SEditPick1; pickedObject = iarea->getObject(); pickModifierKey = bstate; - } else { + } else if (iarea->getToolMode() == TMPerspective) { state = SCropImgMove; } press_x = x; @@ -594,7 +595,7 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) } } } else if (button == 3) { - if (iarea->getToolMode () == TMHand) { + if (iarea->getToolMode () == TMHand || iarea->getToolMode() == TMPerspective) { EditSubscriber *editSubscriber = iarea->getCurrSubscriber(); if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) { needRedraw = editSubscriber->button3Pressed(bstate); diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index 395729adb..55013ec4a 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -108,6 +108,7 @@ PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M(" EvPerspControlLines = mapper->newEvent(M_VOID, "HISTORY_MSG_PERSP_CTRL_LINE"); lens_geom_listener = nullptr; + panel_listener = nullptr; metadata = nullptr; Gtk::Image* ipers_draw(new RTImage ("draw.png")); @@ -613,6 +614,17 @@ void PerspCorrection::setAdjusterBehavior (bool badd, bool camera_focal_length_a projection_yaw->setAddMode(projection_angle_add); } +void PerspCorrection::setControlLineEditMode(bool active) +{ + // Only camera-based mode supports control lines, so the mode must be + // switched if not in camera-based mode. + if (method->get_active_row_number() != 1) { + method->set_active(1); + } + + lines_button_edit->set_active(active); +} + void PerspCorrection::setMetadata (const rtengine::FramesMetaData* metadata) { this->metadata = metadata; @@ -748,6 +760,9 @@ void PerspCorrection::linesEditButtonPressed(void) lines_button_apply->set_sensitive(true); lines_button_erase->set_sensitive(true); setCamBasedEventsActive(false); + if (panel_listener) { + panel_listener->controlLineEditModeChanged(true); + } } else { // Leave edit mode. setCamBasedEventsActive(true); lines_button_apply->set_sensitive(false); @@ -758,6 +773,9 @@ void PerspCorrection::linesEditButtonPressed(void) } lines->setDrawMode(false); lines->setActive(false); + if (panel_listener) { + panel_listener->controlLineEditModeChanged(false); + } } } @@ -766,6 +784,13 @@ void PerspCorrection::linesEraseButtonPressed(void) lines->removeAll(); } +void PerspCorrection::requestApplyControlLines(void) +{ + if (lines_button_apply->is_sensitive()) { + linesApplyButtonPressed(); + } +} + void PerspCorrection::setCamBasedEventsActive(bool active) { if (active) { diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 0694ccb4e..6ba169b60 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -25,6 +25,14 @@ #include "lensgeomlistener.h" #include "toolpanel.h" +class PerspCorrectionPanelListener +{ +public: + virtual ~PerspCorrectionPanelListener() = default; + + virtual void controlLineEditModeChanged(bool active) = 0; +}; + class PerspCorrection final : public ToolParamBlock, public AdjusterListener, @@ -79,6 +87,7 @@ protected: rtengine::ProcEvent* event_persp_proj_rotate; rtengine::ProcEvent* event_persp_proj_angle; LensGeomListener* lens_geom_listener; + PerspCorrectionPanelListener* panel_listener; const rtengine::FramesMetaData* metadata; void applyControlLines (void); @@ -101,12 +110,18 @@ public: void linesEditButtonPressed (void); void linesEraseButtonPressed (void); void methodChanged (void); + void requestApplyControlLines(void); void 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); + void setControlLineEditMode(bool active); void setEditProvider (EditDataProvider* provider) override; void setLensGeomListener (LensGeomListener* listener) { lens_geom_listener = listener; } + void setPerspCorrectionPanelListener(PerspCorrectionPanelListener* listener) + { + panel_listener = listener; + } void setMetadata (const rtengine::FramesMetaData* metadata); void switchOffEditMode (void); void trimValues (rtengine::procparams::ProcParams* pp) override; diff --git a/rtgui/toolbar.cc b/rtgui/toolbar.cc index e642ae51b..5cdfc2787 100644 --- a/rtgui/toolbar.cc +++ b/rtgui/toolbar.cc @@ -77,6 +77,12 @@ ToolBar::ToolBar () : showColPickers(true), listener (nullptr), pickerListener(n pack_start (*straTool); + perspTool = Gtk::manage(new Gtk::ToggleButton()); + Gtk::Image* perspimg = Gtk::manage(new RTImage("perspective-vertical-bottom.png")); + perspTool->set_image(*perspimg); + perspTool->set_relief(Gtk::RELIEF_NONE); + pack_start(*perspTool); + handTool->set_active (true); current = TMHand; @@ -87,12 +93,14 @@ ToolBar::ToolBar () : showColPickers(true), listener (nullptr), pickerListener(n cpConn = colPickerTool->signal_button_press_event().connect_notify( sigc::mem_fun(*this, &ToolBar::colPicker_pressed)); cropConn = cropTool->signal_toggled().connect( sigc::mem_fun(*this, &ToolBar::crop_pressed)); straConn = straTool->signal_toggled().connect( sigc::mem_fun(*this, &ToolBar::stra_pressed)); + perspConn = perspTool->signal_toggled().connect( sigc::mem_fun(*this, &ToolBar::persp_pressed)); handTool->set_tooltip_markup (M("TOOLBAR_TOOLTIP_HAND")); wbTool->set_tooltip_markup (M("TOOLBAR_TOOLTIP_WB")); colPickerTool->set_tooltip_markup (M("TOOLBAR_TOOLTIP_COLORPICKER")); cropTool->set_tooltip_markup (M("TOOLBAR_TOOLTIP_CROP")); straTool->set_tooltip_markup (M("TOOLBAR_TOOLTIP_STRAIGHTEN")); + perspTool->set_tooltip_markup(M("TOOLBAR_TOOLTIP_PERSPECTIVE")); } // @@ -107,9 +115,10 @@ void ToolBar::setTool (ToolMode tool) ConnectionBlocker handBlocker(handConn); ConnectionBlocker straBlocker(straConn); ConnectionBlocker cropBlocker(cropConn); + ConnectionBlocker perspBlocker(perspConn); ConnectionBlocker wbWasBlocked(wbTool, wbConn), cpWasBlocked(colPickerTool, cpConn); - stopEdit = tool == TMHand && handTool->get_active() && editingMode && !blockEdit; + stopEdit = tool == TMHand && (handTool->get_active() || (perspTool && perspTool->get_active())) && editingMode && !blockEdit; handTool->set_active (false); @@ -122,6 +131,9 @@ void ToolBar::setTool (ToolMode tool) if (colPickerTool) { colPickerTool->set_active (false); } + if (perspTool) { + perspTool->set_active(false); + } if (tool == TMHand) { handTool->set_active (true); @@ -138,6 +150,12 @@ void ToolBar::setTool (ToolMode tool) if (colPickerTool) { colPickerTool->set_active (true); } + } else if (tool == TMPerspective) { + if (perspTool) { + perspTool->set_active(true); + // Perspective is a hand tool, but has its own button. + handTool->set_image(*handimg); + } } current = tool; @@ -160,6 +178,7 @@ void ToolBar::startEditMode() ConnectionBlocker handBlocker(handConn); ConnectionBlocker straBlocker(straConn); ConnectionBlocker cropBlocker(cropConn); + ConnectionBlocker perspBlocker(perspConn); ConnectionBlocker wbWasBlocked(wbTool, wbConn), cpWasBlocked(colPickerTool, cpConn); if (current != TMHand) { @@ -172,6 +191,9 @@ void ToolBar::startEditMode() cropTool->set_active (false); straTool->set_active (false); + if (perspTool) { + perspTool->set_active(false); + } current = TMHand; } handTool->set_active (true); @@ -204,6 +226,7 @@ void ToolBar::hand_pressed () ConnectionBlocker handBlocker(handConn); ConnectionBlocker straBlocker(straConn); ConnectionBlocker cropBlocker(cropConn); + ConnectionBlocker perspBlocker(perspConn); ConnectionBlocker wbWasBlocked(wbTool, wbConn), cpWasBlocked(colPickerTool, cpConn); if (editingMode && !blockEdit) { @@ -222,6 +245,9 @@ void ToolBar::hand_pressed () cropTool->set_active (false); straTool->set_active (false); + if (perspTool) { + perspTool->set_active(false); + } handTool->set_active (true); if (current != TMHand) { @@ -244,6 +270,7 @@ void ToolBar::wb_pressed () ConnectionBlocker handBlocker(handConn); ConnectionBlocker straBlocker(straConn); ConnectionBlocker cropBlocker(cropConn); + ConnectionBlocker perspBlocker(perspConn); ConnectionBlocker wbWasBlocked(wbTool, wbConn), cpWasBlocked(colPickerTool, cpConn); if (current != TMSpotWB) { @@ -256,6 +283,9 @@ void ToolBar::wb_pressed () handTool->set_active (false); cropTool->set_active (false); straTool->set_active (false); + if (perspTool) { + perspTool->set_active(false); + } if (colPickerTool) { colPickerTool->set_active(false); } @@ -288,6 +318,9 @@ void ToolBar::colPicker_pressed (GdkEventButton* event) wbTool->set_active (false); } straTool->set_active (false); + if (perspTool) { + perspTool->set_active(false); + } if (current != TMColorPicker) { // Disabling all other tools, enabling the Picker tool and entering the "visible pickers" mode @@ -359,6 +392,7 @@ void ToolBar::crop_pressed () ConnectionBlocker handBlocker(handConn); ConnectionBlocker straBlocker(straConn); ConnectionBlocker cropBlocker(cropConn); + ConnectionBlocker perspBlocker(perspConn); ConnectionBlocker wbWasBlocked(wbTool, wbConn), cpWasBlocked(colPickerTool, cpConn); if (editingMode) { @@ -376,6 +410,9 @@ void ToolBar::crop_pressed () } straTool->set_active (false); + if (perspTool) { + perspTool->set_active(false); + } cropTool->set_active (true); if (current != TMCropSelect) { @@ -399,6 +436,7 @@ void ToolBar::stra_pressed () ConnectionBlocker handBlocker(handConn); ConnectionBlocker straBlocker(straConn); ConnectionBlocker cropBlocker(cropConn); + ConnectionBlocker perspBlocker(perspConn); ConnectionBlocker wbWasBlocked(wbTool, wbConn), cpWasBlocked(colPickerTool, cpConn); if (editingMode) { @@ -416,6 +454,9 @@ void ToolBar::stra_pressed () } cropTool->set_active (false); + if (perspTool) { + perspTool->set_active(false); + } straTool->set_active (true); if (current != TMStraighten) { @@ -432,6 +473,35 @@ void ToolBar::stra_pressed () } } +void ToolBar::persp_pressed () +{ + if (listener && !perspTool->get_active()) { + listener->toolDeselected(TMPerspective); + return; + } + + // Unlike other modes, mode switching is handled by the perspective panel. + { + ConnectionBlocker handBlocker(handConn); + ConnectionBlocker straBlocker(straConn); + ConnectionBlocker cropBlocker(cropConn); + ConnectionBlocker perspBlocker(perspConn); + ConnectionBlocker wbWasBlocked(wbTool, wbConn), cpWasBlocked(colPickerTool, cpConn); + + if (editingMode) { + stopEditMode(); + if (listener) { + listener->editModeSwitchedOff(); + } + } + + } + + if (listener) { + listener->toolSelected(TMPerspective); + } +} + bool ToolBar::handleShortcutKey (GdkEventKey* event) { @@ -485,6 +555,11 @@ void ToolBar::setBatchMode() removeIfThere(this, colPickerTool, false); colPickerTool = nullptr; } + if (perspTool) { + perspConn.disconnect(); + removeIfThere(this, perspTool, false); + perspTool = nullptr; + } allowNoTool = true; switch (current) { diff --git a/rtgui/toolbar.h b/rtgui/toolbar.h index a4525019f..85a0c3345 100644 --- a/rtgui/toolbar.h +++ b/rtgui/toolbar.h @@ -30,6 +30,8 @@ class ToolBarListener public: virtual ~ToolBarListener() = default; + /// Callback when a tool is deselected. WARNING: Not yet called for most tools. + virtual void toolDeselected(ToolMode tool) = 0; /// Callback when a tool is selected virtual void toolSelected(ToolMode tool) = 0; @@ -51,6 +53,7 @@ private: void colPicker_pressed (GdkEventButton* event); void crop_pressed (); void stra_pressed (); + void persp_pressed (); bool showColorPickers(bool showCP); void switchColorPickersVisibility(); @@ -60,6 +63,7 @@ protected: Gtk::ToggleButton* colPickerTool; Gtk::ToggleButton* cropTool; Gtk::ToggleButton* straTool; + Gtk::ToggleButton* perspTool; ToolBarListener* listener; LockablePickerToolListener* pickerListener; ToolMode current; @@ -71,6 +75,7 @@ protected: sigc::connection cpConn; sigc::connection cropConn; sigc::connection straConn; + sigc::connection perspConn; public: ToolBar (); diff --git a/rtgui/toolenum.h b/rtgui/toolenum.h index c3bc873f1..424afca87 100644 --- a/rtgui/toolenum.h +++ b/rtgui/toolenum.h @@ -18,4 +18,4 @@ */ #pragma once -enum ToolMode {TMNone = -1, TMHand = 0, TMSpotWB = 1, TMCropSelect = 2, TMStraighten = 3, TMColorPicker = 4}; +enum ToolMode {TMNone = -1, TMHand = 0, TMSpotWB = 1, TMCropSelect = 2, TMStraighten = 3, TMColorPicker = 4, TMPerspective = 5}; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index d25d6414d..1f4ccce06 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -282,6 +282,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit lensgeom->setLensGeomListener(this); rotate->setLensGeomListener(this); perspective->setLensGeomListener(this); + perspective->setPerspCorrectionPanelListener(this); distortion->setLensGeomListener(this); crop->setCropPanelListener(this); icm->setICMPanelListener(this); @@ -1045,6 +1046,17 @@ void ToolPanelCoordinator::cropSelectRequested() toolBar->setTool(TMCropSelect); } +void ToolPanelCoordinator::controlLineEditModeChanged(bool active) +{ + if (!ipc) { + return; + } + + if (active) { + toolBar->setTool(TMPerspective); + } +} + void ToolPanelCoordinator::saveInputICCReference(const Glib::ustring& fname, bool apply_wb) { if (ipc) { @@ -1172,6 +1184,13 @@ void ToolPanelCoordinator::updateTPVScrollbar(bool hide) updateVScrollbars(hide); } +void ToolPanelCoordinator::toolDeselected(ToolMode tool) +{ + if (tool == TMPerspective) { + perspective->requestApplyControlLines(); + } +} + void ToolPanelCoordinator::toolSelected(ToolMode tool) { GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected @@ -1216,6 +1235,20 @@ void ToolPanelCoordinator::toolSelected(ToolMode tool) break; } + case TMPerspective: { + toolBar->blockEditDeactivation(false); // To allow deactivating Locallab when switching to another tool using toolbar + perspective->setControlLineEditMode(true); + perspective->setExpanded(true); + bool isFavorite = checkFavorite(perspective); + if (!isFavorite) { + isFavorite = checkFavorite(lensgeom); + lensgeom->setExpanded(true); + } + toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(isFavorite ? *favoritePanelSW : *transformPanelSW)); + prevPage = toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()); // Updating prevPage as "signal_switch_page" event + break; + } + default: break; } diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 3889e786c..0dcee59eb 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -102,6 +102,7 @@ class ToolPanelCoordinator : public LensGeomListener, public SpotWBListener, public CropPanelListener, + public PerspCorrectionPanelListener, public ICMPanelListener, public ImageAreaToolListener, public rtengine::ImageTypeListener, @@ -323,6 +324,9 @@ public: // croppanellistener interface void cropSelectRequested () override; + // PerspCorrectionPanelListener interface + void controlLineEditModeChanged(bool active) override; + // icmpanellistener interface void saveInputICCReference(const Glib::ustring& fname, bool apply_wb) override; @@ -339,6 +343,7 @@ public: bool handleShortcutKey(GdkEventKey* event); // ToolBarListener interface + void toolDeselected(ToolMode tool) override; void toolSelected (ToolMode tool) override; void editModeSwitchedOff () final;