|
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 #ifdef Q_OS_SYMBIAN |
|
43 /*! |
|
44 * Helper class for cleaning up MCameraBuffer instances. |
|
45 */ |
|
46 class CxeCameraBufferCleanup |
|
47 { |
|
48 public: |
|
49 explicit CxeCameraBufferCleanup(MCameraBuffer *buffer) |
|
50 : mBuffer(buffer) |
|
51 { |
|
52 } |
|
53 |
|
54 ~CxeCameraBufferCleanup() |
|
55 { |
|
56 if (mBuffer) { |
|
57 CX_DEBUG(("CxeCameraBufferCleanup - releasing MCameraBuffer..")); |
|
58 mBuffer->Release(); |
|
59 mBuffer = NULL; |
|
60 } |
|
61 } |
|
62 private: |
|
63 Q_DISABLE_COPY(CxeCameraBufferCleanup) |
|
64 MCameraBuffer *mBuffer; |
|
65 }; |
|
66 #endif // Q_OS_SYMBIAN |
|
67 } |
|
68 |
|
69 |
|
70 /*! |
|
71 * Constructor. |
|
72 * @param parent Public interface of Snapshot control. |
|
73 * @param device Camera device interface, used for getting hold of adaptation snapshot API. |
|
74 */ |
|
75 CxeSnapshotControlPrivate::CxeSnapshotControlPrivate(CxeSnapshotControl *parent, |
|
76 CxeCameraDevice& device) |
|
77 : CxeStateMachine("CxeSnapshotControlPrivate"), |
|
78 q(parent), |
|
79 mDevice(device) |
|
80 { |
|
81 CX_DEBUG_ENTER_FUNCTION(); |
|
82 CX_ASSERT_ALWAYS(q); |
|
83 initializeStates(); |
|
84 CX_DEBUG_EXIT_FUNCTION(); |
|
85 } |
|
86 |
|
87 /*! |
|
88 * Destructor. |
|
89 */ |
|
90 CxeSnapshotControlPrivate::~CxeSnapshotControlPrivate() |
|
91 { |
|
92 CX_DEBUG_ENTER_FUNCTION(); |
|
93 stop(); |
|
94 CX_DEBUG_EXIT_FUNCTION(); |
|
95 } |
|
96 |
|
97 /*! |
|
98 * Called when state changes. |
|
99 * @param newStateId Id of the new state. |
|
100 * @param error Error code or CxeError::None in successful case. |
|
101 */ |
|
102 void CxeSnapshotControlPrivate::handleStateChanged(int newStateId, CxeError::Id error) |
|
103 { |
|
104 emit q->stateChanged(static_cast<CxeSnapshotControl::State>(newStateId), error); |
|
105 } |
|
106 |
|
107 |
|
108 /*! |
|
109 * Get the state of Snapshot Control. |
|
110 * @return The current state. |
|
111 */ |
|
112 CxeSnapshotControl::State CxeSnapshotControlPrivate::state() const |
|
113 { |
|
114 return static_cast<CxeSnapshotControl::State> (stateId()); |
|
115 } |
|
116 |
|
117 /*! |
|
118 * Initialize Snapshot Control states |
|
119 */ |
|
120 void CxeSnapshotControlPrivate::initializeStates() |
|
121 { |
|
122 CX_DEBUG_ENTER_FUNCTION(); |
|
123 // addState( id, name, allowed next states ) |
|
124 addState(new CxeState(CxeSnapshotControl::Idle, "Idle", CxeSnapshotControl::Active)); |
|
125 addState(new CxeState(CxeSnapshotControl::Active, "Active", CxeSnapshotControl::Idle)); |
|
126 |
|
127 setInitialState(CxeSnapshotControl::Idle); |
|
128 CX_DEBUG_EXIT_FUNCTION(); |
|
129 } |
|
130 |
|
131 /*! |
|
132 * Calculate snapshot size based on diplay size and image / video output resolution. |
|
133 * @param displaySize Display size in pixels. |
|
134 * @param outputResolution Resolution of the output image / video in pixels. |
|
135 * @return Proposed best snapshot size. |
|
136 */ |
|
137 QSize CxeSnapshotControlPrivate::calculateSnapshotSize(const QSize &displaySize, const QSize &outputResolution) const |
|
138 { |
|
139 CX_DEBUG_ENTER_FUNCTION(); |
|
140 CX_DEBUG(("CxeSnapshotControlPrivate - output resolution (%d,%d)", outputResolution.width(), outputResolution.height())); |
|
141 CX_DEBUG(("CxeSnapshotControlPrivate - display size (%d,%d)", displaySize.width(), displaySize.height())); |
|
142 |
|
143 // Take resolution as reference for aspect ratio. |
|
144 // Scale keeping aspect ratio to just fit display. |
|
145 QSize size(outputResolution); |
|
146 size.scale(displaySize, Qt::KeepAspectRatio); |
|
147 CX_DEBUG(("CxeSnapshotControlPrivate - calculated size, (%d,%d)", size.width(), size.height())); |
|
148 size.setHeight(displaySize.height()); |
|
149 CX_DEBUG(("CxeSnapshotControlPrivate - adjusted final size, (%d,%d)", size.width(), size.height())); |
|
150 CX_DEBUG_EXIT_FUNCTION(); |
|
151 return size; |
|
152 } |
|
153 |
|
154 /*! |
|
155 * Start getting snapshots from camera. |
|
156 * Throws CxeException with CxeError::Id if error encountered. |
|
157 * @param size Size of the snapshot in pixels. |
|
158 */ |
|
159 void CxeSnapshotControlPrivate::start(const QSize &size) |
|
160 { |
|
161 CX_DEBUG_ENTER_FUNCTION(); |
|
162 #ifdef Q_OS_SYMBIAN |
|
163 CCamera::CCameraSnapshot *ss = mDevice.cameraSnapshot(); |
|
164 CX_ASSERT_ALWAYS(ss); |
|
165 |
|
166 if (ss->IsSnapshotActive()) { |
|
167 CX_DEBUG(("Stop currently active snapshot..")); |
|
168 ss->StopSnapshot(); |
|
169 } |
|
170 |
|
171 // Prepare snapshot |
|
172 CCamera::TFormat snapFormat = CCamera::EFormatFbsBitmapColor16MU; |
|
173 TSize snapSize = TSize(size.width(), size.height()); |
|
174 |
|
175 CX_DEBUG(("Prepare snapshot, size (%d x %d)..", size.width(), size.height())); |
|
176 TRAPD(status, ss->PrepareSnapshotL(snapFormat, snapSize, MAINTAIN_ASPECT)); |
|
177 CxeException::throwIfError(CxeErrorHandlingSymbian::map(status)); |
|
178 CX_DEBUG(("After prepare ECAM modified size to (%d x %d)..", size.width(), size.height())); |
|
179 |
|
180 CX_DEBUG(("Start snapshot..")); |
|
181 ss->StartSnapshot(); |
|
182 #else |
|
183 Q_UNUSED(size); |
|
184 #endif // Q_OS_SYMBIAN |
|
185 setState(CxeSnapshotControl::Active); |
|
186 |
|
187 CX_DEBUG_EXIT_FUNCTION(); |
|
188 } |
|
189 |
|
190 /*! |
|
191 * Stop getting snapshots from camera. |
|
192 */ |
|
193 void CxeSnapshotControlPrivate::stop() |
|
194 { |
|
195 CX_DEBUG_ENTER_FUNCTION(); |
|
196 #ifdef Q_OS_SYMBIAN |
|
197 if (mDevice.cameraSnapshot()) { |
|
198 mDevice.cameraSnapshot()->StopSnapshot(); |
|
199 } |
|
200 #endif // Q_OS_SYMBIAN |
|
201 setState(CxeSnapshotControl::Idle); |
|
202 CX_DEBUG_EXIT_FUNCTION(); |
|
203 } |
|
204 |
|
205 /*! |
|
206 * Helper method for getting the snapshot. |
|
207 * Throws exception if fetching the snapshot fails. |
|
208 * @return QPixmap containing the snapshot. |
|
209 */ |
|
210 QPixmap CxeSnapshotControlPrivate::snapshot() |
|
211 { |
|
212 CX_DEBUG_ENTER_FUNCTION(); |
|
213 QPixmap pixmap; |
|
214 |
|
215 #ifdef Q_OS_SYMBIAN |
|
216 TRAPD(status, { |
|
217 RArray<TInt> frameIndex; |
|
218 CleanupClosePushL(frameIndex); |
|
219 |
|
220 MCameraBuffer &buffer(mDevice.cameraSnapshot()->SnapshotDataL(frameIndex)); |
|
221 // Make sure buffer is released on leave / exception. |
|
222 // Buffer is released once the cleanup item goes out of scope. |
|
223 CxeCameraBufferCleanup cleaner(&buffer); |
|
224 |
|
225 TInt firstImageIndex(frameIndex.Find(0)); |
|
226 CFbsBitmap &snapshot(buffer.BitmapL(firstImageIndex)); |
|
227 |
|
228 CleanupStack::PopAndDestroy(); // frameIndex |
|
229 |
|
230 TSize size = snapshot.SizeInPixels(); |
|
231 TInt sizeInWords = size.iHeight * CFbsBitmap::ScanLineLength(size.iWidth, EColor16MU) / sizeof(TUint32); |
|
232 CX_DEBUG(("size %d x %d, sizeInWords = %d", size.iWidth, size.iHeight, sizeInWords )); |
|
233 |
|
234 TUint32 *pixelData = new (ELeave) TUint32[ sizeInWords ]; |
|
235 // Convert to QImage |
|
236 snapshot.LockHeap(); |
|
237 TUint32 *dataPtr = snapshot.DataAddress(); |
|
238 memcpy(pixelData, dataPtr, sizeof(TUint32)*sizeInWords); |
|
239 snapshot.UnlockHeap(); |
|
240 |
|
241 CX_DEBUG(("Creating QImage")); |
|
242 QImage *snapImage = new QImage((uchar*)pixelData, size.iWidth, size.iHeight, |
|
243 CFbsBitmap::ScanLineLength(size.iWidth, EColor16MU), |
|
244 QImage::Format_RGB32); |
|
245 |
|
246 pixmap = QPixmap::fromImage(*snapImage); |
|
247 delete [] pixelData; |
|
248 delete snapImage; |
|
249 }); |
|
250 // We throw error with the Symbian error code if there was problems. |
|
251 CxeException::throwIfError(status); |
|
252 #endif // Q_OS_SYMBIAN |
|
253 |
|
254 CX_DEBUG_EXIT_FUNCTION(); |
|
255 return pixmap; |
|
256 } |
|
257 |
|
258 /*! |
|
259 * Handle camera snapshot events. |
|
260 * @param id Event uid. |
|
261 * @param error Status code of the event. |
|
262 */ |
|
263 void CxeSnapshotControlPrivate::handleCameraEvent(int id, int error) |
|
264 { |
|
265 CX_DEBUG_ENTER_FUNCTION(); |
|
266 |
|
267 // Ignoring all events if not active. |
|
268 if (state() == CxeSnapshotControl::Active) { |
|
269 #ifdef Q_OS_SYMBIAN |
|
270 if (id == KUidECamEventSnapshotUidValue) { |
|
271 QPixmap ss; |
|
272 |
|
273 if (!error) { |
|
274 try { |
|
275 ss = snapshot(); |
|
276 } catch (const CxeException& e) { |
|
277 // Note: Normally CxeException carries CxeError::Id, |
|
278 // but we intentionally use Symbian code in getSnapshot |
|
279 // as it's easier to handle here. |
|
280 error = e.error(); |
|
281 } catch (...) { |
|
282 error = KErrGeneral; |
|
283 } |
|
284 } |
|
285 |
|
286 // Emit snapshot ready signal through the public interface. |
|
287 emit q->snapshotReady(CxeErrorHandlingSymbian::map(error), ss); |
|
288 } |
|
289 #else |
|
290 Q_UNUSED(id) |
|
291 Q_UNUSED(error) |
|
292 #endif // Q_OS_SYMBIAN |
|
293 } |
|
294 CX_DEBUG_EXIT_FUNCTION(); |
|
295 } |
|
296 |
|
297 // end of file |