|
1 /* |
|
2 * Copyright (c) 2006-2008 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 the License "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 |
|
19 #include "StaticImageDecoder.h" |
|
20 #include "MaskedBitmap.h" |
|
21 #include <imageconversion.h> |
|
22 #include <eikenv.h> |
|
23 #include <fbs.h> |
|
24 #include <oom.h> |
|
25 |
|
26 // CONSTANTS |
|
27 // block all images that would take free ram below this amount when decoded |
|
28 const TInt KFreeRamBitmapBlockLimit = 1536 * 1024; |
|
29 // do the following additional checks if image would take free ram below this amount |
|
30 const TInt KFreeRamBitmapCheckLimit = 7 * 1024 * 1024; |
|
31 // if bitmap size is times this much bigger than data size then it is a high expansion bitmap |
|
32 const TInt KHighExpansionBitmapFactor = 16; |
|
33 // block high expansion bitmap above this size (they are usually backgrounds and so less important) |
|
34 const TInt KLargeHighExpansionBitmapSize = 800*600*2; |
|
35 // block all images that would take more than this percentage of available free ram |
|
36 const TInt KMaxBitmapRamPercent = 25; |
|
37 |
|
38 //============================================================================= |
|
39 // CRawData |
|
40 //============================================================================= |
|
41 CRawData* CRawData::NewL( const TDesC8& aData, TDesC* aMime, CMaskedBitmap* aTarget, MAnimationDecoderObserver* aObserv ) |
|
42 { |
|
43 CRawData* self = new (ELeave) CRawData(); |
|
44 CleanupStack::PushL( self ); |
|
45 self->ConstructL( aData, aMime, aTarget, aObserv ); |
|
46 CleanupStack::Pop(); |
|
47 return self; |
|
48 } |
|
49 |
|
50 void CRawData::ConstructL( const TDesC8& aData, TDesC* aMIMEType, CMaskedBitmap* aTarget, MAnimationDecoderObserver* aObserv ) |
|
51 { |
|
52 // mime type |
|
53 if (aMIMEType) |
|
54 { |
|
55 // it is safer to ignore the server supplied mime type and just recognize |
|
56 // the image type from the data headers. this does not work for all formats though |
|
57 if ( *aMIMEType==KMimeWBMP || *aMIMEType==KMimeOTA || *aMIMEType==KMimeWMF) |
|
58 { |
|
59 // convert to 8 bit |
|
60 iMime = HBufC8::NewL(aMIMEType->Length()); |
|
61 iMime->des().Copy(*aMIMEType); |
|
62 } |
|
63 } |
|
64 |
|
65 // data |
|
66 const TUint8* src = aData.Ptr(); |
|
67 iData = MemoryManager::Alloc( aData.Length() ); |
|
68 Mem::Copy( iData, src, aData.Length() ); |
|
69 iDataPtr.Set((TUint8*)iData, aData.Length(), aData.Length() ); |
|
70 iObserver = aObserv; |
|
71 iTarget = aTarget; |
|
72 } |
|
73 |
|
74 CRawData::~CRawData() |
|
75 { |
|
76 MemoryManager::Free( iData ); |
|
77 delete iMime; |
|
78 } |
|
79 |
|
80 //============================================================================= |
|
81 // CStaticImageDecoder |
|
82 //============================================================================= |
|
83 CStaticImageDecoder::CStaticImageDecoder() : CActive(CActive::EPriorityStandard) |
|
84 { |
|
85 CActiveScheduler::Add( this ); |
|
86 } |
|
87 |
|
88 CStaticImageDecoder* CStaticImageDecoder::NewL() |
|
89 { |
|
90 CStaticImageDecoder* self = new (ELeave) CStaticImageDecoder(); |
|
91 CleanupStack::PushL( self ); |
|
92 self->ConstructL(); |
|
93 CleanupStack::Pop(); |
|
94 return self; |
|
95 } |
|
96 |
|
97 CStaticImageDecoder::~CStaticImageDecoder() |
|
98 { |
|
99 |
|
100 iQueue.Reset(); |
|
101 delete iDecoder; |
|
102 iContext = 0; |
|
103 |
|
104 if (IsActive()) |
|
105 { |
|
106 Cancel(); |
|
107 } |
|
108 } |
|
109 |
|
110 void CStaticImageDecoder::ConstructL() |
|
111 { |
|
112 iDecoder = CBufferedImageDecoder::NewL(CEikonEnv::Static()->FsSession()); |
|
113 } |
|
114 |
|
115 TBool CStaticImageDecoder::LoadNextImage() |
|
116 { |
|
117 if( iQueue.Count() == 0 ) return EFalse; |
|
118 |
|
119 // load the data, FIFO |
|
120 CRawData* data = iQueue[0]; |
|
121 |
|
122 TRAP_IGNORE( |
|
123 if( data->iMime ) |
|
124 iDecoder->OpenL( data->iDataPtr, *(data->iMime), CImageDecoder::EOptionNone ); |
|
125 else |
|
126 iDecoder->OpenL( data->iDataPtr, CImageDecoder::EOptionNone ); |
|
127 ) |
|
128 |
|
129 // set the context |
|
130 if(iDecoder->ValidDecoder() && iDecoder->IsImageHeaderProcessingComplete()) |
|
131 { |
|
132 if( iDecoder->FrameCount() == 1 ) |
|
133 { |
|
134 iContext = data; |
|
135 TFrameInfo frmInfo = iDecoder->FrameInfo( 0 ); |
|
136 |
|
137 // check memory before creating bitmaps |
|
138 if( CheckBitmapMemoryConsumption( frmInfo ) ) |
|
139 { |
|
140 if (frmInfo.iFlags & TFrameInfo::ETransparencyPossible) |
|
141 { |
|
142 TDisplayMode mskMode = (frmInfo.iFlags & TFrameInfo::EAlphaChannel) ? EGray256 : EGray2; |
|
143 iContext->iTarget->Create( frmInfo.iOverallSizeInPixels, GetBestDisplayMode(frmInfo.iFrameDisplayMode), mskMode ); |
|
144 } |
|
145 else |
|
146 { |
|
147 iContext->iTarget->Create( frmInfo.iOverallSizeInPixels, GetBestDisplayMode(frmInfo.iFrameDisplayMode) ); |
|
148 } |
|
149 |
|
150 LoadOneFrame(); |
|
151 return ETrue; |
|
152 } |
|
153 } |
|
154 else |
|
155 { |
|
156 // animated images need a dedicated decoder |
|
157 data->iObserver->StartAnimationDecoder(); |
|
158 |
|
159 iQueue.Remove( 0 ); |
|
160 Reset(); |
|
161 |
|
162 StartLoading(); |
|
163 return ETrue; |
|
164 } |
|
165 } |
|
166 |
|
167 // something must be wrong with the current image |
|
168 data->iObserver->decoderError( -1 ); |
|
169 iQueue.Remove(0); |
|
170 |
|
171 // decode next image |
|
172 Reset(); |
|
173 StartLoading(); |
|
174 |
|
175 return ETrue; |
|
176 } |
|
177 |
|
178 void CStaticImageDecoder::LoadOneFrame() |
|
179 { |
|
180 // static image has only one frame; |
|
181 const TFrameInfo& frameInfo = iDecoder->FrameInfo( 0 ); |
|
182 CFbsBitmap& bmp = iContext->iTarget->BitmapModifyable(); |
|
183 if ( frameInfo.iFlags & TFrameInfo::ETransparencyPossible ) |
|
184 { |
|
185 CFbsBitmap& msk = iContext->iTarget->MaskModifyable(); |
|
186 iDecoder->Convert( &iStatus, bmp, msk, 0 ); |
|
187 } |
|
188 else |
|
189 { |
|
190 iDecoder->Convert( &iStatus, bmp, 0 ); |
|
191 } |
|
192 |
|
193 // the first frame, partial information is available |
|
194 iContext->iObserver->PartialImage(); |
|
195 |
|
196 SetActive(); |
|
197 iState = EOneFrameReady; |
|
198 } |
|
199 |
|
200 TBool CStaticImageDecoder::DecodeL( CRawData* aData ) |
|
201 { |
|
202 // queue the data, FIFO |
|
203 iQueue.AppendL( aData ); |
|
204 StartLoading(); |
|
205 return ETrue; |
|
206 } |
|
207 |
|
208 void CStaticImageDecoder::StartLoading() |
|
209 { |
|
210 if( !IsActive() ) |
|
211 { |
|
212 // initalize decoding |
|
213 TRequestStatus* status = &iStatus; |
|
214 User::RequestComplete( status, 0 ); |
|
215 SetActive(); |
|
216 iState = EStartLoading; |
|
217 } |
|
218 } |
|
219 |
|
220 void CStaticImageDecoder::RunL() |
|
221 { |
|
222 if( iState == EStartLoading ) |
|
223 { |
|
224 LoadNextImage(); |
|
225 } |
|
226 else if( iState == EOneFrameReady ) |
|
227 { |
|
228 if( iStatus.Int() != KErrNone ) |
|
229 { |
|
230 iContext->iObserver->decoderError( iStatus.Int() ); |
|
231 |
|
232 iQueue.Remove(0); |
|
233 |
|
234 // decode next image |
|
235 Reset(); |
|
236 if( iQueue.Count() > 0 ) LoadNextImage(); |
|
237 } |
|
238 else |
|
239 { |
|
240 // pass bitmap's ownership to the observer |
|
241 iContext->iObserver->ImageReady(); |
|
242 |
|
243 iQueue.Remove(0); |
|
244 |
|
245 // decode next image |
|
246 Reset(); |
|
247 if( iQueue.Count() > 0 ) LoadNextImage(); |
|
248 } |
|
249 } |
|
250 } |
|
251 |
|
252 void CStaticImageDecoder::DoCancel() |
|
253 { |
|
254 // TODO: maybe we should delete all the intermediate data? |
|
255 } |
|
256 |
|
257 void CStaticImageDecoder::Reset() |
|
258 { |
|
259 // decoder reset |
|
260 iDecoder->Cancel(); |
|
261 iDecoder->Reset(); |
|
262 iState = EIdle; |
|
263 |
|
264 // context reset |
|
265 iContext = 0; |
|
266 } |
|
267 |
|
268 void CStaticImageDecoder::StopObserving( MAnimationDecoderObserver* aObserv ) |
|
269 { |
|
270 // fast check |
|
271 if( iQueue.Count() == 0 ) return; |
|
272 |
|
273 // is the image currently being decoded? |
|
274 if( iContext && iContext->iObserver == aObserv ) |
|
275 { |
|
276 // stop decoding |
|
277 if( IsActive() ) Cancel(); |
|
278 } |
|
279 |
|
280 // remove image data from the queue |
|
281 TInt idx = KErrNotFound; |
|
282 for( TInt i=0; i<iQueue.Count(); ++i ) |
|
283 if( iQueue[i]->iObserver == aObserv ) |
|
284 idx = i; |
|
285 if( idx == KErrNotFound ) return; |
|
286 |
|
287 CRawData* data = iQueue[idx]; |
|
288 iQueue.Remove( idx ); |
|
289 |
|
290 // decode next image |
|
291 Reset(); |
|
292 if( iQueue.Count() > 0 ) StartLoading(); |
|
293 } |
|
294 |
|
295 TDisplayMode CStaticImageDecoder::GetBestDisplayMode( TDisplayMode /*aMode*/ ) const |
|
296 { |
|
297 return EColor64K; |
|
298 } |
|
299 |
|
300 TBool CStaticImageDecoder::CheckBitmapMemoryConsumption( const TFrameInfo& aFrameInfo ) const |
|
301 { |
|
302 TMemoryInfoV1Buf info; |
|
303 UserHal::MemoryInfo( info ); |
|
304 TInt freeRamInBytes = 10*1024*1024; |
|
305 TInt dataSize = iContext->iDataPtr.Length(); |
|
306 if( UserHal::MemoryInfo( info ) == KErrNone ) |
|
307 freeRamInBytes = info().iFreeRamInBytes; |
|
308 TInt bitmapBytes = aFrameInfo.iOverallSizeInPixels.iWidth*aFrameInfo.iOverallSizeInPixels.iHeight*2; |
|
309 TInt freeRamAfterLoad = freeRamInBytes-bitmapBytes; |
|
310 if (freeRamAfterLoad < KFreeRamBitmapBlockLimit) |
|
311 { |
|
312 // block image decoding when we are below block limit |
|
313 return EFalse; |
|
314 } |
|
315 if (freeRamAfterLoad < KFreeRamBitmapCheckLimit) |
|
316 { |
|
317 // low on memory, do additional checks |
|
318 if (dataSize*KHighExpansionBitmapFactor < bitmapBytes && bitmapBytes>KLargeHighExpansionBitmapSize ) |
|
319 { |
|
320 // bitmaps with high expansion factor are commonly used as background image etc. and are of less importance |
|
321 // block large ones |
|
322 return EFalse; |
|
323 } |
|
324 if (freeRamInBytes*KMaxBitmapRamPercent/100 < bitmapBytes) |
|
325 { |
|
326 // block all bitmaps that would consume more than this percent of available memory |
|
327 return EFalse; |
|
328 } |
|
329 } |
|
330 return ETrue; |
|
331 } |
|
332 |
|
333 // END OF FILE |