×
Namespaces

Variants
Actions

如何同步解码图像

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata
Tested with
Devices(s): S60 Emulator
CompatibilityArticle
Keywords: CImageDecoder EOptionAlwaysThread CFbsBitmap RThread
Created: chenziteng (17 Oct 2009)
Last edited: hamishwillee (30 May 2013)

Contents

概述

Symbian OS C++图像解码API CImageDecoder使用活动对象机制以异步方式工作。但有些情况下(例如简化程序逻辑)开发者可能希望同步解码图像。本文描述了如何以真正同步的方式解码图像文件或者存放于内存缓冲区中的图像数据。

方案

解码图像文件

许多人的尝试证明使用User::WaitForRequest()等待CImageDecoder::Convert()完成会导致程序冻屏(假死),原因是解码工作在默认情况下是在调用者所在的线程以非抢占式的活动对象机制完成的。

    // 注意:这段代码无法正常工作!
CImageDecoder* decoder = CImageDecoder::FileNewL(fs, fileName);
CleanupStack::PushL(decoder);
TFrameInfo frameInfo = decoder->FrameInfo(0);
CFbsBitmap *tex = new(ELeave) CFbsBitmap;
err = tex->Create(frameInfo.iOverallSizeInPixels, frameInfo.iFrameDisplayMode);
User::LeaveIfError(err);
CleanupStack::PushL(tex);
TRequestStatus status = KRequestPending;
decoder->Convert( &status, *tex);
User::WaitForRequest(status); // 永远等待...
CleanupStack::Pop(tex);
CleanupStack::PopAndDestroy(decoder);

使用嵌套活动调度器(即使用CActiveSchedulerWait启动一个新的消息循环)可以在一定程度上解决这个问题。但是通过这个方法只能实现“伪同步”,也就是说在解码过程中应用程序仍然有机会处理用户交互事件或者其它异步事件。这样会使应用程序逻辑失控,比如如果用户交互事件的处理代码试图删除正在工作的解码器.

为了真正解决这个问题必须用EOptionAlwaysThread指示解码器新建线程解码。

CFbsBitmap* MyPicLoader::LoadL(const TDesC& aFileName)
{
RFs fs;
TInt err = fs.Connect();
User::LeaveIfError(err);
CleanupClosePushL(fs);
 
CImageDecoder* decoder = CImageDecoder::FileNewL(fs, aFileName, CImageDecoder::EOptionAlwaysThread);
CleanupStack::PushL(decoder);
TFrameInfo frameInfo = decoder->FrameInfo(0);
CFbsBitmap *tex = new(ELeave) CFbsBitmap;
err = tex->Create(frameInfo.iOverallSizeInPixels, frameInfo.iFrameDisplayMode);
User::LeaveIfError(err);
CleanupStack::PushL(tex);
TRequestStatus status = KRequestPending;
decoder->Convert( &status, *tex);
User::WaitForRequest(status); // Convert() is asynchronous, wait on it
CleanupStack::Pop(tex);
CleanupStack::PopAndDestroy(decoder);
 
CleanupStack::PopAndDestroy(&fs);
 
return tex;
}

解码内存缓冲区中的图像数据

关于这个话题,一个新的问题是CImageDecoder似乎并没有为DataNewL实现EOptionAlwaysThread。即便指定了这个选项,User::WaitForRequest()仍会死等下去。

// 注意:在调用DataNewL时,EOptionAlwaysThread选项不能正常工作
CFbsBitmap* MyPicLoader::LoadL(const TDesC8* aBuffer)
{
RFs fs;
TInt err = fs.Connect();
User::LeaveIfError(err);
CleanupClosePushL(fs);
 
CImageDecoder* decoder = CImageDecoder::DataNewL(fs, *aBuffer, CImageDecoder::EOptionAlwaysThread);
CleanupStack::PushL(decoder);
TFrameInfo frameInfo = decoder->FrameInfo(0);
CFbsBitmap *tex = new(ELeave) CFbsBitmap;
err = tex->Create(frameInfo.iOverallSizeInPixels, frameInfo.iFrameDisplayMode);
User::LeaveIfError(err);
CleanupStack::PushL(tex);
TRequestStatus status = KRequestPending;
decoder->Convert( &status, *tex);
User::WaitForRequest(status); // will wait forever...
CleanupStack::Pop(tex);
CleanupStack::PopAndDestroy(decoder);
 
CleanupStack::PopAndDestroy(&fs);
 
return tex;
}

当然异步化同步的问题总可以用嵌套活动调度器解决,但如果想实现真正的同步调用就必须自己创建新线程完成解码工作。

struct ThreadParameters
{
const TDesC8* iBuffer; // input
TInt iHandle; // handle of the decoded bitmap object
};
 
void ThreadMainL(ThreadParameters* aParam)
{
CMyImageLoader* loader = CMyImageLoader::NewLC();
loader->LoadL(aParam->iBuffer, aParam->iHandle); // send the decoding request
CActiveScheduler::Start(); // and then start the message loop
User::LeaveIfError(loader->iStatus.Int());
CleanupStack::PopAndDestroy(loader);
}
 
TInt ThreadFunc(TAny* aParam)
{
TInt err = KErrNone;
ThreadParameters* param = reinterpret_cast<ThreadParameters*>(aParam);
CTrapCleanup *cs = CTrapCleanup::New(); // create infrastructures like cleanup stack and active scheduler for the thread
if (cs)
{
CActiveScheduler *s = new CActiveScheduler;
if(s)
{
CActiveScheduler::Install(s);
TRAP(err, ThreadMainL(param));
delete s;
}
delete cs;
}
else
{
err = KErrNoMemory;
}
return err;
}
 
 
// thread version
CFbsBitmap* MyPicLoader::LoadL(const TDesC8* aBuffer)
{
RFs fs;
TInt err = fs.Connect();
User::LeaveIfError(err);
CleanupClosePushL(fs);
CImageDecoder* decoder = CImageDecoder::DataNewL(fs, *aBuffer);
TFrameInfo frameInfo = decoder->FrameInfo(0);
delete decoder;
CleanupStack::PopAndDestroy(&fs);
 
CFbsBitmap* bitmap = new(ELeave) CFbsBitmap;
CleanupStack::PushL(bitmap);
err = bitmap->Create(frameInfo.iOverallSizeInPixels, frameInfo.iFrameDisplayMode);
User::LeaveIfError(err);
ThreadParameters param;
param.iBuffer = aBuffer;
param.iHandle = bitmap->Handle();
RThread thread;
_LIT(KThreadName, "thread1" );
thread.Create(KThreadName, ThreadFunc, KDefaultStackSize, NULL, &param); // create a new thread
User::LeaveIfError(err);
CleanupClosePushL(thread);
thread.Resume(); // then let the thread do the job
TRequestStatus status = KRequestPending;
thread.Logon(status);
User::WaitForRequest(status); // and then wait until the thread exits
User::LeaveIfError(status.Int());
CleanupStack::PopAndDestroy(&thread);
CleanupStack::Pop(bitmap);
return bitmap;
}

更详细的信息请查看完整的示例程序。

需要特点注意是在线程退出之前一定要调用REComSession::FinalClose(),否则会有内存泄露。

源代码

完整的示例程序 (仅在仿真器上运行过):

HelloWorld(SynchronousImageBufferDecoder).zip

屏幕截图 (第一张:解码图像文件,第二张:解码内存缓冲区中的图像数据):

FileNewL.PNGDataNewL.PNG

This page was last modified on 30 May 2013, at 07:00.
72 page views in the last 30 days.
×