camerauis/cameraxui/cxengine/src/cxesnapshotcontrolprivate.cpp
author hgs
Thu, 15 Jul 2010 01:44:30 +0300
changeset 38 0f0b4c1d7744
child 37 64817133cd1d
permissions -rw-r--r--
201019

/*
* 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