diff --git a/rtdata/languages/default b/rtdata/languages/default index d2b3aa5ae..e13504f95 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2226,6 +2226,7 @@ TP_COLORTONING_TWOBY;Special a* and b* TP_COLORTONING_TWOCOLOR_TOOLTIP;Standard chroma:\nLinear response, a* = b*.\n\nSpecial chroma:\nLinear response, a* = b*, but unbound - try under the diagonal.\n\nSpecial a* and b*:\nLinear response unbound with separate curves for a* and b*. Intended for special effects.\n\nSpecial chroma 2 colors:\nMore predictable. TP_COLORTONING_TWOSTD;Standard chroma TP_CROP_FIXRATIO;Lock ratio +TP_CROP_GTCENTEREDSQUARE;Centered square TP_CROP_GTDIAGONALS;Rule of Diagonals TP_CROP_GTEPASSPORT;Biometric Passport TP_CROP_GTFRAME;Frame diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index b91e570ef..80c25a5a3 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1795,7 +1795,7 @@ CropParams::CropParams() : fixratio(true), ratio("As Image"), orientation("As Image"), - guide("Frame") + guide(Guide::FRAME) { } @@ -5854,7 +5854,25 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->crop.fixratio, "Crop", "FixedRatio", crop.fixratio, keyFile); saveToKeyfile(!pedited || pedited->crop.ratio, "Crop", "Ratio", crop.ratio, keyFile); saveToKeyfile(!pedited || pedited->crop.orientation, "Crop", "Orientation", crop.orientation, keyFile); - saveToKeyfile(!pedited || pedited->crop.guide, "Crop", "Guide", crop.guide, keyFile); + saveToKeyfile( + !pedited || pedited->crop.guide, + "Crop", + "Guide", + { + {CropParams::Guide::NONE, "None"}, + {CropParams::Guide::FRAME, "Frame"}, + {CropParams::Guide::RULE_OF_THIRDS, "Rule of thirds"}, + {CropParams::Guide::RULE_OF_DIAGONALS, "Rule of diagonals"}, + {CropParams::Guide::HARMONIC_MEANS, "Harmonic means"}, + {CropParams::Guide::GRID, "Grid"}, + {CropParams::Guide::GOLDEN_TRIANGLE_1, "Golden Triangle 1"}, + {CropParams::Guide::GOLDEN_TRIANGLE_2, "Golden Triangle 2"}, + {CropParams::Guide::EPASSPORT, "ePassport"}, + {CropParams::Guide::CENTERED_SQUARE, "Centered square"}, + }, + crop.guide, + keyFile + ); // Coarse transformation saveToKeyfile(!pedited || pedited->coarse.rotate, "Coarse Transformation", "Rotate", coarse.rotate, keyFile); @@ -7732,7 +7750,26 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) } assignFromKeyfile(keyFile, "Crop", "Orientation", pedited, crop.orientation, pedited->crop.orientation); - assignFromKeyfile(keyFile, "Crop", "Guide", pedited, crop.guide, pedited->crop.guide); + assignFromKeyfile( + keyFile, + "Crop", + "Guide", + pedited, + { + {"None", CropParams::Guide::NONE}, + {"Frame", CropParams::Guide::FRAME}, + {"Rule of thirds", CropParams::Guide::RULE_OF_THIRDS}, + {"Rule of diagonals", CropParams::Guide::RULE_OF_DIAGONALS}, + {"Harmonic means", CropParams::Guide::HARMONIC_MEANS}, + {"Grid", CropParams::Guide::GRID}, + {"Golden Triangle 1", CropParams::Guide::GOLDEN_TRIANGLE_1}, + {"Golden Triangle 2", CropParams::Guide::GOLDEN_TRIANGLE_2}, + {"ePassport", CropParams::Guide::EPASSPORT}, + {"Centered square", CropParams::Guide::CENTERED_SQUARE} + }, + crop.guide, + pedited->crop.guide + ); } if (keyFile.has_group("Coarse Transformation")) { diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 34e0f0b96..a4d59ccb7 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -831,15 +831,28 @@ struct SHParams { * Parameters of the cropping */ struct CropParams { - bool enabled; - int x; - int y; - int w; - int h; - bool fixratio; - Glib::ustring ratio; - Glib::ustring orientation; - Glib::ustring guide; + enum class Guide { + NONE, + FRAME, + RULE_OF_THIRDS, + RULE_OF_DIAGONALS, + HARMONIC_MEANS, + GRID, + GOLDEN_TRIANGLE_1, + GOLDEN_TRIANGLE_2, + EPASSPORT, + CENTERED_SQUARE + }; + + bool enabled; + int x; + int y; + int w; + int h; + bool fixratio; + Glib::ustring ratio; + Glib::ustring orientation; + Guide guide; CropParams(); diff --git a/rtgui/crop.cc b/rtgui/crop.cc index ea8906535..853cec255 100644 --- a/rtgui/crop.cc +++ b/rtgui/crop.cc @@ -24,6 +24,7 @@ #include "rtimage.h" #include "../rtengine/procparams.h" +#include "../rtengine/utils.h" using namespace rtengine; using namespace rtengine::procparams; @@ -295,6 +296,7 @@ Crop::Crop(): guide->append (M("TP_CROP_GTTRIANGLE1")); guide->append (M("TP_CROP_GTTRIANGLE2")); guide->append (M("TP_CROP_GTEPASSPORT")); + guide->append (M("TP_CROP_GTCENTEREDSQUARE")); guide->set_active (0); w->set_range (1, maxw); @@ -413,24 +415,19 @@ void Crop::read (const ProcParams* pp, const ParamsEdited* pedited) orientation->set_active (2); } - if (pp->crop.guide == "None") { - guide->set_active (0); - } else if (pp->crop.guide == "Frame") { - guide->set_active (1); - } else if (pp->crop.guide == "Rule of thirds") { - guide->set_active (2); - } else if (pp->crop.guide == "Rule of diagonals") { - guide->set_active (3); - } else if (!strncmp(pp->crop.guide.data(), "Harmonic means", 14)) { - guide->set_active (4); - } else if (pp->crop.guide == "Grid") { - guide->set_active (5); - } else if (pp->crop.guide == "Golden Triangle 1") { - guide->set_active (6); - } else if (pp->crop.guide == "Golden Triangle 2") { - guide->set_active (7); - } else if (pp->crop.guide == "ePassport") { - guide->set_active (8); + switch (pp->crop.guide) { + case procparams::CropParams::Guide::NONE: + case procparams::CropParams::Guide::FRAME: + case procparams::CropParams::Guide::RULE_OF_THIRDS: + case procparams::CropParams::Guide::RULE_OF_DIAGONALS: + case procparams::CropParams::Guide::HARMONIC_MEANS: + case procparams::CropParams::Guide::GRID: + case procparams::CropParams::Guide::GOLDEN_TRIANGLE_1: + case procparams::CropParams::Guide::GOLDEN_TRIANGLE_2: + case procparams::CropParams::Guide::EPASSPORT: + case procparams::CropParams::Guide::CENTERED_SQUARE: { + guide->set_active(toUnderlying(pp->crop.guide)); + } } x->set_value(pp->crop.x); @@ -531,25 +528,7 @@ void Crop::write (ProcParams* pp, ParamsEdited* pedited) pp->crop.orientation = "As Image"; } - if (guide->get_active_row_number() == 0) { - pp->crop.guide = "None"; - } else if (guide->get_active_row_number() == 1) { - pp->crop.guide = "Frame"; - } else if (guide->get_active_row_number() == 2) { - pp->crop.guide = "Rule of thirds"; - } else if (guide->get_active_row_number() == 3) { - pp->crop.guide = "Rule of diagonals"; - } else if (guide->get_active_row_number() == 4) { - pp->crop.guide = "Harmonic means"; - } else if (guide->get_active_row_number() == 5) { - pp->crop.guide = "Grid"; - } else if (guide->get_active_row_number() == 6) { - pp->crop.guide = "Golden Triangle 1"; - } else if (guide->get_active_row_number() == 7) { - pp->crop.guide = "Golden Triangle 2"; - } else if (guide->get_active_row_number() == 8) { - pp->crop.guide = "ePassport"; - } + pp->crop.guide = procparams::CropParams::Guide(guide->get_active_row_number()); if (pedited) { pedited->crop.enabled = !get_inconsistent(); diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 2927cdf32..c7d7348b3 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -1470,10 +1470,10 @@ void CropWindow::expose (Cairo::RefPtr cr) if (state == SNormal) { switch (options.cropGuides) { case Options::CROP_GUIDE_NONE: - cropParams.guide = "None"; + cropParams.guide = procparams::CropParams::Guide::NONE; break; case Options::CROP_GUIDE_FRAME: - cropParams.guide = "Frame"; + cropParams.guide = procparams::CropParams::Guide::FRAME; break; default: break; diff --git a/rtgui/filebrowserentry.cc b/rtgui/filebrowserentry.cc index 5e8c730aa..bf3f11a79 100644 --- a/rtgui/filebrowserentry.cc +++ b/rtgui/filebrowserentry.cc @@ -176,10 +176,10 @@ void FileBrowserEntry::customBackBufferUpdate (Cairo::RefPtr c) rtengine::procparams::CropParams cparams = thumbnail->getProcParams().crop; switch (options.cropGuides) { case Options::CROP_GUIDE_NONE: - cparams.guide = "None"; + cparams.guide = rtengine::procparams::CropParams::Guide::NONE; break; case Options::CROP_GUIDE_FRAME: - cparams.guide = "Frame"; + cparams.guide = rtengine::procparams::CropParams::Guide::FRAME; break; default: break; diff --git a/rtgui/guiutils.cc b/rtgui/guiutils.cc index f3100c291..61cbde140 100644 --- a/rtgui/guiutils.cc +++ b/rtgui/guiutils.cc @@ -267,7 +267,7 @@ void drawCrop (Cairo::RefPtr cr, int imx, int imy, int imw, int cr->fill (); // rectangle around the cropped area and guides - if (cparams.guide != "None" && drawGuide) { + if (cparams.guide != rtengine::procparams::CropParams::Guide::NONE && drawGuide) { double rectx1 = round(c1x) + imx + 0.5; double recty1 = round(c1y) + imy + 0.5; double rectx2 = round(c2x) + imx + 0.5; @@ -296,60 +296,98 @@ void drawCrop (Cairo::RefPtr cr, int imx, int imy, int imw, int cr->stroke (); cr->set_dash (std::valarray(), 0); - if (cparams.guide != "Rule of diagonals" && cparams.guide != "Golden Triangle 1" && cparams.guide != "Golden Triangle 2") { + if ( + cparams.guide != rtengine::procparams::CropParams::Guide::RULE_OF_DIAGONALS + && cparams.guide != rtengine::procparams::CropParams::Guide::GOLDEN_TRIANGLE_1 + && cparams.guide != rtengine::procparams::CropParams::Guide::GOLDEN_TRIANGLE_2 + ) { // draw guide lines std::vector horiz_ratios; std::vector vert_ratios; - if (cparams.guide == "Rule of thirds") { - horiz_ratios.push_back (1.0 / 3.0); - horiz_ratios.push_back (2.0 / 3.0); - vert_ratios.push_back (1.0 / 3.0); - vert_ratios.push_back (2.0 / 3.0); - } else if (!strncmp(cparams.guide.data(), "Harmonic means", 14)) { - horiz_ratios.push_back (1.0 - 0.618); - horiz_ratios.push_back (0.618); - vert_ratios.push_back (0.618); - vert_ratios.push_back (1.0 - 0.618); - } else if (cparams.guide == "Grid") { - // To have even distribution, normalize it a bit - const int longSideNumLines = 10; + switch (cparams.guide) { + case rtengine::procparams::CropParams::Guide::NONE: + case rtengine::procparams::CropParams::Guide::FRAME: + case rtengine::procparams::CropParams::Guide::RULE_OF_DIAGONALS: + case rtengine::procparams::CropParams::Guide::GOLDEN_TRIANGLE_1: + case rtengine::procparams::CropParams::Guide::GOLDEN_TRIANGLE_2: { + break; + } - int w = rectx2 - rectx1, h = recty2 - recty1; + case rtengine::procparams::CropParams::Guide::RULE_OF_THIRDS: { + horiz_ratios.push_back (1.0 / 3.0); + horiz_ratios.push_back (2.0 / 3.0); + vert_ratios.push_back (1.0 / 3.0); + vert_ratios.push_back (2.0 / 3.0); + break; + } - if (w > longSideNumLines && h > longSideNumLines) { - if (w > h) { - for (int i = 1; i < longSideNumLines; i++) { - vert_ratios.push_back ((double)i / longSideNumLines); - } + case rtengine::procparams::CropParams::Guide::HARMONIC_MEANS: { + horiz_ratios.push_back (1.0 - 0.618); + horiz_ratios.push_back (0.618); + vert_ratios.push_back (0.618); + vert_ratios.push_back (1.0 - 0.618); + break; + } - int shortSideNumLines = (int)round(h * (double)longSideNumLines / w); + case rtengine::procparams::CropParams::Guide::GRID: { + // To have even distribution, normalize it a bit + const int longSideNumLines = 10; - for (int i = 1; i < shortSideNumLines; i++) { - horiz_ratios.push_back ((double)i / shortSideNumLines); - } - } else { - for (int i = 1; i < longSideNumLines; i++) { - horiz_ratios.push_back ((double)i / longSideNumLines); - } + int w = rectx2 - rectx1, h = recty2 - recty1; - int shortSideNumLines = (int)round(w * (double)longSideNumLines / h); + if (w > longSideNumLines && h > longSideNumLines) { + if (w > h) { + for (int i = 1; i < longSideNumLines; i++) { + vert_ratios.push_back ((double)i / longSideNumLines); + } - for (int i = 1; i < shortSideNumLines; i++) { - vert_ratios.push_back ((double)i / shortSideNumLines); + int shortSideNumLines = (int)round(h * (double)longSideNumLines / w); + + for (int i = 1; i < shortSideNumLines; i++) { + horiz_ratios.push_back ((double)i / shortSideNumLines); + } + } else { + for (int i = 1; i < longSideNumLines; i++) { + horiz_ratios.push_back ((double)i / longSideNumLines); + } + + int shortSideNumLines = (int)round(w * (double)longSideNumLines / h); + + for (int i = 1; i < shortSideNumLines; i++) { + vert_ratios.push_back ((double)i / shortSideNumLines); + } } } + break; + } + + case rtengine::procparams::CropParams::Guide::EPASSPORT: { + /* Official measurements do not specify exact ratios, just min/max measurements within which the eyes and chin-crown distance must lie. I averaged those measurements to produce these guides. + * The first horizontal guide is for the crown, the second is roughly for the nostrils, the third is for the chin. + * http://www.homeoffice.gov.uk/agencies-public-bodies/ips/passports/information-photographers/ + * "(...) the measurement of the face from the bottom of the chin to the crown (ie the top of the head, not the top of the hair) is between 29mm and 34mm." + */ + horiz_ratios.push_back (7.0 / 45.0); + horiz_ratios.push_back (26.0 / 45.0); + horiz_ratios.push_back (37.0 / 45.0); + vert_ratios.push_back (0.5); + break; + } + + case rtengine::procparams::CropParams::Guide::CENTERED_SQUARE: { + const double w = rectx2 - rectx1, h = recty2 - recty1; + double ratio = w / h; + if (ratio < 1.0) { + ratio = 1.0 / ratio; + horiz_ratios.push_back((ratio - 1.0) / (2.0 * ratio)); + horiz_ratios.push_back(1.0 - (ratio - 1.0) / (2.0 * ratio)); + } else { + vert_ratios.push_back((ratio - 1.0) / (2.0 * ratio)); + vert_ratios.push_back(1.0 - (ratio - 1.0) / (2.0 * ratio)); + } + break; } - } else if (cparams.guide == "ePassport") { - /* Official measurements do not specify exact ratios, just min/max measurements within which the eyes and chin-crown distance must lie. I averaged those measurements to produce these guides. - * The first horizontal guide is for the crown, the second is roughly for the nostrils, the third is for the chin. - * http://www.homeoffice.gov.uk/agencies-public-bodies/ips/passports/information-photographers/ - * "(...) the measurement of the face from the bottom of the chin to the crown (ie the top of the head, not the top of the hair) is between 29mm and 34mm." - */ - horiz_ratios.push_back (7.0 / 45.0); - horiz_ratios.push_back (26.0 / 45.0); - horiz_ratios.push_back (37.0 / 45.0); - vert_ratios.push_back (0.5); } // Horizontals @@ -385,7 +423,7 @@ void drawCrop (Cairo::RefPtr cr, int imx, int imy, int imw, int ds.resize (0); cr->set_dash (ds, 0); } - } else if (cparams.guide == "Rule of diagonals") { + } else if (cparams.guide == rtengine::procparams::CropParams::Guide::RULE_OF_DIAGONALS) { double corners_from[4][2]; double corners_to[4][2]; int mindim = min(rectx2 - rectx1, recty2 - recty1); @@ -421,9 +459,12 @@ void drawCrop (Cairo::RefPtr cr, int imx, int imy, int imw, int ds.resize (0); cr->set_dash (ds, 0); } - } else if (cparams.guide == "Golden Triangle 1" || cparams.guide == "Golden Triangle 2") { + } else if ( + cparams.guide == rtengine::procparams::CropParams::Guide::GOLDEN_TRIANGLE_1 + || cparams.guide == rtengine::procparams::CropParams::Guide::GOLDEN_TRIANGLE_2 + ) { // main diagonal - if(cparams.guide == "Golden Triangle 2") { + if(cparams.guide == rtengine::procparams::CropParams::Guide::GOLDEN_TRIANGLE_2) { std::swap(rectx1, rectx2); } diff --git a/rtgui/previewwindow.cc b/rtgui/previewwindow.cc index 67fa87e0c..90d0a7b4b 100644 --- a/rtgui/previewwindow.cc +++ b/rtgui/previewwindow.cc @@ -93,10 +93,10 @@ void PreviewWindow::updatePreviewImage () rtengine::procparams::CropParams cparams = previewHandler->getCropParams(); switch (options.cropGuides) { case Options::CROP_GUIDE_NONE: - cparams.guide = "None"; + cparams.guide = rtengine::procparams::CropParams::Guide::NONE; break; case Options::CROP_GUIDE_FRAME: - cparams.guide = "Frame"; + cparams.guide = rtengine::procparams::CropParams::Guide::FRAME; break; default: break;