f4f4c8c820a741b3dff934e4f2a8ebe5c0af3256
[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: renderArea.x
33     y: renderArea.y
34     width: renderArea.width
35     height: renderArea.height
36     drag.minimumX: 0
37     drag.minimumY: 0
38     drag.maximumX: width - reticle.width
39     drag.maximumY: height - reticle.height
40
41     property variant videoResolution
42     property variant renderArea
43
44     property bool locked: false
45
46     property int cafStatus: AutoFocus.None
47     property int status: AutoFocus.None
48     property Camera cam
49     property bool touchMode
50
51     property variant touchPoint: Qt.point(mouse.width / 2, mouse.height / 2)
52
53     // A 100x100 central "rectangle"
54     property variant centerRect: Qt.rect((mouse.width / 2 - 50), (mouse.height / 2) - 50, 100, 100)
55
56     // ROI:
57     property variant primaryRoiRect: Qt.rect(0, 0, 0, 0)
58     property variant roiRects
59     property variant allRoiRects
60     property bool roiMode: allRoiRects != null && allRoiRects.length > 0 && !touchMode && !pressed
61
62     enabled: !cam.quirks.hasQuirk(Quirks.NoTouchFocus)
63
64     property variant __initialPos
65     onPressed: {
66
67         if (mouse.x >= reticle.x &&
68             mouse.y >= reticle.y &&
69             mouse.x <= reticle.x + reticle.width &&
70             mouse.y <= reticle.y + reticle.height) {
71             locked = true
72         }
73
74         __initialPos = touchPoint
75         calculateTouchPoint(mouse.x, mouse.y)
76     }
77
78     onReleased: {
79         calculateTouchPoint(mouse.x, mouse.y)
80         locked = false
81     }
82
83     onPositionChanged: calculateTouchPoint(mouse.x, mouse.y)
84     onCanceled: {
85         calculateTouchPoint(__initialPos.x, __initialPos.y)
86         locked = false
87     }
88
89     function resetReticle() {
90         calculateTouchPoint(centerRect.x, centerRect.y)
91     }
92
93     function setRegionOfInterest() {
94         if (!cam) {
95             // console.log("Cannot set ROI without camera object")
96             return
97         } else if (mouse.pressed) {
98             // console.log("Will not set ROI while pressed")
99             return
100         } else if (!touchMode && !roiMode) {
101             // console.log("resetting ROI")
102             if (cam.roi) {
103                 cam.roi.resetRegionOfInterest()
104             }
105
106             return
107         }
108
109         // TODO: rework this and move to unnormalized coordinates
110         // in terms of video resolution:
111         var rx = (videoResolution.width * reticle.x) / mouse.width
112         var rwidth = (videoResolution.width * reticle.width) / mouse.width
113         var ry = (videoResolution.height * reticle.y) / mouse.height
114         var rheight = (videoResolution.height * reticle.height) / mouse.height
115
116         // Translate to normalized coordinates (1x1 square) as expected by our C++ backend
117         rx = rx / videoResolution.width
118         rwidth = rwidth / videoResolution.width
119         ry = ry / videoResolution.height
120         rheight = rheight / videoResolution.height
121
122         // console.log("Setting ROI to: " + rx + "x" + ry)
123         cam.roi.setRegionOfInterest(Qt.rect(rx, ry, rwidth, rheight))
124     }
125
126     function calculateTouchPoint(x, y) {
127         if (x >= centerRect.x && y >= centerRect.y &&
128             x <= centerRect.x + centerRect.width &&
129             y <= centerRect.y + centerRect.height) {
130                 touchMode = false
131                 touchPoint = Qt.point(mouse.width / 2, mouse.height / 2)
132                 return
133         }
134
135         touchMode = true
136         touchPoint = Qt.point(x, y)
137     }
138
139     function predictColor(caf, status) {
140         if (status == AutoFocus.Success) {
141             return "steelblue"
142         } else if (status == AutoFocus.Fail) {
143             return "red"
144         } else if (status == AutoFocus.Running) {
145             return "white"
146         } else if (caf == AutoFocus.Success) {
147             return "steelblue"
148         } else {
149             return "white"
150         }
151     }
152
153     Repeater {
154         anchors.fill: parent
155         model: roiMode ? roiRects : 0
156
157         delegate: Rectangle {
158             x: modelData.x
159             y: modelData.y
160             width: modelData.width
161             height: modelData.height
162             color: "transparent"
163             border.color: "gray"
164             border.width: 2
165         }
166     }
167
168     FocusRectangle {
169         id: reticle
170         width: mouse.pressed ? 150 : mouse.touchMode ? 200 : roiMode ? primaryRoiRect.width : 250
171         height: mouse.pressed ? 90 : mouse.touchMode ? 120 : roiMode ? primaryRoiRect.height : 150
172         x: Math.min(Math.max(mouse.touchPoint.x - (width / 2), drag.minimumX), drag.maximumX)
173         y: Math.min(Math.max(mouse.touchPoint.y - (height / 2), drag.minimumY), drag.maximumY)
174         color: predictColor(cafStatus, status)
175
176         onXChanged: setRegionOfInterest()
177         onYChanged: setRegionOfInterest()
178         /*
179         Behavior on x {
180             PropertyAnimation { duration: 100 }
181             enabled: !mouse.pressed
182         }
183
184         Behavior on y {
185             PropertyAnimation { duration: 100 }
186             enabled: !mouse.pressed
187         }
188         */
189         Behavior on width {
190             PropertyAnimation { duration: 100 }
191         }
192
193         Behavior on height {
194             PropertyAnimation { duration: 100 }
195         }
196
197     }
198
199     Connections {
200         target: settings
201         // Changing mode (which implies changing pages) will not reset ROI
202         // thus we do it here
203         onModeChanged: resetReticle()
204     }
205
206     Connections {
207         target: cam
208         onRunningChanged: resetReticle()
209         onVideoResolutionChanged: resetReticle()
210     }
211
212     Connections {
213         target: cam.roi
214         onRegionsChanged: {
215             allRoiRects = regions
216             primaryRoiRect = primary
217             roiRects = rest
218
219             if (regions.length == 0) {
220                 resetReticle()
221                 return
222             }
223
224             touchPoint = Qt.point(primary.x + (reticle.width / 2),
225                 primary.y + (reticle.height / 2))
226         }
227     }
228
229     /*
230     // This is for debugging
231     Rectangle {
232         color: "blue"
233         opacity: 0.2
234         anchors.fill: parent
235     }
236
237     Rectangle {
238         color: "red"
239         opacity: 0.4
240         x: centerRect.x
241         y: centerRect.y
242         width: centerRect.width
243         height: centerRect.height
244     }
245     */
246     Timer {
247         interval: 500
248         running: status == AutoFocus.Running
249         triggeredOnStart: true
250         repeat: true
251         onTriggered: reticle.visible = !reticle.visible
252         onRunningChanged: {
253             if (!running) {
254                 reticle.visible = true
255             }
256         }
257     }
258 }