|
1 // Copyright (c) 1997-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // Licensed under US Patent No 4,558,302 and foreign counterparts |
|
15 // |
|
16 // |
|
17 |
|
18 // CGifWriteCodec::TableEntry() is based on compress_byte() from wrgif.c |
|
19 // in the IJG V6 code, Copyright (C) 1991-1997, Thomas G. Lane. |
|
20 |
|
21 #include <fbs.h> |
|
22 #include "GIFcodec.h" |
|
23 #include "rawimageprocessor.h" |
|
24 |
|
25 const TInt KPass2StartLine = 4; |
|
26 const TInt KPass3StartLine = 2; |
|
27 const TInt KPass4StartLine = 1; |
|
28 const TInt KPass1YPosIncrement = 8; |
|
29 const TInt KPass2YPosIncrement = 8; |
|
30 const TInt KPass3YPosIncrement = 4; |
|
31 const TInt KPass4YPosIncrement = 2; |
|
32 const TInt KPass1LineRepeat = 7; |
|
33 const TInt KPass2LineRepeat = 3; |
|
34 const TInt KPass3LineRepeat = 1; |
|
35 const TInt KPass4LineRepeat = 0; |
|
36 const TUint KLZWLimit = 20000; |
|
37 const TInt KSetCommentsLimit = 64; |
|
38 const TUint16 KTranspColIdxNotPresent = 0xFFFF; |
|
39 |
|
40 |
|
41 // CGifReadCodec |
|
42 CGifReadCodec::CGifReadCodec(TRgb* aGlobalPalette,TInt aBackgroundColorIndex, TSize aScreenSize,TBool aFastDecode): |
|
43 iBackgroundColorIndex(aBackgroundColorIndex), |
|
44 iScreenSize(aScreenSize), |
|
45 iGlobalPalette(aGlobalPalette), |
|
46 iFastDecode(aFastDecode) |
|
47 { |
|
48 } |
|
49 |
|
50 CGifReadCodec::~CGifReadCodec() |
|
51 { |
|
52 iComment.ResetAndDestroy(); |
|
53 delete [] iPrefixIndex; |
|
54 delete [] iSuffixCode; |
|
55 delete [] iOutputString; |
|
56 delete [] i64KPalette; |
|
57 delete [] iPixelBuffer; |
|
58 delete [] iMaskBuffer; |
|
59 } |
|
60 |
|
61 CGifReadCodec* CGifReadCodec::NewL(TRgb* aGlobalPalette,TInt aBackgroundColorIndex, TSize aScreenSize,TBool aFastDecode) |
|
62 { |
|
63 CGifReadCodec* self = new(ELeave) CGifReadCodec(aGlobalPalette, aBackgroundColorIndex, aScreenSize, aFastDecode); |
|
64 CleanupStack::PushL(self); |
|
65 |
|
66 self->ConstructL(); |
|
67 |
|
68 CleanupStack::Pop(self); |
|
69 return self; |
|
70 } |
|
71 |
|
72 void CGifReadCodec::ConstructL() |
|
73 { |
|
74 CImageMaskProcessorReadCodec::ConstructL(); |
|
75 |
|
76 iPrefixIndex = new (ELeave) TInt16[KGifConversionTableSize +1]; |
|
77 iSuffixCode = new (ELeave) TUint8 [KGifConversionTableSize+1]; |
|
78 iOutputString = new (ELeave) TUint8 [KGifConversionTableSize]; |
|
79 iPPos = &Pos(); |
|
80 iPixelBuffer = NULL; |
|
81 iMaskBuffer = NULL; |
|
82 } |
|
83 |
|
84 void CGifReadCodec::InitFrameHeader(TFrameInfo& aFrameSettings, CFrameImageData& aFrameImageData) |
|
85 { |
|
86 ASSERT(aFrameSettings.CurrentFrameState() == TFrameInfo::EFrameInfoUninitialised); |
|
87 iFrameInfo = &aFrameSettings; |
|
88 iFrameData = &aFrameImageData; |
|
89 iFrameImageDesc = NULL; |
|
90 iFrameImageControl = NULL; |
|
91 iReadingOtherExtensionBlock=EFalse; |
|
92 iReadingCommentExtensionBlock = EFalse; |
|
93 iComment.ResetAndDestroy(); |
|
94 iCommentIndex = 0; |
|
95 iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingFrameHeader); |
|
96 } |
|
97 |
|
98 TFrameState CGifReadCodec::ProcessFrameHeaderL(TBufPtr8& aData) |
|
99 { |
|
100 iStartDataPtr = CONST_CAST(TUint8*,aData.Ptr()); |
|
101 iDataPtr = iStartDataPtr; |
|
102 iDataPtrLimit = iStartDataPtr + aData.Length(); |
|
103 |
|
104 if (iFrameInfo->CurrentFrameState() == TFrameInfo::EFrameInfoProcessingFrame) |
|
105 { |
|
106 do { |
|
107 //we need the block length (iDataPtr[0]+1) + the first byte of the next block |
|
108 //(used in the KGifTerminatorId evaluation below) |
|
109 if((iDataPtr + iDataPtr[0] + 2)<iDataPtrLimit) |
|
110 {// If there is enough data in the buffer to continue processing header info |
|
111 iFrameLZWInfo->iCompressedBytes += iDataPtr[0] + 1; |
|
112 |
|
113 // Shift the local buffer data pointer to point to the beginning of the next block |
|
114 iDataPtr += iDataPtr[0] + 1; |
|
115 } |
|
116 else |
|
117 { // Else if there is not enough data in the buffer shift the 'aData' pointer |
|
118 // to point to the degining of the unused data and return to the calling method |
|
119 // so that the buffer can be refilled. |
|
120 aData.Shift((iDataPtr - iStartDataPtr)); |
|
121 return EFrameIncomplete; |
|
122 } |
|
123 } while(iDataPtr[0]!=KGifFrameTerminatorId); //Continue until end of frame is reached |
|
124 |
|
125 if(iDataPtr[1] == KGifTerminatorId) |
|
126 { // End of Image File |
|
127 DoSaveCommentsL(); |
|
128 iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingComplete); |
|
129 return EFrameComplete; |
|
130 } |
|
131 else if ((iDataPtr[1] == KGifImageDescriptorId) || (iDataPtr[1] == KGifExtensionId)) |
|
132 { // End of Frame |
|
133 DoSaveCommentsL(); |
|
134 aData.Shift((iDataPtr - iStartDataPtr)+1); |
|
135 iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingComplete); |
|
136 return EFrameIncomplete; |
|
137 } |
|
138 else |
|
139 { // Unexpected end of data |
|
140 return EUnexpectedEndOfData; |
|
141 } |
|
142 } |
|
143 |
|
144 if (iFrameInfo->CurrentFrameState() == TFrameInfo::EFrameInfoProcessingFrameHeader) |
|
145 { |
|
146 TRAPD(err,DoProcessInfoL(iDataPtr,iDataPtrLimit)); |
|
147 if (err == KErrCompletion) |
|
148 { |
|
149 DoSaveCommentsL(); |
|
150 return EFrameComplete; // KGifTerminatorId found - no more images |
|
151 } |
|
152 |
|
153 if (err == KErrUnderflow) |
|
154 { |
|
155 TInt dataUsed = iDataPtr - iStartDataPtr; |
|
156 aData.Shift(dataUsed); |
|
157 TInt frameDataOffset = iFrameInfo->FrameDataOffset(); |
|
158 iFrameInfo->SetFrameDataOffset(frameDataOffset + dataUsed); |
|
159 return EFrameIncomplete; |
|
160 } |
|
161 |
|
162 if (err!=KErrNone) |
|
163 { |
|
164 User::LeaveIfError(err); // A real error occured |
|
165 } |
|
166 |
|
167 if (iFrameImageDesc == NULL) |
|
168 { |
|
169 User::Leave(KErrCorrupt); |
|
170 } |
|
171 |
|
172 TInt frameDataOffset = iFrameInfo->FrameDataOffset(); |
|
173 iFrameInfo->SetFrameDataOffset(frameDataOffset + (iDataPtr - iStartDataPtr)); |
|
174 aData.Shift(iDataPtr - iStartDataPtr); |
|
175 |
|
176 iFrameInfo->iFrameSizeInTwips.SetSize(0,0); |
|
177 iFrameInfo->iDelay = (iFrameImageControl) ? iFrameImageControl->iDelayTimeInCentiseconds * KGifCentiSecondsToMicroSeconds : 0; |
|
178 iFrameInfo->iFlags = TFrameInfo::EColor | TFrameInfo::ECanDither | TFrameInfo::EUsesFrameSizeInPixels; |
|
179 if (iFrameImageControl) |
|
180 { |
|
181 if (iFrameImageControl->iTransparentColorIndex != KErrNotFound) |
|
182 { |
|
183 iFrameInfo->iFlags |= TFrameInfo::ETransparencyPossible; |
|
184 if(iFrameImageDesc->iInterlaced) |
|
185 iFrameInfo->iFlags |= TFrameInfo::EPartialDecodeInvalid; |
|
186 } |
|
187 switch (iFrameImageControl->iDisposalMethod) |
|
188 { |
|
189 case TGifImageControl::ELeaveInPlace: |
|
190 iFrameInfo->iFlags |= TFrameInfo::ELeaveInPlace; |
|
191 break; |
|
192 case TGifImageControl::ERestoreToBackground: |
|
193 iFrameInfo->iFlags |= TFrameInfo::ERestoreToBackground; |
|
194 break; |
|
195 case TGifImageControl::ERestoreToPrevious: |
|
196 iFrameInfo->iFlags |= TFrameInfo::ERestoreToPrevious; |
|
197 break; |
|
198 case ENone: |
|
199 default: |
|
200 break; |
|
201 }; |
|
202 } |
|
203 |
|
204 iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingFrame); |
|
205 } |
|
206 |
|
207 return EFrameIncomplete; |
|
208 } |
|
209 |
|
210 TFrameState CGifReadCodec::ProcessFrameL(TBufPtr8& aSrc) |
|
211 { |
|
212 if ((iFrameSize.iWidth == 0) || (iFrameSize.iHeight == 0)) |
|
213 return EFrameComplete; |
|
214 |
|
215 iPixRead = 0; |
|
216 |
|
217 while (aSrc.Length() > 2 && !iComplete) |
|
218 { |
|
219 iDataPtr = aSrc.Ptr(); |
|
220 const TInt dataSize = iDataPtr[0]; |
|
221 iDataPtr++; |
|
222 if (dataSize == 0 || dataSize >= aSrc.Length()) |
|
223 { |
|
224 break; |
|
225 } |
|
226 iDataPtrLimit = iDataPtr + dataSize; |
|
227 DoProcessDataL(); |
|
228 |
|
229 aSrc.Shift(dataSize + 1); |
|
230 } |
|
231 |
|
232 TPoint& pos = Pos(); |
|
233 pos = *iPPos; |
|
234 FlushPixBuffer(iLatestPixSize); |
|
235 |
|
236 if (iFastAccessMode) |
|
237 { |
|
238 const TInt frameWidth = iFrameSize.iWidth; |
|
239 TInt rowPixels = Min(frameWidth - pos.iX, iPixRead); |
|
240 pos.iX += rowPixels; |
|
241 iPixRead -= rowPixels; |
|
242 if (pos.iX == frameWidth) |
|
243 { |
|
244 TInt rowCount = iPixRead/frameWidth+1; |
|
245 pos.iY += rowCount; |
|
246 iPixRead -= (rowCount-1) * frameWidth; |
|
247 pos.iX = iPixRead; |
|
248 } |
|
249 iPixRead = 0; |
|
250 } |
|
251 |
|
252 if (pos.iY >= (iFrameSize.iHeight + iFrameOffset.iY) || iComplete) |
|
253 { |
|
254 ImageProcessor()->FlushPixels(); |
|
255 CImageProcessor*const maskProc = MaskProcessor(); |
|
256 if (maskProc) |
|
257 maskProc->FlushPixels(); |
|
258 pos = iFrameOffset; |
|
259 //in case of iFast64kMode == true, palette is of type T64KPixel |
|
260 if(iGifImageControl && iGifImageControl->iTransparentColorIndex != KErrNotFound && !iFast64kMode) |
|
261 { |
|
262 // reset the transparency index |
|
263 if (iTranspColIdx != KTranspColIdxNotPresent) |
|
264 { |
|
265 const_cast<TRgb*>(&iPalette[iTranspColIdx])->SetAlpha(0xFF); |
|
266 } |
|
267 } |
|
268 |
|
269 return EFrameComplete; |
|
270 } |
|
271 |
|
272 return EFrameIncomplete; |
|
273 } |
|
274 |
|
275 void CGifReadCodec::InitFrameL(TFrameInfo& aFrameInfo, CFrameImageData& aFrameImageData, TBool aDisableErrorDiffusion, CFbsBitmap& aDestination, CFbsBitmap* aDestinationMask) |
|
276 { |
|
277 iFrameInfo = &aFrameInfo; |
|
278 iFrameData = &aFrameImageData; |
|
279 |
|
280 //Make the first frame's size equal to the image size |
|
281 if(CurrentFrame()==0) |
|
282 { |
|
283 iFrameSize = iFirstFrameSize; |
|
284 iFrameOffset = iFirstFrameCoords.iTl; |
|
285 } |
|
286 else |
|
287 { |
|
288 iFrameSize = iFrameInfo->iOverallSizeInPixels; |
|
289 iFrameOffset = TPoint(0,0); |
|
290 } |
|
291 iFrameCoords = iFrameInfo->iFrameCoordsInPixels; |
|
292 |
|
293 // If the width or height is zero return. A similar test in ProcessFrameL() will complete the image |
|
294 if ((iFrameSize.iWidth == 0) || (iFrameSize.iHeight == 0)) |
|
295 { |
|
296 return; |
|
297 } |
|
298 |
|
299 if(iPixelBuffer == NULL || CurrentFrame() == 0) |
|
300 { |
|
301 iPixBufferSize = iFrameSize.iWidth * 8;//allocate buffer for first 8 lines |
|
302 if (iPixBufferSize > KPixelBufMaxSize) |
|
303 { |
|
304 iPixBufferSize = KPixelBufMaxSize; |
|
305 } |
|
306 if (aDestinationMask != NULL) |
|
307 { |
|
308 iPixBufferSize /= 2;//reserve space for mask |
|
309 } |
|
310 delete[] iPixelBuffer; |
|
311 iPixelBuffer = new (ELeave) TUint32 [iPixBufferSize]; |
|
312 } |
|
313 |
|
314 // Extract the image descriptor and control blocks. |
|
315 iGifImageDesc = NULL; |
|
316 iGifLZWInfo = NULL; |
|
317 iGifImageControl = NULL; |
|
318 iGifColorTable = NULL; |
|
319 |
|
320 TInt frameDataCount = iFrameData->FrameDataCount(); |
|
321 for (TInt frameDataIndex = 0 ; frameDataIndex<frameDataCount ; frameDataIndex++) |
|
322 { |
|
323 TFrameDataBlock* frameData = iFrameData->GetFrameData(frameDataIndex); |
|
324 if (frameData->DataType() == KGIFImageDescriptorUid) |
|
325 { |
|
326 iGifImageDesc = STATIC_CAST(TGifImageDescriptor*, frameData); |
|
327 } |
|
328 else if(frameData->DataType() == KGIFColorTableUid) |
|
329 { |
|
330 iGifColorTable = STATIC_CAST(TGifColorTable*, frameData); |
|
331 } |
|
332 else if(frameData->DataType() == KGIFLZWInfoUid) |
|
333 { |
|
334 iGifLZWInfo = STATIC_CAST(TGifLZWInfo*, frameData); |
|
335 } |
|
336 else if(frameData->DataType() == KGIFImageControlUid) |
|
337 { |
|
338 iGifImageControl = STATIC_CAST(TGifImageControl*, frameData); |
|
339 } |
|
340 } |
|
341 iTranspColIdx = (iGifImageControl) ? iGifImageControl->iTransparentColorIndex : KTranspColIdxNotPresent; |
|
342 |
|
343 // Set decode info |
|
344 iPass = 1; |
|
345 iYPosIncrement = KPass1YPosIncrement; |
|
346 iBitBuffer = 0; |
|
347 iBitBuffSize = 0; |
|
348 iFirstChar = 0; |
|
349 iPreviousCode = -1; |
|
350 iComplete = EFalse; |
|
351 |
|
352 ASSERT(iGifImageDesc); |
|
353 ASSERT(iGifLZWInfo); |
|
354 |
|
355 // Set palette to use |
|
356 if(iGifImageDesc && iGifImageDesc->iLocalColorMap) |
|
357 { |
|
358 //Use of a frame palette was requested but was not found |
|
359 if(iGifColorTable == NULL) |
|
360 { |
|
361 User::Leave(KErrCorrupt); |
|
362 } |
|
363 iPalette = iGifColorTable->iPalette; |
|
364 } |
|
365 else |
|
366 { |
|
367 iPalette = iGlobalPalette; |
|
368 } |
|
369 |
|
370 // Set table.Check for iClearCode value between 0 and 4096. |
|
371 iClearCode = (1 << (iGifLZWInfo->iInitialCodeLength - 1)); |
|
372 if((iClearCode < 0) || (iClearCode > KGifConversionTableSize)) |
|
373 { |
|
374 User::Leave(KErrCorrupt); |
|
375 } |
|
376 iEoiCode = iClearCode + 1; |
|
377 for (TInt singleByteCodes = 0; singleByteCodes < iClearCode; singleByteCodes++) |
|
378 { |
|
379 iSuffixCode[singleByteCodes] = STATIC_CAST(TUint8,singleByteCodes); |
|
380 } |
|
381 |
|
382 TInt prefixLength = (KGifConversionTableSize + 1) * sizeof(TInt16); |
|
383 Mem::Fill(iPrefixIndex, prefixLength, TChar(static_cast<TUint>(-1))); |
|
384 |
|
385 iNextFree = ResetTableL(); |
|
386 |
|
387 iReductionFactor = 0; |
|
388 |
|
389 CImageProcessor* imageProc = NULL; |
|
390 |
|
391 const TSize destinationSize(aDestination.SizeInPixels()); |
|
392 |
|
393 if(iUseFrameSizeInPixels) |
|
394 { |
|
395 iReductionFactor = ReductionFactor(iFrameInfo->iFrameSizeInPixels,destinationSize); |
|
396 } |
|
397 else |
|
398 { |
|
399 iReductionFactor = ReductionFactor(iFrameInfo->iOverallSizeInPixels,destinationSize); |
|
400 } |
|
401 |
|
402 const TDisplayMode destMode = aDestination.DisplayMode(); |
|
403 |
|
404 iFast64kMode = (iFastDecode && |
|
405 destMode == EColor64K && |
|
406 iReductionFactor == 0 && |
|
407 !iGifImageDesc->iInterlaced |
|
408 ); |
|
409 |
|
410 if (iFast64kMode && i64KPalette==NULL) |
|
411 { |
|
412 i64KPalette = new (ELeave) T64KPixel[KGifColorTableMaxEntries]; |
|
413 Mem::FillZ(i64KPalette, KGifColorTableMaxEntries*sizeof(T64KPixel) ); |
|
414 } |
|
415 |
|
416 |
|
417 if (!iGifImageDesc->iInterlaced && iReductionFactor==0 && |
|
418 (iFast64kMode || destMode == EColor16M || destMode == EColor16MU || destMode == EColor16MA) ) |
|
419 { |
|
420 if (destMode == EColor16M) |
|
421 { |
|
422 imageProc = CRawImageUtilProcessor::NewL(); |
|
423 } |
|
424 else |
|
425 { |
|
426 imageProc = CRawImageProcessor::NewL(); |
|
427 } |
|
428 } |
|
429 |
|
430 if (iFast64kMode) |
|
431 { |
|
432 // calculate fast 64K conversion palette |
|
433 TInt idx = 0; |
|
434 do |
|
435 { |
|
436 i64KPalette[idx] = iPalette[idx]._Color64K(); |
|
437 } |
|
438 while (++idx < KGifColorTableMaxEntries); |
|
439 } |
|
440 |
|
441 if (imageProc == NULL) |
|
442 { |
|
443 imageProc = ImageProcessorUtility::NewImageProcessorL(aDestination, iReductionFactor, iFast64kMode? EColor64K:ERgb, aDisableErrorDiffusion); |
|
444 } |
|
445 |
|
446 SetImageProcessor(imageProc); |
|
447 iImgProc = imageProc; |
|
448 |
|
449 iPositionOffset = TPoint(iFrameCoords.iTl.iX >> iReductionFactor,iFrameCoords.iTl.iY >> iReductionFactor); |
|
450 |
|
451 TRect imageRegion(iPositionOffset + iFrameOffset,iPositionOffset + iFrameOffset + iFrameSize); |
|
452 imageProc->PrepareL(aDestination,imageRegion); |
|
453 imageProc->SetPos(iPositionOffset + iFrameOffset); |
|
454 SetPos(iFrameOffset); |
|
455 |
|
456 TInt lineLength = (TDisplayModeUtils::NumDisplayModeBitsPerPixel( aDestination.DisplayMode() ) * aDestination.SizeInPixels().iWidth) >> 3; |
|
457 |
|
458 |
|
459 iFastAccessMode = (iReductionFactor == 0 && |
|
460 !iGifImageDesc->iInterlaced && |
|
461 imageRegion.Size() == aDestination.SizeInPixels() && |
|
462 lineLength == aDestination.ScanLineLength(aDestination.SizeInPixels().iWidth, aDestination.DisplayMode()) |
|
463 ); // for direct access to bitmap buffer line length must be aligned to 4 bytes boundary |
|
464 |
|
465 |
|
466 if (iGifImageDesc->iInterlaced) |
|
467 { |
|
468 imageProc->SetYPosIncrement(iYPosIncrement); |
|
469 if(!iGifImageControl || (iGifImageControl->iTransparentColorIndex==KErrNotFound)) |
|
470 { |
|
471 imageProc->SetLineRepeat(KPass1LineRepeat); |
|
472 } |
|
473 } |
|
474 |
|
475 CImageProcessor* maskProc = NULL; |
|
476 SetMaskProcessor(NULL); |
|
477 iMaskProc = NULL; |
|
478 |
|
479 if(aDestinationMask != NULL) |
|
480 { |
|
481 |
|
482 ASSERT(aDestinationMask->SizeInPixels() == aDestination.SizeInPixels()); |
|
483 // this should be trapped at CImageDecoder::Convert() |
|
484 |
|
485 if (iGifImageControl && (iGifImageControl->iTransparentColorIndex != KErrNotFound) && aDestinationMask->Handle()) |
|
486 { |
|
487 ASSERT(MaskProcessor()==NULL); |
|
488 |
|
489 if (iMaskBuffer == NULL || CurrentFrame() == 0) |
|
490 { |
|
491 delete [] iMaskBuffer; |
|
492 iMaskBuffer = new (ELeave) TUint32 [iPixBufferSize]; |
|
493 } |
|
494 |
|
495 iOpaqueMask = 0xFF;// default for 256 mask |
|
496 //Create a mask processor and disable error diffusion |
|
497 if (iGifImageDesc->iInterlaced || iReductionFactor > 0) |
|
498 { |
|
499 maskProc = ImageProcessorUtility::NewImageProcessorL(*aDestinationMask, iReductionFactor, EGray2, ETrue); |
|
500 } |
|
501 else |
|
502 { |
|
503 // use optimized processor for non interlaced mask |
|
504 maskProc = CRawImageUtilProcessor::NewL(); |
|
505 if(aDestinationMask->DisplayMode() == EGray2) |
|
506 { |
|
507 iOpaqueMask = 1; |
|
508 } |
|
509 } |
|
510 SetMaskProcessor(maskProc); |
|
511 iMaskProc = maskProc; |
|
512 maskProc->PrepareL(*aDestinationMask,imageRegion); |
|
513 if (iGifImageDesc->iInterlaced) |
|
514 { |
|
515 maskProc->SetYPosIncrement(iYPosIncrement); |
|
516 } |
|
517 // fill mask with black, so by default unknown parts don't draw |
|
518 ClearBitmapL(*aDestinationMask,KRgbBlack); |
|
519 maskProc->SetPos(iPositionOffset + iFrameOffset); |
|
520 |
|
521 lineLength = (TDisplayModeUtils::NumDisplayModeBitsPerPixel( aDestinationMask->DisplayMode() ) * aDestinationMask->SizeInPixels().iWidth) >> 3; |
|
522 |
|
523 iFastAccessMode = iFastAccessMode && lineLength == aDestinationMask->ScanLineLength(aDestinationMask->SizeInPixels().iWidth, aDestinationMask->DisplayMode()); // for direct access |
|
524 // to bitmap buffer line length must be aligned to 4 bytes boundary |
|
525 |
|
526 } |
|
527 } |
|
528 else |
|
529 { |
|
530 if(destMode == EColor16MA && iGifImageControl && iGifImageControl->iTransparentColorIndex != KErrNotFound) |
|
531 { |
|
532 if (iTranspColIdx != KTranspColIdxNotPresent) |
|
533 { |
|
534 const_cast<TRgb*>(&iPalette[iTranspColIdx])->SetAlpha(0); |
|
535 } |
|
536 } |
|
537 delete [] iMaskBuffer; |
|
538 iMaskBuffer = NULL; |
|
539 } |
|
540 |
|
541 if ( iGifImageControl && (iGifImageControl->iDisposalMethod == TGifImageControl::ERestoreToBackground) || |
|
542 maskProc==NULL) |
|
543 { |
|
544 // Clear to background colour if requested or if we have no mask, so that |
|
545 // on streamed partial decodes are background |
|
546 ClearBitmapL(aDestination,iFrameInfo->iBackgroundColor); |
|
547 } |
|
548 |
|
549 } |
|
550 |
|
551 void CGifReadCodec::SetUseFrameSizeInPixels(TBool aUseFrameSizeInPixels) |
|
552 { |
|
553 iUseFrameSizeInPixels = aUseFrameSizeInPixels; |
|
554 } |
|
555 |
|
556 |
|
557 void CGifReadCodec::DoProcessInfoL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit) |
|
558 { |
|
559 if(!iReadingExtensionBlock) |
|
560 { |
|
561 iBlockId = aDataPtr[0]; |
|
562 while (!iBlockId) |
|
563 { |
|
564 aDataPtr++; |
|
565 if (aDataPtr >= aDataPtrLimit) |
|
566 { |
|
567 User::Leave(KErrUnderflow); |
|
568 } |
|
569 |
|
570 iBlockId = aDataPtr[0]; |
|
571 } |
|
572 } |
|
573 |
|
574 while (iBlockId == KGifExtensionId || iReadingExtensionBlock) |
|
575 { |
|
576 iReadingExtensionBlock = ETrue; |
|
577 DoProcessExtensionL(aDataPtr,aDataPtrLimit); |
|
578 iReadingExtensionBlock = EFalse; |
|
579 |
|
580 // we skip empty blocks here |
|
581 while( aDataPtr < aDataPtrLimit && (iBlockId = *aDataPtr) == 0) |
|
582 { |
|
583 ++aDataPtr; |
|
584 } |
|
585 |
|
586 if (aDataPtr >= aDataPtrLimit) |
|
587 { |
|
588 User::Leave(KErrUnderflow); |
|
589 } |
|
590 } |
|
591 |
|
592 if (iBlockId == KGifImageDescriptorId) |
|
593 { |
|
594 DoProcessImageDescriptorL(aDataPtr,aDataPtrLimit); |
|
595 } |
|
596 else if (iBlockId == KGifTerminatorId) |
|
597 { |
|
598 User::Leave(KErrCompletion); |
|
599 } |
|
600 else |
|
601 { |
|
602 User::Leave(KErrCorrupt); |
|
603 } |
|
604 } |
|
605 |
|
606 void CGifReadCodec::DoProcessExtensionL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit) |
|
607 { |
|
608 // Make sure we leave a block id for DoProcessInfoL() |
|
609 aDataPtrLimit--; |
|
610 |
|
611 TInt blockLength; |
|
612 TInt dataPtrVal; |
|
613 const TUint8* dataPtr = aDataPtr; |
|
614 if (iReadingCommentExtensionBlock) |
|
615 { |
|
616 if ((aDataPtr + 2) > aDataPtrLimit) |
|
617 { |
|
618 User::Leave(KErrUnderflow); |
|
619 } |
|
620 |
|
621 dataPtrVal = KGifCommentExtensionId; |
|
622 blockLength = aDataPtr[0]; |
|
623 |
|
624 if ((aDataPtr + blockLength + 2) > aDataPtrLimit) |
|
625 { |
|
626 User::Leave(KErrUnderflow); |
|
627 } |
|
628 } |
|
629 else if (iReadingOtherExtensionBlock) |
|
630 { |
|
631 if ((aDataPtr + 2) > aDataPtrLimit) |
|
632 { |
|
633 User::Leave(KErrUnderflow); |
|
634 } |
|
635 |
|
636 dataPtrVal = KGifApplicationExtensionId; |
|
637 blockLength = aDataPtr[0]; |
|
638 |
|
639 if ((aDataPtr + blockLength + 2) > aDataPtrLimit) |
|
640 { |
|
641 User::Leave(KErrUnderflow); |
|
642 } |
|
643 } |
|
644 else |
|
645 { |
|
646 // we start reading block, there should be at least 3 bytes - id, ext. id, length |
|
647 if ((aDataPtr + KGifExtBlkHeaderSize) > aDataPtrLimit) |
|
648 { |
|
649 User::Leave(KErrUnderflow); |
|
650 } |
|
651 |
|
652 ++dataPtr; |
|
653 dataPtrVal = *dataPtr; // get the ext. ID |
|
654 ++dataPtr; |
|
655 blockLength = *dataPtr; |
|
656 |
|
657 // ensure that we've got all the block data in the buffer |
|
658 // that's possible since max. block length for GIF is 255 bytes |
|
659 if ((dataPtr + blockLength) > aDataPtrLimit) |
|
660 { |
|
661 User::Leave(KErrUnderflow); |
|
662 } |
|
663 } |
|
664 |
|
665 switch (dataPtrVal) |
|
666 { |
|
667 case KGifApplicationExtensionId: |
|
668 case KGifPlainTextExtensionId: |
|
669 { |
|
670 aDataPtr = dataPtr; |
|
671 iReadingOtherExtensionBlock = ETrue; |
|
672 |
|
673 while (blockLength != 0) |
|
674 { |
|
675 if (dataPtr + blockLength >= aDataPtrLimit) |
|
676 { |
|
677 aDataPtr = dataPtr; |
|
678 User::Leave(KErrUnderflow); |
|
679 } |
|
680 if (iReadingLoopIterationExtensionBlock) |
|
681 { |
|
682 TInt loopIterations = dataPtr[2] + (dataPtr[3] << 8); |
|
683 TGifLoopIterations* iterationsDataBlock = new(ELeave) TGifLoopIterations; |
|
684 CleanupStack::PushL(iterationsDataBlock); |
|
685 iterationsDataBlock->iLoopIterations = loopIterations; |
|
686 User::LeaveIfError(iFrameData->AppendImageData(iterationsDataBlock)); |
|
687 CleanupStack::Pop(iterationsDataBlock); |
|
688 iReadingLoopIterationExtensionBlock = EFalse; |
|
689 } |
|
690 else if (blockLength == KAppIdBlockLength) |
|
691 { |
|
692 TPtrC8 appId(dataPtr + 1, KAppIdLength); |
|
693 TPtrC8 appAuthenticCode(dataPtr + 1 + KAppIdLength, KAppAuthenticCodeLength); |
|
694 if ((appId == KAppIdNetscape) && (appAuthenticCode == KAppAuthenticCode2_0)) |
|
695 { |
|
696 iReadingLoopIterationExtensionBlock = ETrue; |
|
697 } |
|
698 } |
|
699 |
|
700 dataPtr += blockLength + 1; |
|
701 if (dataPtr >= aDataPtrLimit) |
|
702 { |
|
703 User::Leave(KErrUnderflow); |
|
704 } |
|
705 blockLength = dataPtr[0]; |
|
706 } |
|
707 |
|
708 iReadingOtherExtensionBlock = EFalse; |
|
709 aDataPtr = dataPtr; |
|
710 } |
|
711 break; |
|
712 |
|
713 case KGifCommentExtensionId: |
|
714 { // The problem here is a Block header is expected when were actually half way through the block!!!!!!! |
|
715 if (!iReadingCommentExtensionBlock) |
|
716 { |
|
717 if(iCommentIndex < KSetCommentsLimit) |
|
718 { |
|
719 ASSERT(iComment.Count()==iCommentIndex); |
|
720 HBufC8* comment = HBufC8::NewL(blockLength); |
|
721 CleanupStack::PushL(comment); |
|
722 User::LeaveIfError(iComment.Append(comment)); |
|
723 CleanupStack::Pop(comment); |
|
724 } |
|
725 iReadingCommentExtensionBlock = ETrue; |
|
726 } |
|
727 else |
|
728 { |
|
729 ASSERT(iComment.Count()==iCommentIndex+1); |
|
730 HBufC8* comment = iComment[iCommentIndex]; |
|
731 iComment[iCommentIndex] = comment->ReAllocL(comment->Length()+blockLength); |
|
732 } |
|
733 |
|
734 TPtr8 commentPtr(NULL,0); |
|
735 if(iCommentIndex < KSetCommentsLimit) |
|
736 { |
|
737 commentPtr.Set( iComment[iCommentIndex]->Des() ); |
|
738 } |
|
739 while (blockLength != 0) |
|
740 { |
|
741 // Append the block. |
|
742 TPtrC8 newBlock(&dataPtr[1], blockLength); |
|
743 if(iCommentIndex < KSetCommentsLimit) |
|
744 { |
|
745 commentPtr.Append(newBlock); |
|
746 } |
|
747 |
|
748 // Calculate the new block/comment length. |
|
749 dataPtr += blockLength + 1; |
|
750 if(dataPtr > aDataPtrLimit) |
|
751 { |
|
752 aDataPtr = dataPtr; |
|
753 User::Leave(KErrUnderflow); |
|
754 } |
|
755 blockLength = dataPtr[0]; |
|
756 |
|
757 // Leave if the data for the new block is not present. |
|
758 if (blockLength && ((dataPtr+blockLength+1) >= aDataPtrLimit)) |
|
759 { |
|
760 aDataPtr = dataPtr; |
|
761 User::Leave(KErrUnderflow); |
|
762 } |
|
763 |
|
764 // Re-allocate the comment, if there is another block. |
|
765 if (blockLength != 0&&(iCommentIndex < KSetCommentsLimit)) |
|
766 { |
|
767 HBufC8* comment = iComment[iCommentIndex]; |
|
768 iComment[iCommentIndex] = comment->ReAllocL(comment->Length()+blockLength); |
|
769 commentPtr.Set(iComment[iCommentIndex]->Des()); |
|
770 } |
|
771 } |
|
772 |
|
773 iCommentIndex++; |
|
774 |
|
775 aDataPtr = dataPtr; |
|
776 } |
|
777 |
|
778 iReadingCommentExtensionBlock = EFalse; |
|
779 break; |
|
780 |
|
781 case KGifGraphicControlExtensionId: |
|
782 if (aDataPtr + blockLength + KGifExtBlkHeaderSize + 1 >= aDataPtrLimit) |
|
783 { |
|
784 User::Leave(KErrUnderflow); |
|
785 } |
|
786 |
|
787 aDataPtr += (KGifExtBlkHeaderSize + blockLength); |
|
788 // we ignore malformed extension block |
|
789 if (blockLength < KMinGifGraphicControlBlkLen) |
|
790 { |
|
791 break; |
|
792 } |
|
793 |
|
794 ++dataPtr; //go into block data |
|
795 TUint8 flags = *dataPtr++; |
|
796 |
|
797 TGifImageControl* gifImageControl = new(ELeave) TGifImageControl; |
|
798 CleanupStack::PushL(gifImageControl); |
|
799 |
|
800 gifImageControl->iDelayTimeInCentiseconds = *dataPtr++; |
|
801 gifImageControl->iDelayTimeInCentiseconds |= (*dataPtr++) << 8; |
|
802 if (flags & 0x01) |
|
803 gifImageControl->iTransparentColorIndex = *dataPtr++; |
|
804 else |
|
805 { |
|
806 gifImageControl->iTransparentColorIndex = KErrNotFound; |
|
807 dataPtr++; |
|
808 } |
|
809 |
|
810 gifImageControl->iDisposalMethod = TGifImageControl::TDisposalMethod((flags & 0x1c) >> 2); |
|
811 gifImageControl->iUserInputFlag = (flags & 0x02) != 0; |
|
812 |
|
813 User::LeaveIfError(iFrameData->AppendFrameData(gifImageControl)); |
|
814 |
|
815 CleanupStack::Pop(); // gifImageControl |
|
816 iFrameImageControl = gifImageControl; |
|
817 break; |
|
818 } |
|
819 |
|
820 if (aDataPtr[0]!=0) |
|
821 { |
|
822 User::Leave(KErrCorrupt); |
|
823 } |
|
824 |
|
825 aDataPtr++; // Absorb trailing zero |
|
826 } |
|
827 |
|
828 void CGifReadCodec::DoProcessImageDescriptorL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit) |
|
829 { |
|
830 if (iFrameImageDesc != NULL) |
|
831 { |
|
832 User::Leave(KErrCorrupt); |
|
833 } |
|
834 |
|
835 if (aDataPtr + KGifImageInformationSize + KGifInitialCodeLengthSize > aDataPtrLimit) |
|
836 { |
|
837 User::Leave(KErrUnderflow); |
|
838 } |
|
839 |
|
840 TPoint topLeftCorner; |
|
841 topLeftCorner.iX = aDataPtr[1] + (aDataPtr[2] << 8); |
|
842 topLeftCorner.iY = aDataPtr[3] + (aDataPtr[4] << 8); |
|
843 |
|
844 TSize imageSize; |
|
845 imageSize.iWidth = aDataPtr[5] + (aDataPtr[6] << 8); |
|
846 imageSize.iHeight = aDataPtr[7] + (aDataPtr[8] << 8); |
|
847 |
|
848 //Save the first frame's real dimensions, but use the complete |
|
849 //image size for the first frame |
|
850 if(CurrentFrame()==0) |
|
851 { |
|
852 iFirstFrameSize = imageSize; |
|
853 // TPoint (0,0) means that iFrameOffset is set to 0 as all frames are now treated the same for offset data |
|
854 // But real (firstframe) imageSize is saved here as clients use first frames overallsizeinPixels |
|
855 iFirstFrameCoords.SetRect(TPoint(0,0),imageSize); |
|
856 const TSize screenSize(iScreenSize); |
|
857 // there is leniency here for rogue gifs with first frames bigger than global screen size from gif header |
|
858 TSize lenientImageSize = TSize(Max(imageSize.iWidth,screenSize.iWidth), |
|
859 Max(imageSize.iHeight,screenSize.iHeight)); |
|
860 iFrameInfo->iOverallSizeInPixels = lenientImageSize; |
|
861 //if the first frame has an offset, put it in the frame info |
|
862 // this will be translated into the iPositionOffset variable |
|
863 iFrameInfo->iFrameCoordsInPixels.SetRect(topLeftCorner,lenientImageSize); |
|
864 } |
|
865 else |
|
866 { |
|
867 iFrameInfo->iOverallSizeInPixels = imageSize; |
|
868 iFrameInfo->iFrameCoordsInPixels.SetRect(topLeftCorner,imageSize); |
|
869 } |
|
870 iFrameInfo->iFrameSizeInPixels = imageSize; |
|
871 |
|
872 //adjust iFrameCoordsInPixels for proper behaviour |
|
873 if(iUseFrameSizeInPixels) |
|
874 { |
|
875 iFrameInfo->iFrameCoordsInPixels.SetSize(iFrameInfo->iFrameSizeInPixels); |
|
876 } |
|
877 |
|
878 TUint8 flags = aDataPtr[9]; |
|
879 |
|
880 TGifImageDescriptor* gifImageDesc = new(ELeave) TGifImageDescriptor; |
|
881 CleanupStack::PushL(gifImageDesc); |
|
882 |
|
883 gifImageDesc->iLocalColorMap = flags & 0x80; |
|
884 gifImageDesc->iInterlaced = flags & 0x40; |
|
885 gifImageDesc->iSortedLocalMap = flags & 0x20; |
|
886 if (gifImageDesc->iLocalColorMap) |
|
887 { |
|
888 iFrameInfo->iBitsPerPixel = (flags & 0x07) + 1; |
|
889 TInt paletteEntries = 1 << iFrameInfo->iBitsPerPixel; |
|
890 TInt paletteBytes = paletteEntries * KGifPaletteEntrySize; |
|
891 if (aDataPtr + KGifImageInformationSize + paletteBytes + KGifInitialCodeLengthSize > aDataPtrLimit) |
|
892 { |
|
893 User::Leave(KErrUnderflow); |
|
894 } |
|
895 |
|
896 aDataPtr += KGifImageInformationSize; |
|
897 const TUint8* paletteLimit = aDataPtr + paletteBytes; |
|
898 |
|
899 TGifColorTable* gifColorTable = new(ELeave) TGifColorTable; |
|
900 CleanupStack::PushL(gifColorTable); |
|
901 TRgb* rgbPtr = gifColorTable->iPalette; |
|
902 while (aDataPtr < paletteLimit) |
|
903 { |
|
904 *rgbPtr++ = TRgb(aDataPtr[0],aDataPtr[1],aDataPtr[2]); |
|
905 aDataPtr += 3; |
|
906 } |
|
907 User::LeaveIfError(iFrameData->AppendFrameData(gifColorTable)); |
|
908 CleanupStack::Pop(gifColorTable); |
|
909 } |
|
910 else |
|
911 { |
|
912 aDataPtr += KGifImageInformationSize; |
|
913 } |
|
914 |
|
915 User::LeaveIfError(iFrameData->AppendFrameData(gifImageDesc)); |
|
916 CleanupStack::Pop(gifImageDesc); |
|
917 |
|
918 TGifLZWInfo* gifLZWInfo = new(ELeave) TGifLZWInfo; |
|
919 CleanupStack::PushL(gifLZWInfo); |
|
920 |
|
921 gifLZWInfo->iInitialCodeLength = aDataPtr[0] + 1; |
|
922 User::LeaveIfError(iFrameData->AppendFrameData(gifLZWInfo)); |
|
923 aDataPtr++; |
|
924 |
|
925 CleanupStack::Pop(gifLZWInfo); |
|
926 iFrameImageDesc = gifImageDesc; |
|
927 iFrameLZWInfo = gifLZWInfo; |
|
928 } |
|
929 |
|
930 void CGifReadCodec::DoSaveCommentsL() |
|
931 { |
|
932 //If we have comments add it |
|
933 TInt noOfComments = iComment.Count(); |
|
934 //If we have more than KSetCommentsLimit comments just restrict them as KSetCommentsLimit |
|
935 if (noOfComments > KSetCommentsLimit) |
|
936 { |
|
937 noOfComments = KSetCommentsLimit; |
|
938 } |
|
939 for(TInt commentNo=0; commentNo<noOfComments; commentNo++) |
|
940 { |
|
941 TGifComment* comment = new(ELeave) TGifComment; |
|
942 CleanupStack::PushL(comment); |
|
943 comment->iComment = iComment[0]; |
|
944 User::LeaveIfError(iFrameData->AppendImageData(comment)); |
|
945 CleanupStack::Pop(); // comment |
|
946 |
|
947 User::LeaveIfError(iFrameData->AppendImageBuffer(iComment[0])); |
|
948 iComment.Remove(0); |
|
949 } |
|
950 } |
|
951 |
|
952 void CGifReadCodec::DoProcessDataL() |
|
953 { |
|
954 TInt nextFree = iNextFree; |
|
955 TInt prevCode = iPreviousCode; |
|
956 TBitBuffer bBuf = iBitBuffer; |
|
957 TInt bbSize = iBitBuffSize; |
|
958 TInt curCodeLen = iCurrentCodeLength; |
|
959 iLatestPixSize = iFast64kMode ? sizeof(TUint16) : sizeof (TRgb); |
|
960 |
|
961 do |
|
962 { |
|
963 TInt code = NextCode(bBuf, bbSize, curCodeLen); |
|
964 if (code == KErrNotFound) |
|
965 { |
|
966 break; // not enought data in the buffer to get the code |
|
967 } |
|
968 |
|
969 if (iTableReset) |
|
970 { |
|
971 iTableReset = EFalse; |
|
972 WriteCodeL(code); |
|
973 prevCode = code; |
|
974 } |
|
975 else if (code == iClearCode) |
|
976 { |
|
977 nextFree = ResetTableL(); |
|
978 curCodeLen = iCurrentCodeLength; |
|
979 iTableReset = ETrue; |
|
980 } |
|
981 else if (code == iEoiCode) |
|
982 { |
|
983 iComplete = ETrue; |
|
984 break; |
|
985 } |
|
986 else |
|
987 { |
|
988 if (prevCode >= nextFree) |
|
989 { |
|
990 User::Leave(KErrCorrupt); |
|
991 } |
|
992 |
|
993 if ( (nextFree == (1 << curCodeLen)-1) && curCodeLen < KGifMaxBits) |
|
994 { |
|
995 ++curCodeLen; |
|
996 } |
|
997 |
|
998 if (code < nextFree) |
|
999 { |
|
1000 WriteCodeL(code); |
|
1001 if (prevCode != -1) |
|
1002 { |
|
1003 iPrefixIndex[nextFree] = STATIC_CAST(TInt16,prevCode); |
|
1004 iSuffixCode[nextFree] = iFirstChar; |
|
1005 if (nextFree < KGifConversionTableSize ) |
|
1006 { |
|
1007 nextFree++; |
|
1008 } |
|
1009 } |
|
1010 } |
|
1011 else |
|
1012 { |
|
1013 iPrefixIndex[nextFree] = STATIC_CAST(TInt16,prevCode); |
|
1014 iSuffixCode[nextFree] = iFirstChar; |
|
1015 WriteCodeL(nextFree); |
|
1016 if (nextFree < KGifConversionTableSize ) |
|
1017 { |
|
1018 nextFree++; |
|
1019 } |
|
1020 } |
|
1021 |
|
1022 if(code < nextFree) |
|
1023 { |
|
1024 prevCode = code; |
|
1025 } |
|
1026 else |
|
1027 { |
|
1028 if(nextFree <= 1) |
|
1029 { |
|
1030 User::Leave(KErrCorrupt); |
|
1031 } |
|
1032 prevCode = iPrefixIndex[nextFree-1]; |
|
1033 } |
|
1034 |
|
1035 } |
|
1036 } |
|
1037 while (iDataPtr <= iDataPtrLimit); |
|
1038 |
|
1039 ASSERT(iDataPtr <= iDataPtrLimit); |
|
1040 |
|
1041 SetCurrentCodeLengthL(curCodeLen); |
|
1042 iBitBuffer = bBuf; |
|
1043 iBitBuffSize = bbSize; |
|
1044 iNextFree = nextFree; |
|
1045 iPreviousCode = prevCode; |
|
1046 } |
|
1047 |
|
1048 inline |
|
1049 TInt CGifReadCodec::ResetTableL() |
|
1050 { |
|
1051 const TInt nFree = iEoiCode + 1; |
|
1052 |
|
1053 SetCurrentCodeLengthL(iGifLZWInfo->iInitialCodeLength); |
|
1054 return nFree; |
|
1055 } |
|
1056 |
|
1057 #if defined(__ARMCC__) |
|
1058 __forceinline |
|
1059 #else |
|
1060 inline |
|
1061 #endif |
|
1062 TInt CGifReadCodec::NextCode(TBitBuffer& aBitBuffer, TInt& aBitBufSize,const TInt aCurCodeLen ) |
|
1063 { |
|
1064 |
|
1065 if (aCurCodeLen > aBitBufSize) |
|
1066 { |
|
1067 const TUint8* dataPtr = iDataPtr; |
|
1068 ASSERT( dataPtr <= iDataPtrLimit ); |
|
1069 |
|
1070 TInt bytesToRead = (sizeof(TBitBuffer) * 8 - aBitBufSize) >> 3; |
|
1071 if (dataPtr + bytesToRead >= iDataPtrLimit) |
|
1072 { |
|
1073 bytesToRead = iDataPtrLimit - dataPtr; |
|
1074 if (bytesToRead == 0) |
|
1075 { |
|
1076 return KErrNotFound; |
|
1077 } |
|
1078 } |
|
1079 |
|
1080 do |
|
1081 { |
|
1082 aBitBuffer |= *dataPtr++ << aBitBufSize; |
|
1083 aBitBufSize +=8; |
|
1084 } |
|
1085 while (--bytesToRead > 0); |
|
1086 |
|
1087 iDataPtr = dataPtr; |
|
1088 if (aBitBufSize < aCurCodeLen) |
|
1089 { |
|
1090 return KErrNotFound; |
|
1091 } |
|
1092 |
|
1093 } |
|
1094 TInt code = aBitBuffer; |
|
1095 aBitBuffer >>= aCurCodeLen; |
|
1096 code &= (1 << aCurCodeLen) - 1; |
|
1097 aBitBufSize -= aCurCodeLen; |
|
1098 |
|
1099 return code; |
|
1100 } |
|
1101 |
|
1102 template <class TPalType, TInt aPtrDelta> |
|
1103 inline |
|
1104 TUint8* CGifReadCodec::WriteGifBuffer(TUint8* aOutputStringPtr, TUint8* aOutputStringLimit) |
|
1105 { |
|
1106 TInt bufUsed = iPixBufCount; |
|
1107 TInt bufFree = iPixBufferSize - bufUsed; |
|
1108 |
|
1109 if ((aOutputStringLimit-aOutputStringPtr) > bufFree) |
|
1110 { |
|
1111 aOutputStringLimit = aOutputStringPtr+bufFree; |
|
1112 } |
|
1113 |
|
1114 register TPalType* pixelBufferPtr = reinterpret_cast<TPalType*>( iPixelBuffer ) + bufUsed; |
|
1115 const TPalType* tmp = pixelBufferPtr; |
|
1116 const TPalType* palette; |
|
1117 |
|
1118 if (aPtrDelta == 4) |
|
1119 { |
|
1120 palette = reinterpret_cast<const TPalType*>( iPalette ); |
|
1121 } |
|
1122 else |
|
1123 { |
|
1124 palette = reinterpret_cast<const TPalType*>( i64KPalette ); |
|
1125 } |
|
1126 |
|
1127 if (iMaskProc) |
|
1128 { |
|
1129 register TUint32* maskPixelBufferPtr = iMaskBuffer + bufUsed; |
|
1130 const TUint16 KTranspIdx= iTranspColIdx; |
|
1131 const TUint32 KTranspMask = 0; |
|
1132 const TUint32 KOpaqueMask = iOpaqueMask; |
|
1133 const TPalType bgCol = palette[ KTranspIdx ]; |
|
1134 |
|
1135 while (aOutputStringPtr < aOutputStringLimit) |
|
1136 { |
|
1137 const TUint8 code = *aOutputStringPtr; |
|
1138 if (code == KTranspIdx) |
|
1139 { |
|
1140 *pixelBufferPtr = bgCol; |
|
1141 *maskPixelBufferPtr = KTranspMask; |
|
1142 } |
|
1143 else |
|
1144 { |
|
1145 *pixelBufferPtr = palette[code]; |
|
1146 *maskPixelBufferPtr = KOpaqueMask; |
|
1147 } |
|
1148 maskPixelBufferPtr++; |
|
1149 pixelBufferPtr++; |
|
1150 aOutputStringPtr++; |
|
1151 } |
|
1152 } |
|
1153 else |
|
1154 { |
|
1155 |
|
1156 while (aOutputStringPtr < aOutputStringLimit) |
|
1157 { |
|
1158 *pixelBufferPtr = palette[*aOutputStringPtr]; |
|
1159 pixelBufferPtr++; |
|
1160 aOutputStringPtr++; |
|
1161 } |
|
1162 } |
|
1163 |
|
1164 iPixBufCount += pixelBufferPtr-tmp; |
|
1165 return aOutputStringPtr; |
|
1166 } |
|
1167 |
|
1168 /** |
|
1169 That shall only leave with KErrCorrupt |
|
1170 which means that image decoding is not possible |
|
1171 */ |
|
1172 void CGifReadCodec::WriteCodeL(TInt aCode) |
|
1173 { |
|
1174 TUint8* outputStringLimit = iOutputString + KGifConversionTableSize; |
|
1175 register TUint8* outputStringPtr = outputStringLimit; |
|
1176 register TUint16 latestChar=0; |
|
1177 |
|
1178 while (aCode >= 0) |
|
1179 { |
|
1180 ASSERT((aCode >= 0) && (aCode <= KGifConversionTableSize)); |
|
1181 |
|
1182 outputStringPtr--; |
|
1183 latestChar = iSuffixCode[aCode]; |
|
1184 outputStringPtr[0] = latestChar; |
|
1185 aCode = iPrefixIndex[aCode]; |
|
1186 } |
|
1187 |
|
1188 iFirstChar = latestChar; |
|
1189 if (aCode != KErrNotFound) |
|
1190 { |
|
1191 User::Leave(KErrCorrupt); |
|
1192 } |
|
1193 |
|
1194 while (true) |
|
1195 { |
|
1196 if (iFast64kMode) |
|
1197 { |
|
1198 outputStringPtr = WriteGifBuffer<TUint16, sizeof(TUint16)>(outputStringPtr, outputStringLimit); |
|
1199 } |
|
1200 else |
|
1201 { |
|
1202 outputStringPtr = WriteGifBuffer<TRgb, sizeof(TRgb)>(outputStringPtr, outputStringLimit); |
|
1203 } |
|
1204 |
|
1205 if (outputStringPtr != outputStringLimit) |
|
1206 { |
|
1207 FlushPixBuffer(iLatestPixSize); |
|
1208 } |
|
1209 else |
|
1210 { |
|
1211 break; |
|
1212 } |
|
1213 } |
|
1214 } |
|
1215 |
|
1216 inline |
|
1217 void CGifReadCodec::FlushPixBuffer(TInt aPixSize) |
|
1218 { |
|
1219 if (iPixBufCount) |
|
1220 { |
|
1221 if (iMaskProc) |
|
1222 { |
|
1223 WriteStringWithTransparency(reinterpret_cast<TUint8*>(iPixelBuffer), aPixSize, iMaskBuffer, iPixBufCount); |
|
1224 } |
|
1225 else |
|
1226 { |
|
1227 WriteStringWithoutTransparency(reinterpret_cast<TUint8*>(iPixelBuffer), aPixSize, iPixBufCount); |
|
1228 } |
|
1229 |
|
1230 iPixBufCount = 0; |
|
1231 } |
|
1232 } |
|
1233 |
|
1234 void CGifReadCodec::WriteStringWithoutTransparency(TUint8* aOutputString, TInt aPixSize, TInt aNumOfPixels) |
|
1235 { |
|
1236 CImageProcessor*const imageProc = iImgProc; |
|
1237 |
|
1238 if (iFastAccessMode) |
|
1239 { |
|
1240 imageProc->SetPixels(reinterpret_cast<TRgb*>(aOutputString), aNumOfPixels); |
|
1241 iPixRead += aNumOfPixels; |
|
1242 aNumOfPixels = 0; |
|
1243 return; |
|
1244 } |
|
1245 |
|
1246 const TInt KImgXLimit = iFrameSize.iWidth + iFrameOffset.iX; |
|
1247 |
|
1248 while ( aNumOfPixels ) |
|
1249 { |
|
1250 TInt rowPixels = Min(KImgXLimit - iPPos->iX, aNumOfPixels); |
|
1251 |
|
1252 imageProc->SetPixels(reinterpret_cast<TRgb*>(aOutputString), rowPixels); |
|
1253 aNumOfPixels -= rowPixels; |
|
1254 aOutputString += rowPixels * aPixSize; |
|
1255 |
|
1256 iPPos->iX += rowPixels; |
|
1257 if (iPPos->iX == KImgXLimit) |
|
1258 { |
|
1259 imageProc->FlushPixels(); |
|
1260 UpdateYPos(); |
|
1261 imageProc->SetPos( TPoint(iPPos->iX+iPositionOffset.iX, iPPos->iY+iPositionOffset.iY) ); |
|
1262 } |
|
1263 } |
|
1264 } |
|
1265 |
|
1266 void CGifReadCodec::WriteStringWithTransparency(TUint8* aOutputString, TInt aPixSize, TUint32* aMaskString, TInt aNumOfPixels) |
|
1267 { |
|
1268 CImageProcessor*const imageProc = iImgProc; |
|
1269 CImageProcessor*const maskProc = iMaskProc; |
|
1270 |
|
1271 if (iFastAccessMode) |
|
1272 { |
|
1273 imageProc->SetPixels(reinterpret_cast<TRgb*>(aOutputString), aNumOfPixels); |
|
1274 if(maskProc) // For non-single 16MA bitmaps |
|
1275 { |
|
1276 maskProc->SetMonoPixels(reinterpret_cast<TUint32*>(aMaskString), aNumOfPixels); |
|
1277 } |
|
1278 |
|
1279 iPixRead += aNumOfPixels; |
|
1280 aNumOfPixels = 0; |
|
1281 return; |
|
1282 } |
|
1283 |
|
1284 const TInt KImgXLimit = iFrameSize.iWidth + iFrameOffset.iX; |
|
1285 |
|
1286 while ( aNumOfPixels ) |
|
1287 { |
|
1288 TInt rowPixels = Min(KImgXLimit - iPPos->iX, aNumOfPixels); |
|
1289 |
|
1290 imageProc->SetPixels(reinterpret_cast<TRgb*>(aOutputString), rowPixels); |
|
1291 maskProc->SetMonoPixels(reinterpret_cast<TUint32*>(aMaskString), rowPixels); |
|
1292 |
|
1293 iPPos->iX += rowPixels; |
|
1294 aOutputString += rowPixels * aPixSize; |
|
1295 aMaskString += rowPixels; |
|
1296 aNumOfPixels -= rowPixels; |
|
1297 |
|
1298 if (iPPos->iX == KImgXLimit) |
|
1299 { |
|
1300 imageProc->FlushPixels(); |
|
1301 UpdateYPos(); |
|
1302 const TPoint newPos(iPPos->iX+iPositionOffset.iX, iPPos->iY+iPositionOffset.iY); |
|
1303 imageProc->SetPos(newPos); |
|
1304 maskProc->SetPos(newPos); |
|
1305 } |
|
1306 } |
|
1307 } |
|
1308 |
|
1309 inline |
|
1310 void CGifReadCodec::SetCurrentCodeLengthL(TInt aCodeLength) |
|
1311 { |
|
1312 if ((aCodeLength > KGifMaxBits) || (aCodeLength < 0)) |
|
1313 { |
|
1314 User::Leave(KErrCorrupt); |
|
1315 } |
|
1316 iCurrentCodeLength = aCodeLength; |
|
1317 } |
|
1318 |
|
1319 inline |
|
1320 void CGifReadCodec::UpdateYPos() |
|
1321 { |
|
1322 iPPos->iX = iFrameOffset.iX; |
|
1323 |
|
1324 if (!iGifImageDesc->iInterlaced) |
|
1325 { |
|
1326 iPPos->iY++; |
|
1327 return; |
|
1328 } |
|
1329 |
|
1330 UpdateYPosInterlaced(); |
|
1331 } |
|
1332 |
|
1333 void CGifReadCodec::UpdateYPosInterlaced() |
|
1334 { |
|
1335 iPPos->iY += iYPosIncrement; |
|
1336 |
|
1337 CImageProcessor*const imageProc = ImageProcessor(); |
|
1338 while (iPPos->iY >= iFrameSize.iHeight + iFrameOffset.iY) |
|
1339 { |
|
1340 iPass++; |
|
1341 TInt lineRepeat; |
|
1342 if (iPass == 2) |
|
1343 { |
|
1344 iPPos->iY = iFrameOffset.iY + KPass2StartLine; |
|
1345 iYPosIncrement = KPass2YPosIncrement; |
|
1346 lineRepeat = KPass2LineRepeat; |
|
1347 } |
|
1348 else if (iPass == 3) |
|
1349 { |
|
1350 iPPos->iY = iFrameOffset.iY + KPass3StartLine; |
|
1351 iYPosIncrement = KPass3YPosIncrement; |
|
1352 lineRepeat = KPass3LineRepeat; |
|
1353 } |
|
1354 else if (iPass == 4) |
|
1355 { |
|
1356 iPPos->iY = iFrameOffset.iY + KPass4StartLine; |
|
1357 iYPosIncrement = KPass4YPosIncrement; |
|
1358 lineRepeat = KPass4LineRepeat; |
|
1359 } |
|
1360 else |
|
1361 break; |
|
1362 imageProc->SetYPosIncrement(iYPosIncrement); |
|
1363 if(!iGifImageControl || (iGifImageControl->iTransparentColorIndex==KErrNotFound)) |
|
1364 { |
|
1365 imageProc->SetLineRepeat(lineRepeat); |
|
1366 } |
|
1367 } |
|
1368 } |
|
1369 |
|
1370 // CGifWriteCodec |
|
1371 CGifWriteCodec* CGifWriteCodec::NewL(const CGifEncoder& aEncoder) |
|
1372 { |
|
1373 CGifWriteCodec* self = new(ELeave) CGifWriteCodec(aEncoder); |
|
1374 CleanupStack::PushL(self); |
|
1375 self->ConstructL(); |
|
1376 CleanupStack::Pop(self); |
|
1377 return self; |
|
1378 } |
|
1379 |
|
1380 CGifWriteCodec::CGifWriteCodec(const CGifEncoder& aEncoder) |
|
1381 :iEncoder(aEncoder) |
|
1382 { |
|
1383 } |
|
1384 |
|
1385 TFrameState CGifWriteCodec::ProcessFrameL(TBufPtr8& aDst) |
|
1386 { |
|
1387 iDestStartPtr = CONST_CAST(TUint8*,aDst.Ptr()); |
|
1388 iDestPtr = iDestStartPtr; |
|
1389 iDestPtrLimit = iDestPtr + aDst.MaxLength(); |
|
1390 |
|
1391 FillBufferL(*Source()); |
|
1392 |
|
1393 if (iImageComplete) |
|
1394 { |
|
1395 while (iBufferSize > 0) |
|
1396 { |
|
1397 if (!WriteBuffer()) |
|
1398 { |
|
1399 aDst.SetLength(iDestPtr - iDestStartPtr); |
|
1400 return EFrameIncomplete; |
|
1401 } |
|
1402 } |
|
1403 TBool terminatorWritten = WriteTerminator(); |
|
1404 aDst.SetLength(iDestPtr - iDestStartPtr); |
|
1405 |
|
1406 return (terminatorWritten) ? EFrameComplete : EFrameIncomplete; |
|
1407 } |
|
1408 else |
|
1409 { |
|
1410 WriteBuffer(); |
|
1411 } |
|
1412 |
|
1413 aDst.SetLength(iDestPtr - iDestStartPtr); |
|
1414 return EFrameIncomplete; |
|
1415 } |
|
1416 |
|
1417 void CGifWriteCodec::FillBufferL(const CFbsBitmap& aFrame) |
|
1418 { |
|
1419 TUint pixelsScanned =0; |
|
1420 TBool pixelWritten = EFalse; |
|
1421 |
|
1422 const CPalette* palette = iEncoder.Palette(); |
|
1423 while (iPos.iY < iSourceRect.iBr.iY) |
|
1424 { |
|
1425 TRgb pixelColor; |
|
1426 aFrame.GetPixel(pixelColor,iPos); |
|
1427 iPos.iX++; |
|
1428 if (iPos.iX == iSourceRect.iBr.iX) |
|
1429 { |
|
1430 iPos.iX = iSourceRect.iTl.iX; |
|
1431 iPos.iY++; |
|
1432 } |
|
1433 TUint8 nextColorIndex; |
|
1434 if (palette) |
|
1435 { |
|
1436 nextColorIndex = palette->NearestIndex(pixelColor); |
|
1437 } |
|
1438 else |
|
1439 { |
|
1440 nextColorIndex = STATIC_CAST(TUint8, pixelColor.Color256()); |
|
1441 } |
|
1442 |
|
1443 TUint32 hashValue=0; |
|
1444 TInt index=-1; |
|
1445 if (!TableEntry(iWaitingCode,nextColorIndex,index,hashValue)) |
|
1446 { |
|
1447 if(index!=KErrNotFound) |
|
1448 { |
|
1449 pixelWritten = ETrue; |
|
1450 WriteData(iWaitingCode); |
|
1451 AddToTableL(index,hashValue); |
|
1452 |
|
1453 if (iNextFree > iNextLimit) |
|
1454 { |
|
1455 iCodeLength++; |
|
1456 if (iCodeLength > KGifMaxBits) |
|
1457 { |
|
1458 iCodeLength=KGifMaxBits; |
|
1459 ResetTable(); |
|
1460 } |
|
1461 iNextLimit = 1 << iCodeLength; |
|
1462 } |
|
1463 } |
|
1464 iWaitingCode = nextColorIndex; |
|
1465 } |
|
1466 else |
|
1467 { |
|
1468 iWaitingCode = iHashCode[index]; |
|
1469 } |
|
1470 |
|
1471 if ((iBufferFull) || ((pixelsScanned++ > KLZWLimit) && pixelWritten !=EFalse)) |
|
1472 { |
|
1473 return; |
|
1474 } |
|
1475 |
|
1476 } |
|
1477 |
|
1478 |
|
1479 if (iPos.iY == iSourceRect.iBr.iY) |
|
1480 { |
|
1481 if (iWaitingCode != KErrNotFound) |
|
1482 { |
|
1483 WriteData(iWaitingCode); |
|
1484 } |
|
1485 WriteData(iClearCode + 1); // End-Of-Information |
|
1486 if (iBitOffset > 0) |
|
1487 { |
|
1488 iBufferSize++; |
|
1489 iBitOffset = 0; |
|
1490 } |
|
1491 iCodeLength = 8; |
|
1492 iBufferPtr++; |
|
1493 iImageComplete = ETrue; |
|
1494 } |
|
1495 } |
|
1496 |
|
1497 void CGifWriteCodec::InitFrameL(TBufPtr8& aDst, const CFbsBitmap& aSource) |
|
1498 { |
|
1499 SetSource(&aSource); |
|
1500 |
|
1501 iDestStartPtr = CONST_CAST(TUint8*,aDst.Ptr()); |
|
1502 iDestPtr = iDestStartPtr; |
|
1503 iDestPtrLimit = iDestPtr + aDst.MaxLength(); |
|
1504 |
|
1505 iSourceRect = TRect(aSource.SizeInPixels()); |
|
1506 iPos.SetXY(0,0); |
|
1507 |
|
1508 iBitsPerPixel = 8; |
|
1509 |
|
1510 iBufferPtr = iBuffer; |
|
1511 iBufferSize = 0; |
|
1512 |
|
1513 iClearCode = 1 << iBitsPerPixel; |
|
1514 iCodeLength = iBitsPerPixel + 1; |
|
1515 iWaitingCode = -1; |
|
1516 |
|
1517 ResetTable(); |
|
1518 |
|
1519 *iDestPtr++ = KGifImageDescriptorId; // Image Separator Header |
|
1520 *iDestPtr++ = 0; // Coordinate Left Border |
|
1521 *iDestPtr++ = 0; // Coordinate Left Border |
|
1522 *iDestPtr++ = 0; // Coordinate Top Border |
|
1523 *iDestPtr++ = 0; // Coordinate Top Border |
|
1524 *iDestPtr++ = TUint8(iSourceRect.Width()); |
|
1525 *iDestPtr++ = TUint8(iSourceRect.Width() >> 8); |
|
1526 *iDestPtr++ = TUint8(iSourceRect.Height()); |
|
1527 *iDestPtr++ = TUint8(iSourceRect.Height() >> 8); |
|
1528 *iDestPtr++ = 0; // Flags |
|
1529 |
|
1530 *iDestPtr++ = TUint8(iCodeLength - 1); // Initial Code Length - 1 |
|
1531 |
|
1532 aDst.SetLength(iDestPtr - iDestStartPtr); |
|
1533 } |
|
1534 |
|
1535 void CGifWriteCodec::AddToTableL(TInt aIndex, TUint32 aHashValue) |
|
1536 { |
|
1537 ASSERT(aIndex<KGifHashTableSize); |
|
1538 ASSERT(iNextFree<=KGifConversionTableSize); |
|
1539 iHashCode[aIndex] = static_cast<TInt16>(iNextFree++); |
|
1540 iHashValue[aIndex] = aHashValue; |
|
1541 } |
|
1542 |
|
1543 // The hash algoritm use open addressing double hashing (no chaining) |
|
1544 // on the prefix code / suffix character combination. A variant of Knuth's |
|
1545 // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime |
|
1546 // secondary probe. |
|
1547 TBool CGifWriteCodec::TableEntry(TInt16 aWaitingCode, TUint8 aNewCode, TInt& aIndex, TUint32& aHashValue) |
|
1548 { |
|
1549 TInt offset; |
|
1550 |
|
1551 ASSERT(aWaitingCode>=KErrNotFound && aWaitingCode<KGifConversionTableSize); |
|
1552 |
|
1553 aIndex = KErrNotFound; |
|
1554 |
|
1555 //Very first code |
|
1556 if(aWaitingCode==KErrNotFound) |
|
1557 return EFalse; |
|
1558 |
|
1559 aIndex = static_cast<TInt>(aNewCode<<(KGifMaxBits-8)) + aWaitingCode; |
|
1560 if(aIndex>=KGifHashTableSize) |
|
1561 aIndex -= KGifHashTableSize; |
|
1562 ASSERT(aIndex < KGifHashTableSize); |
|
1563 |
|
1564 //hash function |
|
1565 aHashValue = static_cast<TUint32>(aWaitingCode<<8)|aNewCode; |
|
1566 |
|
1567 if(iHashCode[aIndex]==KErrNotFound) //slot is empty |
|
1568 return EFalse; |
|
1569 |
|
1570 //slot has an entry |
|
1571 if(iHashValue[aIndex]==aHashValue) //symbol already in table |
|
1572 return ETrue; |
|
1573 |
|
1574 if(aIndex==0) // secondary hash (after G. Knott) |
|
1575 offset = 1; |
|
1576 else |
|
1577 offset = KGifHashTableSize - aIndex; |
|
1578 |
|
1579 FOREVER |
|
1580 { |
|
1581 aIndex -= offset; |
|
1582 if(aIndex<0) |
|
1583 aIndex += KGifHashTableSize; |
|
1584 |
|
1585 if(iHashCode[aIndex]==KErrNotFound) |
|
1586 return EFalse; //slot is empty |
|
1587 |
|
1588 if(iHashValue[aIndex]==aHashValue) //symbol already in table |
|
1589 return ETrue; |
|
1590 } |
|
1591 } |
|
1592 |
|
1593 void CGifWriteCodec::ResetTable() |
|
1594 { |
|
1595 WriteData(iClearCode); // Send reset code |
|
1596 |
|
1597 iNextFree = iClearCode + 2; |
|
1598 iCodeLength = iBitsPerPixel + 1; |
|
1599 iNextLimit = 1 << iCodeLength; |
|
1600 |
|
1601 for (TInt index = 0; index < KGifHashTableSize; index++) |
|
1602 iHashCode[index] = KErrNotFound; |
|
1603 } |
|
1604 |
|
1605 void CGifWriteCodec::WriteData(TInt aIndex) |
|
1606 { |
|
1607 aIndex <<= iBitOffset; |
|
1608 iBufferPtr[0] |= TUint8(aIndex); |
|
1609 iBufferPtr[1] |= TUint8(aIndex >> 8); |
|
1610 iBufferPtr[2] |= TUint8(aIndex >> 16); |
|
1611 |
|
1612 TInt newBitOffset = iBitOffset + iCodeLength; |
|
1613 iBitOffset = newBitOffset & 7; |
|
1614 TInt byteOffset = newBitOffset >> 3; |
|
1615 iBufferPtr += byteOffset; |
|
1616 iBufferSize += byteOffset; |
|
1617 |
|
1618 if (iBufferSize >= KGifBlockSize) |
|
1619 { |
|
1620 ASSERT(iBufferSize <= KGifBufferSize); |
|
1621 iBufferFull = ETrue; |
|
1622 } |
|
1623 } |
|
1624 |
|
1625 TBool CGifWriteCodec::WriteBuffer() |
|
1626 { |
|
1627 ASSERT(iBufferSize <= KGifBufferSize); |
|
1628 if (iBufferSize == 0) |
|
1629 return ETrue; |
|
1630 |
|
1631 TInt blockSize = Min(KGifBlockSize,iBufferSize); |
|
1632 if (iDestPtrLimit - iDestPtr < blockSize + 1) |
|
1633 return EFalse; |
|
1634 |
|
1635 *iDestPtr++ = STATIC_CAST(TUint8,blockSize); |
|
1636 Mem::Copy(iDestPtr,iBuffer,blockSize); |
|
1637 iDestPtr += blockSize; |
|
1638 iBufferFull = EFalse; |
|
1639 |
|
1640 iBufferSize -= blockSize; |
|
1641 iBufferPtr = iBuffer + iBufferSize; |
|
1642 |
|
1643 TInt bitBufferSize = iBufferSize; |
|
1644 if (iBitOffset) |
|
1645 bitBufferSize++; |
|
1646 Mem::Copy(iBuffer,&iBuffer[blockSize],bitBufferSize); |
|
1647 Mem::FillZ(iBuffer + bitBufferSize,KGifBufferSize - bitBufferSize); |
|
1648 |
|
1649 return ETrue; |
|
1650 } |
|
1651 |
|
1652 TBool CGifWriteCodec::WriteTerminator() |
|
1653 { |
|
1654 if (iDestPtrLimit - iDestPtr < 2) |
|
1655 return EFalse; |
|
1656 |
|
1657 *iDestPtr++ = TUint8(0); |
|
1658 *iDestPtr++ = TUint8(KGifTerminatorId); |
|
1659 return ETrue; |
|
1660 } |