|
1 // Copyright (c) 1999-2009 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 // |
|
15 |
|
16 #include <barsc.h> |
|
17 #include <barsread.h> |
|
18 #include <bautils.h> |
|
19 #include <imageconversion.h> |
|
20 #include "icl/ImageCodec.h" |
|
21 #include "ImageClientMain.h" |
|
22 #include <101F45D6_extra.rsg> |
|
23 |
|
24 #include "JpegTypes.h" |
|
25 #include "jpegwritecodec.h" |
|
26 #include "JpegYuvDecoder.h" |
|
27 #include "icl/ICL_UIDS.hrh" |
|
28 #include <bitmaptransforms.h> |
|
29 #include "exifdecoder.h" |
|
30 #include "ExifEncoder.h" |
|
31 #include "exiftransform.h" |
|
32 #include "ImageUtils.h" |
|
33 |
|
34 #include "JPEGCodec.h" |
|
35 #include "JPEGConvert.h" |
|
36 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS |
|
37 #include <icl/icl_uids_const.hrh> |
|
38 #include <icl/icl_uids_def.hrh> |
|
39 #include <icl/imagecodecdef.h> |
|
40 #endif |
|
41 |
|
42 _LIT(KJPEGPanicCategory, "JPEGConvertPlugin"); |
|
43 |
|
44 const TInt KBlockSignatureSearchSize = 256; |
|
45 const TInt KSizeOfBlockSignaturePlusBlockLength = sizeof(KJpgMarker) + sizeof(TUint16); |
|
46 |
|
47 // |
|
48 // this file doesn't contain much of performance-critical code so use thumb |
|
49 // instruction set to save on some ROM footprint |
|
50 // |
|
51 #if defined(__ARMCC__) |
|
52 #pragma thumb |
|
53 #endif |
|
54 |
|
55 // Global panic function |
|
56 GLDEF_C void Panic(TIclPanic aError) |
|
57 { |
|
58 User::Panic(KJPEGPanicCategory, aError); |
|
59 } |
|
60 |
|
61 // Jpeg decoder. |
|
62 CJpegDecoder* CJpegDecoder::NewL() |
|
63 { |
|
64 return new(ELeave)CJpegDecoder(); |
|
65 } |
|
66 |
|
67 CJpegDecoder::CJpegDecoder() |
|
68 : iImageType(CImageDecoder::EImageTypeMain) |
|
69 { |
|
70 } |
|
71 |
|
72 CJpegDecoder::~CJpegDecoder() |
|
73 { |
|
74 delete iExtensionManager; |
|
75 delete iStreamedDecodeExt; |
|
76 |
|
77 delete iExifDecoder; |
|
78 iComment.ResetAndDestroy(); |
|
79 Cleanup(); |
|
80 } |
|
81 |
|
82 void CJpegDecoder::ImageType(TInt aFrameNumber, TUid& aImageType, TUid& aImageSubType) const |
|
83 { |
|
84 __ASSERT_ALWAYS(aFrameNumber == 0, Panic(EFrameNumberOutOfRange)); |
|
85 aImageType = KImageTypeJPGUid; |
|
86 aImageSubType = KNullUid; |
|
87 } |
|
88 |
|
89 TInt CJpegDecoder::NumberOfFrameComments(TInt aFrameNumber) const |
|
90 { |
|
91 __ASSERT_ALWAYS(IsImageHeaderProcessingComplete(), Panic(EHeaderProcessingNotComplete)); |
|
92 __ASSERT_ALWAYS((aFrameNumber >= 0) && (aFrameNumber < NumberOfFrames()), Panic(EFrameNumberOutOfRange)); |
|
93 |
|
94 const CFrameImageData& frameImageData = FrameData(aFrameNumber); |
|
95 TInt frameCommentCount = 0; |
|
96 // Because this is a single frame format, the comments are stored |
|
97 // in the global rather than local comment block |
|
98 TInt imageDataCount = frameImageData.ImageDataCount(); |
|
99 for (TInt count = 0; count < imageDataCount; count++) |
|
100 { |
|
101 const TImageDataBlock* imageData = frameImageData.GetImageData(count); |
|
102 if (imageData->DataType() == KJPGCommentUid) |
|
103 frameCommentCount++; |
|
104 } |
|
105 |
|
106 return frameCommentCount; |
|
107 } |
|
108 |
|
109 HBufC* CJpegDecoder::FrameCommentL(TInt aFrameNumber, TInt aCommentNumber) const |
|
110 { |
|
111 __ASSERT_ALWAYS(IsImageHeaderProcessingComplete(), Panic(EHeaderProcessingNotComplete)); |
|
112 __ASSERT_ALWAYS((aFrameNumber >= 0) && (aFrameNumber < NumberOfFrames()), Panic(EFrameNumberOutOfRange)); |
|
113 __ASSERT_ALWAYS((aCommentNumber >= 0) && (aCommentNumber < NumberOfFrameComments(aFrameNumber)), Panic(ECommentNumberOutOfRange)); |
|
114 |
|
115 const CFrameImageData& frameImageData = FrameData(aFrameNumber); |
|
116 TInt commentCount = 0; |
|
117 // Because this is a single frame format, the comments are stored |
|
118 // in the global rather than local comment block |
|
119 TInt imageDataCount = frameImageData.ImageDataCount(); |
|
120 const TImageDataBlock* imageData = NULL; |
|
121 for (TInt count = 0; count < imageDataCount; count++) |
|
122 { |
|
123 imageData = frameImageData.GetImageData(count); |
|
124 if (imageData->DataType() == KJPGCommentUid) |
|
125 { |
|
126 if (commentCount == aCommentNumber) |
|
127 { |
|
128 break; |
|
129 } |
|
130 commentCount++; |
|
131 } |
|
132 } |
|
133 |
|
134 const TJpegComment* jpegComment = static_cast<const TJpegComment*>(imageData); |
|
135 HBufC* comment = NULL; |
|
136 if (jpegComment) |
|
137 { |
|
138 comment = HBufC::NewL(jpegComment->iComment->Length()); |
|
139 comment->Des().Copy(*(jpegComment->iComment)); // Create a 16 bit copy of the 8 bit original |
|
140 } |
|
141 return comment; |
|
142 } |
|
143 |
|
144 TInt CJpegDecoder::FrameHeaderBlockSize(TInt /*aFrameNumber*/) const |
|
145 { |
|
146 return KJfifHeaderBlockSizeInBytes; |
|
147 } |
|
148 |
|
149 TInt CJpegDecoder::FrameBlockSize(TInt /*aFrameNumber*/) const |
|
150 { |
|
151 return KJfifDataBlockSizeInBytes; |
|
152 } |
|
153 |
|
154 // |
|
155 // |
|
156 // |
|
157 TInt CJpegDecoder::GetDestinationSize(TSize& aSize, TInt aFrameNumber) |
|
158 { |
|
159 ASSERT(iExtensionManager); |
|
160 const TFrameInfo frameInfo = FrameInfo(aFrameNumber); |
|
161 TSize originalSize = frameInfo.iOverallSizeInPixels; |
|
162 TInt err = iExtensionManager->GetDestinationSize(originalSize); |
|
163 if(err == KErrNone) |
|
164 { |
|
165 aSize = originalSize; |
|
166 } |
|
167 return err; |
|
168 } |
|
169 |
|
170 void CJpegDecoder::InitConvertL() |
|
171 { |
|
172 iState = EStateStart; // when starting convert, always start from the top |
|
173 CImageDecoderPlugin::InitConvertL(); |
|
174 } |
|
175 |
|
176 void CJpegDecoder::DoConvert() |
|
177 { |
|
178 switch(iState) |
|
179 { |
|
180 case EStateStart: |
|
181 { |
|
182 TRAPD(errCode, PrepareForProcessFrameL()); |
|
183 if (errCode!=KErrNone) |
|
184 { |
|
185 RequestComplete(errCode); |
|
186 return; |
|
187 } |
|
188 |
|
189 iCodecState = EFrameIncomplete; |
|
190 |
|
191 TBufPtr8& sourceData = SourceData(); |
|
192 TInt sourceLength = sourceData.Length(); |
|
193 if (sourceLength!=0) |
|
194 { |
|
195 TRAP(errCode, iCodecState = ImageReadCodec()->ProcessFrameL(sourceData)); |
|
196 } |
|
197 |
|
198 if (errCode == KErrNone && (sourceData.Length() == sourceLength || iCodecState == EFrameComplete || iCodecState == EBlockComplete)) |
|
199 { |
|
200 CJpgReadCodec* readCodec = static_cast<CJpgReadCodec*>(ImageReadCodec()); |
|
201 JPEG_ASSERT(readCodec); |
|
202 readCodec->InitDrawFrame(); |
|
203 iState=EStateDrawFrame; |
|
204 SelfComplete(KErrNone); |
|
205 } |
|
206 else |
|
207 { |
|
208 ASSERT(iState==EStateStart); |
|
209 HandleProcessFrameResult(errCode,iCodecState); |
|
210 } |
|
211 } |
|
212 break; |
|
213 case EStateDrawFrame: |
|
214 { |
|
215 CJpgReadCodec* readCodec = static_cast<CJpgReadCodec*>(ImageReadCodec()); |
|
216 JPEG_ASSERT(readCodec); |
|
217 TBool result = EFalse; |
|
218 TRAPD(errCode, result = readCodec->DrawFrameL()); |
|
219 if (errCode != KErrNone) |
|
220 { |
|
221 HandleProcessFrameResult(errCode, iCodecState); |
|
222 } |
|
223 else if (result) |
|
224 { |
|
225 HandleProcessFrameResult(KErrNone, iCodecState); |
|
226 iState = EStateStart; |
|
227 } |
|
228 else |
|
229 { |
|
230 ASSERT(iState==EStateDrawFrame); |
|
231 SelfComplete(KErrNone); |
|
232 } |
|
233 } |
|
234 break; |
|
235 default: |
|
236 ASSERT(EFalse); |
|
237 } |
|
238 } |
|
239 |
|
240 MExifMetadata* CJpegDecoder::ExifMetadata() |
|
241 { |
|
242 return iExifDecoder; |
|
243 } |
|
244 |
|
245 // Scan header. |
|
246 // Validate that format is correct. |
|
247 // Create codec. |
|
248 // Fill in image info. (All frames) |
|
249 void CJpegDecoder::ScanDataL() |
|
250 { |
|
251 ReadFormatL(); |
|
252 |
|
253 ASSERT(ImageReadCodec() == NULL); |
|
254 |
|
255 CJpgReadCodec* imageReadCodec = NULL; |
|
256 |
|
257 if (iJpgFrameInfo.iProgressive) |
|
258 { |
|
259 imageReadCodec = CProgressiveJpgReadCodec::NewL( |
|
260 iJpgFrameInfo, |
|
261 iScanInfo, |
|
262 iDCHuffmanTable, |
|
263 iACHuffmanTable, |
|
264 iQTable); |
|
265 } |
|
266 else if (iJpgFrameInfo.iNumberOfComponents == iScanInfo.iNumberOfComponents) |
|
267 { |
|
268 imageReadCodec = CSequentialJpgReadCodec::NewL( |
|
269 iJpgFrameInfo, |
|
270 iScanInfo, |
|
271 iDCHuffmanTable, |
|
272 iACHuffmanTable, |
|
273 iQTable); |
|
274 } |
|
275 else |
|
276 { |
|
277 iJpgFrameInfo.iMultiScan = ETrue; |
|
278 imageReadCodec = CMultiScanSequentialJpgReadCodec::NewL( |
|
279 iJpgFrameInfo, |
|
280 iScanInfo, |
|
281 iDCHuffmanTable, |
|
282 iACHuffmanTable, |
|
283 iQTable); |
|
284 } |
|
285 |
|
286 CleanupStack::PushL(imageReadCodec); |
|
287 |
|
288 if (!iExtensionManager) |
|
289 { |
|
290 // Manager settings persist over all frames. |
|
291 iExtensionManager = CPluginExtensionManager::NewL(imageReadCodec); |
|
292 } |
|
293 else |
|
294 { |
|
295 // Maintain manager settings, but reset the codec that it points to. |
|
296 iExtensionManager->ResetCodecExtension(imageReadCodec); |
|
297 } |
|
298 |
|
299 |
|
300 CleanupStack::Pop(); // imageReadCodec |
|
301 |
|
302 imageReadCodec->SetExtensionManager(iExtensionManager); |
|
303 imageReadCodec->SetHighSpeedMode( (DecoderOptions() & CImageDecoder::EPreferFastDecode) == CImageDecoder::EPreferFastDecode ); |
|
304 |
|
305 // If this is the main image - pull the thumbnail out and give it to the |
|
306 // framework |
|
307 if (iImageType == CImageDecoder::EImageTypeMain) |
|
308 { |
|
309 if (iExifDecoder != NULL) |
|
310 { |
|
311 TInt err = 0; |
|
312 TInt thumbDataOffset = 0; |
|
313 |
|
314 HBufC8* thumbnailData = NULL; |
|
315 // Hold the thumbnail data (extracted from the Exif object). |
|
316 |
|
317 // Get both the offset of the thumbnail data and its length. |
|
318 err = iExifDecoder->GetIntegerParam(KJPEGInterchangeFormat, 1, thumbDataOffset); |
|
319 if(err == KErrNone) |
|
320 { |
|
321 TInt thumbDataLength = 0; |
|
322 err = iExifDecoder->GetIntegerParam(KJPEGInterchangeFormatLength, 1, thumbDataLength); |
|
323 if(err == KErrNone) |
|
324 { |
|
325 //get the thumbnail data. |
|
326 thumbnailData = iExifDecoder->GetJpegThumbnailData(); |
|
327 // Pass the thumbnailData back to the framework (ownership remains here). |
|
328 SetThumbnailData(thumbnailData); |
|
329 } |
|
330 } |
|
331 } |
|
332 } |
|
333 |
|
334 imageReadCodec->SetAutoRotateFlag(iAutoRotateFlag); |
|
335 |
|
336 SetImageReadCodec(imageReadCodec); |
|
337 |
|
338 ReadFrameHeadersL(); |
|
339 } |
|
340 |
|
341 void CJpegDecoder::NotifyImageTypeChangeL(TInt aImageType) |
|
342 { |
|
343 TInt prevImageType = iImageType; |
|
344 iImageType = aImageType; |
|
345 TRAPD(err, ScanDataL()); |
|
346 if (err != KErrNone) |
|
347 { |
|
348 iImageType = prevImageType; |
|
349 JPEG_LEAVE(err, "ScanDataL"); |
|
350 } |
|
351 } |
|
352 |
|
353 void CJpegDecoder::ReadFormatL() |
|
354 { |
|
355 iJpgFrameInfo.iProgressive = EFalse; // Reset |
|
356 |
|
357 TPtrC8 bufferDes; |
|
358 ReadDataL(0, bufferDes, KJfifInitialHeaderSize); |
|
359 |
|
360 // Validate the header. |
|
361 if (bufferDes.Length() < KJfifInitialHeaderSize) |
|
362 { |
|
363 JPEG_LEAVE(KErrUnderflow, "Not enough data for JFIF header"); |
|
364 } |
|
365 |
|
366 iPtr = &bufferDes[0]; |
|
367 const TUint8* ptr = iPtr; |
|
368 TInt startPosition = 6; // Move past SOISignature + first block sig/length. |
|
369 TUint16 soiSig = ReadBigEndianUint16(ptr); |
|
370 if (soiSig != CJpgReadCodec::EMarkerSOI) |
|
371 { |
|
372 JPEG_LEAVE(KErrCorrupt, "Expected SOI marker"); |
|
373 } |
|
374 |
|
375 iJpgFrameInfo.iRestartInterval = KErrNotFound; |
|
376 SetStartPosition(startPosition); |
|
377 |
|
378 //delete all comments |
|
379 iComment.ResetAndDestroy(); |
|
380 |
|
381 for (;;) |
|
382 { |
|
383 // Read the signature and length of the next block. |
|
384 iBlockSignature = ReadBigEndianUint16(ptr); |
|
385 |
|
386 // Skip until next block signature is valid |
|
387 if ((iBlockSignature & KJpgMarkerMask) != KJpgMarker) |
|
388 { |
|
389 TInt blockIndex = KErrNotFound; |
|
390 TChar jpgMarker = TChar(KJpgMarker >> 8); |
|
391 for (;;) |
|
392 { |
|
393 // search in blocks of size KBlockSignatureSearchSize |
|
394 // for occurrence of a jpeg marker (0xff) |
|
395 |
|
396 ReadDataL(startPosition, bufferDes, KBlockSignatureSearchSize); |
|
397 if (!bufferDes.Length()) |
|
398 { |
|
399 // no marker found, no data left |
|
400 JPEG_LEAVE(KErrCorrupt, "No marker, no data"); |
|
401 } |
|
402 blockIndex = bufferDes.Locate(jpgMarker); |
|
403 if (blockIndex != KErrNotFound) |
|
404 { |
|
405 startPosition += blockIndex; |
|
406 break; |
|
407 } |
|
408 startPosition += bufferDes.Length(); |
|
409 } |
|
410 |
|
411 if ((bufferDes.Length() - blockIndex) < KSizeOfBlockSignaturePlusBlockLength) |
|
412 { |
|
413 // ensure there are at least KSizeOfBlockSignaturePlusBlockLength bytes in bufferDes after the blockIndex |
|
414 ReadDataL(startPosition, bufferDes, KSizeOfBlockSignaturePlusBlockLength); |
|
415 if (bufferDes.Length() < KSizeOfBlockSignaturePlusBlockLength) |
|
416 { |
|
417 JPEG_LEAVE(KErrUnderflow, "Not enough data"); // Not enough data present |
|
418 } |
|
419 blockIndex = 0; |
|
420 } |
|
421 iPtr = &bufferDes[blockIndex]; |
|
422 ptr = iPtr; |
|
423 iBlockSignature = ReadBigEndianUint16(ptr); |
|
424 startPosition += KSizeOfBlockSignaturePlusBlockLength; |
|
425 SetStartPosition(startPosition); |
|
426 } |
|
427 |
|
428 // Read the current block's length. |
|
429 iBlockLength = ReadBigEndianUint16(ptr); |
|
430 |
|
431 // Read the next block plus the signature and size of block to follow |
|
432 ReadDataL(StartPosition(),bufferDes,iBlockLength+2); |
|
433 |
|
434 if (bufferDes.Length() < iBlockLength+2) |
|
435 { |
|
436 JPEG_LEAVE(KErrUnderflow, "Not enough data"); // Not enough data present |
|
437 } |
|
438 |
|
439 iPtr = &bufferDes[0]; |
|
440 switch (iBlockSignature) |
|
441 { |
|
442 case CJpgReadCodec::EMarkerAPP0: |
|
443 ProcessApp0L(); |
|
444 break; |
|
445 |
|
446 case CJpgReadCodec::EMarkerAPP1: |
|
447 // Process the EXIF information |
|
448 // Check if the flag to ignore is set. If yes, set the data to NULL. |
|
449 if (DecoderOptions() & CImageDecoder::EOptionIgnoreExifMetaData) |
|
450 { |
|
451 delete iExifDecoder; |
|
452 iExifDecoder = NULL; |
|
453 } |
|
454 else |
|
455 { |
|
456 TRAPD(err, ProcessApp1L()); |
|
457 if ((err != KErrCorrupt) && (err != KErrNotSupported)) |
|
458 { |
|
459 JPEG_LEAVE_IF_ERROR(err, "ProcessApp1L"); |
|
460 } |
|
461 } |
|
462 break; |
|
463 |
|
464 case CJpgReadCodec::EMarkerAPP14: |
|
465 ProcessAppEL(); |
|
466 break; |
|
467 |
|
468 case CJpgReadCodec::EMarkerDHT: |
|
469 ProcessHuffmanTableL(); |
|
470 break; |
|
471 |
|
472 case CJpgReadCodec::EMarkerDQT: |
|
473 ProcessQTableL(); |
|
474 break; |
|
475 |
|
476 case CJpgReadCodec::EMarkerSOF2: |
|
477 iJpgFrameInfo.iProgressive = ETrue; // Fall through to ProcessStartOfFrameL() |
|
478 case CJpgReadCodec::EMarkerSOF0: |
|
479 case CJpgReadCodec::EMarkerSOF1: |
|
480 ProcessStartOfFrameL(); |
|
481 break; |
|
482 |
|
483 case CJpgReadCodec::EMarkerSOF3: |
|
484 User::Leave(KErrNotSupported); |
|
485 break; |
|
486 |
|
487 case CJpgReadCodec::EMarkerSOS: |
|
488 ProcessStartOfScanL(); |
|
489 FinishedProcessing(); |
|
490 // Fill in image data structures and give control to the codec. |
|
491 InitialiseImageDataL(); |
|
492 return; |
|
493 |
|
494 case CJpgReadCodec::EMarkerDRI: |
|
495 ProcessRestartInterval(); |
|
496 break; |
|
497 |
|
498 case CJpgReadCodec::EMarkerCOM: |
|
499 CheckCommentBlockL(); |
|
500 ProcessCommentL(); |
|
501 break; |
|
502 |
|
503 default: |
|
504 // ProcessOtherBlockL(); |
|
505 break; |
|
506 } |
|
507 |
|
508 ptr = iPtr + (iBlockLength - 2); // Advance past this block. |
|
509 startPosition += iBlockLength + 2; // Advance past this block and the signature and length of the next block |
|
510 SetStartPosition(startPosition); |
|
511 } |
|
512 } |
|
513 |
|
514 // |
|
515 // APP markers are reserved for application use. Different applications are |
|
516 // free to use the same APP marker for different purposes. |
|
517 // In our case, we look for the EXIF header and ignore it otherwise. |
|
518 // |
|
519 void CJpegDecoder::ProcessApp1L() |
|
520 { |
|
521 if (iBlockLength < KExifHeaderLength) |
|
522 { |
|
523 return; |
|
524 } |
|
525 TPtrC8 exifHeaderPtr(iPtr, KExifHeaderLength); |
|
526 if ((KExifHeader().Compare(exifHeaderPtr) == KErrNone) && (iExifDecoder == NULL)) |
|
527 { |
|
528 // This is the EXIF header block, so instantiate the EXIF decoder object |
|
529 iExifDecoder = CExifDecoder::NewL(iBlockLength, iPtr); |
|
530 } |
|
531 } |
|
532 |
|
533 // |
|
534 // Check that length of comment block is correct, if not |
|
535 // calculate real size of comment block and adjust data read. |
|
536 // |
|
537 void CJpegDecoder::CheckCommentBlockL() |
|
538 { |
|
539 // INC076787 - |
|
540 // Allow successful decoding of a JPEG file with incorrect comment block length. |
|
541 TUint16 nextBlockSignature = PtrReadUtil::ReadBigEndianUint16(iPtr + iBlockLength - 2); |
|
542 if ((nextBlockSignature & KJpgMarkerMask) != KJpgMarker) |
|
543 { |
|
544 // Block length is incorrect. Calculate real length of comment block |
|
545 TPtrC8 commentBuffer; |
|
546 TInt realLength = 0; |
|
547 |
|
548 // First check whether the block is smaller than expected. |
|
549 nextBlockSignature = PtrReadUtil::ReadBigEndianUint16(iPtr); |
|
550 while (((nextBlockSignature & KJpgMarkerMask) != KJpgMarker) && (realLength <= (iBlockLength - 2))) |
|
551 { |
|
552 realLength++; |
|
553 nextBlockSignature = PtrReadUtil::ReadBigEndianUint16(iPtr + realLength); |
|
554 } |
|
555 if (realLength <= (iBlockLength - 2)) |
|
556 { |
|
557 // Correct block length |
|
558 iBlockLength = realLength + 2; // Add the two bytes which contain length |
|
559 } |
|
560 else // Otherwise check for more data |
|
561 { |
|
562 TInt offset = 1; |
|
563 TInt startPos = StartPosition() + iBlockLength - 2; |
|
564 |
|
565 ReadDataL(startPos + offset, commentBuffer, 2); |
|
566 if (!commentBuffer.Length()) |
|
567 { |
|
568 // Not enough data in the 'commentBuffer' |
|
569 JPEG_LEAVE(KErrUnderflow, "Not enough data"); |
|
570 } |
|
571 nextBlockSignature = PtrReadUtil::ReadBigEndianUint16(&commentBuffer[0]); |
|
572 |
|
573 while ((nextBlockSignature & KJpgMarkerMask) != KJpgMarker) |
|
574 { |
|
575 offset++; |
|
576 ReadDataL(startPos + offset, commentBuffer, 2); |
|
577 if (!commentBuffer.Length()) |
|
578 { |
|
579 // Not enough data in the 'commentBuffer' |
|
580 JPEG_LEAVE(KErrUnderflow, "Not enough data"); |
|
581 } |
|
582 nextBlockSignature = PtrReadUtil::ReadBigEndianUint16(&commentBuffer[0]); |
|
583 } |
|
584 // Correct block length |
|
585 iBlockLength += offset; |
|
586 } |
|
587 |
|
588 // Re-read the comment block again plus the signature and size of block which follows |
|
589 ReadDataL(StartPosition(), commentBuffer, iBlockLength + 2); |
|
590 iPtr = &commentBuffer[0]; |
|
591 } |
|
592 } |
|
593 |
|
594 |
|
595 CFrameInfoStrings* CJpegDecoder::FrameInfoStringsL(RFs& aFs, TInt aFrameNumber) |
|
596 { |
|
597 const TUid KJpgCodecDllUid = {KJPGCodecDllUidValue}; |
|
598 |
|
599 RResourceFile resourceFile; |
|
600 OpenExtraResourceFileLC(aFs, KJpgCodecDllUid, resourceFile); |
|
601 |
|
602 HBufC8* resourceInfo = resourceFile.AllocReadLC(THEDECODERINFO); |
|
603 TResourceReader resourceReader; |
|
604 resourceReader.SetBuffer(resourceInfo); |
|
605 |
|
606 TBuf<KCodecResourceStringMax> info; |
|
607 TBuf<KCodecResourceStringMax> templte; |
|
608 |
|
609 const TFrameInfo& frameInfo = FrameInfo(aFrameNumber); |
|
610 CFrameInfoStrings* frameInfoStrings = CFrameInfoStrings::NewLC(); |
|
611 |
|
612 info = resourceReader.ReadTPtrC(); |
|
613 frameInfoStrings->SetDecoderL(info); |
|
614 |
|
615 info = resourceReader.ReadTPtrC(); |
|
616 frameInfoStrings->SetFormatL(info); |
|
617 |
|
618 TInt width = frameInfo.iOverallSizeInPixels.iWidth; |
|
619 TInt height = frameInfo.iOverallSizeInPixels.iHeight; |
|
620 TInt depth = frameInfo.iBitsPerPixel; |
|
621 |
|
622 templte = resourceReader.ReadTPtrC(); |
|
623 info.Format(templte, width, height); |
|
624 frameInfoStrings->SetDimensionsL(info); |
|
625 |
|
626 CDesCArrayFlat* resourceArray = resourceReader.ReadDesCArrayL(); |
|
627 CleanupStack::PushL(resourceArray); |
|
628 TUint formatIndex = (frameInfo.iFlags & TFrameInfo::EColor) ? 1 : 0; |
|
629 templte = (*resourceArray)[formatIndex]; |
|
630 CleanupStack::PopAndDestroy(resourceArray); |
|
631 info.Format(templte, depth); |
|
632 frameInfoStrings->SetDepthL(info); |
|
633 |
|
634 resourceArray = resourceReader.ReadDesCArrayL(); |
|
635 CleanupStack::PushL(resourceArray); |
|
636 formatIndex = (iJpgFrameInfo.iProgressive) ? 1 : 0; |
|
637 info = (*resourceArray)[formatIndex]; |
|
638 CleanupStack::PopAndDestroy(resourceArray); |
|
639 frameInfoStrings->SetDetailsL(info); |
|
640 |
|
641 CleanupStack::Pop(frameInfoStrings); |
|
642 CleanupStack::PopAndDestroy(2); // resourceInfo + resourceFile |
|
643 return frameInfoStrings; |
|
644 } |
|
645 |
|
646 void CJpegDecoder::InitialiseImageDataL() |
|
647 { |
|
648 // Fill in image data info. |
|
649 TInt quality = 0; |
|
650 TInt tables = 0; |
|
651 TInt qTableIndex; |
|
652 for (qTableIndex = 0; qTableIndex < KJpgMaxNumberOfTables; qTableIndex++) |
|
653 { |
|
654 if (iQTable[qTableIndex].QualityFactor() > 0) |
|
655 { |
|
656 quality += iQTable[qTableIndex].QualityFactor(); |
|
657 tables++; |
|
658 } |
|
659 } |
|
660 if (tables > 0) |
|
661 quality /= tables; |
|
662 |
|
663 TJpegImageData* jpegImageData = new(ELeave) TJpegImageData; |
|
664 CleanupStack::PushL(jpegImageData); |
|
665 |
|
666 jpegImageData->iQualityFactor = quality; |
|
667 if (iJpgFrameInfo.iNumberOfComponents == 1) |
|
668 jpegImageData->iSampleScheme = TJpegImageData::EMonochrome; |
|
669 else |
|
670 { |
|
671 if (iJpgFrameInfo.iMaxHorzSampleFactor == 1 && iJpgFrameInfo.iMaxVertSampleFactor == 1) |
|
672 jpegImageData->iSampleScheme = TJpegImageData::EColor444; |
|
673 else if (iJpgFrameInfo.iMaxHorzSampleFactor == 2 && iJpgFrameInfo.iMaxVertSampleFactor == 1) |
|
674 jpegImageData->iSampleScheme = TJpegImageData::EColor422; |
|
675 else |
|
676 jpegImageData->iSampleScheme = TJpegImageData::EColor420; |
|
677 } |
|
678 |
|
679 JPEG_LEAVE_IF_ERROR(AppendImageData(jpegImageData), "AppendImageData: jpegImageData"); |
|
680 CleanupStack::Pop(jpegImageData); |
|
681 |
|
682 // Fill in QTable image data. |
|
683 for (qTableIndex = 0 ; qTableIndex < KJpgMaxNumberOfTables ; qTableIndex++) |
|
684 { |
|
685 if (iQTable[qTableIndex].QualityFactor() > 0) |
|
686 { |
|
687 TJpegQTable* jpegQTable = new(ELeave) TJpegQTable; |
|
688 CleanupStack::PushL(jpegQTable); |
|
689 |
|
690 TQTable& qTable = iQTable[qTableIndex]; |
|
691 TUint8 values[2 * KJpgQTableEntries]; |
|
692 |
|
693 const TInt precisionBytes = qTable.Get(values) / KJpgQTableEntries; |
|
694 const TUint8* zigZagPtr = KZigZagSequence.iZigZag; |
|
695 TUint8* valuePtr = values; |
|
696 const TUint8* valuePtrLimit = valuePtr + (KJpgQTableEntries * precisionBytes); |
|
697 |
|
698 while (valuePtr < valuePtrLimit) |
|
699 { |
|
700 jpegQTable->iEntries[*zigZagPtr++] = valuePtr[0]; // Lose the low byte of 16 bit precision values |
|
701 valuePtr += precisionBytes; |
|
702 } |
|
703 |
|
704 jpegQTable->iTableIndex = qTableIndex; |
|
705 JPEG_LEAVE_IF_ERROR(AppendImageData(jpegQTable), "AppendImageData: jpegQTable"); |
|
706 CleanupStack::Pop(); |
|
707 } |
|
708 } |
|
709 |
|
710 // Add the comments |
|
711 const TInt noOfComments = iComment.Count(); |
|
712 for(TInt commentNo=0; commentNo < noOfComments; commentNo++) |
|
713 { |
|
714 TJpegComment* jpegComment = new(ELeave) TJpegComment; |
|
715 CleanupStack::PushL(jpegComment); |
|
716 |
|
717 HBufC8* comment = iComment[0]; |
|
718 jpegComment->iComment = comment; |
|
719 |
|
720 JPEG_LEAVE_IF_ERROR(AppendImageDataBuffer(comment), "AppendImageDataBuffer"); |
|
721 iComment.Remove(0); |
|
722 |
|
723 JPEG_LEAVE_IF_ERROR(AppendImageData(jpegComment), "AppendImageData(jpegComment)"); |
|
724 CleanupStack::Pop(); // jpegComment |
|
725 } |
|
726 } |
|
727 |
|
728 void CJpegDecoder::ProcessApp0L() |
|
729 { |
|
730 // iBlockLength must specify at least the 2 byte |
|
731 // length field itself for Application Data! |
|
732 if (iBlockLength<2) |
|
733 { |
|
734 JPEG_LEAVE(KErrCorrupt, "Not enough data for App0"); |
|
735 } |
|
736 |
|
737 const TUint8* ptr = iPtr; |
|
738 |
|
739 TPtrC8 id(ptr, Min(KJpgApp0IdSize, iBlockLength-2)); |
|
740 |
|
741 if (id == KJfifId) |
|
742 { |
|
743 // We know jfif will always have a minimum of 14 bytes of |
|
744 // data in addition to the 2 byte length field. |
|
745 if (iBlockLength<16) |
|
746 { |
|
747 JPEG_LEAVE(KErrCorrupt, "Not enough data in App0"); |
|
748 } |
|
749 |
|
750 ptr += KJpgApp0IdSize + 1; |
|
751 TUint16 version = ReadBigEndianUint16(ptr); |
|
752 if (version != KJfifVersion0100 && |
|
753 version != KJfifVersion0101 && |
|
754 version != KJfifVersion0102) |
|
755 { |
|
756 JPEG_LEAVE(KErrNotSupported, "Unsupported jfif version"); |
|
757 } |
|
758 |
|
759 iJpgFrameInfo.iUnits = TJpgFrameInfo::TJpgImageUnits(*ptr++); |
|
760 iJpgFrameInfo.iSizeInTwips.iWidth = ReadBigEndianUint16(ptr); |
|
761 iJpgFrameInfo.iSizeInTwips.iHeight = ReadBigEndianUint16(ptr); |
|
762 iJpgFrameInfo.iThumbnailSize.iWidth = *ptr++; |
|
763 iJpgFrameInfo.iThumbnailSize.iHeight = *ptr; |
|
764 |
|
765 JPEG_DEBUG2("Frame units: %d", iJpgFrameInfo.iUnits); |
|
766 JPEG_DEBUG2("Frame twips width: %d", iJpgFrameInfo.iSizeInTwips.iWidth); |
|
767 JPEG_DEBUG2("Frame twips height: %d", iJpgFrameInfo.iSizeInTwips.iHeight); |
|
768 JPEG_DEBUG2("Frame thumbnail width: %d", iJpgFrameInfo.iThumbnailSize.iWidth); |
|
769 JPEG_DEBUG2("Frame twips width: %d", iJpgFrameInfo.iThumbnailSize.iHeight); |
|
770 |
|
771 // skip over JFIF thumbnail data without storing it |
|
772 // there are currently no client APIs for accessing it |
|
773 ptr += iJpgFrameInfo.iThumbnailSize.iWidth * iJpgFrameInfo.iThumbnailSize.iHeight * 3; |
|
774 |
|
775 iJFIFMarkerPresent = ETrue; |
|
776 } |
|
777 } |
|
778 |
|
779 void CJpegDecoder::ProcessAppEL() |
|
780 { |
|
781 const TUint8* ptr = iPtr; |
|
782 |
|
783 TPtrC8 id(ptr,KJpgAppEIdSize); |
|
784 |
|
785 if (id == KAdobeId) |
|
786 { |
|
787 iAdobeMarkerPresent = ETrue; |
|
788 iAdobeTransform = ptr[11]; |
|
789 } |
|
790 } |
|
791 |
|
792 void CJpegDecoder::ProcessHuffmanTableL() |
|
793 { |
|
794 const TUint8* dataPtr = iPtr; |
|
795 const TUint8* dataPtrLimit = dataPtr + iBlockLength - 2; |
|
796 THuffmanTableProcessor::ProcessHuffmanTableL(dataPtr, dataPtrLimit, iDCHuffmanTable, iACHuffmanTable); |
|
797 } |
|
798 |
|
799 void CJpegDecoder::ProcessQTableL() |
|
800 { |
|
801 const TUint8* dataPtr = iPtr; |
|
802 const TUint8* dataPtrLimit = dataPtr + iBlockLength - 2; |
|
803 |
|
804 while (dataPtr < dataPtrLimit) |
|
805 { |
|
806 TInt index = *dataPtr++; |
|
807 TBool sixteenBitPrecision = index & 0x10; |
|
808 index &= 0x0f; |
|
809 if (index >= KJpgMaxNumberOfTables) |
|
810 { |
|
811 JPEG_LEAVE(KErrCorrupt, "table index out of range"); |
|
812 } |
|
813 |
|
814 dataPtr += iQTable[index].Set(dataPtr,sixteenBitPrecision); |
|
815 } |
|
816 } |
|
817 |
|
818 void CJpegDecoder::ProcessStartOfFrameL() |
|
819 { |
|
820 const TUint8* ptr = iPtr; |
|
821 |
|
822 iJpgFrameInfo.iSamplePrecision = *ptr++; |
|
823 if (iJpgFrameInfo.iSamplePrecision != 8) |
|
824 { |
|
825 JPEG_LEAVE(KErrNotSupported, "Unsupported sample precision"); |
|
826 } |
|
827 |
|
828 iJpgFrameInfo.iSizeInPixels.iHeight = ReadBigEndianUint16(ptr); |
|
829 iJpgFrameInfo.iSizeInPixels.iWidth = ReadBigEndianUint16(ptr); |
|
830 |
|
831 JPEG_DEBUG2("Frame pixel width: %d", iJpgFrameInfo.iSizeInPixels.iWidth); |
|
832 JPEG_DEBUG2("Frame pixel height: %d", iJpgFrameInfo.iSizeInPixels.iHeight); |
|
833 |
|
834 if ((iJpgFrameInfo.iSizeInPixels.iHeight <= 0) || (iJpgFrameInfo.iSizeInPixels.iWidth <= 0)) |
|
835 { |
|
836 JPEG_LEAVE(KErrCorrupt, "Invalid pixel height or width"); |
|
837 } |
|
838 |
|
839 if (iJpgFrameInfo.iSizeInTwips.iWidth > 0 && iJpgFrameInfo.iSizeInTwips.iHeight > 0) |
|
840 { |
|
841 if (iJpgFrameInfo.iUnits == TJpgFrameInfo::EDotsPerInch) |
|
842 { |
|
843 iJpgFrameInfo.iSizeInTwips.iWidth = iJpgFrameInfo.iSizeInPixels.iWidth * KTwipsPerInch / iJpgFrameInfo.iSizeInTwips.iWidth; |
|
844 iJpgFrameInfo.iSizeInTwips.iHeight = iJpgFrameInfo.iSizeInPixels.iHeight * KTwipsPerInch / iJpgFrameInfo.iSizeInTwips.iHeight; |
|
845 } |
|
846 else if (iJpgFrameInfo.iUnits == TJpgFrameInfo::EDotsPerCm) |
|
847 { |
|
848 iJpgFrameInfo.iSizeInTwips.iWidth = iJpgFrameInfo.iSizeInPixels.iWidth * KTwipsPerCm / iJpgFrameInfo.iSizeInTwips.iWidth; |
|
849 iJpgFrameInfo.iSizeInTwips.iHeight = iJpgFrameInfo.iSizeInPixels.iHeight * KTwipsPerCm / iJpgFrameInfo.iSizeInTwips.iHeight; |
|
850 } |
|
851 else |
|
852 iJpgFrameInfo.iSizeInTwips.SetSize(0,0); |
|
853 } |
|
854 |
|
855 iJpgFrameInfo.iMaxHorzSampleFactor = 0; |
|
856 iJpgFrameInfo.iMaxVertSampleFactor = 0; |
|
857 iJpgFrameInfo.iNumberOfComponents = *ptr++; |
|
858 JPEG_DEBUG2("Frame number of components: %d", iJpgFrameInfo.iNumberOfComponents); |
|
859 |
|
860 //At least one component and not more than we can cope with |
|
861 if(iJpgFrameInfo.iNumberOfComponents < KJpgMinNumberOfComponents) |
|
862 { |
|
863 JPEG_LEAVE(KErrCorrupt, "Not enough components"); |
|
864 } |
|
865 |
|
866 if ((iJpgFrameInfo.iNumberOfComponents != KJpgMinNumberOfComponents) && (iJpgFrameInfo.iNumberOfComponents != KJpgNumberOfComponents)) |
|
867 { |
|
868 JPEG_LEAVE(KErrNotSupported, "Bad number of components"); |
|
869 } |
|
870 |
|
871 for (TInt count = 0; count < iJpgFrameInfo.iNumberOfComponents; count++) |
|
872 { |
|
873 TJpgFrameInfo::TComponentInfo& compInfo = iJpgFrameInfo.iComponent[count]; |
|
874 compInfo.iId = *ptr++; |
|
875 TUint8 sampleFactor = *ptr++; |
|
876 |
|
877 compInfo.iHorzSampleFactor = sampleFactor >> 4; |
|
878 if(compInfo.iHorzSampleFactor < KJpgMinSampleFactor || compInfo.iHorzSampleFactor > KJpgMaxSampleFactor) |
|
879 { |
|
880 JPEG_LEAVE(KErrCorrupt, "Bad horizontal sample factor"); |
|
881 } |
|
882 |
|
883 compInfo.iVertSampleFactor = sampleFactor & 0x0f; |
|
884 if(compInfo.iVertSampleFactor < KJpgMinSampleFactor || compInfo.iVertSampleFactor > KJpgMaxSampleFactor) |
|
885 { |
|
886 JPEG_LEAVE(KErrCorrupt, "Bad vertical sample factor"); |
|
887 } |
|
888 |
|
889 compInfo.iQTable = *ptr++; |
|
890 |
|
891 // Number of tables should be in range (0..3) |
|
892 if ((compInfo.iQTable < 0) || (compInfo.iQTable > (KJpgMaxNumberOfTables - 1))) |
|
893 { |
|
894 JPEG_LEAVE(KErrCorrupt, "Bad QTable index"); |
|
895 } |
|
896 |
|
897 iJpgFrameInfo.iMaxHorzSampleFactor = Max(iJpgFrameInfo.iMaxHorzSampleFactor,compInfo.iHorzSampleFactor); |
|
898 iJpgFrameInfo.iMaxVertSampleFactor = Max(iJpgFrameInfo.iMaxVertSampleFactor,compInfo.iVertSampleFactor); |
|
899 } |
|
900 |
|
901 if (iJpgFrameInfo.iNumberOfComponents == 1) |
|
902 { |
|
903 iJpgFrameInfo.iComponent[0].iHorzSampleFactor = 1; |
|
904 iJpgFrameInfo.iComponent[0].iVertSampleFactor = 1; |
|
905 iJpgFrameInfo.iMaxHorzSampleFactor = 1; |
|
906 iJpgFrameInfo.iMaxVertSampleFactor = 1; |
|
907 } |
|
908 else if (!iJFIFMarkerPresent) |
|
909 { |
|
910 if (iAdobeMarkerPresent && (iAdobeTransform != KAdobeColorTransformYCbCr)) |
|
911 { |
|
912 JPEG_LEAVE(KErrNotSupported, "Unsupported Adobe transform"); |
|
913 } |
|
914 else if (!iAdobeMarkerPresent) |
|
915 { |
|
916 // No markers present, so check for RGB and YCC, which are not supported. |
|
917 if ((iJpgFrameInfo.iComponent[0].iId == 82) && (iJpgFrameInfo.iComponent[1].iId == 71) && (iJpgFrameInfo.iComponent[2].iId == 66)) |
|
918 { |
|
919 JPEG_LEAVE(KErrNotSupported, "RGB and YCC not supported"); |
|
920 } |
|
921 |
|
922 if ((iJpgFrameInfo.iComponent[0].iId == 89) && (iJpgFrameInfo.iComponent[1].iId == 67) && (iJpgFrameInfo.iComponent[2].iId == 99)) |
|
923 { |
|
924 JPEG_LEAVE(KErrNotSupported, "RGB and YCC not supported"); |
|
925 } |
|
926 } |
|
927 } |
|
928 |
|
929 JPEG_DEBUG2("Frame max horiz sample factor: %d", iJpgFrameInfo.iMaxHorzSampleFactor); |
|
930 JPEG_DEBUG2("Frame max vert sample factor: %d", iJpgFrameInfo.iMaxHorzSampleFactor); |
|
931 } |
|
932 |
|
933 void CJpegDecoder::ProcessStartOfScanL() |
|
934 { |
|
935 const TUint8* ptr = iPtr; |
|
936 iImageOffset = StartPosition() + iBlockLength - 2; |
|
937 iScanInfo.iImageOffset = iImageOffset; |
|
938 TJpgScanInfoProcessor::ProcessStartOfScanL(ptr, iJpgFrameInfo, iScanInfo, iDCHuffmanTable, iACHuffmanTable); |
|
939 } |
|
940 |
|
941 void CJpegDecoder::ProcessRestartInterval() |
|
942 { |
|
943 const TUint8* ptr = iPtr; |
|
944 iJpgFrameInfo.iRestartInterval = ReadBigEndianUint16(ptr); |
|
945 JPEG_DEBUG2("Frame restart interval: %d", iJpgFrameInfo.iRestartInterval); |
|
946 } |
|
947 |
|
948 void CJpegDecoder::ProcessCommentL() |
|
949 { |
|
950 if(iBlockLength < 2) // need at least 2 bytes for length field |
|
951 { |
|
952 JPEG_LEAVE(KErrCorrupt, "Not enough data for comment"); |
|
953 } |
|
954 |
|
955 TPtrC8 commentDes(iPtr, iBlockLength - 2); |
|
956 HBufC8* comment = commentDes.AllocLC(); |
|
957 JPEG_LEAVE_IF_ERROR(iComment.Append(comment), "Appending comment"); |
|
958 CleanupStack::Pop(comment); |
|
959 } |
|
960 |
|
961 void CJpegDecoder::FinishedProcessing() |
|
962 { |
|
963 SetStartPosition(iImageOffset); |
|
964 SetDataLength(KMaxTInt); |
|
965 |
|
966 TFrameInfo imageInfo; |
|
967 imageInfo = ImageInfo(); |
|
968 imageInfo.iFrameCoordsInPixels.SetRect(TPoint(0,0),iJpgFrameInfo.iSizeInPixels); |
|
969 imageInfo.iOverallSizeInPixels = iJpgFrameInfo.iSizeInPixels; |
|
970 |
|
971 imageInfo.iFrameSizeInTwips = iJpgFrameInfo.iSizeInTwips; |
|
972 imageInfo.iBitsPerPixel = 8 * iJpgFrameInfo.iNumberOfComponents; |
|
973 imageInfo.iDelay = 0; |
|
974 imageInfo.iFlags = TFrameInfo::ECanDither; |
|
975 if (iJpgFrameInfo.iNumberOfComponents > 1) |
|
976 imageInfo.iFlags |= TFrameInfo::EColor; |
|
977 |
|
978 // Set display mode. |
|
979 switch (imageInfo.iBitsPerPixel) |
|
980 { |
|
981 case 1: |
|
982 imageInfo.iFrameDisplayMode = EGray2; |
|
983 break; |
|
984 |
|
985 case 2: |
|
986 imageInfo.iFrameDisplayMode = EGray4; |
|
987 break; |
|
988 |
|
989 case 4: |
|
990 imageInfo.iFrameDisplayMode = (imageInfo.iFlags & TFrameInfo::EColor) ? EColor16 : EGray16; |
|
991 break; |
|
992 |
|
993 case 8: |
|
994 imageInfo.iFrameDisplayMode = (imageInfo.iFlags & TFrameInfo::EColor) ? EColor256 : EGray256; |
|
995 break; |
|
996 |
|
997 case 12: |
|
998 imageInfo.iFrameDisplayMode = EColor4K; |
|
999 break; |
|
1000 |
|
1001 case 16: |
|
1002 imageInfo.iFrameDisplayMode = EColor64K; |
|
1003 break; |
|
1004 |
|
1005 case 24: |
|
1006 imageInfo.iFrameDisplayMode = EColor16M; |
|
1007 break; |
|
1008 } |
|
1009 |
|
1010 if ((DecoderOptions() & CImageDecoder::EOptionAutoRotate) && iExifDecoder) |
|
1011 { |
|
1012 TUint16 orientation = 0; |
|
1013 |
|
1014 // if orientation tag is not found in the Ifd it is 0 |
|
1015 if (iImageType == CImageDecoder::EImageTypeMain) |
|
1016 { |
|
1017 iExifDecoder->GetShortParam(0x0112, KExifIfdZero, orientation); |
|
1018 } |
|
1019 else if (iImageType == CImageDecoder::EImageTypeThumbnail) |
|
1020 { |
|
1021 iExifDecoder->GetShortParam(0x0112, KExifIfdOne, orientation); |
|
1022 } |
|
1023 |
|
1024 // check for corrupted orientation tag value |
|
1025 if (orientation > 8) |
|
1026 { |
|
1027 orientation = 0; |
|
1028 } |
|
1029 |
|
1030 if (orientation >= 4 && orientation <= 8) |
|
1031 { |
|
1032 imageInfo.iOverallSizeInPixels.iWidth = iJpgFrameInfo.iSizeInPixels.iHeight; |
|
1033 imageInfo.iOverallSizeInPixels.iHeight = iJpgFrameInfo.iSizeInPixels.iWidth; |
|
1034 // not sure we need the lines below but just in case |
|
1035 imageInfo.iFrameSizeInTwips.iWidth = iJpgFrameInfo.iSizeInTwips.iHeight; |
|
1036 imageInfo.iFrameSizeInTwips.iHeight = iJpgFrameInfo.iSizeInTwips.iWidth; |
|
1037 imageInfo.iFrameSizeInPixels.iWidth = iJpgFrameInfo.iSizeInPixels.iHeight; |
|
1038 imageInfo.iFrameSizeInPixels.iHeight = iJpgFrameInfo.iSizeInPixels.iWidth; |
|
1039 imageInfo.iFrameCoordsInPixels.SetRect(TPoint(0, 0), TPoint(iJpgFrameInfo.iSizeInPixels.iHeight, iJpgFrameInfo.iSizeInPixels.iWidth)); |
|
1040 } |
|
1041 |
|
1042 iAutoRotateFlag = orientation; |
|
1043 } |
|
1044 |
|
1045 SetImageInfo(imageInfo); |
|
1046 } |
|
1047 |
|
1048 void CJpegDecoder::InitCustomAsyncL(TInt aParam) |
|
1049 { |
|
1050 if (aParam == KOptionConvertFrameUidValue) |
|
1051 { |
|
1052 HandleConvertFrameL(); |
|
1053 } |
|
1054 else if (aParam == KOptionContinueConvertFrameUidValue) |
|
1055 { |
|
1056 HandleContinueConvertFrameL(); |
|
1057 } |
|
1058 } |
|
1059 |
|
1060 void CJpegDecoder::HandleConvertFrameL() |
|
1061 { |
|
1062 CImageDecoderPlugin::RequestInitL(FrameNumber()); |
|
1063 |
|
1064 CJpgReadCodec* jpgReadCodec = reinterpret_cast<CJpgReadCodec*>(ImageReadCodec()); |
|
1065 JPEG_ASSERT(jpgReadCodec); |
|
1066 jpgReadCodec->InitFrameL(DstImageFrame()); |
|
1067 } |
|
1068 |
|
1069 void CJpegDecoder::HandleContinueConvertFrameL() |
|
1070 { |
|
1071 CJpgReadCodec* jpgReadCodec = reinterpret_cast<CJpgReadCodec*>(ImageReadCodec()); |
|
1072 JPEG_ASSERT(jpgReadCodec); |
|
1073 CJpgImageFrameReadCodec* imageframeCodec = jpgReadCodec->ImageFrameCodec(); |
|
1074 if(imageframeCodec == NULL) |
|
1075 { |
|
1076 // ConvertFrame() should have been called to initiate the image frame processor |
|
1077 // before calling ContinueConvertFrame() |
|
1078 JPEG_LEAVE(KErrNotReady, "Continue called before Convert"); |
|
1079 } |
|
1080 else |
|
1081 { |
|
1082 if (imageframeCodec->Destination() == NULL) |
|
1083 { |
|
1084 // ConvertFrame() should have been called to initiate the destination |
|
1085 // before calling ContinueConvertFrame() |
|
1086 JPEG_LEAVE(KErrNotReady, "Continue called before Convert"); |
|
1087 } |
|
1088 } |
|
1089 } |
|
1090 |
|
1091 void CJpegDecoder::HandleCustomSyncL(TInt aParam) |
|
1092 { |
|
1093 CJpgReadCodec* codec = reinterpret_cast<CJpgReadCodec*>(ImageReadCodec()); |
|
1094 JPEG_ASSERT(codec); |
|
1095 |
|
1096 if(aParam == CJpegYuvDecoder::EOptionYuvDecode) |
|
1097 { |
|
1098 codec->SetYuvDecode(ETrue); |
|
1099 } |
|
1100 else if(aParam == KOptionRecommendBufferSizeUidValue) |
|
1101 { |
|
1102 TUid formatCode = FormatCode(); |
|
1103 TInt bufferSize = codec->RecommendBufferSizeL(formatCode); |
|
1104 SetRecommendedBufferSize(bufferSize); |
|
1105 } |
|
1106 else |
|
1107 { |
|
1108 JPEG_LEAVE(KErrNotSupported, "Unsupported custom sync"); |
|
1109 } |
|
1110 } |
|
1111 |
|
1112 void CJpegDecoder::NotifyComplete() |
|
1113 { |
|
1114 iState = EStateStart; // ensure that however we exit one run, start at top of next DoConvert() |
|
1115 CJpgReadCodec* jpgReadCodec = REINTERPRET_CAST(CJpgReadCodec*, ImageReadCodec()); |
|
1116 JPEG_ASSERT(jpgReadCodec); |
|
1117 |
|
1118 if (iCodecState == EFrameComplete) |
|
1119 { |
|
1120 // should be safe to clear buffers here |
|
1121 jpgReadCodec->CleanupBuffers(); |
|
1122 } |
|
1123 |
|
1124 jpgReadCodec->SetYuvDecode(EFalse); |
|
1125 } |
|
1126 |
|
1127 // System Wide Define |
|
1128 #ifdef SYMBIAN_ENABLE_1630_JPEG_EXTENSIONS |
|
1129 void CJpegDecoder::SetClippingRectL(const TRect* aRect) |
|
1130 { |
|
1131 RPointerArray<TFrameInfo> frameInfo; |
|
1132 CleanupClosePushL(frameInfo); |
|
1133 |
|
1134 // JPEG only has a single frame |
|
1135 frameInfo.AppendL(&FrameInfo(0)); |
|
1136 |
|
1137 // The clipping operation is reset by passing a NULL rect pointer. |
|
1138 ASSERT(iExtensionManager); |
|
1139 iExtensionManager->SetClippingRectL(aRect, frameInfo); |
|
1140 CleanupStack::PopAndDestroy(); // frameInfo |
|
1141 } |
|
1142 #else |
|
1143 void CJpegDecoder::SetClippingRectL(const TRect* /*aRect*/) |
|
1144 { |
|
1145 User::Leave(KErrNotSupported); |
|
1146 } |
|
1147 #endif |
|
1148 |
|
1149 TInt CJpegDecoder::SamplingScheme(TJpegImageData::TColorSampling& aSamplingScheme) const |
|
1150 { |
|
1151 ASSERT(iJpgFrameInfo.iMaxHorzSampleFactor != 0); |
|
1152 ASSERT(iJpgFrameInfo.iMaxVertSampleFactor != 0); |
|
1153 |
|
1154 TInt retValue = KErrNotSupported; |
|
1155 if (iJpgFrameInfo.iNumberOfComponents == 1) |
|
1156 { |
|
1157 aSamplingScheme = TJpegImageData::EMonochrome; |
|
1158 retValue = KErrNone; |
|
1159 } |
|
1160 else |
|
1161 { |
|
1162 if (iJpgFrameInfo.iMaxHorzSampleFactor == 1 && iJpgFrameInfo.iMaxVertSampleFactor == 1) |
|
1163 { |
|
1164 aSamplingScheme = TJpegImageData::EColor444; |
|
1165 retValue = KErrNone; |
|
1166 } |
|
1167 else if (iJpgFrameInfo.iMaxHorzSampleFactor == 2 && iJpgFrameInfo.iMaxVertSampleFactor == 1) |
|
1168 { |
|
1169 aSamplingScheme = TJpegImageData::EColor422; |
|
1170 retValue = KErrNone; |
|
1171 } |
|
1172 else if (iJpgFrameInfo.iMaxHorzSampleFactor == 2 && iJpgFrameInfo.iMaxVertSampleFactor == 2) |
|
1173 { |
|
1174 aSamplingScheme = TJpegImageData::EColor420; |
|
1175 retValue = KErrNone; |
|
1176 } |
|
1177 } |
|
1178 return retValue; |
|
1179 } |
|
1180 |
|
1181 void CJpegEncoder::WriteThumbnailL() |
|
1182 { |
|
1183 iThumbnailSize=0; |
|
1184 if(iGenerateThumbnail) |
|
1185 { |
|
1186 if (iExifEncoder) |
|
1187 { |
|
1188 // EXIF thumbnail is surrounded by SOI and EOI |
|
1189 iThumbnailSize += WriteSOIL(Position()); |
|
1190 |
|
1191 // encode and write the thumbnail |
|
1192 iThumbnailSize += WriteThumbnailDataL(); |
|
1193 |
|
1194 iThumbnailSize += WriteEOIL(Position()); |
|
1195 } |
|
1196 else |
|
1197 { |
|
1198 iThumbnailSize += WriteThumbnailDataL(); |
|
1199 } |
|
1200 } |
|
1201 |
|
1202 StartPosition() = Position(); |
|
1203 Position()=0; |
|
1204 } |
|
1205 |
|
1206 |
|
1207 void CJpegEncoder::CreateScaledBitmapL(TRequestStatus*& aScaleCompletionStatus) |
|
1208 { |
|
1209 // Create the Scaled Thumbnail |
|
1210 iSourceThumbnailImage=NULL; |
|
1211 if(iThumbnailImage) |
|
1212 { |
|
1213 delete iThumbnailImage; |
|
1214 iThumbnailImage=NULL; |
|
1215 } |
|
1216 if(iBitmapScaler) |
|
1217 { |
|
1218 delete iBitmapScaler; |
|
1219 iBitmapScaler=NULL; |
|
1220 } |
|
1221 iThumbnailImage=new (ELeave) CFbsBitmap; |
|
1222 |
|
1223 CImageWriteCodec* imageWriteCodec=ImageWriteCodec(); |
|
1224 JPEG_ASSERT(imageWriteCodec); |
|
1225 |
|
1226 |
|
1227 // the scaler does not modify the source bmp, so we apply it directly on this |
|
1228 iSourceThumbnailImage=const_cast<CFbsBitmap*>(imageWriteCodec->Source()); |
|
1229 |
|
1230 if (iFrameInfo.iNumberOfComponents == 1) |
|
1231 { |
|
1232 //DEF113733: Monochrome image conversion so thumbnail should be grayscale |
|
1233 TInt err = iThumbnailImage->Create(TSize(KThumbnailWidth, KThumbnailHeight), EGray256); |
|
1234 JPEG_LEAVE_IF_ERROR(err, "Failed to create thumbnail bitmap"); |
|
1235 } |
|
1236 else |
|
1237 { |
|
1238 TInt err = iThumbnailImage->Create(TSize(KThumbnailWidth, KThumbnailHeight), KThumbnailDisplayMode); |
|
1239 JPEG_LEAVE_IF_ERROR(err, "Failed to create thumbnail bitmap"); |
|
1240 } |
|
1241 |
|
1242 iBitmapScaler=CBitmapScaler::NewL(); |
|
1243 *aScaleCompletionStatus=KRequestPending; |
|
1244 iBitmapScaler->Scale(aScaleCompletionStatus, *iSourceThumbnailImage, *iThumbnailImage); |
|
1245 } |
|
1246 |
|
1247 void CJpegEncoder::Cleanup() |
|
1248 { |
|
1249 if (iBitmapScaler) |
|
1250 { |
|
1251 iBitmapScaler->Cancel(); |
|
1252 } |
|
1253 CImageEncoderPlugin::Cleanup(); |
|
1254 } |
|
1255 |
|
1256 void CJpegEncoder::TransformBitmapL(CFbsBitmap& aSource, CFbsBitmap& aDest) |
|
1257 { |
|
1258 TPositionProcessor posProcess; |
|
1259 TPoint pos; |
|
1260 |
|
1261 TUint8 *sourceBitmap = reinterpret_cast<TUint8*>( aSource.DataAddress() ); |
|
1262 |
|
1263 CJpgWriteCodec::InitTransformCoordinates(posProcess, TRect(TPoint(0,0),TPoint(aSource.SizeInPixels().iWidth, aSource.SizeInPixels().iHeight)), TSize(KJpgPixelRatio, KJpgPixelRatio), iOperationExtPtr->iImgConvOperations); |
|
1264 |
|
1265 if(posProcess.SwapDimensions()) |
|
1266 { |
|
1267 User::LeaveIfError(aDest.Create(TSize(iThumbnailImage->SizeInPixels().iHeight, iThumbnailImage->SizeInPixels().iWidth), KThumbnailDisplayMode)); |
|
1268 } |
|
1269 else |
|
1270 { |
|
1271 User::LeaveIfError(aDest.Create(iThumbnailImage->SizeInPixels(), KThumbnailDisplayMode)); |
|
1272 TInt tempDimension = iFrameInfo.iSizeInPixels.iHeight; |
|
1273 iFrameInfo.iSizeInPixels.iHeight = iFrameInfo.iSizeInPixels.iWidth; // Y |
|
1274 iFrameInfo.iSizeInPixels.iWidth = tempDimension; // X |
|
1275 } |
|
1276 |
|
1277 TUint8 *destBitmap = reinterpret_cast<TUint8*>( aDest.DataAddress() ); |
|
1278 |
|
1279 TInt srcScanlineWidth = aSource.ScanLineLength(aSource.SizeInPixels().iWidth, KThumbnailDisplayMode); |
|
1280 TInt destScanlineWidth = aDest.ScanLineLength(aDest.SizeInPixels().iWidth, KThumbnailDisplayMode); |
|
1281 |
|
1282 TInt destWidthCount = 0; |
|
1283 TInt destWidth = aDest.SizeInPixels().iWidth; |
|
1284 |
|
1285 do |
|
1286 { |
|
1287 posProcess.GetCurrentPosition(pos); |
|
1288 TUint8* srcPtr = (sourceBitmap + (pos.iX + (pos.iY * (srcScanlineWidth)))); |
|
1289 if(++destWidthCount >= destWidth) |
|
1290 { |
|
1291 destBitmap = destBitmap + (destScanlineWidth - destWidth); //move the destination bitmap pointer past the padded width to make sure we don't write actual pixel data on target padded portion. Works only when thumbnail bitmap diaplay mode is EColor256. |
|
1292 destWidthCount = 0; |
|
1293 } |
|
1294 *destBitmap++ = *srcPtr; |
|
1295 posProcess.MoveNext(); |
|
1296 } while (!posProcess.IsEndOfImage()); |
|
1297 } |
|
1298 |
|
1299 TInt CJpegEncoder::WriteThumbnailDataL() |
|
1300 { |
|
1301 //TUint8 cast only works because src and dest bitmap are EColor256 |
|
1302 ASSERT( KThumbnailDisplayMode == EColor256); |
|
1303 |
|
1304 // iSourceThumbnailImage is a pointer on the image of the codec |
|
1305 // we just need to nullify if |
|
1306 iSourceThumbnailImage=NULL; |
|
1307 |
|
1308 if(iBitmapScaler) |
|
1309 { |
|
1310 delete iBitmapScaler; |
|
1311 iBitmapScaler=NULL; |
|
1312 } |
|
1313 if(iThumbnailImage==NULL) |
|
1314 { |
|
1315 User::Leave(KErrNotFound); |
|
1316 } |
|
1317 |
|
1318 TInt writtenSize=0; |
|
1319 |
|
1320 CFbsBitmap* tranformedThumbnailImage = NULL; |
|
1321 CFbsBitmap* thumbImage = NULL; |
|
1322 |
|
1323 thumbImage = iThumbnailImage; |
|
1324 |
|
1325 if(iOperationExtPtr) |
|
1326 { |
|
1327 tranformedThumbnailImage = new (ELeave) CFbsBitmap; |
|
1328 CleanupStack::PushL(tranformedThumbnailImage); |
|
1329 TransformBitmapL(*iThumbnailImage, *tranformedThumbnailImage); |
|
1330 thumbImage = tranformedThumbnailImage; |
|
1331 } |
|
1332 |
|
1333 |
|
1334 if (iExifEncoder) |
|
1335 { |
|
1336 // write thumbnail as EXIF |
|
1337 writtenSize = WriteThumbnailAsExifL(*thumbImage); |
|
1338 } |
|
1339 else |
|
1340 { |
|
1341 // write thumbnail as JFIF |
|
1342 writtenSize = WriteThumbnailAsJfifL(*thumbImage); |
|
1343 } |
|
1344 |
|
1345 delete iThumbnailImage; |
|
1346 iThumbnailImage = NULL; |
|
1347 if(iOperationExtPtr) |
|
1348 { |
|
1349 CleanupStack::PopAndDestroy(tranformedThumbnailImage); |
|
1350 } |
|
1351 |
|
1352 return writtenSize; |
|
1353 } |
|
1354 |
|
1355 TInt CJpegEncoder::WriteThumbnailAsExifL(CFbsBitmap& aBitmap) |
|
1356 { |
|
1357 TInt writtenSize=0; |
|
1358 CImageWriteCodec* thumbnailImageWriteCodec; |
|
1359 |
|
1360 thumbnailImageWriteCodec = CJpgWriteCodec::NewL(iFrameInfo,iQualityFactor,iLumaTable,iChromaTable,iComment,NULL); |
|
1361 |
|
1362 CleanupStack::PushL(thumbnailImageWriteCodec); |
|
1363 // create the buffer for the thumbnail data |
|
1364 HBufC8* encodeBuffer=HBufC8::NewL(KIOBlockSize); |
|
1365 CleanupStack::PushL(encodeBuffer); |
|
1366 |
|
1367 TBufPtr8 encodeBufPtr; |
|
1368 encodeBuffer->Des().FillZ(); |
|
1369 encodeBufPtr.Set(encodeBuffer->Des()); |
|
1370 |
|
1371 thumbnailImageWriteCodec->InitFrameL(encodeBufPtr, aBitmap); |
|
1372 |
|
1373 WriteDataPositionIncL(Position(), encodeBufPtr); |
|
1374 writtenSize+=encodeBufPtr.Length()*sizeof(TUint8); |
|
1375 |
|
1376 TFrameState frameErr; |
|
1377 while((frameErr=thumbnailImageWriteCodec->ProcessFrameL(encodeBufPtr)) == EFrameIncomplete) |
|
1378 { |
|
1379 WriteDataPositionIncL(Position(), encodeBufPtr); |
|
1380 writtenSize+=encodeBufPtr.Length()*sizeof(TUint8); |
|
1381 } |
|
1382 |
|
1383 switch(frameErr) |
|
1384 { |
|
1385 case EFrameComplete: |
|
1386 // happy ending |
|
1387 WriteDataPositionIncL(Position(), encodeBufPtr); |
|
1388 writtenSize+=encodeBufPtr.Length()*sizeof(TUint8); |
|
1389 break; |
|
1390 case EUnexpectedEndOfData: |
|
1391 case EFrameIncompleteRepositionRequest: |
|
1392 User::Leave(KErrEof); |
|
1393 break; |
|
1394 default: |
|
1395 User::Leave(KErrUnknown); |
|
1396 break; |
|
1397 } |
|
1398 |
|
1399 CleanupStack::PopAndDestroy(encodeBuffer); |
|
1400 CleanupStack::PopAndDestroy(thumbnailImageWriteCodec); |
|
1401 |
|
1402 return writtenSize; |
|
1403 } |
|
1404 |
|
1405 |
|
1406 TInt CJpegEncoder::WriteThumbnailAsJfifL(CFbsBitmap& aBitmap) |
|
1407 { |
|
1408 TInt writtenSize=0; |
|
1409 // CBitmapScaler might not produce exact KThumbnailWidth * KThumbnailHeight |
|
1410 // so get the actual size |
|
1411 iGeneratedThumbnailSize = aBitmap.SizeInPixels(); |
|
1412 |
|
1413 HBufC8* buffer = HBufC8::NewL(iGeneratedThumbnailSize.iWidth * iGeneratedThumbnailSize.iHeight * 3); |
|
1414 CleanupStack::PushL(buffer); |
|
1415 TBufPtr8 bufferPtr; |
|
1416 bufferPtr.Set(buffer->Des()); |
|
1417 |
|
1418 TPoint currentPos(0, 0); |
|
1419 TBitmapUtil bitmapUtil(&aBitmap); |
|
1420 bitmapUtil.Begin(currentPos); |
|
1421 for (; currentPos.iY < iGeneratedThumbnailSize.iHeight; currentPos.iY++) |
|
1422 { |
|
1423 currentPos.iX = 0; |
|
1424 bitmapUtil.SetPos(currentPos); |
|
1425 TRgb currentPixel; |
|
1426 for (; currentPos.iX < iGeneratedThumbnailSize.iWidth; currentPos.iX++) |
|
1427 { |
|
1428 if (iFrameInfo.iNumberOfComponents == 1) |
|
1429 { |
|
1430 //DEF113733: if number of components is 1, we want to use grey scale pixels |
|
1431 currentPixel = TRgb::Gray256(bitmapUtil.GetPixel()); |
|
1432 } |
|
1433 else |
|
1434 { |
|
1435 currentPixel = TRgb::Color256(bitmapUtil.GetPixel()); |
|
1436 } |
|
1437 |
|
1438 bufferPtr.Append(currentPixel.Red()); |
|
1439 bufferPtr.Append(currentPixel.Green()); |
|
1440 bufferPtr.Append(currentPixel.Blue()); |
|
1441 |
|
1442 bitmapUtil.IncXPos(); |
|
1443 } |
|
1444 } |
|
1445 bitmapUtil.End(); |
|
1446 |
|
1447 WriteDataPositionIncL(Position(), bufferPtr); |
|
1448 writtenSize += bufferPtr.Length() * sizeof(TUint8); |
|
1449 |
|
1450 CleanupStack::PopAndDestroy(buffer); |
|
1451 |
|
1452 return writtenSize; |
|
1453 } |
|
1454 |
|
1455 void CJpegEncoder::WriteExifDataL(TRequestStatus*& aScaleCompletionStatus) |
|
1456 { |
|
1457 User::LeaveIfError( iThumbnailStatus ); |
|
1458 |
|
1459 if (iExifEncoder) |
|
1460 { |
|
1461 // we get the exifData |
|
1462 // exif data is own by the exif encoder. We don't push it on the cleanupstack |
|
1463 iExifDataPosition=Position(); |
|
1464 TPtrC8 exifDataStr=iExifEncoder->CreateExifHeaderL(); |
|
1465 // and write it |
|
1466 iExifSize=exifDataStr.Length()*sizeof(TUint8); |
|
1467 WriteDataPositionIncL(iExifDataPosition, exifDataStr); |
|
1468 } |
|
1469 |
|
1470 if(iGenerateThumbnail) |
|
1471 { |
|
1472 CreateScaledBitmapL(aScaleCompletionStatus); |
|
1473 } |
|
1474 else |
|
1475 { |
|
1476 // the framework waits for aScaleCompletionStatus to complete |
|
1477 SelfComplete(KErrNone); |
|
1478 } |
|
1479 } |
|
1480 |
|
1481 // Jpeg encoder. |
|
1482 CJpegEncoder* CJpegEncoder::NewL() |
|
1483 { |
|
1484 CJpegEncoder* self=new(ELeave) CJpegEncoder; |
|
1485 CleanupStack::PushL(self); |
|
1486 self->ConstructL(); |
|
1487 CleanupStack::Pop(self); |
|
1488 |
|
1489 return self; |
|
1490 } |
|
1491 |
|
1492 void CJpegEncoder::ConstructL() |
|
1493 { |
|
1494 } |
|
1495 |
|
1496 CJpegEncoder::CJpegEncoder() |
|
1497 : iGenerateThumbnail(EFalse) |
|
1498 { |
|
1499 // Set relevant defaults for the write codec. |
|
1500 // Output precision is 8, not progressive, no restarts and no thumbnail |
|
1501 iFrameInfo.iNumberOfComponents = 3; |
|
1502 iFrameInfo.iComponent[0].iHorzSampleFactor = 2; |
|
1503 iFrameInfo.iComponent[0].iVertSampleFactor = 2; |
|
1504 iFrameInfo.iComponent[0].iQTable = 0; |
|
1505 iFrameInfo.iComponent[1].iHorzSampleFactor = 1; |
|
1506 iFrameInfo.iComponent[1].iVertSampleFactor = 1; |
|
1507 iFrameInfo.iComponent[1].iQTable = 1; |
|
1508 iFrameInfo.iComponent[2].iHorzSampleFactor = 1; |
|
1509 iFrameInfo.iComponent[2].iVertSampleFactor = 1; |
|
1510 iFrameInfo.iComponent[2].iQTable = 1; |
|
1511 iFrameInfo.iMaxHorzSampleFactor = 2; |
|
1512 iFrameInfo.iMaxVertSampleFactor = 2; |
|
1513 |
|
1514 iExtOperationActive = ENone; |
|
1515 } |
|
1516 |
|
1517 CJpegEncoder::~CJpegEncoder() |
|
1518 { |
|
1519 Cleanup(); |
|
1520 delete iLumaTable; |
|
1521 delete iChromaTable; |
|
1522 delete iExifEncoder; |
|
1523 |
|
1524 iSourceThumbnailImage = NULL; |
|
1525 delete iBitmapScaler; |
|
1526 delete iThumbnailImage; |
|
1527 |
|
1528 delete iOperationExtPtr; |
|
1529 delete iStreamedEncodeExt; |
|
1530 |
|
1531 iComment.ResetAndDestroy(); |
|
1532 } |
|
1533 |
|
1534 MExifMetadata* CJpegEncoder::ExifMetadata() |
|
1535 { |
|
1536 if (!iExifEncoder) |
|
1537 { |
|
1538 // set thumbnail on for EXIF (ensure continuity of behaviour) |
|
1539 iGenerateThumbnail = ETrue; |
|
1540 |
|
1541 // creating the CExifEncoder has the required side-effect |
|
1542 // of switching from JFIF to EXIF header output |
|
1543 TInt err; |
|
1544 TRAP(err, iExifEncoder = CExifEncoder::NewL(iGenerateThumbnail)); |
|
1545 } |
|
1546 return iExifEncoder; |
|
1547 }; |
|
1548 |
|
1549 |
|
1550 void CJpegEncoder::PrepareEncoderL(const CFrameImageData* aFrameImageData) |
|
1551 { |
|
1552 iQualityFactor = 0; |
|
1553 |
|
1554 TInt count = (aFrameImageData) ? aFrameImageData->ImageDataCount() : 0; |
|
1555 |
|
1556 if (count == 0) |
|
1557 { |
|
1558 // Use the default value. |
|
1559 // Note: This default value is also used by the TJpegImageData default |
|
1560 // constructor. So if it changed to a different value here, then it should |
|
1561 // be changed there as well. |
|
1562 iQualityFactor = 75; |
|
1563 } |
|
1564 |
|
1565 TBool jpgImageDataProcessed = EFalse; |
|
1566 for (TInt index = 0 ; index<count ; index++) |
|
1567 { |
|
1568 const TImageDataBlock& encoderData = *aFrameImageData->GetImageData(index); |
|
1569 if (encoderData.DataType() == KJPGImageDataUid) |
|
1570 { |
|
1571 // Leave if we have already processed one of these. |
|
1572 if (jpgImageDataProcessed) |
|
1573 User::Leave(KErrCorrupt); |
|
1574 |
|
1575 const TJpegImageData& jpegImageData = STATIC_CAST(const TJpegImageData&, encoderData); |
|
1576 iQualityFactor = jpegImageData.iQualityFactor; |
|
1577 |
|
1578 TJpegImageData::TColorSampling sampleScheme = jpegImageData.iSampleScheme; |
|
1579 iFrameInfo.iComponent[0].iQTable = 0; |
|
1580 if (sampleScheme == TJpegImageData::EMonochrome) |
|
1581 { |
|
1582 iFrameInfo.iNumberOfComponents = 1; |
|
1583 iFrameInfo.iComponent[0].iHorzSampleFactor = 1; |
|
1584 iFrameInfo.iComponent[0].iVertSampleFactor = 1; |
|
1585 } |
|
1586 else |
|
1587 { |
|
1588 iFrameInfo.iNumberOfComponents = 3; |
|
1589 switch (sampleScheme) |
|
1590 { |
|
1591 case TJpegImageData::EColor420: |
|
1592 iFrameInfo.iComponent[0].iHorzSampleFactor = 2; |
|
1593 iFrameInfo.iComponent[0].iVertSampleFactor = 2; |
|
1594 iFrameInfo.iComponent[1].iVertSampleFactor = 1; |
|
1595 iFrameInfo.iComponent[2].iVertSampleFactor = 1; |
|
1596 break; |
|
1597 case TJpegImageData::EColor422: |
|
1598 iFrameInfo.iComponent[0].iHorzSampleFactor = 2; |
|
1599 iFrameInfo.iComponent[0].iVertSampleFactor = 1; |
|
1600 iFrameInfo.iComponent[1].iVertSampleFactor = 1; |
|
1601 iFrameInfo.iComponent[2].iVertSampleFactor = 1; |
|
1602 break; |
|
1603 case TJpegImageData::EColor444: |
|
1604 iFrameInfo.iComponent[0].iHorzSampleFactor = 1; |
|
1605 iFrameInfo.iComponent[0].iVertSampleFactor = 1; |
|
1606 iFrameInfo.iComponent[1].iVertSampleFactor = 1; |
|
1607 iFrameInfo.iComponent[2].iVertSampleFactor = 1; |
|
1608 break; |
|
1609 default: |
|
1610 User::Leave(KErrNotSupported); |
|
1611 } |
|
1612 |
|
1613 iFrameInfo.iComponent[1].iHorzSampleFactor = 1; |
|
1614 iFrameInfo.iComponent[1].iQTable = 1; |
|
1615 iFrameInfo.iComponent[2].iHorzSampleFactor = 1; |
|
1616 iFrameInfo.iComponent[2].iQTable = 1; |
|
1617 } |
|
1618 |
|
1619 iFrameInfo.iMaxHorzSampleFactor = iFrameInfo.iComponent[0].iHorzSampleFactor; |
|
1620 iFrameInfo.iMaxVertSampleFactor = iFrameInfo.iComponent[0].iVertSampleFactor; |
|
1621 |
|
1622 jpgImageDataProcessed = ETrue; |
|
1623 } |
|
1624 else if (encoderData.DataType() == KJPGQTableUid) |
|
1625 { |
|
1626 const TJpegQTable& jpegQTable = STATIC_CAST(const TJpegQTable&, encoderData); |
|
1627 TInt tableIndex = jpegQTable.iTableIndex; |
|
1628 if ((tableIndex != TJpegQTable::ELumaTable) && (tableIndex != TJpegQTable::EChromaTable)) |
|
1629 User::Leave(KErrNotSupported); |
|
1630 |
|
1631 TUint8 values[KJpgQTableEntries]; |
|
1632 TUint8* valuePtr = values; |
|
1633 const TUint8* zigZagPtr = KZigZagSequence.iZigZag; |
|
1634 const TUint8* valuePtrLimit = valuePtr + KJpgQTableEntries; |
|
1635 |
|
1636 while (valuePtr < valuePtrLimit) |
|
1637 *valuePtr++ = jpegQTable.iEntries[*zigZagPtr++]; |
|
1638 |
|
1639 if (tableIndex == TJpegQTable::ELumaTable) |
|
1640 { |
|
1641 if (!iLumaTable) |
|
1642 iLumaTable = new(ELeave) TQTable; |
|
1643 |
|
1644 iLumaTable->Set(values, EFalse); |
|
1645 } |
|
1646 else if (tableIndex == TJpegQTable::EChromaTable) |
|
1647 { |
|
1648 if (!iChromaTable) |
|
1649 iChromaTable = new(ELeave) TQTable; |
|
1650 |
|
1651 iChromaTable->Set(values, EFalse); |
|
1652 } |
|
1653 else |
|
1654 User::Leave(KErrNotSupported); |
|
1655 } |
|
1656 else if (encoderData.DataType() == KJPGCommentUid) |
|
1657 { |
|
1658 const TJpegComment& jpegComment = STATIC_CAST(const TJpegComment&, encoderData); |
|
1659 if (!jpegComment.iComment) |
|
1660 User::Leave(KErrNotFound); |
|
1661 |
|
1662 if ((jpegComment.iComment->Length() == 0) || (jpegComment.iComment->Length() > 65534)) |
|
1663 User::Leave(KErrNotSupported); |
|
1664 |
|
1665 HBufC8* comment = jpegComment.iComment->AllocL(); |
|
1666 TInt ret = iComment.Append(comment); |
|
1667 if (ret != KErrNone) |
|
1668 { |
|
1669 delete comment; |
|
1670 User::Leave(ret); |
|
1671 } |
|
1672 } |
|
1673 else |
|
1674 User::Leave(KErrCorrupt); |
|
1675 } |
|
1676 |
|
1677 |
|
1678 // Create codec. |
|
1679 ASSERT(ImageWriteCodec() == NULL); |
|
1680 |
|
1681 CJpgWriteCodec* imageWriteCodec; |
|
1682 |
|
1683 if(!iOperationExtPtr) |
|
1684 { |
|
1685 imageWriteCodec = CJpgWriteCodec::NewL(iFrameInfo,iQualityFactor,iLumaTable,iChromaTable,iComment, NULL); |
|
1686 } |
|
1687 else |
|
1688 { |
|
1689 imageWriteCodec = CJpgWriteCodec::NewL(iFrameInfo,iQualityFactor,iLumaTable,iChromaTable,iComment, &iOperationExtPtr->iImgConvOperations); |
|
1690 } |
|
1691 |
|
1692 imageWriteCodec->SetHighSpeedMode( (EncoderOptions() & CImageEncoder::EPreferFastEncode) == CImageEncoder::EPreferFastEncode); |
|
1693 |
|
1694 SetImageWriteCodec(imageWriteCodec); |
|
1695 |
|
1696 } |
|
1697 |
|
1698 void CJpegEncoder::SetThumbnail(TBool aDoGenerateThumbnail) |
|
1699 { |
|
1700 if (iExifEncoder) |
|
1701 { |
|
1702 TRAP(iThumbnailStatus, iExifEncoder->SetEnableThumbnailL(aDoGenerateThumbnail) ); |
|
1703 if (iThumbnailStatus != KErrNone) |
|
1704 { |
|
1705 iGenerateThumbnail = EFalse; |
|
1706 return; |
|
1707 } |
|
1708 } |
|
1709 iGenerateThumbnail = aDoGenerateThumbnail; |
|
1710 } |
|
1711 |
|
1712 void CJpegEncoder::UpdateHeaderL() |
|
1713 { |
|
1714 User::LeaveIfError( iThumbnailStatus ); |
|
1715 // Codec writes everything, except for EOI |
|
1716 WriteEOIL(StartPosition()+Position()); |
|
1717 |
|
1718 if (iExifEncoder) |
|
1719 { |
|
1720 TBuf8<2> buffer2Bytes; |
|
1721 buffer2Bytes.SetLength(2); |
|
1722 |
|
1723 TUint8* headerPtr2 = &buffer2Bytes[0]; |
|
1724 |
|
1725 WriteBigEndianUint16(headerPtr2, iExifSize + iThumbnailSize - KApp1MarkerByteCount); // APP1 marker is not included in the length |
|
1726 |
|
1727 // Since we are at the end of an encode and we have an explicit offset to the buffer |
|
1728 // (i.e. to write the size of the APP1 header near the start of the buffer, and so |
|
1729 // Position() is irrelevant) we can use WriteDataL. |
|
1730 WriteDataL(iExifDataPosition+iExifEncoder->ExifLengthOffset(),buffer2Bytes); |
|
1731 |
|
1732 if (iGenerateThumbnail) |
|
1733 { |
|
1734 TBuf8<4> buffer4Bytes; |
|
1735 buffer4Bytes.SetLength(4); |
|
1736 TUint8* headerPtr4 = &buffer4Bytes[0]; |
|
1737 |
|
1738 Mem::Copy(headerPtr4, &iThumbnailSize, sizeof(TUint)); |
|
1739 // Write the offset of the thumbnail (from the start of the buffer) in the |
|
1740 // APP1 header. Again, Position() is irrelevant and so use WriteDataL. |
|
1741 WriteDataL(iExifDataPosition+iExifEncoder->ThumbnailLengthOffset(),buffer4Bytes); |
|
1742 } |
|
1743 } |
|
1744 else if (iGenerateThumbnail) |
|
1745 { |
|
1746 TInt headerSize = iThumbnailSize + KJfifApp0DataSize; |
|
1747 TBuf8<2> buffer2Bytes; |
|
1748 buffer2Bytes.Append(TUint8((headerSize & 0xff00) >> 8)); |
|
1749 buffer2Bytes.Append(TUint8(headerSize & 0xff)); |
|
1750 WriteDataL(KJfifLengthOffset, buffer2Bytes); |
|
1751 |
|
1752 buffer2Bytes.Zero(); |
|
1753 buffer2Bytes.Append(iGeneratedThumbnailSize.iWidth); |
|
1754 buffer2Bytes.Append(iGeneratedThumbnailSize.iHeight); |
|
1755 |
|
1756 WriteDataL(KJfifThumbnailXYOffset, buffer2Bytes); |
|
1757 } |
|
1758 } |
|
1759 |
|
1760 void CJpegEncoder::InitConvertL() |
|
1761 { |
|
1762 CImageEncoderPlugin::InitConvertL(); |
|
1763 // this way, the bitmap is initialized |
|
1764 |
|
1765 WriteSOIL(StartPosition()+Position()); |
|
1766 if (!iExifEncoder) |
|
1767 { |
|
1768 // no EXIF data so default to JFIF header |
|
1769 WriteImageHeaderL(); |
|
1770 } |
|
1771 } |
|
1772 |
|
1773 void CJpegEncoder::InitCustomAsyncL(TInt aParam) |
|
1774 { |
|
1775 if (aParam == KOptionConvertFrameUidValue) |
|
1776 { |
|
1777 HandleConvertFrameL(); |
|
1778 } |
|
1779 } |
|
1780 |
|
1781 void CJpegEncoder::HandleConvertFrameL() |
|
1782 { |
|
1783 // Prepare Frame Info and instantiates write codec |
|
1784 PrepareEncoderL(&FrameImageData()); |
|
1785 |
|
1786 // ConvertFrame option does not support thumbnail |
|
1787 SetThumbnail(EFalse); |
|
1788 |
|
1789 // ConvertFrame bypasses the framework. Force required initialization here. |
|
1790 CImageEncoderPlugin::RequestInitL(); |
|
1791 |
|
1792 // Initialize write codec with relevant data |
|
1793 // This will also check consistency between FrameImageData and the souce ImageFrame format. |
|
1794 CJpgWriteCodec* jpgWriteCodec = reinterpret_cast<CJpgWriteCodec*>(ImageWriteCodec()); |
|
1795 TBufPtr8& destData = DestinationData(); |
|
1796 jpgWriteCodec->InitFrameL(destData, &SrcImageFrame(), &FrameImageData()); |
|
1797 |
|
1798 // If everything went fine start writting data to the destination file. |
|
1799 WriteSOIL(StartPosition()+Position()); |
|
1800 WriteImageHeaderL(); |
|
1801 |
|
1802 // If this image has exif metadata, then generate the exif data and write it |
|
1803 if (iExifEncoder) |
|
1804 { |
|
1805 // we get the exifData |
|
1806 // exif data is own by the exif encoder. We don't push it on the cleanupstack |
|
1807 iExifDataPosition=Position(); |
|
1808 TPtrC8 exifDataStr=iExifEncoder->CreateExifHeaderL(); |
|
1809 // and write it |
|
1810 iExifSize=exifDataStr.Length()*sizeof(TUint8); |
|
1811 WriteDataPositionIncL(iExifDataPosition, exifDataStr); |
|
1812 } |
|
1813 } |
|
1814 |
|
1815 void CJpegEncoder::HandleStreamInitFrameL(TUid aFormat, TInt aFrameNumber, const TSize& aFrameSizeInPixels, const TSize& aBlockSizeInPixels, const CFrameImageData* aFrameImageData) |
|
1816 { |
|
1817 // Prepare Frame Info and instantiates write codec |
|
1818 PrepareEncoderL(aFrameImageData); |
|
1819 |
|
1820 // Stream option does not support thumbnail |
|
1821 SetThumbnail(EFalse); |
|
1822 |
|
1823 // ConvertFrame bypasses the framework. Force required initialization here. |
|
1824 CImageEncoderPlugin::RequestInitL(); |
|
1825 |
|
1826 // Initialize write codec with relevant data |
|
1827 // This will also check consistency between FrameImageData and the souce ImageFrame format. |
|
1828 CJpgWriteCodec* jpgWriteCodec = reinterpret_cast<CJpgWriteCodec*>(ImageWriteCodec()); |
|
1829 TBufPtr8& destData = DestinationData(); |
|
1830 jpgWriteCodec->InitFrameL(destData, aFormat, aFrameNumber, aFrameSizeInPixels, aBlockSizeInPixels, aFrameImageData); |
|
1831 |
|
1832 // If everything went fine start writting data to the destination file. |
|
1833 WriteSOIL(StartPosition()+Position()); |
|
1834 WriteImageHeaderL(); |
|
1835 |
|
1836 // If this image has exif metadata, then generate the exif data and write it |
|
1837 if (iExifEncoder) |
|
1838 { |
|
1839 // we get the exifData |
|
1840 // exif data is own by the exif encoder. We don't push it on the cleanupstack |
|
1841 iExifDataPosition=Position(); |
|
1842 TPtrC8 exifDataStr=iExifEncoder->CreateExifHeaderL(); |
|
1843 // and write it |
|
1844 iExifSize=exifDataStr.Length()*sizeof(TUint8); |
|
1845 WriteDataPositionIncL(iExifDataPosition, exifDataStr); |
|
1846 } |
|
1847 } |
|
1848 |
|
1849 void CJpegEncoder::HandleStreamAppendBlock(const CImageFrame& aBlocks, TInt aNumBlocksToAdd) |
|
1850 { |
|
1851 // Initialize write codec with relevant data |
|
1852 // This will also check consistency between FrameImageData and the souce ImageFrame format. |
|
1853 CJpgWriteCodec* jpgWriteCodec = reinterpret_cast<CJpgWriteCodec*>(ImageWriteCodec()); |
|
1854 JPEG_ASSERT(jpgWriteCodec); |
|
1855 |
|
1856 TRAPD(errCode, jpgWriteCodec->AppendFrameBlockL(aBlocks, aNumBlocksToAdd)); |
|
1857 if (errCode!=KErrNone) |
|
1858 { |
|
1859 RequestComplete(errCode); |
|
1860 return; |
|
1861 } |
|
1862 } |
|
1863 |
|
1864 void CJpegEncoder::HandleStreamAddBlock(const CImageFrame& /*aBlocks*/, const TInt& /*aSeqPosition*/) |
|
1865 { |
|
1866 //Add blocks is not supported |
|
1867 RequestComplete(KErrNotSupported); |
|
1868 return; |
|
1869 } |
|
1870 |
|
1871 void CJpegEncoder::HandleStreamCompleteFrame() |
|
1872 { |
|
1873 CJpgWriteCodec* jpgWriteCodec = reinterpret_cast<CJpgWriteCodec*>(ImageWriteCodec()); |
|
1874 JPEG_ASSERT(jpgWriteCodec); |
|
1875 |
|
1876 jpgWriteCodec->SetCompleteFrame(); |
|
1877 } |
|
1878 |
|
1879 TInt CJpegEncoder::WriteSOIL(const TInt aPosition) |
|
1880 { |
|
1881 // Codec writes everything, except for EOI |
|
1882 TBuf8<2> buffer2Bytes; |
|
1883 buffer2Bytes.SetLength(2); |
|
1884 |
|
1885 TUint8* headerPtr = &buffer2Bytes[0]; |
|
1886 WriteBigEndianUint16(headerPtr,KJpgSOISignature); // Start of information |
|
1887 |
|
1888 WriteDataPositionIncL(aPosition,buffer2Bytes); |
|
1889 |
|
1890 return buffer2Bytes.Length()*sizeof(TUint8); |
|
1891 } |
|
1892 |
|
1893 |
|
1894 |
|
1895 TInt CJpegEncoder::WriteImageHeaderL() |
|
1896 { |
|
1897 TBuf8<KJfifApp0DataSize+sizeof(KJpgApp0Signature)> buffer; |
|
1898 |
|
1899 TJpegUtilities::CreateJfifHeader(buffer); |
|
1900 WriteDataPositionIncL(StartPosition()+Position(),buffer); |
|
1901 |
|
1902 return buffer.Length()*sizeof(TUint8); |
|
1903 } |
|
1904 //Helper function to crate a default JFIF header |
|
1905 void TJpegUtilities::CreateJfifHeader(TDes8& aBuffer) |
|
1906 { |
|
1907 ASSERT(aBuffer.MaxLength() >= (KJfifApp0DataSize+sizeof(KJpgApp0Signature))); |
|
1908 |
|
1909 TUint8* destPtr = const_cast<TUint8*>(aBuffer.Ptr()); |
|
1910 ASSERT(destPtr != NULL); |
|
1911 TUint8* startDestPtr=destPtr; |
|
1912 |
|
1913 WriteBigEndianUint16(destPtr,KJpgApp0Signature); // APP0 |
|
1914 WriteBigEndianUint16(destPtr,KJfifApp0DataSize); // Lp |
|
1915 Mem::Copy(destPtr,KJfifId().Ptr(),KJpgApp0IdSize); |
|
1916 destPtr += KJpgApp0IdSize; |
|
1917 *destPtr++ = 0; |
|
1918 WriteBigEndianUint16(destPtr,KJfifVersion0101); // Version |
|
1919 *destPtr++ = TJpgFrameInfo::ENone; // Units |
|
1920 WriteBigEndianUint16(destPtr,KJpgPixelRatio); // X density |
|
1921 WriteBigEndianUint16(destPtr,KJpgPixelRatio); // Y density |
|
1922 *destPtr++ = 0; // Thumbnail width |
|
1923 *destPtr++ = 0; // Thumbnail height |
|
1924 |
|
1925 aBuffer.SetLength((destPtr-startDestPtr)/sizeof(destPtr[0])); |
|
1926 } |
|
1927 |
|
1928 |
|
1929 TInt CJpegEncoder::WriteEOIL(const TInt aPosition) |
|
1930 { |
|
1931 // Codec writes everything, except for EOI |
|
1932 TBuf8<2> buffer2Bytes; |
|
1933 buffer2Bytes.SetLength(2); |
|
1934 |
|
1935 TUint8* headerPtr = &buffer2Bytes[0]; |
|
1936 WriteBigEndianUint16(headerPtr, KJpgEOISignature); // End of information |
|
1937 WriteDataPositionIncL(aPosition,buffer2Bytes); |
|
1938 |
|
1939 return buffer2Bytes.Length()*sizeof(TUint8); |
|
1940 } |
|
1941 |
|
1942 CJpegEncoder::TExtensionOperation CJpegEncoder::GetActiveExtensionOperation() const |
|
1943 { |
|
1944 return iExtOperationActive; |
|
1945 } |
|
1946 |
|
1947 void CJpegEncoder::SetActiveExtensionOperation(CJpegEncoder::TExtensionOperation aExtOperation) |
|
1948 { |
|
1949 iExtOperationActive = aExtOperation; |
|
1950 } |
|
1951 |
|
1952 // System Wide Define |
|
1953 #ifdef SYMBIAN_ENABLE_1630_JPEG_EXTENSIONS |
|
1954 void CJpegEncoder::GetExtensionL(TUid aExtUid, MImageConvExtension*& aExtPtr) |
|
1955 { |
|
1956 switch (aExtUid.iUid) |
|
1957 { |
|
1958 case KUidImageConvExtOperationValue: |
|
1959 { |
|
1960 if (!iOperationExtPtr) |
|
1961 { |
|
1962 iOperationExtPtr = CEncodeOperationExtension::NewL(this); |
|
1963 aExtPtr = iOperationExtPtr; |
|
1964 } |
|
1965 else |
|
1966 { |
|
1967 aExtPtr = iOperationExtPtr; |
|
1968 } |
|
1969 |
|
1970 break; |
|
1971 } |
|
1972 |
|
1973 case KUidImageConvExtStreamedEncodeValue: |
|
1974 { |
|
1975 if (!iStreamedEncodeExt) |
|
1976 { |
|
1977 iStreamedEncodeExt = CStreamedEncodeExtension::NewL(this); |
|
1978 aExtPtr = iStreamedEncodeExt; |
|
1979 } |
|
1980 else |
|
1981 { |
|
1982 aExtPtr = iStreamedEncodeExt; |
|
1983 } |
|
1984 |
|
1985 break; |
|
1986 } |
|
1987 |
|
1988 default: |
|
1989 { |
|
1990 User::Leave(KErrNotSupported); |
|
1991 } |
|
1992 } |
|
1993 } |
|
1994 #else |
|
1995 void CJpegEncoder::GetExtensionL(TUid /*aExtUid*/, MImageConvExtension*& /*aExtPtr*/) |
|
1996 { |
|
1997 User::Leave(KErrNotSupported); |
|
1998 } |
|
1999 #endif |
|
2000 |
|
2001 CEncodeOperationExtension* CEncodeOperationExtension::NewL(CJpegEncoder* aJpegEncoder) |
|
2002 { |
|
2003 return new (ELeave) CEncodeOperationExtension(aJpegEncoder); |
|
2004 } |
|
2005 |
|
2006 CEncodeOperationExtension::CEncodeOperationExtension(CJpegEncoder* aJpegEncoder) |
|
2007 :iJpegEncoder(aJpegEncoder), iOperationCaps(KJpgEncoderCapabilities) |
|
2008 { |
|
2009 IncrementRef(); |
|
2010 } |
|
2011 |
|
2012 CEncodeOperationExtension::~CEncodeOperationExtension() |
|
2013 { |
|
2014 ASSERT(iReferenceCount == 0); |
|
2015 iImgConvOperations.Close(); |
|
2016 } |
|
2017 |
|
2018 TUid CEncodeOperationExtension::Uid() const |
|
2019 { |
|
2020 TUid KUidImgCovOperation = {KUidImageConvExtOperationValue}; |
|
2021 return KUidImgCovOperation; |
|
2022 } |
|
2023 |
|
2024 void CEncodeOperationExtension::IncrementRef() |
|
2025 { |
|
2026 iReferenceCount++; |
|
2027 } |
|
2028 |
|
2029 void CEncodeOperationExtension::Release() |
|
2030 { |
|
2031 iReferenceCount--; |
|
2032 ASSERT( iReferenceCount >= 0 ); |
|
2033 } |
|
2034 |
|
2035 TUint CEncodeOperationExtension::Capabilities() const |
|
2036 { |
|
2037 return iOperationCaps; |
|
2038 } |
|
2039 |
|
2040 void CEncodeOperationExtension::AddOperationL(TImageConvOperation::TOperation aOperation) |
|
2041 { |
|
2042 User::LeaveIfError(iImgConvOperations.Append(aOperation)); |
|
2043 //check to exclude interoperability of stream and operation interfaces. |
|
2044 if(iJpegEncoder->GetActiveExtensionOperation() == CJpegEncoder::ENone) |
|
2045 { |
|
2046 iJpegEncoder->SetActiveExtensionOperation(CJpegEncoder::EOperationEncode); |
|
2047 } |
|
2048 else if(iJpegEncoder->GetActiveExtensionOperation() == CJpegEncoder::EStreamEncode) |
|
2049 { |
|
2050 User::Leave(KErrNotSupported); |
|
2051 } |
|
2052 } |
|
2053 |
|
2054 void CEncodeOperationExtension::ClearOperationStack() |
|
2055 { |
|
2056 if(iJpegEncoder->GetActiveExtensionOperation() == CJpegEncoder::EOperationEncode) |
|
2057 { |
|
2058 iImgConvOperations.Reset(); |
|
2059 iJpegEncoder->SetActiveExtensionOperation(CJpegEncoder::ENone); |
|
2060 } |
|
2061 } |
|
2062 |
|
2063 // Concrete Streamed Encode Extension class |
|
2064 CStreamedEncodeExtension* CStreamedEncodeExtension::NewL(CJpegEncoder* aJpegEncoder) |
|
2065 { |
|
2066 return new (ELeave) CStreamedEncodeExtension(aJpegEncoder); |
|
2067 } |
|
2068 |
|
2069 CStreamedEncodeExtension::CStreamedEncodeExtension(CJpegEncoder* aJpegEncoder) |
|
2070 :iJpegEncoder(aJpegEncoder), iReferenceCount(0) |
|
2071 { |
|
2072 IncrementRef(); |
|
2073 } |
|
2074 |
|
2075 CStreamedEncodeExtension::~CStreamedEncodeExtension() |
|
2076 { |
|
2077 ASSERT(iReferenceCount == 0); |
|
2078 } |
|
2079 |
|
2080 TUid CStreamedEncodeExtension::Uid() const |
|
2081 { |
|
2082 TUid KUidImgCovOperation = {KUidImageConvExtStreamedEncodeValue}; |
|
2083 return KUidImgCovOperation; |
|
2084 } |
|
2085 |
|
2086 void CStreamedEncodeExtension::IncrementRef() |
|
2087 { |
|
2088 iReferenceCount++; |
|
2089 } |
|
2090 |
|
2091 void CStreamedEncodeExtension::Release() |
|
2092 { |
|
2093 iReferenceCount--; |
|
2094 ASSERT( iReferenceCount >= 0 ); |
|
2095 } |
|
2096 |
|
2097 void CStreamedEncodeExtension::GetSupportedFormatsL(RArray<TUid>& aFormats, TUid& aOptimalFormat) const |
|
2098 { |
|
2099 aFormats.AppendL(KUidFormatYUVMonochrome); |
|
2100 aFormats.AppendL(KUidFormatYUV422Interleaved); |
|
2101 aFormats.AppendL(KUidFormatYUV420Planar); |
|
2102 aFormats.AppendL(KUidFormatYUV420PlanarReversed); |
|
2103 |
|
2104 aOptimalFormat = KUidFormatYUV420Planar; |
|
2105 } |
|
2106 |
|
2107 void CStreamedEncodeExtension::GetCapabilities(TUid aFormat, TEncodeStreamCaps& aCaps) const |
|
2108 { |
|
2109 TSize minBlockSizeInPixels; |
|
2110 TInt optimalBlocksPerRequest = KOptimalBlockSize; |
|
2111 TEncodeStreamCaps::TNavigation navigation = TEncodeStreamCaps::ENavigationSequentialForward; |
|
2112 TInt maxBlocksPerRequest= KMaxTInt; |
|
2113 |
|
2114 switch (aFormat.iUid) |
|
2115 { |
|
2116 case KFormatYUVMonochromeUidValue: |
|
2117 minBlockSizeInPixels = TSize(KSamplingMonoMCUWidthInPixels, KSamplingMonoMCUHeightInPixels); |
|
2118 break; |
|
2119 case KFormatYUV422InterleavedUidValue: |
|
2120 minBlockSizeInPixels = TSize(KSampling422MCUWidthInPixels, KSampling422MCUHeightInPixels); |
|
2121 break; |
|
2122 case KFormatYUV420PlanarReversedUidValue: |
|
2123 case KFormatYUV420PlanarUidValue: |
|
2124 minBlockSizeInPixels = TSize(KSampling420MCUWidthInPixels, KSampling420MCUHeightInPixels); |
|
2125 break; |
|
2126 } |
|
2127 |
|
2128 TEncodeStreamCaps streamCaps(maxBlocksPerRequest, minBlockSizeInPixels, |
|
2129 optimalBlocksPerRequest, navigation); |
|
2130 aCaps = streamCaps; |
|
2131 } |
|
2132 |
|
2133 void CStreamedEncodeExtension::InitFrameL(TUid aFormat, TInt aFrameNumber, const TSize& aFrameSizeInPixels, const TSize& aBlockSizeInPixels, TEncodeStreamCaps::TNavigation aNavigation, const CFrameImageData* aFrameImageData) |
|
2134 { |
|
2135 ValidateInitParamsL(aFormat, aFrameNumber, aFrameSizeInPixels, aBlockSizeInPixels, aNavigation, aFrameImageData); |
|
2136 |
|
2137 iJpegEncoder->HandleStreamInitFrameL(aFormat, aFrameNumber, aFrameSizeInPixels, aBlockSizeInPixels, aFrameImageData); |
|
2138 |
|
2139 //check to exclude interoperability of stream and operation interfaces. |
|
2140 if(iJpegEncoder->GetActiveExtensionOperation() == CJpegEncoder::ENone) |
|
2141 { |
|
2142 iJpegEncoder->SetActiveExtensionOperation(CJpegEncoder::EStreamEncode); |
|
2143 } |
|
2144 else if(iJpegEncoder->GetActiveExtensionOperation()== CJpegEncoder::EOperationEncode) |
|
2145 { |
|
2146 User::Leave(KErrNotSupported); |
|
2147 } |
|
2148 } |
|
2149 |
|
2150 void CStreamedEncodeExtension::AppendBlocks(TRequestStatus* /*aReq*/, const CImageFrame& aBlocks, TInt aNumBlocksToAdd) |
|
2151 { |
|
2152 iJpegEncoder->HandleStreamAppendBlock(aBlocks, aNumBlocksToAdd); |
|
2153 } |
|
2154 |
|
2155 void CStreamedEncodeExtension::AddBlocks(TRequestStatus* /*aReq*/, const CImageFrame& aBlocks, const TInt& aSeqPosition) |
|
2156 { |
|
2157 iJpegEncoder->HandleStreamAddBlock(aBlocks, aSeqPosition); |
|
2158 } |
|
2159 |
|
2160 void CStreamedEncodeExtension::Complete(TRequestStatus* /*aReq*/) |
|
2161 { |
|
2162 if(iJpegEncoder->GetActiveExtensionOperation() == CJpegEncoder::EStreamEncode) |
|
2163 { |
|
2164 iJpegEncoder->HandleStreamCompleteFrame(); |
|
2165 iJpegEncoder->SetActiveExtensionOperation(CJpegEncoder::ENone); |
|
2166 } |
|
2167 } |
|
2168 |
|
2169 void CStreamedEncodeExtension::ValidateInitParamsL(TUid& aFormat, TInt aFrameNumber, const TSize& aFrameSizeInPixels, const TSize& aBlockSizeInPixels, TEncodeStreamCaps::TNavigation aNavigation, const CFrameImageData* aFrameImageData) |
|
2170 { |
|
2171 // Prepare Frame Info and instantiates write codec |
|
2172 // Note : Can either specify format through aFormat or aImageFrameData. Conflicts should leave with KErrArgument. |
|
2173 if (aFormat.iUid == KFormatYUVMonochromeUidValue || aFormat.iUid == KFormatYUV422InterleavedUidValue || aFormat.iUid == KFormatYUV420PlanarUidValue || aFormat.iUid == KFormatYUV420PlanarReversedUidValue) |
|
2174 { |
|
2175 if(aFrameNumber) |
|
2176 { |
|
2177 User::Leave(KErrArgument); |
|
2178 } |
|
2179 } |
|
2180 else |
|
2181 { |
|
2182 User::Leave(KErrArgument); |
|
2183 } |
|
2184 |
|
2185 TInt count = (aFrameImageData) ? aFrameImageData->ImageDataCount() : 0; |
|
2186 // look for Frame Image Data |
|
2187 for (TInt index = 0 ; index<count ; index++) |
|
2188 { |
|
2189 const TImageDataBlock& encoderData = *aFrameImageData->GetImageData(index); |
|
2190 if (encoderData.DataType() == KJPGImageDataUid) |
|
2191 { |
|
2192 const TJpegImageData& jpegImageData = static_cast<const TJpegImageData&>(encoderData); |
|
2193 TJpegImageData::TColorSampling sampleScheme = jpegImageData.iSampleScheme; |
|
2194 |
|
2195 switch (sampleScheme) |
|
2196 { |
|
2197 case TJpegImageData::EMonochrome: |
|
2198 { |
|
2199 if (aFormat.iUid != KFormatYUVMonochromeUidValue) |
|
2200 { |
|
2201 User::Leave(KErrNotSupported); |
|
2202 } |
|
2203 break; |
|
2204 } |
|
2205 |
|
2206 case TJpegImageData::EColor420: |
|
2207 { |
|
2208 if (!(aFormat.iUid == KFormatYUV420PlanarUidValue || aFormat.iUid == KFormatYUV420PlanarReversedUidValue)) |
|
2209 { |
|
2210 User::Leave(KErrNotSupported); |
|
2211 } |
|
2212 break; |
|
2213 } |
|
2214 |
|
2215 case TJpegImageData::EColor422: |
|
2216 { |
|
2217 if (aFormat.iUid != KFormatYUV422InterleavedUidValue) |
|
2218 { |
|
2219 User::Leave(KErrNotSupported); |
|
2220 } |
|
2221 break; |
|
2222 } |
|
2223 |
|
2224 case TJpegImageData::EColor444: |
|
2225 { |
|
2226 // YUV 4:4:4 sampling scheme is not supported by this codec |
|
2227 User::Leave(KErrNotSupported); |
|
2228 break; |
|
2229 } |
|
2230 |
|
2231 default: |
|
2232 { |
|
2233 User::Leave(KErrNotSupported); |
|
2234 break; |
|
2235 } |
|
2236 } |
|
2237 //format found - break out of for loop |
|
2238 break; |
|
2239 } |
|
2240 } |
|
2241 |
|
2242 GetCapabilities(aFormat, iEncodeCaps); |
|
2243 |
|
2244 //validate that the frame size used is multiple of min block size |
|
2245 CJpgWriteCodec::ValidateBlockSizeL(aFrameSizeInPixels, iEncodeCaps.MinBlockSizeInPixels()); |
|
2246 |
|
2247 //validate that the block size used is multiple of min block size |
|
2248 CJpgWriteCodec::ValidateBlockSizeL(aBlockSizeInPixels, iEncodeCaps.MinBlockSizeInPixels()); |
|
2249 |
|
2250 if (aBlockSizeInPixels.iHeight != iEncodeCaps.MinBlockSizeInPixels().iHeight) |
|
2251 { |
|
2252 User::Leave(KErrNotSupported); |
|
2253 } |
|
2254 |
|
2255 if(aNavigation != iEncodeCaps.Navigation()) |
|
2256 { |
|
2257 User::Leave(KErrNotSupported); |
|
2258 } |
|
2259 } |
|
2260 // Concrete Streamed Decode Extension class |
|
2261 CStreamedDecodeExtension* CStreamedDecodeExtension::NewL(CJpegDecoder* aJpegDecoder) |
|
2262 { |
|
2263 return new (ELeave) CStreamedDecodeExtension(aJpegDecoder); |
|
2264 } |
|
2265 |
|
2266 CStreamedDecodeExtension::CStreamedDecodeExtension(CJpegDecoder* aJpegDecoder) |
|
2267 :iJpegDecoder(aJpegDecoder) |
|
2268 { |
|
2269 IncrementRef(); |
|
2270 } |
|
2271 |
|
2272 CStreamedDecodeExtension::~CStreamedDecodeExtension() |
|
2273 { |
|
2274 ASSERT(iReferenceCount == 0); |
|
2275 } |
|
2276 |
|
2277 TUid CStreamedDecodeExtension::Uid() const |
|
2278 { |
|
2279 TUid uidImgConvOperation = {KUidImageConvExtStreamedDecodeValue}; |
|
2280 return uidImgConvOperation; |
|
2281 } |
|
2282 |
|
2283 void CStreamedDecodeExtension::IncrementRef() |
|
2284 { |
|
2285 iReferenceCount++; |
|
2286 } |
|
2287 |
|
2288 void CStreamedDecodeExtension::Release() |
|
2289 { |
|
2290 iReferenceCount--; |
|
2291 ASSERT( iReferenceCount >= 0 ); |
|
2292 } |
|
2293 |
|
2294 void CStreamedDecodeExtension::GetSupportedFormatsL(RArray<TUid>& aFormats, TUid& aOptimalFormat) const |
|
2295 { |
|
2296 TJpegImageData::TColorSampling samplingScheme = TJpegImageData::EMonochrome; // intialise to mono |
|
2297 User::LeaveIfError(iJpegDecoder->SamplingScheme(samplingScheme)); |
|
2298 |
|
2299 switch(samplingScheme) |
|
2300 { |
|
2301 case TJpegImageData::EMonochrome: |
|
2302 aFormats.AppendL(KUidFormatYUVMonochrome); |
|
2303 aOptimalFormat = KUidFormatYUVMonochrome; |
|
2304 break; |
|
2305 case TJpegImageData::EColor420: |
|
2306 aFormats.AppendL(KUidFormatYUV420Planar); |
|
2307 aFormats.AppendL(KUidFormatYUV420PlanarReversed); |
|
2308 aOptimalFormat = KUidFormatYUV420Planar; |
|
2309 break; |
|
2310 case TJpegImageData::EColor422: |
|
2311 aFormats.AppendL(KUidFormatYUV422Interleaved); |
|
2312 aOptimalFormat = KUidFormatYUV422Interleaved; |
|
2313 break; |
|
2314 default: |
|
2315 User::Leave(KErrNotSupported); |
|
2316 } |
|
2317 } |
|
2318 |
|
2319 void CStreamedDecodeExtension::GetCapabilities(TUid aFormat, TInt /*aFrameNumber*/, TDecodeStreamCaps& aCaps) const |
|
2320 { |
|
2321 TSize minBlockSizeInPixels; |
|
2322 TInt optimalBlocksPerRequest = KOptimalBlockSize; |
|
2323 TDecodeStreamCaps::TNavigation navigation = static_cast<TDecodeStreamCaps::TNavigation> (TDecodeStreamCaps::ENavigationSequentialForward | TDecodeStreamCaps::ENavigationRandomForward | TDecodeStreamCaps::ENavigationRandomBackwards); |
|
2324 |
|
2325 TInt maxBlocksPerRequest= KMaxTInt; |
|
2326 TInt minStreamSizeInBlocks = 1; |
|
2327 |
|
2328 switch (aFormat.iUid) |
|
2329 { |
|
2330 case KFormatYUVMonochromeUidValue: |
|
2331 minBlockSizeInPixels = TSize(KSamplingMonoMCUWidthInPixels, KSamplingMonoMCUHeightInPixels); |
|
2332 break; |
|
2333 case KFormatYUV422InterleavedUidValue: |
|
2334 minBlockSizeInPixels = TSize(KSampling422MCUWidthInPixels, KSampling422MCUHeightInPixels); |
|
2335 break; |
|
2336 case KFormatYUV420PlanarReversedUidValue: |
|
2337 case KFormatYUV420PlanarUidValue: |
|
2338 minBlockSizeInPixels = TSize(KSampling420MCUWidthInPixels, KSampling420MCUHeightInPixels); |
|
2339 break; |
|
2340 default: |
|
2341 //does nothing |
|
2342 maxBlocksPerRequest = 0; |
|
2343 } |
|
2344 |
|
2345 TDecodeStreamCaps streamCaps(maxBlocksPerRequest, minBlockSizeInPixels, |
|
2346 optimalBlocksPerRequest, minStreamSizeInBlocks, navigation); |
|
2347 aCaps = streamCaps; |
|
2348 } |
|
2349 |
|
2350 TInt CStreamedDecodeExtension::GetBufferSize(TUid aFormat, TSize& aBlockSizeInPixels, TInt aNumBlocks) const |
|
2351 { |
|
2352 return iJpegDecoder->HandleStreamGetBufferSize(aFormat, aBlockSizeInPixels, aNumBlocks); |
|
2353 } |
|
2354 |
|
2355 void CStreamedDecodeExtension::InitFrameL(TUid aFormat, TInt aFrameNumber, TDecodeStreamCaps::TNavigation aNavigation) |
|
2356 { |
|
2357 if(aNavigation == TDecodeStreamCaps::ENavigationSequentialForward || aNavigation == TDecodeStreamCaps::ENavigationRandomForward || aNavigation == TDecodeStreamCaps::ENavigationRandomBackwards) |
|
2358 { |
|
2359 if(aFrameNumber) |
|
2360 { |
|
2361 User::Leave(KErrArgument); |
|
2362 } |
|
2363 } |
|
2364 else |
|
2365 { |
|
2366 User::Leave(KErrNotSupported); |
|
2367 } |
|
2368 |
|
2369 iJpegDecoder->HandleStreamInitFrameL(aFormat, aFrameNumber, aNavigation); |
|
2370 } |
|
2371 |
|
2372 void CStreamedDecodeExtension::GetBlocks(TRequestStatus* /*aStatus*/, CImageFrame* aFrame, TInt aSeqPosition, TInt aNumBlocksToGet, TInt* aNumBlocksRead) |
|
2373 { |
|
2374 iJpegDecoder->HandleStreamGetBlocks(aFrame, aSeqPosition, aNumBlocksToGet, aNumBlocksRead); |
|
2375 } |
|
2376 |
|
2377 void CStreamedDecodeExtension::GetNextBlocks(TRequestStatus* /*aStatus*/, CImageFrame* aFrame, TInt aNumBlocksToGet, TInt* aNumBlocksRead, TBool* aHaveMoreBlocks) |
|
2378 { |
|
2379 iJpegDecoder->HandleStreamGetNextBlocks(aFrame, aNumBlocksToGet, aNumBlocksRead, aHaveMoreBlocks); |
|
2380 } |
|
2381 |
|
2382 //return The memory buffer size in bytes to hold the requested blocks. Returns error if unable to retrieve buffer size. |
|
2383 TInt CJpegDecoder::HandleStreamGetBufferSize(TUid aFormat, TSize& aBlockSizeInPixels, TInt aNumBlocks) const |
|
2384 { |
|
2385 CJpgReadCodec* jpgReadCodec = reinterpret_cast<CJpgReadCodec*>(ImageReadCodec()); |
|
2386 TInt bufferSize = KErrNone; |
|
2387 TRAPD(errCode, bufferSize = jpgReadCodec->GetStreamBufferSizeL(aFormat, aBlockSizeInPixels, aNumBlocks)); |
|
2388 if (errCode!=KErrNone) |
|
2389 { |
|
2390 bufferSize = errCode; |
|
2391 } |
|
2392 |
|
2393 return bufferSize; |
|
2394 } |
|
2395 |
|
2396 void CJpegDecoder::HandleStreamInitFrameL(TUid aFormat, TInt aFrameNumber, TDecodeStreamCaps::TNavigation aNavigation) |
|
2397 { |
|
2398 //Validate source image dimensions. Make sure that image is multiple of MCUs |
|
2399 CJpgWriteCodec::ValidateBlockSizeL(iJpgFrameInfo.iSizeInPixels, TSize(iJpgFrameInfo.MCUWidthInPixels(), iJpgFrameInfo.MCUHeightInPixels())); |
|
2400 |
|
2401 if(iExtensionManager) |
|
2402 { |
|
2403 if(iExtensionManager->ClippingRectExtensionRequested() || iExtensionManager->ScalerExtensionRequested() || iExtensionManager->OperationExtensionRequested()) |
|
2404 { |
|
2405 User::Leave(KErrNotSupported); |
|
2406 } |
|
2407 } |
|
2408 |
|
2409 CImageDecoderPlugin::RequestInitL(aFrameNumber); |
|
2410 |
|
2411 CJpgReadCodec* jpgReadCodec = reinterpret_cast<CJpgReadCodec*>(ImageReadCodec()); |
|
2412 jpgReadCodec->InitFrameL(aFormat, aNavigation); |
|
2413 } |
|
2414 |
|
2415 void CJpegDecoder::HandleStreamGetBlocks(CImageFrame* aFrame, TInt aSeqPosition, TInt aNumBlocksToGet, TInt* aNumBlocksRead) |
|
2416 { |
|
2417 CJpgReadCodec* jpgReadCodec = reinterpret_cast<CJpgReadCodec*>(ImageReadCodec()); |
|
2418 |
|
2419 TRAPD(errCode, jpgReadCodec->GetBlocksL(aFrame, aSeqPosition, aNumBlocksToGet, aNumBlocksRead)); |
|
2420 if (errCode!=KErrNone) |
|
2421 { |
|
2422 RequestComplete(errCode); |
|
2423 return; |
|
2424 } |
|
2425 } |
|
2426 |
|
2427 void CJpegDecoder::HandleStreamGetNextBlocks(CImageFrame* aFrame, TInt aNumBlocksToGet, TInt* aNumBlocksRead, TBool* aHaveMoreBlocks) |
|
2428 { |
|
2429 CJpgReadCodec* jpgReadCodec = reinterpret_cast<CJpgReadCodec*>(ImageReadCodec()); |
|
2430 |
|
2431 TRAPD(errCode, jpgReadCodec->GetNextBlocksL(aFrame, aNumBlocksToGet, aNumBlocksRead, aHaveMoreBlocks)); |
|
2432 if (errCode!=KErrNone) |
|
2433 { |
|
2434 RequestComplete(errCode); |
|
2435 return; |
|
2436 } |
|
2437 } |
|
2438 |
|
2439 // System Wide Define |
|
2440 #ifdef SYMBIAN_ENABLE_1630_JPEG_EXTENSIONS |
|
2441 void CJpegDecoder::GetExtensionL(TUid aExtUid, MImageConvExtension*& aExtPtr) |
|
2442 { |
|
2443 |
|
2444 switch (aExtUid.iUid) |
|
2445 { |
|
2446 case KUidImageConvExtStreamedDecodeValue: |
|
2447 { |
|
2448 if (iJpgFrameInfo.iProgressive) |
|
2449 { |
|
2450 User::Leave(KErrNotSupported); //codec dosen't support progressive decoding |
|
2451 } |
|
2452 |
|
2453 if (iJpgFrameInfo.iMultiScan) |
|
2454 { |
|
2455 JPEG_LEAVE(KErrNotSupported, "Multiscan in GetExtensionL"); |
|
2456 } |
|
2457 |
|
2458 if (!iStreamedDecodeExt) |
|
2459 { |
|
2460 iStreamedDecodeExt = CStreamedDecodeExtension::NewL(this); |
|
2461 } |
|
2462 |
|
2463 aExtPtr = iStreamedDecodeExt; |
|
2464 break; |
|
2465 } |
|
2466 |
|
2467 default: |
|
2468 { |
|
2469 ASSERT(iExtensionManager); |
|
2470 iExtensionManager->GetExtensionL(aExtUid, aExtPtr); |
|
2471 } |
|
2472 } |
|
2473 } |
|
2474 #else |
|
2475 void CJpegDecoder::GetExtensionL(TUid /*aExtUid*/, MImageConvExtension*& /*aExtPtr*/) |
|
2476 { |
|
2477 User::Leave(KErrNotSupported); |
|
2478 } |
|
2479 #endif |
|
2480 |