Call cam.roi.resetRegionOfInterest only if cam.roi is not null
[harmattan/cameraplus] / qml / FocusReticle.qml
1 // -*- qml -*-
2
3 /*!
4  * This file is part of CameraPlus.
5  *
6  * Copyright (C) 2012-2013 Mohammed Sameer <msameer@foolab.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 import QtQuick 2.0
24 import QtCamera 1.0
25 import CameraPlus 1.0
26
27 // TODO: hide all controls when we are focusing
28 // TODO: hide all controls when we are dragging
29
30 MouseArea {
31     id: mouse
32     x: cam ? cam.renderArea.x : 0
33     y: cam ? cam.renderArea.y : 0
34     width: cam ? cam.renderArea.width : 0
35     height: cam ? cam.renderArea.height : 0
36     drag.minimumX: 0
37     drag.minimumY: 0
38     drag.maximumX: width - reticle.width
39     drag.maximumY: height - reticle.height
40
41     property bool locked: false
42
43     property int cafStatus: AutoFocus.None
44     property int status: AutoFocus.None
45     property Camera cam
46     property bool touchMode
47
48     property variant touchPoint: Qt.point(mouse.width / 2, mouse.height / 2)
49
50     // A 100x100 central "rectangle"
51     property variant centerRect: Qt.rect((mouse.width / 2 - 50), (mouse.height / 2) - 50, 100, 100)
52
53     // ROI:
54     property variant primaryRoiRect: Qt.rect(0, 0, 0, 0)
55     property variant roiRects
56     property variant allRoiRects
57     property bool roiMode: allRoiRects != null && allRoiRects.length > 0 && !touchMode && !pressed
58
59     property variant __initialPos
60     onPressed: {
61
62         if (mouse.x >= reticle.x &&
63             mouse.y >= reticle.y &&
64             mouse.x <= reticle.x + reticle.width &&
65             mouse.y <= reticle.y + reticle.height) {
66             locked = true
67         }
68
69         __initialPos = touchPoint
70         calculateTouchPoint(mouse.x, mouse.y)
71     }
72
73     onReleased: {
74         calculateTouchPoint(mouse.x, mouse.y)
75         locked = false
76     }
77
78     onPositionChanged: calculateTouchPoint(mouse.x, mouse.y)
79     onCanceled: {
80         calculateTouchPoint(__initialPos.x, __initialPos.y)
81         locked = false
82     }
83
84     function resetReticle() {
85         calculateTouchPoint(centerRect.x, centerRect.y)
86     }
87
88     function setRegionOfInterest() {
89         if (!cam) {
90             // console.log("Cannot set ROI without camera object")
91             return
92         } else if (mouse.pressed) {
93             // console.log("Will not set ROI while pressed")
94             return
95         } else if (!touchMode && !roiMode) {
96             // console.log("resetting ROI")
97             if (cam.roi) {
98                 cam.roi.resetRegionOfInterest()
99             }
100
101             return
102         }
103
104         // TODO: rework this and move to unnormalized coordinates
105         // in terms of video resolution:
106         var rx = (cam.videoResolution.width * reticle.x) / mouse.width
107         var rwidth = (cam.videoResolution.width * reticle.width) / mouse.width
108         var ry = (cam.videoResolution.height * reticle.y) / mouse.height
109         var rheight = (cam.videoResolution.height * reticle.height) / mouse.height
110
111         // Translate to normalized coordinates (1x1 square) as expected by our C++ backend
112         rx = rx / cam.videoResolution.width
113         rwidth = rwidth / cam.videoResolution.width
114         ry = ry / cam.videoResolution.height
115         rheight = rheight / cam.videoResolution.height
116
117         // console.log("Setting ROI to: " + rx + "x" + ry)
118         cam.roi.setRegionOfInterest(Qt.rect(rx, ry, rwidth, rheight))
119     }
120
121     function calculateTouchPoint(x, y) {
122         if (x >= centerRect.x && y >= centerRect.y &&
123             x <= centerRect.x + centerRect.width &&
124             y <= centerRect.y + centerRect.height) {
125                 touchMode = false
126                 touchPoint = Qt.point(mouse.width / 2, mouse.height / 2)
127                 return
128         }
129
130         touchMode = true
131         touchPoint = Qt.point(x, y)
132     }
133
134     function predictColor(caf, status) {
135         if (status == AutoFocus.Success) {
136             return "steelblue"
137         } else if (status == AutoFocus.Fail) {
138             return "red"
139         } else if (status == AutoFocus.Running) {
140             return "white"
141         } else if (caf == AutoFocus.Success) {
142             return "steelblue"
143         } else {
144             return "white"
145         }
146     }
147
148     Repeater {
149         anchors.fill: parent
150         model: roiMode ? roiRects : 0
151
152         delegate: Rectangle {
153             x: modelData.x
154             y: modelData.y
155             width: modelData.width
156             height: modelData.height
157             color: "transparent"
158             border.color: "gray"
159             border.width: 2
160         }
161     }
162
163     FocusRectangle {
164         id: reticle
165         width: mouse.pressed ? 150 : mouse.touchMode ? 200 : roiMode ? primaryRoiRect.width : 250
166         height: mouse.pressed ? 90 : mouse.touchMode ? 120 : roiMode ? primaryRoiRect.height : 150
167         x: Math.min(Math.max(mouse.touchPoint.x - (width / 2), drag.minimumX), drag.maximumX)
168         y: Math.min(Math.max(mouse.touchPoint.y - (height / 2), drag.minimumY), drag.maximumY)
169         color: predictColor(cafStatus, status)
170
171         onXChanged: setRegionOfInterest()
172         onYChanged: setRegionOfInterest()
173         /*
174         Behavior on x {
175             PropertyAnimation { duration: 100 }
176             enabled: !mouse.pressed
177         }
178
179         Behavior on y {
180             PropertyAnimation { duration: 100 }
181             enabled: !mouse.pressed
182         }
183         */
184         Behavior on width {
185             PropertyAnimation { duration: 100 }
186         }
187
188         Behavior on height {
189             PropertyAnimation { duration: 100 }
190         }
191
192     }
193
194     Connections {
195         target: settings
196         // Changing mode (which implies changing pages) will not reset ROI
197         // thus we do it here
198         onModeChanged: resetReticle()
199     }
200
201     Connections {
202         target: cam
203         onRunningChanged: resetReticle()
204         onVideoResolutionChanged: resetReticle()
205     }
206
207     Connections {
208         target: cam.roi
209         onRegionsChanged: {
210             allRoiRects = regions
211             primaryRoiRect = primary
212             roiRects = rest
213
214             if (regions.length == 0) {
215                 resetReticle()
216                 return
217             }
218
219             touchPoint = Qt.point(primary.x + (reticle.width / 2),
220                 primary.y + (reticle.height / 2))
221         }
222     }
223
224     /*
225     // This is for debugging
226     Rectangle {
227         color: "blue"
228         opacity: 0.2
229         anchors.fill: parent
230     }
231
232     Rectangle {
233         color: "red"
234         opacity: 0.4
235         x: centerRect.x
236         y: centerRect.y
237         width: centerRect.width
238         height: centerRect.height
239     }
240     */
241     Timer {
242         interval: 500
243         running: status == AutoFocus.Running
244         triggeredOnStart: true
245         repeat: true
246         onTriggered: reticle.visible = !reticle.visible
247         onRunningChanged: {
248             if (!running) {
249                 reticle.visible = true
250             }
251         }
252     }
253 }