Merge pull request #5814 from Lawrence37/perspective-lines

GUI improvements for perspective correction
This commit is contained in:
Lawrence37 2020-10-08 09:55:37 -07:00 committed by GitHub
commit eb01f99bf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1883 additions and 71 deletions

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
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"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="bidirectional-arrow-horizontal-hicontrast.svg">
<sodipodi:namedview
id="base"
pagecolor="#E0E1E2"
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">
<inkscape:grid
type="xygrid"
id="grid1374"
originx="1"
originy="1"
empspacing="11"
dotted="false" />
</sodipodi:namedview>
<defs
id="defs815" />
<metadata
id="metadata818">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>Lawrence</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:rights>
<dc:description>RawTherapee icon.</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5.75,13.5 h 12.5 v -3 H 5.75 Z"
id="path857"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1"
d="m 6.4999667,13.5 v 2.25 l -5,-3.75 5,-3.75 v 2.25"
id="path853"
sodipodi:nodetypes="ccccc" />
<path
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" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
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"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="bidirectional-arrow-horizontal-prelight.svg">
<sodipodi:namedview
id="base"
pagecolor="#E0E1E2"
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">
<inkscape:grid
type="xygrid"
id="grid1374"
originx="1"
originy="1"
empspacing="11"
dotted="false" />
</sodipodi:namedview>
<defs
id="defs815" />
<metadata
id="metadata818">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>Lawrence</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:rights>
<dc:description>RawTherapee icon.</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
style="fill:#ff6400;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5.75,13.5 h 12.5 v -3 H 5.75 Z"
id="path857"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#ff6400;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1"
d="m 6.4999667,13.5 v 2.25 l -5,-3.75 5,-3.75 v 2.25"
id="path853"
sodipodi:nodetypes="ccccc" />
<path
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" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
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"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="bidirectional-arrow-vertical-hicontrast.svg">
<sodipodi:namedview
id="base"
pagecolor="#E0E1E2"
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">
<inkscape:grid
type="xygrid"
id="grid1374"
originx="1"
originy="1"
empspacing="11"
dotted="false" />
</sodipodi:namedview>
<defs
id="defs815" />
<metadata
id="metadata818">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>Lawrence</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:rights>
<dc:description>RawTherapee icon.</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 10.5,5.75 v 12.5 h 3 V 5.75 Z"
id="path857"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:square;stroke-linejoin:round;stroke-opacity:1"
d="M 10.5,6.4999667 H 8.25 l 3.75,-5 3.75,5 H 13.5"
id="path853"
sodipodi:nodetypes="ccccc" />
<path
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" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
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-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"
height="24px"
width="24px">
<sodipodi:namedview
inkscape:snap-page="true"
inkscape:snap-text-baseline="true"
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">
<inkscape:grid
dotted="false"
empspacing="11"
originy="1"
originx="1"
id="grid1374"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs815" />
<metadata
id="metadata818">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>Lawrence</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:rights>
<dc:description>RawTherapee icon.</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
sodipodi:nodetypes="ccccc"
id="path857"
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" />
<path
sodipodi:nodetypes="ccccc"
id="path853"
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" />
<path
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" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

108
rtdata/images/svg/draw.svg Normal file
View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
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"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="draw.svg">
<sodipodi:namedview
id="base"
pagecolor="#E0E1E2"
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">
<inkscape:grid
type="xygrid"
id="grid1374"
originx="1"
originy="1"
empspacing="11"
dotted="false" />
</sodipodi:namedview>
<defs
id="defs815" />
<metadata
id="metadata818">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>Lawrence</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:rights>
<dc:description>RawTherapee icon.</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
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" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -221,8 +221,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
@ -1213,6 +1215,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
@ -1814,6 +1817,7 @@ THRESHOLDSELECTOR_TR;Top-right
TOOLBAR_TOOLTIP_COLORPICKER;Lockable Color Picker\n\nWhen the tool is active:\n- Add a picker: <b>left-click</b>.\n- Drag a picker: <b>left-click and drag</b>.\n- Delete a picker: <b>right-click</b>.\n- Delete all pickers: <b>Ctrl</b>+<b>Shift</b>+<b>right-click</b>.\n- Revert to hand tool: <b>right-click</b> outside any picker.
TOOLBAR_TOOLTIP_CROP;<b>Crop</b> selection.\nShortcut: <b>c</b>\nMove the crop using <b>Shift</b>+<b>mouse drag</b>.
TOOLBAR_TOOLTIP_HAND;Hand tool.\nShortcut: <b>h</b>
TOOLBAR_TOOLTIP_PERSPECTIVE;<b>Perspective Correction</b>\n\nEdit control lines to correct perspective distortion. Click this button again to apply correction.
TOOLBAR_TOOLTIP_STRAIGHTEN;<b>Straighten</b> / <b>fine rotation</b>.\nShortcut: <b>s</b>\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: <b>w</b>
TP_BWMIX_ALGO;Algorithm OYCPM
@ -2950,6 +2954,8 @@ 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_CONTROL_LINES_TOOLTIP;<b>Ctrl</b>+<b>drag</b>: Draw new line\n<b>Right-click</b>: Delete line
TP_PERSPECTIVE_HORIZONTAL;Horizontal
TP_PERSPECTIVE_LABEL;Perspective
TP_PERSPECTIVE_METHOD;Method

View File

@ -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;

View File

@ -454,7 +454,7 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector<Coord2D> &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.
@ -1182,7 +1182,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;
@ -1471,7 +1471,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 ||

View File

@ -225,10 +225,44 @@ void get_view_size(int w, int h, const procparams::PerspectiveParams &params, do
}
*/
/**
* Allocates a new array and populates it with ashift lines corresponding to the
* provided control lines.
*/
std::unique_ptr<dt_iop_ashift_line_t[]> toAshiftLines(const std::vector<ControlLine> *lines)
{
std::unique_ptr<dt_iop_ashift_line_t[]> retval(new dt_iop_ashift_line_t[lines->size()]);
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;
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 std::vector<ControlLine> *control_lines)
{
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<Imagefloat> 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<Imagefloat> 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<float *>(malloc(sizeof(float) * w * h * 4));
g.buf_width = w;
g.buf_height = h;
// allocate the gui buffer
g.buf = static_cast<float *>(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 {
std::unique_ptr<dt_iop_ashift_line_t[]> ashift_lines = toAshiftLines(control_lines);
dt_iop_ashift_gui_data_t *g = module.gui_data;
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,
.pitch = p.camera_pitch,
@ -322,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;

View File

@ -20,12 +20,32 @@
#pragma once
#include "coord2d.h"
#include "procparams.h"
#include "imagesource.h"
#include <vector>
namespace rtengine {
namespace procparams
{
class ProcParams;
}
class ImageSource;
class FramesMetaData;
class ControlLine
{
public:
enum Type
{
HORIZONTAL,
VERTICAL
};
float x1, y1, x2, y2;
Type type;
};
class PerspectiveCorrection {
public:
struct Params
@ -35,7 +55,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 std::vector<ControlLine> *control_lines = nullptr);
//static void autocrop(int width, int height, bool fixratio, const procparams::PerspectiveParams &params, const FramesMetaData *metadata, int &x, int &y, int &w, int &h);
};

View File

@ -1870,6 +1870,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),
@ -1891,6 +1892,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
@ -1904,7 +1906,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
@ -5454,6 +5461,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);
@ -7137,6 +7146,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")) {

View File

@ -932,6 +932,7 @@ struct LensProfParams {
*/
struct PerspectiveParams {
Glib::ustring method;
bool render;
double horizontal;
double vertical;
double camera_crop_factor;
@ -946,6 +947,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<int> control_line_values;
/** 0 is vertical, 1 is horizontal, undefined otherwise. */
std::vector<int> control_line_types;
PerspectiveParams();

View File

@ -34,6 +34,7 @@ set(NONCLISOURCEFILES
colorappearance.cc
coloredbar.cc
colortoning.cc
controllines.cc
controlspotpanel.cc
coordinateadjuster.cc
crop.cc

492
rtgui/controllines.cc Normal file
View File

@ -0,0 +1,492 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2020 Lawrence Lee <billee@ucdavis.edu>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory>
#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(CSHandOpen),
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<RTSurface>(new RTSurface(
"bidirectional-arrow-horizontal-hicontrast.png"));
line_icon_v = Cairo::RefPtr<RTSurface>(new RTSurface(
"bidirectional-arrow-vertical-hicontrast.png"));
line_icon_h_prelight = Cairo::RefPtr<RTSurface>(new RTSurface(
"bidirectional-arrow-horizontal-prelight.png"));
line_icon_v_prelight = Cairo::RefPtr<RTSurface>(new RTSurface(
"bidirectional-arrow-vertical-prelight.png"));
}
ControlLineManager::~ControlLineManager() = 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];
// 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;
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 = CSCrosshair;
} else {
cursor = CSHandOpen;
}
} 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<rtengine::ControlLine>&
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> line;
std::shared_ptr<OPIcon> icon_h, icon_v;
std::unique_ptr<Circle> begin_c, end_c;
line = std::unique_ptr<Line>(new Line());
line->datum = Geometry::IMAGE;
line->innerLineWidth = line_width;
line->begin = begin;
line->end = end;
const Cairo::RefPtr<RTSurface> null_surface =
Cairo::RefPtr<RTSurface>(nullptr);
icon_h = std::make_shared<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 = std::make_shared<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 = std::unique_ptr<Circle>(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<Circle>(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<OPIcon> 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<rtengine::ControlLine>&
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;
}
}

115
rtgui/controllines.h Normal file
View File

@ -0,0 +1,115 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2020 Lawrence Lee <billee@ucdavis.edu>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#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> line;
std::shared_ptr<OPIcon> icon;
std::shared_ptr<OPIcon> icon_h, icon_v;
std::unique_ptr<Circle> begin, end;
rtengine::ControlLine::Type type;
~ControlLine();
};
class ControlLineManager: EditSubscriber
{
protected:
/** Hidden object for capturing mouse events. */
std::unique_ptr<Rectangle> canvas_area;
rtengine::Coord drag_delta;
std::vector<std::unique_ptr<ControlLine>> control_lines;
CursorShape cursor;
bool draw_mode;
bool drawing_line;
bool edited;
Cairo::RefPtr<RTSurface> line_icon_h, line_icon_v;
Cairo::RefPtr<RTSurface> 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> 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<rtengine::ControlLine>& 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<rtengine::ControlLine>& 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;
};

View File

@ -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,6 +430,8 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y)
state = SEditPick1;
pickedObject = iarea->getObject();
pickModifierKey = bstate;
} else if (iarea->getToolMode() == TMPerspective) {
state = SCropImgMove;
}
press_x = x;
press_y = y;
@ -592,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);
@ -764,7 +767,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);

View File

@ -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<Geometry*>& getVisibleGeometry ();

View File

@ -18,6 +18,15 @@
*/
#pragma once
#include <cstddef>
#include <vector>
namespace rtengine
{
class ControlLine;
class ProcEvent;
}
class LensGeomListener
{
public:
@ -25,5 +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) = 0;
virtual void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw, const std::vector<rtengine::ControlLine> *lines = nullptr) = 0;
virtual void updateTransformPreviewRequested (rtengine::ProcEvent event, bool render_perspective) = 0;
};

View File

@ -354,6 +354,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;
@ -1026,6 +1027,7 @@ void ParamsEdited::initFrom(const std::vector<rtengine::procparams::ProcParams>&
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;
@ -3062,6 +3064,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;
}

View File

@ -914,6 +914,7 @@ struct PerspectiveParamsEdited {
bool projection_shift_horiz;
bool projection_shift_vert;
bool projection_yaw;
bool control_lines;
};
struct GradientParamsEdited {

View File

@ -20,16 +20,75 @@
#include "perspective.h"
#include "rtimage.h"
#include "rtsurface.h"
#include "../rtengine/procparams.h"
using namespace rtengine;
using namespace rtengine::procparams;
namespace
{
void controlLinesToValues(const std::vector<rtengine::ControlLine>& lines,
std::vector<int>& values, std::vector<int>& 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<rtengine::ControlLine> valuesToControlLines(
const std::vector<int>& values, const std::vector<int>& types)
{
int line_count = min(values.size() / 4, types.size());
std::vector<rtengine::ControlLine> 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"))
{
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");
@ -37,9 +96,25 @@ 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);
// 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();
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"));
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"));
Gtk::Image* ipersVL = Gtk::manage (new RTImage ("perspective-vertical-bottom-small.png"));
@ -108,6 +183,39 @@ 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_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));
lines = std::unique_ptr<ControlLineManager>(new ControlLineManager());
lines->callbacks = std::make_shared<LinesCallbacks>(this);
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_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);
control_lines_box->pack_start(*lines_button_erase);
// End control lines interface.
auto_pitch = Gtk::manage (new Gtk::Button ());
auto_pitch->set_image(*ipers_auto_pitch);
auto_pitch->signal_pressed().connect( sigc::bind(sigc::mem_fun(*this, &PerspCorrection::autoCorrectionPressed), auto_pitch) );
@ -165,6 +273,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);
@ -213,6 +324,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);
@ -228,6 +340,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);
@ -243,6 +357,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 ();
@ -258,6 +374,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<rtengine::ControlLine> 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) {
@ -280,6 +401,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();
}
}
@ -345,21 +467,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(),
@ -368,17 +490,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(),
@ -388,6 +510,39 @@ void PerspCorrection::adjusterChanged(Adjuster* a, double newval)
}
}
void PerspCorrection::applyControlLines(void)
{
if (!lens_geom_listener) {
return;
}
std::vector<rtengine::ControlLine> 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++;
} 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);
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) {
@ -427,6 +582,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) {
@ -454,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;
@ -497,6 +668,7 @@ void PerspCorrection::setBatchMode (bool batchMode)
projection_shift_vert->showEditedCB ();
projection_yaw->showEditedCB ();
lines_button_edit->set_sensitive(false);
auto_pitch->set_sensitive(false);
auto_yaw->set_sensitive(false);
auto_pitch_yaw->set_sensitive(false);
@ -549,3 +721,110 @@ void PerspCorrection::setFocalLengthValue (const ProcParams* pparams, const Fram
camera_focal_length->setValue(default_focal_length);
}
}
void PerspCorrection::switchOffEditMode(void)
{
lines_button_edit->set_active(false);
}
void PerspCorrection::setEditProvider(EditDataProvider* provider)
{
lines->setEditProvider(provider);
}
void PerspCorrection::lineChanged(void)
{
if (listener) {
listener->panelChanged(EvPerspControlLines, M("HISTORY_CHANGED"));
}
}
void PerspCorrection::linesApplyButtonPressed(void)
{
if (method->get_active_row_number() == 1) {
// Calculate perspective distortion if in camera-based mode.
applyControlLines();
}
lines_button_edit->set_active(false);
}
void PerspCorrection::linesEditButtonPressed(void)
{
if (lines_button_edit->get_active()) { // Enter edit mode.
lines->setActive(true);
lines->setDrawMode(true);
render = false;
if (lens_geom_listener) {
lens_geom_listener->updateTransformPreviewRequested(EvPerspRender, false);
}
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);
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 (panel_listener) {
panel_listener->controlLineEditModeChanged(false);
}
}
}
void PerspCorrection::linesEraseButtonPressed(void)
{
lines->removeAll();
}
void PerspCorrection::requestApplyControlLines(void)
{
if (lines_button_apply->is_sensitive()) {
linesApplyButtonPressed();
}
}
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;
}
}
LinesCallbacks::LinesCallbacks(PerspCorrection* tool):
tool(tool)
{
}
void LinesCallbacks::lineChanged(void)
{
if (tool) {
tool->lineChanged();
}
}
void LinesCallbacks::switchOffEditMode(void)
{
if (tool) {
tool->switchOffEditMode();
}
}

View File

@ -21,9 +21,18 @@
#include <gtkmm.h>
#include "adjuster.h"
#include "controllines.h"
#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,
@ -31,6 +40,7 @@ class PerspCorrection final :
{
protected:
bool render = true;
MyComboBoxText* method;
Gtk::VBox* simple;
Adjuster* horiz;
@ -46,6 +56,10 @@ protected:
Adjuster* camera_shift_horiz;
Adjuster* camera_shift_vert;
Adjuster* camera_yaw;
std::unique_ptr<ControlLineManager> lines;
Gtk::Button* lines_button_apply;
Gtk::ToggleButton* lines_button_edit;
Gtk::Button* lines_button_erase;
Adjuster* projection_pitch;
Adjuster* projection_rotate;
Adjuster* projection_shift_horiz;
@ -54,13 +68,30 @@ protected:
rtengine::ProcEvent EvPerspCamFocalLength;
rtengine::ProcEvent EvPerspCamShift;
rtengine::ProcEvent EvPerspCamAngle;
rtengine::ProcEvent EvPerspControlLines;
rtengine::ProcEvent EvPerspMethod;
rtengine::ProcEvent EvPerspProjShift;
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;
PerspCorrectionPanelListener* panel_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:
@ -74,12 +105,35 @@ 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);
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;
};
class LinesCallbacks: public ControlLineManager::Callbacks
{
protected:
PerspCorrection* tool;
public:
explicit LinesCallbacks(PerspCorrection* tool);
void lineChanged (void) override;
void switchOffEditMode (void) override;
};

View File

@ -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) {

View File

@ -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 ();

View File

@ -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};

View File

@ -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);
@ -986,7 +987,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 std::vector<rtengine::ControlLine> *lines)
{
if (!(ipc && (corr_pitch || corr_yaw))) {
return;
@ -1000,7 +1001,7 @@ void ToolPanelCoordinator::autoPerspRequested (bool corr_pitch, bool corr_yaw, d
rtengine::procparams::ProcParams params;
ipc->getParams(&params);
auto res = rtengine::PerspectiveCorrection::autocompute(src, corr_pitch, corr_yaw, &params, src->getMetaData());
auto res = rtengine::PerspectiveCorrection::autocompute(src, corr_pitch, corr_yaw, &params, src->getMetaData(), lines);
rot = res.angle;
pitch = res.pitch;
yaw = res.yaw;
@ -1015,6 +1016,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)
{
@ -1035,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) {
@ -1162,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
@ -1206,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;
}

View File

@ -102,6 +102,7 @@ class ToolPanelCoordinator :
public LensGeomListener,
public SpotWBListener,
public CropPanelListener,
public PerspCorrectionPanelListener,
public ICMPanelListener,
public ImageAreaToolListener,
public rtengine::ImageTypeListener,
@ -313,8 +314,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 std::vector<rtengine::ControlLine> *lines = nullptr) override;
double autoDistorRequested () override;
void updateTransformPreviewRequested (rtengine::ProcEvent event, bool render_perspective) override;
// spotwblistener interface
void spotWBRequested (int size) override;
@ -322,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;
@ -338,6 +343,7 @@ public:
bool handleShortcutKey(GdkEventKey* event);
// ToolBarListener interface
void toolDeselected(ToolMode tool) override;
void toolSelected (ToolMode tool) override;
void editModeSwitchedOff () final;