symba (779893), страница 37
Текст из файла (страница 37)
CImageDecoder::FrameInfo() is populated only for the currentframe. This plug-in is usually included with UIQ smartphones but is excluded from S60 smartphones.170IMAGE CONVERSION LIBRARYThe MNG decoder does not support frame count information. So howcan a client iterate over all the frames? There is a special flag definedwithin the TFrameInfo, which can be used to check if there are moreframes in the animation to decode: TFrameInfo::EMngMoreFramesToDecode.
An application should use a piece of code such as thefollowing to access all the MNG animation frames:while ((decoder->FrameInfo(0).iFlags &TFrameInfo::EMngMoreFramesToDecode) ==TFrameInfo::EMngMoreFramesToDecode){//decode current frame}MNG animation may have several (sometimes even infinite) animationloops. This is one of the reasons why FrameCount() is not supported.You should not cache MNG animation frames as your application mayeasily run out of memory.6.2.13 Streamed DecodingThe image decoder component has basic support for streamed decoding.It is possible to decode an image in which physical content is not availableimmediately in full. This kind of use case is mostly for web-browser-likeimage downloading.
The image decoder API does not support ‘true’streamed decoding, which means that an application has to accumulateincoming image data on its own.Figure 6.7 shows an activity diagram for the following streamed decoding scenario. If CImageDecoder::NewL() leaves with KErrUnderflow, then it means that the ICL has insufficient data to find an appropriate decoder plug-in or a plug-in might have failed to initialize itself giventhe amount of available data.
In this case, the API client has to add moredata into the original source and this why an image decoder API doesnot support streaming in full. The client has to maintain all the input dataduring the decoding process and keep adding new data to the existingdata source, whether a memory buffer or a file.CImageDecoder::DataNewL() accepts a reference to a descriptoras the image data input buffer. In the case of streamed decoding, thedescriptor is used during the decoding process, so you must ensure thatthe descriptor object reflects any changes due to data added to thebuffer.
HBufC:ReAlloc() is not suitable in this case as it creates anew descriptor object. The best way of doing this is to create an instanceof TPtrC and keep it pointing to the currently active buffer using theTPtrC::Set() method. You must also keep the TPtrC instance inDECODING IMAGES171CImageDecoder::*NewL()[KErrUnderflow]Add more data toimage data buffer[KErrNone][other error codes]:Add more data toimage data buffer:CImageDecoder::ContinueProcessingHeadersL()[IsHeaderProcessingComplete() == false][Required frame index >= FrameCount()]CImageDecoder::Convert()[other error codes]:Add more data toimage data bufferCImageDecoder::ContinueProcessingHeadersL():Add more data toimage data buffer[KErrUnderflow]Handle error - decodingis probably not possibleFigure 6.7 Activity diagram for streamed decodingscope – it can’t be a stack-allocated object.
Addition of new data into theimage data buffer may look like the code snippet below:// declared as membersTUint8* iBuffer;TPtrC8 iBufPtr;// buffer creationiBuffer = User::AllocL(initialSize);172IMAGE CONVERSION LIBRARYiBufPtr.Set(iBuffer, initialSize);// buffer data addition to the existing bufferTInt currentSize = iBufPtr.Size();iBuffer = User::ReAllocL(iBuffer, newSize);// append new data at the "currentSize" offset and set iBufPtr object// to point to the new buffer locationiBufPtr.Set(iBuffer, newSize);If the framework has enough data to find a plug-in, the loaded plug-intries to parse input data and construct image information structures, suchas TFrameInfo. The plug-in may encounter an unexpected end of dataduring this process:• If there is not enough data to populate all the frame information structures, the plug-in returns ‘false’ from CImageDecoder::IsHeaderProcessingComplete() and the result of CImageDecoder::FrameCount() may be inaccurate.
A client may assume that theapplication should keep adding more data to the buffer until CImageDecoder::IsHeaderProcessingComplete() returns ‘true’.This approach may work in many cases but it may fail with certain image formats where, instead of frame information data for all theframes being placed at the beginning of the file, it is spread across theentire file.
In such cases, ‘streamed’ decoding won’t be seen by an enduser as a progressive process, but more as ‘entire file pre-buffering’.• If there is sufficient data to load and initialize a plug-in but not enoughdata to populate at least one TFrameInfo structure then the defaultbehavior for the framework is to leave with KErrUnderflow. Thisensures that CImageDecoder::FrameCount() always returns atleast 1.
This approach however may incur a severe performancepenalty if many attempts to create a decoder fail due to insufficientdata, as every decoder creation attempt involves plug-in resolutionand loading. A more advanced application may use the EOptionAllowZeroFrameOpen option of CImageDecoder to instruct theframework to return a valid decoder object even if no TFrameInfostructures have been created.• If CImageDecoder::IsHeaderProcessingComplete() isEFalse, then the application keeps adding new data to the bufferand calling CImageDecoder::ContinueProcessingHeadersL() to let the decoder create the rest of the TFrameInfo structuresor reach the required number of frame headers being parsed (whichcan be obtained by calling CImageDecoder::FrameCount()).Once the desired frame number is less than the CImageDecoder::FrameCount() or CImageDecoder::IsHeaderProcessingCom-DECODING IMAGES173plete() is ‘true’, then the application may call CImageDecoder::Convert() for this frame.
The plug-in will decode as much of the framepixel data as possible and may leave with KErrUnderflow when notall of the frame pixels are available. In the case of KErrUnderflowan application may (at its discretion) display partially decoded framepixels if the EPartialDecodeInvalid flag of the relevant frame’sTFrameInfo is not set. The application should keep adding data tothe buffer and calling CImageDecoder::ContinueConvert() untilit completes with KErrNone.Implementing flexible streamed decoding can be quite tricky since itrequires programming a sophisticated state machine which can take inputfrom several active objects.6.2.14 CBufferedImageDecoderThe CBufferedImageDecoder API can be used to simplify the streamed decoding process somewhat.
It is a wrapper that encapsulates boththe CImageDecoder and its input buffer (thus it can be used only whenimage data resides in memory).In contrast to the CImageDecoder, the CBufferedImageDecodercan always be created even though there is not enough data to find anappropriate decoder plug-in. An application does not have to manage theentire image buffer on its own, but instead can pass only new data in andall the buffer management is done inside the wrapper.
However, it meansthat an application has to check the CBufferedImageDecoder::ValidDecoder() method before calling any methods which requireexistence of a decoder plug-in, such as CBufferedImageDecoder::FrameCount(), otherwise it will panic.The algorithm for using the CBufferedImageDecoder class shouldlook like this:1. An instance of the wrapper is created using the CBufferedImageDecoder::NewL() factory method.2. A one-off operation, CBufferedImageDecoder::OpenL(), isrequired to open the buffered image decoder.3.
The application checks CBufferedImageDecoder::ValidDecoder(). If there was not enough data to create an underlyingimage decoder object (e.g. OpenL() has left with KErrUnderflow)then more data has to be added by calling CBufferedImageDecoder::AppendDataL() and CBufferedImageDecoder::ContinueOpenL() should be called to re-try the decoder creationattempt. Note that there is no need to keep the input buffer data‘alive’, as a copy is taken by CBufferedImageDecoder.174IMAGE CONVERSION LIBRARY4.The CBufferedImageDecoder methods IsImageHeaderProcessingComplete(), ContinueProcesessingHeaderL()and ContinueConvert() are used in the same way as for theCImageDecoder class.5.Once image decoding has been completed, it is possible to re-usethe same instance of the CBufferedImageDecoder for decodingother images by resetting its state (CBufferedImageDecoder::Reset()).Overall, the CBufferedImageDecoder can be seen as a fairlysimple helper class which can simplify buffered decoding a little at thecost of hiding all the advanced features of CImageDecoder from theclient.6.2.15 Performance HintsThere are several measures which you can take to increase performanceof image decoding under some circumstances.• Use CImageDecoder::DataNewL() instead of FileNewL() if theimage is going to be decoded several times, so the file is read fromthe file system just once.• Use the CImageDecoder::EOptionNoDither | CImageDecoder::EPreferFastDecode options during decoder creation ifyou don’t need print-like quality and prefer faster decoding.• Add the CImageDecoder::EOptionIgnoreExifMetaDataoption if you don’t need Exif metadata to be parsed (and henceto be available).• Either decode to the frame’s ‘native’ color mode (TFrameInfo::iFrameDisplayMode) or EColor16M bitmap.