1 /* |
|
2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 #include <QSize> |
|
19 #include <QPixmap> |
|
20 // Note: Keep atleast one Qt include before preprocessor flags |
|
21 #ifdef Q_OS_SYMBIAN |
|
22 #include <e32base.h> |
|
23 #include <fbs.h> |
|
24 #include <ecam.h> |
|
25 #include <ecam/camerasnapshot.h> |
|
26 #endif // Q_OS_SYMBIAN |
|
27 |
|
28 #include "cxutils.h" |
|
29 #include "cxeerror.h" |
|
30 #include "cxeexception.h" |
|
31 #include "cxeerrormappingsymbian.h" |
|
32 #include "cxecameradevice.h" |
|
33 #include "cxestate.h" |
|
34 #include "cxesnapshotcontrol.h" |
|
35 #include "cxesnapshotcontrolprivate.h" |
|
36 |
|
37 |
|
38 namespace |
|
39 { |
|
40 const int MAINTAIN_ASPECT = false; |
|
41 |
|
42 const QSize ASPECT_RATIO_SIZE_4BY3 = QSize(4,3); |
|
43 const QSize ASPECT_RATIO_SIZE_16BY9 = QSize(16, 9); |
|
44 const QSize ASPECT_RATIO_SIZE_11BY9 = QSize(11, 9); |
|
45 |
|
46 #ifdef Q_OS_SYMBIAN |
|
47 /*! |
|
48 * Helper class for cleaning up MCameraBuffer instances. |
|
49 */ |
|
50 class CxeCameraBufferCleanup |
|
51 { |
|
52 public: |
|
53 explicit CxeCameraBufferCleanup(MCameraBuffer *buffer) |
|
54 : mBuffer(buffer) |
|
55 { |
|
56 } |
|
57 |
|
58 ~CxeCameraBufferCleanup() |
|
59 { |
|
60 if (mBuffer) { |
|
61 CX_DEBUG(("CxeCameraBufferCleanup - releasing MCameraBuffer..")); |
|
62 mBuffer->Release(); |
|
63 mBuffer = NULL; |
|
64 } |
|
65 } |
|
66 private: |
|
67 Q_DISABLE_COPY(CxeCameraBufferCleanup) |
|
68 MCameraBuffer *mBuffer; |
|
69 }; |
|
70 #endif // Q_OS_SYMBIAN |
|
71 } |
|
72 |
|
73 |
|
74 /*! |
|
75 * Constructor. |
|
76 * @param parent Public interface of Snapshot control. |
|
77 * @param device Camera device interface, used for getting hold of adaptation snapshot API. |
|
78 */ |
|
79 CxeSnapshotControlPrivate::CxeSnapshotControlPrivate(CxeSnapshotControl *parent, |
|
80 CxeCameraDevice& device) |
|
81 : CxeStateMachine("CxeSnapshotControlPrivate"), |
|
82 q(parent), |
|
83 mDevice(device) |
|
84 { |
|
85 CX_DEBUG_ENTER_FUNCTION(); |
|
86 CX_ASSERT_ALWAYS(q); |
|
87 initializeStates(); |
|
88 CX_DEBUG_EXIT_FUNCTION(); |
|
89 } |
|
90 |
|
91 /*! |
|
92 * Destructor. |
|
93 */ |
|
94 CxeSnapshotControlPrivate::~CxeSnapshotControlPrivate() |
|
95 { |
|
96 CX_DEBUG_ENTER_FUNCTION(); |
|
97 stop(); |
|
98 CX_DEBUG_EXIT_FUNCTION(); |
|
99 } |
|
100 |
|
101 /*! |
|
102 * Called when state changes. |
|
103 * @param newStateId Id of the new state. |
|
104 * @param error Error code or CxeError::None in successful case. |
|
105 */ |
|
106 void CxeSnapshotControlPrivate::handleStateChanged(int newStateId, CxeError::Id error) |
|
107 { |
|
108 emit q->stateChanged(static_cast<CxeSnapshotControl::State>(newStateId), error); |
|
109 } |
|
110 |
|
111 |
|
112 /*! |
|
113 * Get the state of Snapshot Control. |
|
114 * @return The current state. |
|
115 */ |
|
116 CxeSnapshotControl::State CxeSnapshotControlPrivate::state() const |
|
117 { |
|
118 return static_cast<CxeSnapshotControl::State> (stateId()); |
|
119 } |
|
120 |
|
121 /*! |
|
122 * Initialize Snapshot Control states |
|
123 */ |
|
124 void CxeSnapshotControlPrivate::initializeStates() |
|
125 { |
|
126 CX_DEBUG_ENTER_FUNCTION(); |
|
127 // addState( id, name, allowed next states ) |
|
128 addState(new CxeState(CxeSnapshotControl::Idle, "Idle", CxeSnapshotControl::Active)); |
|
129 addState(new CxeState(CxeSnapshotControl::Active, "Active", CxeSnapshotControl::Idle)); |
|
130 |
|
131 setInitialState(CxeSnapshotControl::Idle); |
|
132 CX_DEBUG_EXIT_FUNCTION(); |
|
133 } |
|
134 |
|
135 /*! |
|
136 * Calculate snapshot size based on diplay size and image / video output resolution. |
|
137 * @param displaySize Display size in pixels. |
|
138 * @param Cxe::AspectRatio Aspect ratio of image/video resolution. |
|
139 * @return Proposed best snapshot size. |
|
140 */ |
|
141 |
|
142 QSize CxeSnapshotControlPrivate::calculateSnapshotSize(const QSize& displaySize, Cxe::AspectRatio aspectRatio) const |
|
143 { |
|
144 CX_DEBUG_ENTER_FUNCTION(); |
|
145 CX_DEBUG(("CxeSnapshotControlPrivate - display size (%d,%d)", displaySize.width(), displaySize.height())); |
|
146 |
|
147 // Take resolution as reference for aspect ratio. |
|
148 // Scale keeping aspect ratio to just fit display. |
|
149 QSize size; |
|
150 |
|
151 if (aspectRatio == Cxe::AspectRatio4to3) { |
|
152 size = ASPECT_RATIO_SIZE_4BY3; |
|
153 } else if (aspectRatio == Cxe::AspectRatio16to9) { |
|
154 size = ASPECT_RATIO_SIZE_16BY9; |
|
155 } else if (aspectRatio == Cxe::AspectRatio11to9) { |
|
156 size = ASPECT_RATIO_SIZE_11BY9; |
|
157 } |
|
158 size.scale(displaySize, Qt::KeepAspectRatio); |
|
159 |
|
160 CX_DEBUG(("CxeSnapshotControlPrivate - adjusted final size, (%d,%d)", size.width(), size.height())); |
|
161 |
|
162 CX_DEBUG_EXIT_FUNCTION(); |
|
163 |
|
164 return size; |
|
165 } |
|
166 |
|
167 |
|
168 /*! |
|
169 * Start getting snapshots from camera. |
|
170 * Throws CxeException with CxeError::Id if error encountered. |
|
171 * @param size Size of the snapshot in pixels. |
|
172 */ |
|
173 void CxeSnapshotControlPrivate::start(const QSize &size) |
|
174 { |
|
175 CX_DEBUG_ENTER_FUNCTION(); |
|
176 #ifdef Q_OS_SYMBIAN |
|
177 CCamera::CCameraSnapshot *ss = mDevice.cameraSnapshot(); |
|
178 CX_ASSERT_ALWAYS(ss); |
|
179 |
|
180 if (ss->IsSnapshotActive()) { |
|
181 CX_DEBUG(("Stop currently active snapshot..")); |
|
182 ss->StopSnapshot(); |
|
183 } |
|
184 |
|
185 // Prepare snapshot |
|
186 CCamera::TFormat snapFormat = CCamera::EFormatFbsBitmapColor16MU; |
|
187 TSize snapSize = TSize(size.width(), size.height()); |
|
188 |
|
189 CX_DEBUG(("Prepare snapshot, size (%d x %d)..", size.width(), size.height())); |
|
190 TRAPD(status, ss->PrepareSnapshotL(snapFormat, snapSize, MAINTAIN_ASPECT)); |
|
191 CxeException::throwIfError(CxeErrorHandlingSymbian::map(status)); |
|
192 CX_DEBUG(("After prepare ECAM modified size to (%d x %d)..", size.width(), size.height())); |
|
193 |
|
194 CX_DEBUG(("Start snapshot..")); |
|
195 ss->StartSnapshot(); |
|
196 #else |
|
197 Q_UNUSED(size); |
|
198 #endif // Q_OS_SYMBIAN |
|
199 setState(CxeSnapshotControl::Active); |
|
200 |
|
201 CX_DEBUG_EXIT_FUNCTION(); |
|
202 } |
|
203 |
|
204 /*! |
|
205 * Stop getting snapshots from camera. |
|
206 */ |
|
207 void CxeSnapshotControlPrivate::stop() |
|
208 { |
|
209 CX_DEBUG_ENTER_FUNCTION(); |
|
210 #ifdef Q_OS_SYMBIAN |
|
211 if (mDevice.cameraSnapshot()) { |
|
212 mDevice.cameraSnapshot()->StopSnapshot(); |
|
213 } |
|
214 #endif // Q_OS_SYMBIAN |
|
215 setState(CxeSnapshotControl::Idle); |
|
216 CX_DEBUG_EXIT_FUNCTION(); |
|
217 } |
|
218 |
|
219 /*! |
|
220 * Helper method for getting the snapshot. |
|
221 * Throws exception if fetching the snapshot fails. |
|
222 * @return QImage containing the snapshot. |
|
223 */ |
|
224 QImage CxeSnapshotControlPrivate::snapshot() |
|
225 { |
|
226 CX_DEBUG_ENTER_FUNCTION(); |
|
227 QImage image; |
|
228 |
|
229 #ifdef Q_OS_SYMBIAN |
|
230 |
|
231 CFbsBitmap *snapshot = NULL; |
|
232 TRAPD(status, { |
|
233 RArray<TInt> frameIndex; |
|
234 CleanupClosePushL(frameIndex); |
|
235 |
|
236 MCameraBuffer &buffer(mDevice.cameraSnapshot()->SnapshotDataL(frameIndex)); |
|
237 // Make sure buffer is released on leave / exception. |
|
238 // Buffer is released once the cleanup item goes out of scope. |
|
239 CxeCameraBufferCleanup cleaner(&buffer); |
|
240 |
|
241 TInt firstImageIndex(frameIndex.Find(0)); |
|
242 snapshot = &buffer.BitmapL(firstImageIndex); |
|
243 |
|
244 CleanupStack::PopAndDestroy(); // frameIndex |
|
245 |
|
246 TSize size = snapshot->SizeInPixels(); |
|
247 TInt sizeInWords = size.iHeight * CFbsBitmap::ScanLineLength(size.iWidth, EColor16MU) / sizeof(TUint32); |
|
248 CX_DEBUG(("size %d x %d, sizeInWords = %d", size.iWidth, size.iHeight, sizeInWords )); |
|
249 |
|
250 CX_DEBUG(("Creating QImage")); |
|
251 image = QImage(size.iWidth, size.iHeight, QImage::Format_RGB32); |
|
252 |
|
253 // Convert to QImage |
|
254 snapshot->LockHeap(); |
|
255 const uchar *dataPtr = (const uchar*) snapshot->DataAddress(); |
|
256 uchar *dst = image.bits(); |
|
257 memcpy(dst, dataPtr, image.numBytes()); |
|
258 snapshot->UnlockHeap(); |
|
259 |
|
260 }); |
|
261 // We throw error with the Symbian error code if there was problems. |
|
262 CxeException::throwIfError(status); |
|
263 |
|
264 |
|
265 #endif // Q_OS_SYMBIAN |
|
266 |
|
267 CX_DEBUG_EXIT_FUNCTION(); |
|
268 return image; |
|
269 } |
|
270 |
|
271 /*! |
|
272 * Handle camera snapshot events. |
|
273 * @param id Event uid. |
|
274 * @param error Status code of the event. |
|
275 */ |
|
276 void CxeSnapshotControlPrivate::handleCameraEvent(int id, int error) |
|
277 { |
|
278 CX_DEBUG_ENTER_FUNCTION(); |
|
279 |
|
280 // Ignoring all events if not active. |
|
281 if (state() == CxeSnapshotControl::Active) { |
|
282 #ifdef Q_OS_SYMBIAN |
|
283 if (id == KUidECamEventSnapshotUidValue) { |
|
284 QImage ss; |
|
285 |
|
286 if (!error) { |
|
287 try { |
|
288 ss = snapshot(); |
|
289 } catch (const CxeException& e) { |
|
290 // Note: Normally CxeException carries CxeError::Id, |
|
291 // but we intentionally use Symbian code in getSnapshot |
|
292 // as it's easier to handle here. |
|
293 error = e.error(); |
|
294 } catch (...) { |
|
295 error = KErrGeneral; |
|
296 } |
|
297 } |
|
298 |
|
299 // Emit snapshot ready signal through the public interface. |
|
300 emit q->snapshotReady(CxeErrorHandlingSymbian::map(error), ss); |
|
301 } |
|
302 #else |
|
303 Q_UNUSED(id) |
|
304 Q_UNUSED(error) |
|
305 #endif // Q_OS_SYMBIAN |
|
306 } |
|
307 CX_DEBUG_EXIT_FUNCTION(); |
|
308 } |
|
309 |
|
310 // end of file |
|