/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#include <QSize>
#include <QPixmap>
// Note: Keep atleast one Qt include before preprocessor flags
#ifdef Q_OS_SYMBIAN
#include <e32base.h>
#include <fbs.h>
#include <ecam.h>
#include <ecam/camerasnapshot.h>
#endif // Q_OS_SYMBIAN
#include "cxutils.h"
#include "cxeerror.h"
#include "cxeexception.h"
#include "cxeerrormappingsymbian.h"
#include "cxecameradevice.h"
#include "cxestate.h"
#include "cxesnapshotcontrol.h"
#include "cxesnapshotcontrolprivate.h"
namespace
{
const int MAINTAIN_ASPECT = false;
#ifdef Q_OS_SYMBIAN
/*!
* Helper class for cleaning up MCameraBuffer instances.
*/
class CxeCameraBufferCleanup
{
public:
explicit CxeCameraBufferCleanup(MCameraBuffer *buffer)
: mBuffer(buffer)
{
}
~CxeCameraBufferCleanup()
{
if (mBuffer) {
CX_DEBUG(("CxeCameraBufferCleanup - releasing MCameraBuffer.."));
mBuffer->Release();
mBuffer = NULL;
}
}
private:
Q_DISABLE_COPY(CxeCameraBufferCleanup)
MCameraBuffer *mBuffer;
};
#endif // Q_OS_SYMBIAN
}
/*!
* Constructor.
* @param parent Public interface of Snapshot control.
* @param device Camera device interface, used for getting hold of adaptation snapshot API.
*/
CxeSnapshotControlPrivate::CxeSnapshotControlPrivate(CxeSnapshotControl *parent,
CxeCameraDevice& device)
: CxeStateMachine("CxeSnapshotControlPrivate"),
q(parent),
mDevice(device)
{
CX_DEBUG_ENTER_FUNCTION();
CX_ASSERT_ALWAYS(q);
initializeStates();
CX_DEBUG_EXIT_FUNCTION();
}
/*!
* Destructor.
*/
CxeSnapshotControlPrivate::~CxeSnapshotControlPrivate()
{
CX_DEBUG_ENTER_FUNCTION();
stop();
CX_DEBUG_EXIT_FUNCTION();
}
/*!
* Called when state changes.
* @param newStateId Id of the new state.
* @param error Error code or CxeError::None in successful case.
*/
void CxeSnapshotControlPrivate::handleStateChanged(int newStateId, CxeError::Id error)
{
emit q->stateChanged(static_cast<CxeSnapshotControl::State>(newStateId), error);
}
/*!
* Get the state of Snapshot Control.
* @return The current state.
*/
CxeSnapshotControl::State CxeSnapshotControlPrivate::state() const
{
return static_cast<CxeSnapshotControl::State> (stateId());
}
/*!
* Initialize Snapshot Control states
*/
void CxeSnapshotControlPrivate::initializeStates()
{
CX_DEBUG_ENTER_FUNCTION();
// addState( id, name, allowed next states )
addState(new CxeState(CxeSnapshotControl::Idle, "Idle", CxeSnapshotControl::Active));
addState(new CxeState(CxeSnapshotControl::Active, "Active", CxeSnapshotControl::Idle));
setInitialState(CxeSnapshotControl::Idle);
CX_DEBUG_EXIT_FUNCTION();
}
/*!
* Calculate snapshot size based on diplay size and image / video output resolution.
* @param displaySize Display size in pixels.
* @param outputResolution Resolution of the output image / video in pixels.
* @return Proposed best snapshot size.
*/
QSize CxeSnapshotControlPrivate::calculateSnapshotSize(const QSize &displaySize, const QSize &outputResolution) const
{
CX_DEBUG_ENTER_FUNCTION();
CX_DEBUG(("CxeSnapshotControlPrivate - output resolution (%d,%d)", outputResolution.width(), outputResolution.height()));
CX_DEBUG(("CxeSnapshotControlPrivate - display size (%d,%d)", displaySize.width(), displaySize.height()));
// Take resolution as reference for aspect ratio.
// Scale keeping aspect ratio to just fit display.
QSize size(outputResolution);
size.scale(displaySize, Qt::KeepAspectRatio);
CX_DEBUG(("CxeSnapshotControlPrivate - calculated size, (%d,%d)", size.width(), size.height()));
size.setHeight(displaySize.height());
CX_DEBUG(("CxeSnapshotControlPrivate - adjusted final size, (%d,%d)", size.width(), size.height()));
CX_DEBUG_EXIT_FUNCTION();
return size;
}
/*!
* Start getting snapshots from camera.
* Throws CxeException with CxeError::Id if error encountered.
* @param size Size of the snapshot in pixels.
*/
void CxeSnapshotControlPrivate::start(const QSize &size)
{
CX_DEBUG_ENTER_FUNCTION();
#ifdef Q_OS_SYMBIAN
CCamera::CCameraSnapshot *ss = mDevice.cameraSnapshot();
CX_ASSERT_ALWAYS(ss);
if (ss->IsSnapshotActive()) {
CX_DEBUG(("Stop currently active snapshot.."));
ss->StopSnapshot();
}
// Prepare snapshot
CCamera::TFormat snapFormat = CCamera::EFormatFbsBitmapColor16MU;
TSize snapSize = TSize(size.width(), size.height());
CX_DEBUG(("Prepare snapshot, size (%d x %d)..", size.width(), size.height()));
TRAPD(status, ss->PrepareSnapshotL(snapFormat, snapSize, MAINTAIN_ASPECT));
CxeException::throwIfError(CxeErrorHandlingSymbian::map(status));
CX_DEBUG(("After prepare ECAM modified size to (%d x %d)..", size.width(), size.height()));
CX_DEBUG(("Start snapshot.."));
ss->StartSnapshot();
#else
Q_UNUSED(size);
#endif // Q_OS_SYMBIAN
setState(CxeSnapshotControl::Active);
CX_DEBUG_EXIT_FUNCTION();
}
/*!
* Stop getting snapshots from camera.
*/
void CxeSnapshotControlPrivate::stop()
{
CX_DEBUG_ENTER_FUNCTION();
#ifdef Q_OS_SYMBIAN
if (mDevice.cameraSnapshot()) {
mDevice.cameraSnapshot()->StopSnapshot();
}
#endif // Q_OS_SYMBIAN
setState(CxeSnapshotControl::Idle);
CX_DEBUG_EXIT_FUNCTION();
}
/*!
* Helper method for getting the snapshot.
* Throws exception if fetching the snapshot fails.
* @return QPixmap containing the snapshot.
*/
QPixmap CxeSnapshotControlPrivate::snapshot()
{
CX_DEBUG_ENTER_FUNCTION();
QPixmap pixmap;
#ifdef Q_OS_SYMBIAN
TRAPD(status, {
RArray<TInt> frameIndex;
CleanupClosePushL(frameIndex);
MCameraBuffer &buffer(mDevice.cameraSnapshot()->SnapshotDataL(frameIndex));
// Make sure buffer is released on leave / exception.
// Buffer is released once the cleanup item goes out of scope.
CxeCameraBufferCleanup cleaner(&buffer);
TInt firstImageIndex(frameIndex.Find(0));
CFbsBitmap &snapshot(buffer.BitmapL(firstImageIndex));
CleanupStack::PopAndDestroy(); // frameIndex
TSize size = snapshot.SizeInPixels();
TInt sizeInWords = size.iHeight * CFbsBitmap::ScanLineLength(size.iWidth, EColor16MU) / sizeof(TUint32);
CX_DEBUG(("size %d x %d, sizeInWords = %d", size.iWidth, size.iHeight, sizeInWords ));
TUint32 *pixelData = new (ELeave) TUint32[ sizeInWords ];
// Convert to QImage
snapshot.LockHeap();
TUint32 *dataPtr = snapshot.DataAddress();
memcpy(pixelData, dataPtr, sizeof(TUint32)*sizeInWords);
snapshot.UnlockHeap();
CX_DEBUG(("Creating QImage"));
QImage *snapImage = new QImage((uchar*)pixelData, size.iWidth, size.iHeight,
CFbsBitmap::ScanLineLength(size.iWidth, EColor16MU),
QImage::Format_RGB32);
pixmap = QPixmap::fromImage(*snapImage);
delete [] pixelData;
delete snapImage;
});
// We throw error with the Symbian error code if there was problems.
CxeException::throwIfError(status);
#endif // Q_OS_SYMBIAN
CX_DEBUG_EXIT_FUNCTION();
return pixmap;
}
/*!
* Handle camera snapshot events.
* @param id Event uid.
* @param error Status code of the event.
*/
void CxeSnapshotControlPrivate::handleCameraEvent(int id, int error)
{
CX_DEBUG_ENTER_FUNCTION();
// Ignoring all events if not active.
if (state() == CxeSnapshotControl::Active) {
#ifdef Q_OS_SYMBIAN
if (id == KUidECamEventSnapshotUidValue) {
QPixmap ss;
if (!error) {
try {
ss = snapshot();
} catch (const CxeException& e) {
// Note: Normally CxeException carries CxeError::Id,
// but we intentionally use Symbian code in getSnapshot
// as it's easier to handle here.
error = e.error();
} catch (...) {
error = KErrGeneral;
}
}
// Emit snapshot ready signal through the public interface.
emit q->snapshotReady(CxeErrorHandlingSymbian::map(error), ss);
}
#else
Q_UNUSED(id)
Q_UNUSED(error)
#endif // Q_OS_SYMBIAN
}
CX_DEBUG_EXIT_FUNCTION();
}
// end of file