--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mmplugins/imagingplugins/codecs/JPEGCodec/JPEGCodec.cpp Tue Feb 02 01:56:55 2010 +0200
@@ -0,0 +1,4102 @@
+// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+// CJpgReadCodec::GetHuffmanCodeL() is based heavily on HUFFMAN_DECODE() from jdhuff.h
+// in the IJG code, Copyright (C) 1991-1997, Thomas G. Lane.
+/**
+ @file
+ @internalComponent
+*/
+
+#include <fbs.h>
+#include "ImageUtils.h"
+#include "icl/ImageCodec.h"
+#include "JpegTypes.h"
+#include "rawcolorprocessor.h"
+#include "jpgimageframeprocessor.h"
+#include "JPEGCodec.h"
+#include "ImageProcessorPriv.h"
+#include "fwextconstants.h"
+#include <imageprocessor/imageprocessor.h>
+
+#ifdef JPEG_DEBUG_OUTPUT
+// This can be turned off in the MMP.
+#pragma message("Building Debug version of JPEG codec!")
+#endif
+
+const TInt KMaxMCUPerDraw = 100; // Maximum MCUs to draw per run
+// new value should be 48
+const TInt KMarkerLookAhead=32; // number of bytes which are checked ahead for presense of 0xFF
+const TInt KMCUDataLeftThreshhold=192; // approximate value for MCU size, used to avoid re-decoding MCUs when it span data blocks
+const TInt KMCUMaxTotalDataUnits=1024; // Maximum of data units to use on this process' heap; if we need more, we'll create a local chunk to store them.
+
+
+const TInt KUShiftIdx=0; // index of U shift factor for MCU de-sampling
+const TInt KVShiftIdx=1; // index of V shift factor for MCU de-sampling
+
+#if defined(__ARMCC__)
+#pragma push
+#pragma thumb
+#endif
+
+// CJpgReadCodec
+CJpgReadCodec::CJpgReadCodec(const TJpgFrameInfo& aFrameInfo,const TJpgScanInfo& aScanInfo)
+ : iFrameInfo(aFrameInfo),
+ iScanInfo(aScanInfo),
+ iOperation(EDecodeNormal)
+ {
+ }
+
+
+CJpgReadCodec::~CJpgReadCodec()
+ {
+ for (TInt i = 0; i < KJpgNumberOfComponents; i++)
+ {
+ delete[] iComponent[i];
+ }
+
+ delete iImageFrameCodecPtr;
+ delete iRawColorProcessor;
+ User::Free(iRgbBuffer);
+ iRgbBuffer = NULL;
+ }
+
+TFrameState CJpgReadCodec::ProcessFrameL(TBufPtr8& aSrc)
+ {
+ iDataPtr = CONST_CAST(TUint8*,aSrc.Ptr());
+ iDataPtrLimit = iDataPtr + aSrc.Length();
+ iPreviousDataLeft = iDataPtrLimit - iDataPtr;
+
+ TFrameState frameState = EFrameComplete;
+ TRAPD(err, frameState = DoProcessL());
+ if (err != KErrNone)
+ {
+ if (err == KErrCompletion)
+ {
+ RestoreState();
+ frameState = EFrameIncomplete;
+ }
+ else if (err == KErrEof)
+ {
+ frameState = EFrameComplete;
+ }
+ else
+ {
+ JPEG_LEAVE(err, "ProcessFrameL");
+ }
+ }
+
+ aSrc.Shift(iDataPtr - aSrc.Ptr()); // Shift out used data
+
+ return frameState;
+ }
+
+/**
+ used to configure U,V DCT for high-speed scaling mode
+*/
+void CJpgReadCodec::ConfigureUVComponentDCT(TInt aCompIdx)
+ {
+ switch (iScalingFactor)
+ {
+ case -2:
+ if (iHorzSampleFactor[aCompIdx]==iMaxHorzSampleFactor &&
+ iVertSampleFactor[aCompIdx]==iMaxVertSampleFactor)
+ {
+ iCompConf[aCompIdx].iDCT = &iFastHalfDCT;
+ iCompConf[aCompIdx].iDequantFunc = &TQTable::FastHalfDeQuantize;
+ }
+ break;
+
+ case -3:
+ if (iHorzSampleFactor[aCompIdx]==iMaxHorzSampleFactor &&
+ iVertSampleFactor[aCompIdx]==iMaxVertSampleFactor)
+ {
+ iCompConf[aCompIdx].iDCT = &iFastQuarterDCT;
+ iCompConf[aCompIdx].iDequantFunc = &TQTable::FastQuarterDeQuantize;
+ }
+ else if (iHorzSampleFactor[aCompIdx]*2 == iMaxHorzSampleFactor &&
+ iVertSampleFactor[aCompIdx]*2 == iMaxVertSampleFactor)
+ {
+ iCompConf[aCompIdx].iDCT = &iFastHalfDCT;
+ iCompConf[aCompIdx].iDequantFunc = &TQTable::FastHalfDeQuantize;
+ }
+ break;
+ default:
+ {
+ ASSERT(EFalse);
+ }
+ }
+ }
+//
+//
+//
+void CJpgReadCodec::InitComponentsL()
+ {
+ JPEG_ASSERT(iMaxHorzSampleFactor > 0);
+ JPEG_ASSERT(iMaxVertSampleFactor > 0);
+
+ for (TInt i = 0; i < iFrameInfo.iNumberOfComponents; i++)
+ {
+ iHorzSampleFactor[i] = iFrameInfo.iComponent[i].iHorzSampleFactor;
+ iVertSampleFactor[i] = iFrameInfo.iComponent[i].iVertSampleFactor;
+ iHorzSampleRatio[i] = iMaxHorzSampleFactor / iHorzSampleFactor[i];
+ iVertSampleRatio[i] = iMaxVertSampleFactor / iVertSampleFactor[i];
+
+ TInt dataUnits = iHorzSampleFactor[i] * iVertSampleFactor[i];
+ iMCUDataUnitCount[i] = dataUnits;
+ if ((i == 0) || !iMonochrome)
+ {
+ JPEG_ASSERT(dataUnits > 0);
+ delete[] iComponent[i];
+ iComponent[i] = NULL;
+ iComponent[i] = new(ELeave) TDataUnit[dataUnits];
+ }
+ }
+ }
+
+//
+//
+//
+void CJpgReadCodec::InitFrameL(TFrameInfo& /*aFrameInfo*/, CFrameImageData& /*aFrameImageData*/, TBool aDisableErrorDiffusion, CFbsBitmap& aFrame, CFbsBitmap* /*aFrameMask*/)
+ {
+ JPEG_DEBUG1("InitFrameL(TFrameInfo)");
+ JPEG_DEBUG3(" - iFrameInfo dimensions: %d x %d",
+ iFrameInfo.iSizeInPixels.iWidth,
+ iFrameInfo.iSizeInPixels.iHeight);
+
+ JPEG_DEBUG3(" - aFrame dimensions: %d x %d",
+ aFrame.SizeInPixels().iWidth,
+ aFrame.SizeInPixels().iHeight);
+
+ PreInitFrameL();
+
+ iMonochrome |= (aFrame.DisplayMode() <= EGray256);
+
+ if ((iMaxHorzSampleFactor != iFrameInfo.iComponent[KYComp].iHorzSampleFactor) ||
+ (iMaxVertSampleFactor != iFrameInfo.iComponent[KYComp].iVertSampleFactor))
+ {
+ // Current implementation doesn't support images with Y sampling frequency
+ // lower than U or V components
+ JPEG_LEAVE(KErrNotSupported, "Unsupported sampling frequency");
+ }
+
+ InitComponentsL();
+ for (TInt i = 0; i < iFrameInfo.iNumberOfComponents; i++)
+ {
+ if ((iFrameInfo.iComponent[i].iVertSampleFactor == 3) ||
+ (iFrameInfo.iComponent[i].iHorzSampleFactor == 3))
+ {
+ JPEG_LEAVE(KErrCorrupt, "Bad sample factor");
+ }
+
+ if ((i == 1) || (i == 2))
+ {
+ iHorzSampleRatioSh[i - 1] = iHorzSampleRatio[i] / 2;
+ iVertSampleRatioSh[i - 1] = iVertSampleRatio[i] / 2;
+ }
+ }
+
+ TSize requiredSize = iFrameInfo.iSizeInPixels;
+ TInt err = iExtensionManager->GetDestinationSize(requiredSize);
+ JPEG_LEAVE_IF_ERROR(err, "GetDestinationSize failed");
+
+ if (iExtensionManager->ScalerExtensionRequested())
+ {
+ // Explicit scaling
+ // Mandatory check that the destination size matches the calculated size
+ if (requiredSize != aFrame.SizeInPixels())
+ {
+ JPEG_LEAVE(KErrArgument, "Destination bitmap size != GetDestinationSize");
+ }
+
+ TSize sizeToScale = iExtensionManager->ClippingRectExtensionRequested() ?
+ iExtensionManager->ClippingRect().Size() : iFrameInfo.iSizeInPixels;
+
+ if (iExtensionManager->DimensionsSwapped())
+ {
+ sizeToScale.SetSize(sizeToScale.iHeight, sizeToScale.iWidth);
+ }
+
+ err = iExtensionManager->GetScalingCoefficient(iScalingFactor, &sizeToScale);
+ JPEG_LEAVE_IF_ERROR(err, "GetScalingCoefficient failed");
+
+ if (iScalingFactor != -4)
+ {
+ if (!iHighSpeedMode || (iHighSpeedMode && iMonochrome))
+ {
+ // Needs to be the original image/cropped size in correct orientation
+ // and NOT the GetDestinationSize() value
+ requiredSize = sizeToScale;
+ }
+ }
+ }
+ else
+ {
+ // Implicit scaling for compatibility
+ // requiredSize now equals full, cropped or scaled in correct orientation
+ iScalingFactor = ScalingCoefficient(requiredSize, aFrame.SizeInPixels());
+ if ((iHighSpeedMode && !iMonochrome) || iScalingFactor == -4)
+ {
+ err = GetReducedSize(requiredSize, iScalingFactor, requiredSize);
+ JPEG_LEAVE_IF_ERROR(err, "GetReducedSize failed");
+ }
+ }
+
+ if (iScalingFactor == 1)
+ {
+ // For convenience
+ iScalingFactor = -iScalingFactor;
+ }
+
+ JPEG_DEBUG2(" - iMonochrome = %d", iMonochrome);
+ JPEG_DEBUG2(" - iProgressive = %d", iProgressive);
+ JPEG_DEBUG2(" - iHighSpeedMode = %d", iHighSpeedMode);
+ JPEG_DEBUG2(" - iScalingFactor = %d", iScalingFactor);
+
+ // "raw" processor is used when we can directly write data in output format rather than TRgb
+ // that provides with saving on "to/from" TRgb conversion and memory bandwidth
+ TBool useRawProc = (!iMonochrome && !iProgressive &&
+ (aFrame.DisplayMode() == EColor16M ||
+ (aFrame.DisplayMode() == EColor64K &&
+ CPluginExtensionManager::ConvertScalingCoeffToReductionFactor(iScalingFactor) == 3))
+ );
+
+ // defaults which are equal to legacy behaviour
+ iMcuWriteFunc = (iMonochrome ? &CJpgReadCodec::WriteMonoMCU : &CJpgReadCodec::WriteUnScaledMCU);
+
+ iCompConf[KYComp].iDequantFunc = &TQTable::DeQuantize;
+ iCompConf[KUComp].iDequantFunc = &TQTable::DeQuantize;
+ iCompConf[KVComp].iDequantFunc = &TQTable::DeQuantize;
+
+ TInt reductionFactor = 0;
+ switch (iScalingFactor)
+ {
+ case -1:
+ reductionFactor = 0;
+ iCompConf[KYComp].iDCT = &iFullDCT;
+ iCompConf[KUComp].iDCT = &iFullDCT;
+ iCompConf[KVComp].iDCT = &iFullDCT;
+
+ if (iMonochrome)
+ {
+ iMcuWriteFunc = &CJpgReadCodec::WriteMonoMCU;
+ JPEG_DEBUG1(" - Using Mono function");
+ }
+ else
+ {
+ if (useRawProc)
+ {
+ iMcuWriteFunc = &CJpgReadCodec::WriteUnScaledMCU16M;
+ }
+ else
+ {
+ iMcuWriteFunc = &CJpgReadCodec::WriteUnScaledMCU;
+ }
+ JPEG_DEBUG1(" - Using Unscaled functions");
+ }
+ break;
+
+ case -2:
+ reductionFactor = 1;
+ iCompConf[KYComp].iDCT = &iHalfDCT;
+ iCompConf[KUComp].iDCT = &iHalfDCT;
+ iCompConf[KVComp].iDCT = &iHalfDCT;
+ if (!iMonochrome && iHighSpeedMode) // we do not support fast scaling for monochrome
+ {
+ reductionFactor = 0;
+ if (useRawProc)
+ {
+ iMcuWriteFunc = &CJpgReadCodec::WriteDiv2ScaledMCU16M;
+ }
+ else
+ {
+ iMcuWriteFunc = &CJpgReadCodec::WriteDiv2ScaledMCU;
+ }
+ JPEG_DEBUG1(" - Using Div2 functions");
+
+ iCompConf[KYComp].iDCT = &iFastHalfDCT;
+ iCompConf[KYComp].iDequantFunc = &TQTable::FastHalfDeQuantize;
+ ConfigureUVComponentDCT(KUComp);
+ ConfigureUVComponentDCT(KVComp);
+ }
+ else
+ {
+ iScalingFactor = -1;
+ useRawProc = EFalse;
+ }
+ break;
+
+ case -3:
+ reductionFactor = 2;
+ iCompConf[KYComp].iDCT = &iQuarterDCT;
+ iCompConf[KUComp].iDCT = &iQuarterDCT;
+ iCompConf[KVComp].iDCT = &iQuarterDCT;
+ if (!iMonochrome && iHighSpeedMode) // we do not support fast scaling for monochrome
+ {
+ reductionFactor = 0;
+ if (useRawProc)
+ {
+ iMcuWriteFunc = &CJpgReadCodec::WriteDiv4ScaledMCU16M;
+ }
+ else
+ {
+ iMcuWriteFunc = &CJpgReadCodec::WriteDiv4ScaledMCU;
+ }
+ JPEG_DEBUG1(" - Using Div4 functions");
+
+ iCompConf[KYComp].iDCT = &iFastQuarterDCT;
+ iCompConf[KYComp].iDequantFunc = &TQTable::FastQuarterDeQuantize;
+ ConfigureUVComponentDCT(KUComp);
+ ConfigureUVComponentDCT(KVComp);
+ }
+ else
+ {
+ iScalingFactor = -1;
+ useRawProc = EFalse;
+ }
+ break;
+
+ case -4:
+ iCompConf[KYComp].iDCT = &iEighthDCT;
+ iCompConf[KUComp].iDCT = &iEighthDCT;
+ iCompConf[KVComp].iDCT = &iEighthDCT;
+ iCompConf[KYComp].iDequantFunc = &TQTable::Fast18DeQuantize;
+ iCompConf[KUComp].iDequantFunc = &TQTable::Fast18DeQuantize;
+ iCompConf[KVComp].iDequantFunc = &TQTable::Fast18DeQuantize;
+ reductionFactor = 0;
+ if (!iMonochrome) // 1/8 Mono has got its own fast 1/8 inside the function
+ {
+ iMcuWriteFunc = useRawProc? (aFrame.DisplayMode()==EColor16M ? &CJpgReadCodec::WriteDiv8ScaledMCU16M : &CJpgReadCodec::WriteDiv8ScaledMCU64K)
+ : &CJpgReadCodec::WriteDiv8ScaledMCU;
+ JPEG_DEBUG1(" - Using Div8 functions");
+ }
+ break;
+
+ default:
+ JPEG_LEAVE(KErrArgument, "Bad scaling factor");
+ break;
+ }
+
+ iCompConf[KYComp].iDCT->SetPrecision(iFrameInfo.iSamplePrecision);
+ iCompConf[KUComp].iDCT->SetPrecision(iFrameInfo.iSamplePrecision);
+ iCompConf[KVComp].iDCT->SetPrecision(iFrameInfo.iSamplePrecision);
+
+ if (!iMonochrome)
+ {
+ CalculateRgbIndeces();
+ }
+
+ if (iProgressive)
+ {
+ iFrameInfo.iMCUBlocksPerLine = 1;
+ }
+ else
+ {
+ TInt mcuWidth = iFrameInfo.MCUWidthInPixels();
+ iFrameInfo.iMCUBlocksPerLine = (iFrameInfo.iSizeInPixels.iWidth + mcuWidth - 1) / mcuWidth;
+ }
+
+ // Allocate the intermediate buffer.
+ iPixelSize = useRawProc ? TDisplayModeUtils::NumDisplayModeBitsPerPixel(aFrame.DisplayMode()) / 8 : sizeof(TRgb);
+
+ TInt scaleDivisor = CJpgReadCodec::ScaleFactorToDivisorL(iScalingFactor);
+ ConfigureAndAllocateBufferL(scaleDivisor);
+
+ CImageProcessorExtension* imageProc = NULL;
+ if (useRawProc)
+ {
+ JPEG_DEBUG1(" - Using raw processor");
+ imageProc = CRawColorProcessor::NewL();
+ }
+ else
+ {
+ JPEG_DEBUG2(" - Using stock processor, reductionFactor=%d", reductionFactor);
+ imageProc = ImageProcessorUtility::NewImageProcessorExtensionL(
+ aFrame,
+ reductionFactor,
+ iMonochrome ? EGray256 : ERgb,
+ aDisableErrorDiffusion || iHighSpeedMode);
+ }
+
+ ConfigureImageProcessorL(imageProc, aFrame, scaleDivisor, requiredSize);
+ CalculateRenderingParams(scaleDivisor);
+ PostInitFrameL();
+ }
+
+//
+// Configures the image processor that has been created.
+// This function does not take ownership of the image processor.
+//
+void CJpgReadCodec::ConfigureImageProcessorL(CImageProcessorExtension* aImageProc, CFbsBitmap& aFrame, const TInt aScaleDivisor, const TSize& aPrepareLSize)
+ {
+ JPEG_DEBUG2("ConfigureImageProcessorL(aScaleDivisor=%d)", aScaleDivisor);
+ JPEG_ASSERT(aImageProc);
+ JPEG_ASSERT(iExtensionManager);
+ JPEG_ASSERT(aScaleDivisor > 0);
+ JPEG_ASSERT(aPrepareLSize.iWidth > 0);
+ JPEG_ASSERT(aPrepareLSize.iHeight > 0);
+
+
+#ifdef JPEG_DEBUG_OUTPUT
+ TRAPD(err, iExtensionManager->TransferExtensionDataL(aImageProc));
+ JPEG_LEAVE_IF_ERROR(err, "TransferExtensionDataL");
+#else
+ iExtensionManager->TransferExtensionDataL(aImageProc);
+#endif
+
+ SetImageProcessor(aImageProc);
+
+ // Set the padding variables.
+ TRect clip = iExtensionManager->ClippingRect();
+ TInt left = (clip.iTl.iX - iMCUClipRect.iTl.iX) / aScaleDivisor;
+ TInt top = (clip.iTl.iY - iMCUClipRect.iTl.iY) / aScaleDivisor;
+ TInt bottom = (iMCUClipRect.iBr.iY - clip.iBr.iY) / aScaleDivisor;
+ TInt right = (iMCUClipRect.iBr.iX - clip.iBr.iX) / aScaleDivisor;
+
+ JPEG_DEBUG1(" - clipping rect offsets (scaled):");
+ JPEG_DEBUG2(" - top: %d", top);
+ JPEG_DEBUG2(" - left: %d", left);
+ JPEG_DEBUG2(" - bottom: %d", bottom);
+ JPEG_DEBUG2(" - right: %d", right);
+ JPEG_DEBUG3(" - Output size: %d x %d", aPrepareLSize.iWidth, aPrepareLSize.iHeight);
+
+ //TBool bottomUp = ETrue;
+ TInt initialPadding = 0;
+ TInt padding = 0;
+
+ // iRubbish is only valid for non-clipped images.
+ switch (iOperation)
+ {
+ case EDecodeNormal:
+ case EDecodeHorizontalFlip:
+ initialPadding = (iUseClipRect ? top : 0);
+ padding = (iUseClipRect ? left : 0);
+ break;
+
+ case EDecodeVerticalFlip:
+ case EDecodeRotate180:
+ initialPadding = (iUseClipRect ? top : 0);
+ padding = (iUseClipRect ? right : iRubbish.iWidth);
+ break;
+
+ case EDecodeRotate90:
+ case EDecodeVerticalFlipRotate90:
+ initialPadding = (iUseClipRect ? left : 0);
+ padding = (iUseClipRect ? bottom : iRubbish.iHeight);
+ break;
+
+ case EDecodeHorizontalFlipRotate90:
+ case EDecodeRotate270:
+ initialPadding = (iUseClipRect ? left : 0);
+ padding = (iUseClipRect ? top : 0);
+ break;
+
+ default:
+ ASSERT(EFalse);
+ }
+
+ JPEG_DEBUG2(" - initialPadding = %d", initialPadding);
+ JPEG_DEBUG2(" - padding = %d", padding);
+
+ JPEG_ASSERT(aImageProc);
+
+ aImageProc->SetInitialScanlineSkipPadding(initialPadding);
+ aImageProc->SetPixelPadding(padding);
+ aImageProc->PrepareL(aFrame, aPrepareLSize, iBufSize);
+ }
+
+//
+//
+//
+TInt CJpgReadCodec::ScaleFactorToDivisorL(TInt aScalingFactor)
+ {
+ switch (aScalingFactor)
+ {
+ case 1:
+ case -1:
+ return 1;
+
+ case -2:
+ return 2;
+
+ case -3:
+ return 4;
+
+ case -4:
+ return 8;
+
+ default:
+ break;
+ }
+
+ // Keep the compiler happy.
+ JPEG_LEAVE(KErrArgument, "Bad scaling factor");
+ return 0;
+ }
+
+//
+// Calculates the necessary intermediate buffer size and allocates it.
+// It also sets how many MCUs are contained in the buffer (iMCUsPerBuffer).
+//
+void CJpgReadCodec::ConfigureAndAllocateBufferL(const TInt aScale)
+ {
+ JPEG_DEBUG2("ConfigureAndAllocateBufferL(aScale=%d)", aScale);
+ JPEG_ASSERT(aScale > 0);
+
+ TInt horizMCUs = 0;
+ TInt vertMCUs = 0;
+
+ if (iUseClipRect)
+ {
+ // We need to translate the clipping rect from pixels to MCU space.
+ // Clipping is done before any rotation.
+ JPEG_ASSERT(!iProgressive);
+
+ CalculateMCUBoundingRectL(iFrameInfo.iMCUBlocksPerLine);
+ horizMCUs = iMCUClipRect.Width() / iFrameInfo.MCUWidthInPixels();
+ vertMCUs = iMCUClipRect.Height() / iFrameInfo.MCUHeightInPixels();
+ }
+ else
+ {
+ if (iProgressive)
+ {
+ JPEG_ASSERT(aScale == 1);
+ JPEG_ASSERT(iOperation == EDecodeNormal);
+
+ horizMCUs = 1;
+ vertMCUs = 1;
+ }
+ else
+ {
+ horizMCUs = iFrameInfo.iMCUBlocksPerLine;
+ vertMCUs = iFrameInfo.iMCUBlocksPerColumn;
+
+ iRubbish.iWidth = ((horizMCUs * iFrameInfo.MCUWidthInPixels()) - iFrameInfo.iSizeInPixels.iWidth) / aScale;
+ iRubbish.iHeight = ((vertMCUs * iFrameInfo.MCUHeightInPixels()) - iFrameInfo.iSizeInPixels.iHeight) / aScale;
+
+ JPEG_DEBUG3(" - iRubbish: %d x %d (scaled)", iRubbish.iWidth, iRubbish.iHeight);
+ JPEG_ASSERT(iRubbish.iWidth >= 0);
+ JPEG_ASSERT(iRubbish.iHeight >= 0);
+ }
+ }
+
+ // We may need to swap dimensions for some rotates.
+ switch (iOperation)
+ {
+ case EDecodeRotate90:
+ case EDecodeRotate270:
+ case EDecodeHorizontalFlipRotate90:
+ case EDecodeVerticalFlipRotate90:
+ iMCUsPerBuffer = vertMCUs;
+ iBufSize.iWidth = vertMCUs * iFrameInfo.MCUHeightInPixels();
+ iBufSize.iHeight = iFrameInfo.MCUWidthInPixels();
+ break;
+
+ default:
+ iMCUsPerBuffer = horizMCUs;
+ iBufSize.iWidth = horizMCUs * iFrameInfo.MCUWidthInPixels();
+ iBufSize.iHeight = iFrameInfo.MCUHeightInPixels();
+ }
+
+ iBufSize.iWidth /= aScale;
+ iBufSize.iHeight /= aScale;
+
+ // Because JPEG doesn't round up with scaling it is possible to have a scaled image that 0x0 in size.
+ // Things are ok for now because a 1x1 image is actually an 8x8 image and the maximum reduction factor
+ // supported is -4 (divide by 8). It is possible that larger reduction factors will be supported in
+ // the future as cameras take larger pictures. In this case an 8x8 image will scale to 0x0.
+ JPEG_DEBUG3(" - scaled buffer dimensions: %d x %d", iBufSize.iWidth, iBufSize.iHeight);
+ JPEG_ASSERT(iBufSize.iWidth > 0);
+ JPEG_ASSERT(iBufSize.iHeight > 0);
+ JPEG_ASSERT(iPixelSize > 0);
+
+ TInt bufSize = iBufSize.iWidth * iBufSize.iHeight * iPixelSize;
+
+ User::Free(iRgbBuffer);
+ iRgbBuffer = NULL;
+ iRgbBuffer = reinterpret_cast<TRgb*>(User::AllocL(bufSize));
+ }
+
+
+//
+// Sets up the fields that control where the pixels are
+// drawn into the intermediate buffer.
+//
+void CJpgReadCodec::CalculateRenderingParams(const TInt aScale)
+ {
+ JPEG_DEBUG1("CalculateRenderingParams()");
+
+ JPEG_ASSERT(aScale > 0);
+
+ TInt mcuPixelWidth = iFrameInfo.MCUWidthInPixels() / aScale;
+ TInt mcuPixelHeight = iFrameInfo.MCUHeightInPixels() / aScale;
+
+ switch (iOperation)
+ {
+ case EDecodeNormal:
+ case EDecodeHorizontalFlip:
+ // These are the same except FlipHorizontal will draw from bottom up.
+ iFirstPixelOffset = 0;
+ iPixelIncrement = 1;
+ iRgbBufNextLineOffs = iBufSize.iWidth - mcuPixelWidth;
+ iFillBufferBackwards = EFalse;
+ iMCUHorizExtent = mcuPixelWidth;
+ break;
+
+ case EDecodeVerticalFlip:
+ case EDecodeRotate180:
+ iFirstPixelOffset = mcuPixelWidth - 1;
+ iPixelIncrement = -1;
+ iRgbBufNextLineOffs = iBufSize.iWidth + mcuPixelWidth;
+ iFillBufferBackwards = ETrue;
+ iMCUHorizExtent = mcuPixelWidth;
+ break;
+
+ case EDecodeRotate90:
+ case EDecodeVerticalFlipRotate90:
+ // These differ only in the direction the buffer is copied to the bitmap.
+ iFirstPixelOffset = mcuPixelHeight - 1;
+ iPixelIncrement = iBufSize.iWidth;
+ iRgbBufNextLineOffs = -((iBufSize.iWidth * mcuPixelWidth) + 1);
+ iFillBufferBackwards = ETrue;
+ iMCUHorizExtent = mcuPixelHeight;
+ break;
+
+ case EDecodeRotate270:
+ case EDecodeHorizontalFlipRotate90:
+ // These differ only in the direction the buffer is copied to the bitmap.
+ iFirstPixelOffset = 0;
+ iPixelIncrement = iBufSize.iWidth;
+ iRgbBufNextLineOffs = -(iBufSize.iWidth * mcuPixelWidth) + 1;
+ iFillBufferBackwards = EFalse;
+ iMCUHorizExtent = mcuPixelHeight;
+ break;
+
+ default:
+ // Bad operation.
+ ASSERT(EFalse);
+ }
+
+ JPEG_DEBUG2(" - iFirstPixelOffset = %d", iFirstPixelOffset);
+ JPEG_DEBUG2(" - iPixelIncrement = %d", iPixelIncrement);
+ JPEG_DEBUG2(" - iRgbBufNextLineOffs = %d", iRgbBufNextLineOffs);
+ JPEG_DEBUG2(" - iFillBufferBackwards = %d", iFillBufferBackwards);
+ JPEG_DEBUG2(" - iMCUHorizExtent = %d", iMCUHorizExtent);
+ }
+
+//
+// This is called when a clipping rectangle has been set.
+// Subclasses that support clipping must provide a way of
+// mapping the clip rect pixel coordinates to MCUs.
+//
+void CJpgReadCodec::CalculateMCUBoundingRectL(TInt /*aMCUsPerLine*/)
+ {
+ JPEG_LEAVE(KErrNotSupported, "CalculateMCUBoundingRectL");
+ }
+
+/**
+ we would go across the whole block top-to-bottom left-to-right
+*/
+void CJpgReadCodec::CalculateRgbIndeces()
+ {
+ TUVidxElemType* pixIdxPtr=iUVIndeces;
+
+ TUint offsMask=iMaxVertSampleFactor*iMaxHorzSampleFactor*KJpgDCTBlockSize -1;
+ TInt vertStep=1;
+
+ switch (iScalingFactor)
+ {
+ case -4:
+ offsMask &= ~TUint(KJpgDCTBlockSize - 1);
+ break;
+
+ case -3:
+ vertStep = 4;
+ break;
+
+ case -2:
+ vertStep = 2;
+ break;
+
+ default:
+ ASSERT((iScalingFactor == 1) || (iScalingFactor == -1));
+ }
+
+ for (TInt j = 0; j < iMaxVertSampleFactor*KJpgDCTBlockWidth; j+=vertStep)
+ {
+
+ for (TInt i = 0; i < iMaxHorzSampleFactor; i++)
+ {
+ for (TInt k=0; k<2; ++k)
+ {
+ const TInt shiftIdx=k;
+ const TInt comp=k+1;
+ TInt hOffs =((i*KJpgDCTBlockWidth)>>iHorzSampleRatioSh[shiftIdx])&7;
+ TInt hBlkOffs =(i>>iHorzSampleRatioSh[shiftIdx]) *KJpgDCTBlockSize;
+
+ TInt vOffs =((j>>iVertSampleRatioSh[shiftIdx]&7))*KJpgDCTBlockWidth;
+
+ TInt vBlkOffs =((j/KJpgDCTBlockWidth) >>iVertSampleRatioSh[shiftIdx]) *KJpgDCTBlockSize*iHorzSampleFactor[comp];
+
+ *pixIdxPtr++ = offsMask & (hOffs+ hBlkOffs + vOffs + vBlkOffs);
+ }
+
+ if (iScalingFactor == -3)
+ {
+ // for this factor we have 1 more entry per horizontal line
+ // which is offset against previous pixel i.e. (4>>iHorzSampleRatioSh[Comp])
+ *pixIdxPtr++ = vertStep>>iHorzSampleRatioSh[KUShiftIdx];
+ *pixIdxPtr++ = vertStep>>iHorzSampleRatioSh[KVShiftIdx];
+ }
+ }
+ }
+ ASSERT(pixIdxPtr-iUVIndeces <= sizeof(iUVIndeces)/sizeof(iUVIndeces[0]) );
+ }
+
+void CJpgReadCodec::InitFrameL(CImageFrame& aDest)
+ {
+ JPEG_DEBUG1("CJpgReadCodec::InitFrameL(CImageFrame)");
+
+ PreInitFrameL();
+
+ // This JPEG codec does not support scaling when using destination of type CImageFrame
+ // It leaves if scaling has been requested.
+ //TInt reductionFactor = 0;
+
+ if (iOperation != EDecodeNormal)
+ {
+ JPEG_LEAVE(KErrNotSupported, "No operations on CImageFrame");
+ }
+
+ if (iExtensionManager->ScalerExtensionRequested())
+ {
+ JPEG_LEAVE(KErrNotSupported, "No scaling on CImageFrame");
+ }
+
+ if (iUseClipRect)
+ {
+ JPEG_LEAVE(KErrNotSupported, "No clipping on CImageFrame");
+ }
+
+ // Check for implicit scaling.
+ TInt reductionFactor = 0;
+ if (!iUseClipRect)
+ {
+ reductionFactor = ReductionFactor(iFrameInfo.iSizeInPixels, aDest.FrameSizeInPixels());
+ }
+
+ if (reductionFactor != 0)
+ {
+ JPEG_DEBUG2(" - reductionFactor = %d", reductionFactor);
+ JPEG_LEAVE(KErrNotSupported, "Bad reductionFactor");
+ }
+
+ InitComponentsL();
+
+ iCompConf[KYComp].iDequantFunc = &TQTable::DeQuantize;
+ iCompConf[KUComp].iDequantFunc = &TQTable::DeQuantize;
+ iCompConf[KVComp].iDequantFunc = &TQTable::DeQuantize;
+
+ iCompConf[KYComp].iDCT = &iFullDCT;
+ iCompConf[KUComp].iDCT = &iFullDCT;
+ iCompConf[KVComp].iDCT = &iFullDCT;
+
+ iCompConf[KYComp].iDCT->SetPrecision(iFrameInfo.iSamplePrecision);
+ iCompConf[KUComp].iDCT->SetPrecision(iFrameInfo.iSamplePrecision);
+ iCompConf[KVComp].iDCT->SetPrecision(iFrameInfo.iSamplePrecision);
+
+ // Create JPEG read codec extension and the appropriate image processor
+ ASSERT(iImageFrameCodecPtr == NULL);
+ delete iImageFrameCodecPtr;
+ iImageFrameCodecPtr = NULL;
+ iImageFrameCodecPtr = CJpgImageFrameReadCodec::NewL(&aDest);
+ iImageFrameCodecPtr->CreateImageProcessorL(iFrameInfo);
+
+ PostInitFrameL();
+ }
+
+//initialization for streaming
+void CJpgReadCodec::InitFrameL(TUid aFormat, TDecodeStreamCaps::TNavigation aNavigation)
+ {
+ JPEG_DEBUG1("InitFrameL(TUid)");
+
+ //validate format with frameinfo
+ PreInitFrameL();
+
+ ValidateFormatL(iFrameInfo, aFormat);
+
+ iIsBlockStreaming = ETrue;
+
+ iStreamFormat = aFormat;
+ iNavigation = aNavigation;
+
+ InitComponentsL();
+
+ iCompConf[KYComp].iDequantFunc = &TQTable::DeQuantize;
+ iCompConf[KUComp].iDequantFunc = &TQTable::DeQuantize;
+ iCompConf[KVComp].iDequantFunc = &TQTable::DeQuantize;
+
+ iCompConf[KYComp].iDCT = &iFullDCT;
+ iCompConf[KUComp].iDCT = &iFullDCT;
+ iCompConf[KVComp].iDCT = &iFullDCT;
+
+ iCompConf[KYComp].iDCT->SetPrecision(iFrameInfo.iSamplePrecision);
+ iCompConf[KUComp].iDCT->SetPrecision(iFrameInfo.iSamplePrecision);
+ iCompConf[KVComp].iDCT->SetPrecision(iFrameInfo.iSamplePrecision);
+
+ // Create JPEG read codec extension and the appropriate image processor
+ delete iImageFrameCodecPtr;
+ iImageFrameCodecPtr = NULL;
+ iImageFrameCodecPtr = CJpgImageFrameReadCodec::NewL(NULL);
+
+ PostInitFrameL();
+ }
+
+#if defined(__ARMCC__)
+#pragma pop
+#endif
+
+//get blocks for streaming
+void CJpgReadCodec::GetBlocksL(CImageFrame* aFrame, TInt aSeqPosition, TInt aNumBlocksToGet, TInt* aNumBlocksRead)
+ {
+ ASSERT(aNumBlocksRead);
+
+ if(aFrame == NULL || !(iNavigation == TDecodeStreamCaps::ENavigationRandomForward || iNavigation == TDecodeStreamCaps::ENavigationRandomBackwards))
+ {
+ JPEG_LEAVE(KErrArgument, "GetBlocks - bad params");
+ }
+
+ ValidateImageFrameBlockL(aFrame);
+
+ iImageFrameCodecPtr->SetImageFrameBlocksL(aFrame, iFrameInfo);
+
+ *aNumBlocksRead = 0;
+ iStreamDecodeConfig.iSeqPosition = aSeqPosition;
+ iStreamDecodeConfig.iNumBlocksToGet = aNumBlocksToGet;
+ iStreamDecodeConfig.iNumBlocksRead = aNumBlocksRead;
+ }
+
+//get blocks for streaming
+void CSequentialJpgReadCodec::GetBlocksL(CImageFrame* aFrame, TInt aSeqPosition, TInt aNumBlocksToGet, TInt* aNumBlocksRead)
+ {
+ if (aSeqPosition < 0)
+ {
+ JPEG_LEAVE(KErrArgument, "GetBlocksL - bad aSeqPosition");
+ }
+
+ iNeededMCU = aSeqPosition;
+
+ CJpgReadCodec::GetBlocksL(aFrame, aSeqPosition, aNumBlocksToGet, aNumBlocksRead);
+ }
+
+//get blocks for streaming
+void CJpgReadCodec::GetNextBlocksL(CImageFrame* aFrame, TInt aNumBlocksToGet, TInt* aNumBlocksRead, TBool* aHaveMoreBlocks)
+ {
+ ASSERT(aNumBlocksRead);
+ ASSERT(aHaveMoreBlocks);
+
+ if (aFrame == NULL || iNavigation != TDecodeStreamCaps::ENavigationSequentialForward)
+ {
+ JPEG_LEAVE(KErrArgument, "GetNextBlocksL - bad params");
+ }
+
+ ValidateImageFrameBlockL(aFrame);
+
+ iImageFrameCodecPtr->SetImageFrameBlocksL(aFrame, iFrameInfo);
+
+ *aNumBlocksRead = 0;
+ *aHaveMoreBlocks = ETrue;
+ iStreamDecodeConfig.iNumBlocksToGet = aNumBlocksToGet;
+ iStreamDecodeConfig.iNumBlocksRead = aNumBlocksRead;
+ iStreamDecodeConfig.iHaveMoreBlocks = aHaveMoreBlocks;
+ }
+
+//validates the blocks passed.
+void CJpgReadCodec::ValidateImageFrameBlockL(CImageFrame* aFrame)
+ {
+ ASSERT(aFrame);
+
+ TSize aBlockSizeInPixels = aFrame->FrameSizeInPixels();
+ TSize aRefSizeInPixels = TSize(iFrameInfo.MCUWidthInPixels(), iFrameInfo.MCUHeightInPixels());
+
+ const TFrameFormat& format = static_cast<const TFrameFormat&>(aFrame->FrameFormat());
+ TUid imageFrameFormatCode = format.FormatCode();
+
+ TInt oddPixelsWidth = aBlockSizeInPixels.iWidth % aRefSizeInPixels.iWidth;
+ TInt oddPixelsHeight = aBlockSizeInPixels.iHeight % aRefSizeInPixels.iHeight;
+
+ if (oddPixelsWidth != 0 || oddPixelsHeight != 0 || aBlockSizeInPixels.iHeight != aRefSizeInPixels.iHeight)
+ {
+ User::Leave(KErrNotSupported);
+ }
+
+ if(iIsBlockStreaming == EFalse)
+ {
+ User::Leave(KErrNotReady);
+ }
+
+ if(imageFrameFormatCode != KNullUid)
+ {
+ if(imageFrameFormatCode != iStreamFormat)
+ {
+ User::Leave(KErrArgument);
+ }
+ }
+ }
+
+//get buffer and blockSizeInPix for streaming
+TInt CJpgReadCodec::GetStreamBufferSizeL(TUid aFormatCode, TSize& aBlockSizeInPixels, TInt aNumBlocks)
+ {
+ return CJpgImageFrameProcessorUtility::RecommendedStreamBufferSizeL(iFrameInfo, aFormatCode, aBlockSizeInPixels, aNumBlocks);
+ }
+
+//validate format
+void CJpgReadCodec::ValidateFormatL(const TJpgFrameInfo& aFrameInfo, TUid aFormatCode)
+ {
+ TInt dataUnitCount=0;
+
+ if (aFrameInfo.iNumberOfComponents == 1)
+ {
+ dataUnitCount = 1;
+ }
+ else
+ {
+ dataUnitCount = 0;
+ for (TInt index = 0; index < aFrameInfo.iNumberOfComponents; index++)
+ {
+ dataUnitCount += aFrameInfo.iComponent[index].iHorzSampleFactor *
+ aFrameInfo.iComponent[index].iVertSampleFactor;
+ }
+ }
+
+ switch (dataUnitCount)
+ {
+
+ case 1: // Monochrome
+ {
+ if (aFormatCode != KUidFormatYUVMonochrome)
+ {
+ // error transcoding not supported
+ User::Leave(KErrNotSupported);
+ }
+ break;
+ }
+
+ case 4: // 4:2:2
+ {
+ if (aFormatCode != KUidFormatYUV422Interleaved)
+ {
+ // error transcoding not supported
+ User::Leave(KErrNotSupported);
+ }
+ break;
+ }
+
+ case 6: // 4:2:0
+ {
+ if (!(aFormatCode == KUidFormatYUV420Planar || aFormatCode == KUidFormatYUV420PlanarReversed))
+ {
+ // error transcoding not supported
+ User::Leave(KErrNotSupported);
+ }
+ break;
+ }
+
+ default:
+ {
+ User::Leave(KErrNotSupported);
+ break;
+ }
+ }
+ }
+
+//
+// This function should be called at beginning of the
+// various InitFrameL versions. It performs initialisation
+// that's common to all forms of image.
+//
+void CJpgReadCodec::PreInitFrameL()
+ {
+ JPEG_DEBUG1("PreInitFrameL()");
+ JPEG_DEBUG2(" - MCU pixel width: %d", iFrameInfo.MCUWidthInPixels());
+ JPEG_DEBUG2(" - MCU pixel height: %d", iFrameInfo.MCUHeightInPixels());
+
+ iHorzMCUBlkCount = 0;
+ iScalingFactor = 1;
+ iMCUsPerBuffer = (iFrameInfo.iProgressive ? 1 : iFrameInfo.iMCUBlocksPerLine);
+ ASSERT(iMCUsPerBuffer > 0);
+
+ iMonochrome = (iFrameInfo.iNumberOfComponents == 1);
+
+ TInt interval = iFrameInfo.iRestartInterval;
+ iRestartMCUCount = (interval > 0 ? interval : KErrNotFound);
+ JPEG_DEBUG2(" - iRestartMCUCount = %d", iRestartMCUCount);
+
+ iMaxHorzSampleFactor = iFrameInfo.iMaxHorzSampleFactor;
+ iMaxVertSampleFactor = iFrameInfo.iMaxVertSampleFactor;
+ JPEG_DEBUG2(" - iMaxHorzSampleFactor = %d", iMaxHorzSampleFactor);
+ JPEG_DEBUG2(" - iMaxVertSampleFactor = %d", iMaxVertSampleFactor);
+
+ // Get info from the extension manager. The InitFrameL function can
+ // decide whether or not the operation is supported.
+ ASSERT(iExtensionManager);
+
+ if (iAutoRotateFlag > 1 && iAutoRotateFlag < 9)
+ {
+ // To ensure operation extension is created
+ iExtensionManager->CreateExtensionForAutoRotateL();
+ iOperation = iExtensionManager->OperationL(iAutoRotateFlag);
+ }
+ else
+ {
+ iOperation = iExtensionManager->Operation();
+ }
+
+ // check out if we need to clip.
+ iUseClipRect = EFalse;
+ if (iExtensionManager->ClippingRectExtensionRequested())
+ {
+ iUseClipRect = ETrue;
+ }
+
+ ResetState();
+ }
+
+//
+//
+//
+void CJpgReadCodec::PostInitFrameL()
+ {
+ // Default implementation does nothing.
+ }
+
+//
+//
+//
+void CJpgReadCodec::StoreState()
+ {
+ iInitialDataPtr = iDataPtr;
+ iInitialDataValue = iDataValue;
+ iInitialBitsLeft = iBitsLeft;
+ iBitBufferPtrLimit = NULL;
+
+ for (TInt i = 0; i < KJpgNumberOfComponents; i++)
+ {
+ iInitialDCPredictor[i] = iDCPredictor[i];
+ }
+ }
+
+//
+//
+//
+void CJpgReadCodec::RestoreState()
+ {
+ iDataPtr = iInitialDataPtr;
+ iBitBufferPtrLimit = NULL;
+ iDataValue = iInitialDataValue;
+ iBitsLeft = iInitialBitsLeft;
+
+ for (TInt i = 0; i < KJpgNumberOfComponents; i++)
+ {
+ iDCPredictor[i] = iInitialDCPredictor[i];
+ }
+ }
+
+//
+//
+//
+void CJpgReadCodec::ResetState()
+ {
+ for (TInt i = 0; i < KJpgNumberOfComponents; i++)
+ {
+ iDCPredictor[i] = 0;
+ }
+
+ iBitsLeft = 0;
+ iBitBufferPtrLimit = NULL;
+ }
+
+//
+// Returns the number of bytes that had to be skipped before the restart marker was found.
+//
+TInt CJpgReadCodec::RestartStateL()
+ {
+ const TUint8* startPtr = iDataPtr;
+ const TUint8* readLimit = iDataPtrLimit - 2;
+
+ // skip the data until the marker is found
+ while (*iDataPtr != 0xff && iDataPtr < readLimit)
+ {
+ iDataPtr++;
+ }
+
+ if (*iDataPtr != 0xff)
+ {
+ User::Leave(KErrCompletion);
+ }
+
+ iDataPtr++;
+
+ TInt marker = *iDataPtr++;
+
+ if (marker == (KJpgEOISignature & 0x00ff))
+ {
+ User::Leave(KErrEof);
+ }
+ else if ((marker & 0xf0) != 0xd0)
+ {
+#if !defined(RELAX_JPEG_STRICTNESS)
+ User::Leave(KErrCorrupt);
+#endif
+ }
+
+ // if iRestartInterval is 0, iRestartMCUCount is set to a negative value (KErrNotFound) to skip the 0 trigger points that would call RestartStateL
+ // This is done to solve the problem that on some images the iRestartInterval marker is 0 on every frame
+ iRestartMCUCount = iFrameInfo.iRestartInterval > 0 ? iFrameInfo.iRestartInterval : KErrNotFound;
+ ResetState();
+ StoreState();
+
+ return iDataPtr - startPtr;
+ }
+
+CJpgImageFrameReadCodec* CJpgReadCodec::ImageFrameCodec()
+ {
+ return iImageFrameCodecPtr;
+ }
+
+TInt CJpgReadCodec::ReducedSize(const TSize& aOriginalSize, TInt aReductionFactor, TSize& aReducedSize) const
+ {
+ aReducedSize = aOriginalSize;
+ if (aReductionFactor < 0 || aReductionFactor > 3)
+ {
+ return KErrArgument;
+ }
+ else
+ {
+ return CImageReadCodec::ReducedSize(aOriginalSize, aReductionFactor, aReducedSize);
+ }
+ }
+
+#if defined(__ARMCC__)
+// use ARM instruction for performance-critical code
+#pragma push
+#pragma arm
+#pragma O3
+#pragma Otime
+#endif
+
+TInt CJpgReadCodec::FillBitBufferL(TInt aBLeft)
+ {
+ const TInt KBytesToFetch= 4;
+ const TUint8* dataPtr = iDataPtr;
+
+ const TUint8* bitBufLim = iBitBufferPtrLimit;
+ if (dataPtr + KBytesToFetch >= bitBufLim)
+ {
+ bitBufLim = JpegFillBuffer(dataPtr);
+ }
+
+ register TUint dataValue = iDataValue;
+
+ if (dataPtr + KBytesToFetch < bitBufLim )
+ {
+ if (aBLeft == 0)
+ {
+ aBLeft = 8;
+ dataValue = (*dataPtr++);
+ }
+ dataValue = (dataValue<<8) | (*dataPtr++);
+ dataValue = (dataValue<<8) | (*dataPtr++);
+ dataValue = (dataValue<<8) | (*dataPtr++);
+
+ iBitsLeft = aBLeft + (KBytesToFetch-1) * 8;
+
+ iDataValue= dataValue;
+ iDataPtr = dataPtr;
+
+ return iBitsLeft;
+ }
+
+ if (dataPtr == iDataPtrLimit) // no more data available
+ {
+ if (aBLeft) // there are some bits left so make use of them
+ {
+ return (iBitsLeft = aBLeft);
+ }
+ // no more data in buffer - signal that
+ User::Leave(KErrCompletion);
+ }
+
+ iDataValue = (dataValue<<8) | (*iDataPtr++);
+ iBitsLeft = aBLeft + 8;
+
+ if ((iDataValue & 0xFF) == 0xFF)
+ {
+ if (iDataPtr == iDataPtrLimit)
+ {
+ // ----+ +-------
+ // |FF| |??|??|
+ // ----+ +-------
+ --iDataPtr;
+ User::Leave(KErrCompletion);
+ }
+
+ // When the byte sequence 0xFF occurs in encoded image data it must be followed
+ // by 0x00 or the decoder will consider it a marker. The 0x00 is ignored
+ // by the decoder.
+
+ // --------...
+ // |FF|??|
+ // --------...
+ TInt marker = *iDataPtr++;
+ if (marker)//Since (FF,00) stream is valid we send it.For a Non-Zero marker we need to do something
+ {
+ // It's a proper marker
+ if (iBitsLeft > 8) // there are stiil some bits to decode
+ {
+ // Before: iDataValue = [xxxxxxxx][xxxxxxxx][xxxxxxxx][ffffffff]
+ iBitsLeft-=8;
+ iDataValue >>=8;
+ iDataPtr-=2;
+ // After: iDataValue = [00000000][xxxxxxxx][xxxxxxxxx][xxxxxxxx]
+ return iBitsLeft;
+ }
+ // End of encoded image data stream?
+ if (marker == (KJpgEOISignature&0xFF)) //Allow 0xFF as a valid marker only report KErrEof in case of EOI marker
+ {
+ User::Leave(KErrEof); // we've reached the end of the file
+ }
+ else
+ {
+ //If marker is a special marker like 0xFF we need to give a valid iDataValue..
+ if(marker == KJpgMarkerByte)
+ {
+ while(iDataPtr<iDataPtrLimit && *iDataPtr == KJpgMarkerByte)
+ {
+ iDataPtr++;
+ }
+
+ if (iDataPtr==iDataPtrLimit)
+ {
+ --iDataPtr;
+ User::Leave(KErrCompletion);
+ }
+ else
+ {
+ //Check for 0x00 marker since (0xFF,0x00) is a valid stream
+ if(*iDataPtr==0x00)
+ {
+ iDataPtr+=1;
+ return iBitsLeft;
+ }
+ }
+ }
+ iDataPtr-=1; // we'd accept some malformed files, otherwise User::Leave(KErr)
+ }
+ }
+ }
+ return iBitsLeft;
+ }
+
+//
+// Examine the buffer given by ProcessL for 0xFF sequences.
+// This allows for less checking in the FillBitBufferL function.
+//
+inline const TUint8* CJpgReadCodec::JpegFillBuffer(const TUint8* aDataPtr)
+ {
+ register const TUint8* limit = Min(iDataPtrLimit - 1, aDataPtr + KMarkerLookAhead + 1);
+
+ // Try to ignore markers that are at the start of the buffer, except for EOI.
+ while ((aDataPtr < limit) && (*aDataPtr != 0xFF))
+ {
+ ++aDataPtr;
+ }
+
+ return (iBitBufferPtrLimit = (aDataPtr - 1));
+ }
+
+FORCEDINLINE TBool CJpgReadCodec::NextBitL()
+ {
+ if (iBitsLeft == 0)
+ {
+ FillBitBufferL(0);
+ }
+ return (iDataValue & (1 << --iBitsLeft));
+ }
+
+void CJpgReadCodec::SetAutoRotateFlag(TUint16 aAutoRotateFlag)
+ {
+ iAutoRotateFlag = aAutoRotateFlag;
+ }
+
+void CJpgReadCodec::GetComponentBlockL(TDataUnit& aDestination,TInt& aNumValues,TInt& aDCPrediction,const TDecHuffmanTable& aDCTable,const TDecHuffmanTable& aACTable)
+ {
+ TInt size = GetHuffmanCodeL(aDCTable);
+ TInt amplitude = (size > 0) ? GetBinaryNumberL( size & 0x1F ) : 0;
+ aDCPrediction += amplitude;
+
+ if (aDCPrediction > KMaxTInt16 || aDCPrediction < KMinTInt16)
+ {
+ JPEG_LEAVE(KErrCorrupt, "Bad component block");
+ }
+
+ if (iScalingFactor != -4)
+ {
+ FillCompZ(aDestination, KJpgDCTBlockSize);
+ TInt16* valuePtr = aDestination.iCoeff;
+ TInt16* valuePtrLimit = valuePtr + KJpgDCTBlockSize;
+
+ *valuePtr++ = TInt16(aDCPrediction);
+
+ while (valuePtr < valuePtrLimit)
+ {
+ TInt s = GetHuffmanCodeL(aACTable);
+ if (s == 0) // End of block
+ {
+ break;
+ }
+ else
+ {
+ TInt r = s >> 4;
+ s &= 0x0f;
+ if (s > 0)
+ {
+ valuePtr += r;
+
+ if (valuePtr < valuePtrLimit)
+ {
+ *valuePtr++ = GetBinaryNumberL(s);
+ }
+ }
+ else if (r == 15) // Zero run length
+ {
+ valuePtr += 16;
+ }
+ }
+ }
+
+ if (valuePtr > valuePtrLimit)
+ {
+ valuePtr = valuePtrLimit;
+ }
+
+ aNumValues = valuePtr - aDestination.iCoeff;
+ }
+ else // for 1/8 scaling we need only DC value, so perform fast block skipping
+ {
+ aNumValues = 1;
+
+ aDestination.iCoeff[0] = TInt16(aDCPrediction);
+ TInt numValuesRead = 1; //we've already got DC value
+ do
+ {
+ TInt s = GetHuffmanCodeL(aACTable);
+ if (s == 0) // End of block
+ {
+ break;
+ }
+ else
+ {
+ TInt r = s >> 4;
+ s &= 0x0f;
+ if (s > 0)
+ {
+ numValuesRead += r;
+
+ if (numValuesRead < KJpgDCTBlockSize)
+ {
+ numValuesRead++;
+ SkipBitsQuickL(s);
+ }
+ }
+ else if (r == 15) // Zero run length
+ {
+ numValuesRead += 16;
+ }
+ }
+ } while (numValuesRead < KJpgDCTBlockSize);
+ }
+ }
+
+TInt CJpgReadCodec::GetHuffmanCodeL(const TDecHuffmanTable& aTable)
+ {
+ TInt bLeft = iBitsLeft;
+ if (bLeft < KJpgHuffmanLookAhead)
+ {
+ bLeft = FillBitBufferL(bLeft);
+ }
+
+ TUint dv = iDataValue;
+
+ TInt nb=1;
+ //
+ if (bLeft >= KJpgHuffmanLookAhead)
+ {
+
+ TUint32 fastLook = (dv >> (bLeft - KJpgHuffmanLookAhead)) & KJpgHuffmanLookAheadMask;
+ register TUint32 lookupEntry = aTable.GetLookupEntry(fastLook);
+ if (aTable.Found(lookupEntry))
+ {
+ iBitsLeft = (bLeft - aTable.GetSize(lookupEntry));
+ return aTable.GetCode(lookupEntry);
+ }
+ else
+ {
+ nb = (KJpgHuffmanLookAhead+1 > bLeft)? bLeft : KJpgHuffmanLookAhead+1;
+ }
+ }
+
+ register TUint index = 0;
+ TInt bitCount = 0;
+
+ ASSERT(nb>0);
+ register TUint32 look = dv << (32 - bLeft);
+ iBitsLeft -= nb;
+ do
+ {
+ index = (index << 1) + 1;
+ index +=((look & TUint(1<<31)) != 0);
+ look<<=1;
+ } while (++bitCount < nb);
+
+ const TUint8* codeIdxHash = aTable.GetCodeIdxHash();
+
+ for (; bitCount <= 16; bitCount++)
+ {
+ TInt first = codeIdxHash[bitCount];
+ TInt last = codeIdxHash[bitCount+1];
+ while (last >= first)
+ {
+ register TInt notFoundPosition = (first + last) >> 1;
+ TInt codeIndex = aTable.GetIndex(notFoundPosition);
+
+ if (index < codeIndex)
+ {
+ last = notFoundPosition - 1;
+ }
+ else if (index > codeIndex)
+ {
+ first = notFoundPosition + 1;
+ }
+ else
+ {
+ return aTable.GetIndexedCode(notFoundPosition);
+ }
+ }
+
+ index = (index << 1) + 1;
+ index += (NextBitL()!=0);
+ }
+
+#if !defined(RELAX_JPEG_STRICTNESS)
+ User::Leave(KErrCorrupt);
+#endif
+
+ return aTable.GetIndexedCode(0);
+ }
+
+FORCEDINLINE void CJpgReadCodec::SkipBitsQuickL(TInt aNumOfBits)
+ {
+ TInt bLeft = iBitsLeft;
+
+ FOREVER
+ {
+ bLeft -= aNumOfBits;
+ if (bLeft >= 0)
+ {
+ iBitsLeft = bLeft;
+ return;
+ }
+ aNumOfBits = -bLeft;
+ iBitsLeft = 0;
+ bLeft = FillBitBufferL(0);
+ }
+ }
+
+FORCEDINLINE TInt CJpgReadCodec::GetBinaryNumberQuickL(TInt aLength)
+ {
+ register TInt bLeft = iBitsLeft;
+ register TUint number = 0;
+ register TBitBuffer bitBuf;
+
+ FOREVER
+ {
+ bitBuf = iDataValue;
+
+ bLeft -= aLength;
+ if (bLeft >= 0)
+ {
+ break;
+ }
+ bLeft += aLength;
+ aLength -= bLeft;
+ number |= ((bitBuf & ((1<<bLeft)-1)) << aLength);
+
+ bLeft = FillBitBufferL(0);
+ }
+
+ iBitsLeft = bLeft;
+
+ number |= ((bitBuf>>bLeft) & ((1<<aLength)-1));
+ return number;
+ }
+
+FORCEDINLINE TInt16 CJpgReadCodec::GetPositiveBinaryNumberL(TInt aLength)
+ {
+ return TInt16( GetBinaryNumberQuickL(aLength) );
+ }
+
+FORCEDINLINE TInt16 CJpgReadCodec::GetBinaryNumberL(TInt aLength)
+ {
+ TInt mask = (-1) << (aLength - 1);
+ TInt number = GetBinaryNumberQuickL(aLength);
+ return TInt16( (number & mask)? number : number + ( mask<<1 ) + 1);
+ }
+
+/**
+ This class is to provide with "write pixel" functionality
+ for writting pixels into TRgb-type buffer
+*/
+class TRgbWriter
+ {
+public:
+ inline
+ static void WritePixel(TRgb* aPtr, TInt aY, TInt aU, TInt aV)
+ {
+ *aPtr = TYCbCr::YCbCrtoRGB(aY, aU, aV);
+ }
+ inline
+ static TRgb* ShiftPtr(TRgb* aPtr, TInt aUnitsOffs)
+ {
+ return aPtr + aUnitsOffs;
+ }
+ };
+
+/**
+ This class is to provide with "write pixel" functionality
+ for writting pixels into EColor16M-type buffer i.e. 3 bytes per pixel
+*/
+class TRawWriter
+ {
+public:
+ inline
+ static void WritePixel(TRgb* aPtr, TInt aY, TInt aU, TInt aV)
+ {
+ TYCbCr::YCbCrtoRawRGB(aY, aU, aV, aPtr);
+ }
+ inline
+ static TRgb* ShiftPtr(TRgb* aPtr, TInt aUnitsOffs)
+ {
+ return reinterpret_cast<TRgb*>(reinterpret_cast<TUint8*>(aPtr) + (aUnitsOffs<<1) + aUnitsOffs);
+ }
+ };
+
+/**
+ This class is to provide with "write pixel" functionality
+ for writting pixels into EColor16M-type buffer i.e. 3 bytes per pixel
+ It is similar to the TRawWriter but uses inline version of YUV->RGB
+ conversion function
+*/
+class TRawInlineWriter
+ {
+public:
+ inline
+ static void WritePixel(TRgb* aPtr, TInt aY, TInt aU, TInt aV)
+ {
+ TYCbCr::YCbCrtoRawRGBInl(aY, aU, aV, aPtr);
+ }
+ inline
+ static TRgb* ShiftPtr(TRgb* aPtr, TInt aUnitsOffs)
+ {
+ return reinterpret_cast<TRgb*>(reinterpret_cast<TUint8*>(aPtr) + (aUnitsOffs<<1) + aUnitsOffs);
+ }
+ };
+
+/**
+ This class is to provide with "write pixel" functionality
+ for writting pixels into EColor64K-type buffer i.e. 2 bytes per pixel
+*/
+class TRaw64KColorWriter
+ {
+public:
+ inline
+ static void WritePixel(TRgb* aPtr, TInt aY, TInt aU, TInt aV)
+ {
+ TYCbCr::YCbCrtoRaw64K(aY, aU, aV, aPtr);
+ }
+ inline
+ static TRgb* ShiftPtr(TRgb* aPtr, TInt aUnitsOffs)
+ {
+ return reinterpret_cast<TRgb*>(reinterpret_cast<TUint8*>(aPtr) + (aUnitsOffs<<1) );
+ }
+ };
+
+inline void CJpgReadCodec::WriteMCU()
+ {
+ JPEG_ASSERT(iMcuWriteFunc);
+ (this->*iMcuWriteFunc)();
+ }
+
+//
+// Calculate where in the intermediate buffer this MCU should be drawn.
+//
+TInt CJpgReadCodec::GetMCURenderOffset()
+ {
+ TInt mcuPos;
+
+ JPEG_ASSERT(iMCUHorizExtent > 0);
+
+ if (iFillBufferBackwards)
+ {
+ JPEG_ASSERT(!iProgressive);
+ mcuPos = iMCUsPerBuffer - iHorzMCUBlkCount - 1;
+ }
+ else
+ {
+ // Fill from left to right.
+ mcuPos = iHorzMCUBlkCount;
+ }
+
+ JPEG_ASSERT(mcuPos >= 0);
+
+ // iMCUHorizExtent has already been scaled by CalculateRenderingParams().
+ return (mcuPos * iMCUHorizExtent);
+
+ }
+
+//
+// Writes a monochrome MCU.
+//
+void CJpgReadCodec::WriteMonoMCU()
+ {
+ const TInt16* yComp = iComponent[KYComp]->iCoeff;
+
+ if (iScalingFactor == -4)
+ {
+ TInt pixelsToSkip = GetMCURenderOffset() + iFirstPixelOffset;
+ TRgb* writeAddress = iRgbBuffer + pixelsToSkip;
+ /* Coverity may flag as overrun of array yComp by indexing, which is false positive. There is more than one TDataUnit object pointed to by
+ iComponent[KYComp], which Coverity may fail to take into account */
+ for (TInt j = 0; j < iMaxVertSampleFactor; j++)
+ {
+ for (TInt i = 0; i < iMaxHorzSampleFactor; i++)
+ {
+ *writeAddress = TRgb::Gray256(ColorCcomponent::ClampColorComponent(yComp[0]));
+ writeAddress += iPixelIncrement;
+ yComp += KJpgDCTBlockSize;
+ }
+ writeAddress += iRgbBufNextLineOffs;
+ }
+ }
+ else
+ {
+ const TInt KYBlockOffset = KJpgDCTBlockWidth-(iMaxHorzSampleFactor * KJpgDCTBlockSize);
+
+ TInt pixelsToSkip = GetMCURenderOffset() + iFirstPixelOffset;
+ TRgb* writeAddress = iRgbBuffer + pixelsToSkip;
+
+ for (TInt sf = iMaxVertSampleFactor * KJpgDCTBlockWidth; sf;)
+ {
+ TInt hsf = iMaxHorzSampleFactor;
+ do
+ {
+ TInt i = KJpgDCTBlockWidth / 2;
+ do
+ {
+ *writeAddress = TRgb::Gray256(ColorCcomponent::ClampColorComponent(*yComp++));
+ writeAddress += iPixelIncrement;
+ *writeAddress = TRgb::Gray256(ColorCcomponent::ClampColorComponent(*yComp++));
+ writeAddress += iPixelIncrement;
+ }
+ while (--i);
+
+ yComp += (KJpgDCTBlockSize - KJpgDCTBlockWidth);
+ }
+ while (--hsf);
+
+ --sf;
+ yComp += (sf & (KJpgDCTBlockWidth-1))? KYBlockOffset: -(KJpgDCTBlockSize-KJpgDCTBlockWidth);
+ writeAddress += iRgbBufNextLineOffs;
+ }
+ }
+ }
+
+void CJpgReadCodec::WriteDiv8ScaledMCU16M()
+ {
+ WriteDiv8MCUImpl<TRawWriter>();
+ }
+
+void CJpgReadCodec::WriteDiv8ScaledMCU()
+ {
+ WriteDiv8MCUImpl<TRgbWriter>();
+ }
+
+void CJpgReadCodec::WriteDiv8ScaledMCU64K()
+ {
+ WriteDiv8MCUImpl<TRaw64KColorWriter>();
+ }
+
+template <class T>
+inline void CJpgReadCodec::WriteDiv8MCUImpl()
+ {
+ ASSERT(iScalingFactor == -4);
+
+ TInt16* yComp = iComponent[0]->iCoeff;
+
+ TUVidxElemType* uVIndeces = iUVIndeces;
+
+ TInt pixelsToSkip = GetMCURenderOffset() + iFirstPixelOffset;
+ TRgb* writeAddress = T::ShiftPtr(iRgbBuffer, pixelsToSkip);
+
+ const TInt16* uComp = iComponent[KUComp]->iCoeff;
+ const TInt16* vComp = iComponent[KVComp]->iCoeff;
+
+ TInt hsf = iMaxHorzSampleFactor;
+ TInt vsf = iMaxVertSampleFactor;
+ /* Coverity may flag as overrun of array on indexing yComp. This is false positive. Coverity doesn't take into account that
+ iComponent[0] can point to more than one TDataUnit.
+ */
+ do
+ {
+ do
+ {
+ const TInt16 uValue = uComp[*uVIndeces];
+ uVIndeces++;
+ const TInt16 vValue = vComp[*uVIndeces];
+ uVIndeces++;
+ T::WritePixel(writeAddress, *yComp, uValue, vValue);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+
+ yComp += KJpgDCTBlockSize;
+ }
+ while (--hsf);
+
+ hsf = iMaxHorzSampleFactor;
+ uVIndeces += (hsf << 4); // 2 * hsf * KJpgBlockWidth
+ writeAddress = T::ShiftPtr(writeAddress, iRgbBufNextLineOffs);
+ }
+ while (--vsf);
+ }
+
+void CJpgReadCodec::WriteUnScaledMCU16M()
+ {
+ WriteUnScaledMCUImpl<TRawWriter>();
+ }
+
+void CJpgReadCodec::WriteUnScaledMCU()
+ {
+ WriteUnScaledMCUImpl<TRgbWriter>();
+ }
+
+template <class T>
+inline void CJpgReadCodec::WriteUnScaledMCUImpl()
+ {
+ ASSERT((iScalingFactor == 1) || (iScalingFactor == -1));
+
+ TInt pixelsToSkip = GetMCURenderOffset() + iFirstPixelOffset;
+ TRgb* writeAddress = T::ShiftPtr(iRgbBuffer, pixelsToSkip);
+
+ const TInt KYBlockOffset = KJpgDCTBlockWidth - (iFrameInfo.MCUWidthInPixels() * 8);
+
+ register const TInt ush = iHorzSampleRatioSh[KUShiftIdx];
+ register const TInt vsh = iHorzSampleRatioSh[KVShiftIdx];
+ const TInt16* yComp = iComponent[KYComp]->iCoeff;
+ const TUVidxElemType* pixIdx = iUVIndeces;
+
+ TInt sf = iFrameInfo.MCUHeightInPixels();
+ do
+ {
+ TInt hsf = iMaxHorzSampleFactor;
+ do
+ {
+ const TInt16* const u_base = iComponent[KUComp]->iCoeff + *pixIdx++;
+ const TInt16* const v_base = iComponent[KVComp]->iCoeff + *pixIdx++;
+
+#if defined(JPEG_OPTIMIZE_FOR_PERFORMCE)
+
+ T::WritePixel(writeAddress, *yComp++, u_base[0], v_base[0]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+
+ register TInt p=1;
+
+ T::WritePixel(writeAddress, *yComp++, u_base [(p>>ush)], v_base [(p>>vsh)]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ ++p;
+ T::WritePixel(writeAddress, *yComp++, u_base [(p>>ush)], v_base [(p>>vsh)]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ ++p;
+ T::WritePixel(writeAddress, *yComp++, u_base [(p>>ush)], v_base [(p>>vsh)]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ ++p;
+
+ T::WritePixel(writeAddress, *yComp++, u_base [(p>>ush)], v_base [(p>>vsh)]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ ++p;
+ T::WritePixel(writeAddress, *yComp++, u_base [(p>>ush)], v_base [(p>>vsh)]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ ++p;
+ T::WritePixel(writeAddress, *yComp++, u_base [(p>>ush)], v_base [(p>>vsh)]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ ++p;
+ T::WritePixel(writeAddress, *yComp++, u_base [(p>>ush)], v_base [(p>>vsh)]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+#else
+
+ register TInt p = 0;
+ do
+ {
+ TInt y = *yComp++;
+ TInt u = u_base[(p >> ush)];
+ TInt v = v_base[(p >> vsh)];
+
+ T::WritePixel(writeAddress, y, u, v);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ }
+ while (++p < 8);
+#endif
+ yComp += (KJpgDCTBlockSize - KJpgDCTBlockWidth);
+ }
+ while (--hsf);
+
+ --sf;
+ yComp += (sf & (KJpgDCTBlockWidth - 1)) ? KYBlockOffset : -(KJpgDCTBlockSize - KJpgDCTBlockWidth);
+ writeAddress = T::ShiftPtr(writeAddress, iRgbBufNextLineOffs);
+ }
+ while (sf);
+ }
+
+void CJpgReadCodec::WriteDiv2ScaledMCU()
+ {
+ WriteDiv2ScaledMCUImpl<TRgbWriter>();
+ }
+
+void CJpgReadCodec::WriteDiv2ScaledMCU16M()
+ {
+ WriteDiv2ScaledMCUImpl<TRawWriter>();
+ }
+
+template <class T>
+inline void CJpgReadCodec::WriteDiv2ScaledMCUImpl()
+ {
+ ASSERT(iScalingFactor == -2);
+
+ const TInt KScalingFactor = 2;
+ const TInt KYBlockOffset = KJpgDCTBlockWidth -
+ (iMaxHorzSampleFactor * KJpgDCTBlockSize) +
+ (KScalingFactor - 1) * KJpgDCTBlockWidth;
+
+ TInt pixelsToSkip = GetMCURenderOffset() + iFirstPixelOffset;
+ TRgb* writeAddress = T::ShiftPtr(iRgbBuffer, pixelsToSkip);
+
+ const TInt16* yComp = iComponent[KYComp]->iCoeff;
+ const TUVidxElemType* pixIdx = iUVIndeces;
+ register const TInt ush = iHorzSampleRatioSh[KUShiftIdx];
+ register const TInt vsh = iHorzSampleRatioSh[KVShiftIdx];
+
+ TInt sf = iMaxVertSampleFactor * KJpgDCTBlockWidth;
+ /* Coverity may flag as overrun of array by accessing yComp. This is false positive. Coverity doesn't take into account that
+ iComponent[KYComp] can point to more than one TDataUnit.
+ */
+ do
+ {
+ TInt hsf = iMaxHorzSampleFactor;
+ do
+ {
+ const TInt16* const u_base = iComponent[KUComp]->iCoeff + *pixIdx++;
+ const TInt16* const v_base = iComponent[KVComp]->iCoeff + *pixIdx++;
+
+ T::WritePixel(writeAddress, *yComp, u_base[0], v_base[0]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ yComp += KScalingFactor;
+
+ T::WritePixel(writeAddress, *yComp, u_base[KScalingFactor >> ush], v_base[KScalingFactor >> vsh]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ yComp += KScalingFactor;
+
+ T::WritePixel(writeAddress, *yComp, u_base[2 * KScalingFactor >> ush], v_base[2 * KScalingFactor >> vsh]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ yComp += KScalingFactor;
+
+ T::WritePixel(writeAddress, *yComp, u_base[3 * KScalingFactor >> ush], v_base[3 * KScalingFactor >> vsh]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ yComp += KScalingFactor + (KJpgDCTBlockSize - KJpgDCTBlockWidth);
+ }
+ while (--hsf);
+
+ sf -= KScalingFactor;
+ yComp += (sf & (KJpgDCTBlockWidth-1))? KYBlockOffset: KScalingFactor*KJpgDCTBlockWidth-KJpgDCTBlockSize;
+ writeAddress = T::ShiftPtr(writeAddress, iRgbBufNextLineOffs);
+ }
+ while (sf);
+ }
+
+void CJpgReadCodec::WriteDiv4ScaledMCU()
+ {
+ WriteDiv4ScaledMCUImpl<TRgbWriter>();
+ }
+
+void CJpgReadCodec::WriteDiv4ScaledMCU16M()
+ {
+ WriteDiv4ScaledMCUImpl<TRawInlineWriter>();
+ }
+
+template <class T>
+inline void CJpgReadCodec::WriteDiv4ScaledMCUImpl()
+ {
+ ASSERT(iScalingFactor == -3);
+
+ const TInt KScalingFactor = 4;
+ const TInt KYBlockOffset = KJpgDCTBlockWidth -
+ (iMaxHorzSampleFactor * KJpgDCTBlockSize) +
+ (KScalingFactor - 1) * KJpgDCTBlockWidth;
+
+ TInt pixelsToSkip = GetMCURenderOffset() + iFirstPixelOffset;
+ TRgb* writeAddress = T::ShiftPtr(iRgbBuffer, pixelsToSkip);
+
+ const TInt16* yComp = iComponent[KYComp]->iCoeff;
+ const TUVidxElemType* pixIdx=iUVIndeces;
+
+ TInt sf = iMaxVertSampleFactor * KJpgDCTBlockWidth;
+ /* Coverity may flag as overrun of array on accessing yComp. This is false positive. Coverity doesn't take into account that
+ iComponent[KYComp] can point to more than one TDataUnit.
+ */
+ do
+ {
+ TInt hsf = iMaxHorzSampleFactor;
+ do
+ {
+ const TInt16* const u_base1 = iComponent[KUComp]->iCoeff + *pixIdx;
+ pixIdx++;
+ const TInt16* const v_base1 = iComponent[KVComp]->iCoeff + *pixIdx;
+ pixIdx++;
+
+ T::WritePixel(writeAddress, *yComp, u_base1[0], v_base1[0]);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ yComp += KScalingFactor;
+
+ const TInt16 u_base2 = u_base1[*pixIdx];
+ pixIdx++;
+ const TInt16 v_base2 = v_base1[*pixIdx];
+ pixIdx++;
+
+ T::WritePixel(writeAddress, *yComp, u_base2, v_base2);
+ writeAddress = T::ShiftPtr(writeAddress, iPixelIncrement);
+ yComp += KScalingFactor + (KJpgDCTBlockSize - KJpgDCTBlockWidth);
+ }
+ while (--hsf);
+
+ sf -= KScalingFactor;
+ yComp += (sf & (KJpgDCTBlockWidth-1))? KYBlockOffset: KScalingFactor*KJpgDCTBlockWidth-KJpgDCTBlockSize;
+ writeAddress = T::ShiftPtr(writeAddress, iRgbBufNextLineOffs);
+ }
+ while (sf);
+ }
+
+
+TInt CJpgReadCodec::ComponentIndexL(TInt aComponentId) const
+ {
+ for (TInt count = 0; count < iFrameInfo.iNumberOfComponents; count++)
+ {
+ if (iFrameInfo.iComponent[count].iId == aComponentId)
+ return count;
+ }
+
+ User::Leave(KErrCorrupt);
+ return 0;
+ }
+
+void CJpgReadCodec::SetYuvDecode(TBool aYuvDecode)
+ {
+ iYuvDecode = aYuvDecode;
+ }
+
+void CJpgReadCodec::SetHighSpeedMode(TBool aHighSpeedMode)
+ {
+ iHighSpeedMode = aHighSpeedMode;
+ }
+
+TInt CJpgReadCodec::RecommendBufferSizeL(TUid aFormatCode)
+ {
+ return CJpgImageFrameProcessorUtility::RecommendedBufferSizeL(iFrameInfo, aFormatCode);
+ }
+
+void CJpgReadCodec::InitDrawFrame()
+ {//default implementation do nothing
+ }
+
+TBool CJpgReadCodec::DrawFrameL()
+ {//default implementation do nothing
+ return ETrue;
+ }
+
+void CJpgReadCodec::CleanupBuffers()
+ {//default implementation do nothing
+ }
+
+void CJpgReadCodec::InitFrameHeader(TFrameInfo& aFrameInfo, CFrameImageData& /*aFrameData*/)
+ {
+ aFrameInfo.SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingComplete);
+ }
+
+
+TInt CJpgReadCodec::MCUBlockPerRgbBuffer() const
+ {
+ return iMCUsPerBuffer;
+ }
+
+TInt CJpgReadCodec::GetHorzMCUCount()
+ {
+ TInt maxMCUWidth = KJpgDCTBlockWidth * iMaxHorzSampleFactor;
+ return (iFrameInfo.iSizeInPixels.iWidth + maxMCUWidth - 1) / maxMCUWidth;
+ }
+
+TInt CJpgReadCodec::GetVertMCUCount()
+ {
+ TInt maxMCUHeight = KJpgDCTBlockWidth * iMaxVertSampleFactor;
+ return (iFrameInfo.iSizeInPixels.iHeight + maxMCUHeight - 1) / maxMCUHeight;
+ }
+
+//
+// aExtensionManager is not owned.
+//
+void CJpgReadCodec::SetExtensionManager(CPluginExtensionManager* aExtensionManager)
+ {
+ iExtensionManager = aExtensionManager;
+ }
+
+#if defined(__ARMCC__)
+#pragma pop
+#endif
+
+#if defined(__ARMCC__)
+#pragma push
+#pragma thumb
+#endif
+//
+// CSequentialJpgReadCodec
+CSequentialJpgReadCodec::CSequentialJpgReadCodec(
+ const TJpgFrameInfo& aFrameInfo,
+ const TJpgScanInfo& aScanInfo,
+ TDecHuffmanTable aDCHuffmanTable[KJpgMaxNumberOfTables],
+ TDecHuffmanTable aACHuffmanTable[KJpgMaxNumberOfTables],
+ const TQTable aQTable[KJpgMaxNumberOfTables])
+ : CJpgReadCodec(aFrameInfo, aScanInfo),
+ iDCHuffmanTable(aDCHuffmanTable),
+ iACHuffmanTable(aACHuffmanTable),
+ iQTable(aQTable)
+ {
+ iProgressive = EFalse;
+ }
+
+//
+//
+//
+CSequentialJpgReadCodec::~CSequentialJpgReadCodec()
+ {
+ delete iMCUStore;
+ iMCUStore = NULL;
+
+ User::Free(iMCULookup);
+ iMCULookup = NULL;
+ }
+
+//
+//
+//
+CSequentialJpgReadCodec* CSequentialJpgReadCodec::NewL(
+ const TJpgFrameInfo& aFrameInfo,
+ const TJpgScanInfo& aScanInfo,
+ TDecHuffmanTable aDCHuffmanTable[KJpgMaxNumberOfTables],
+ TDecHuffmanTable aACHuffmanTable[KJpgMaxNumberOfTables],
+ const TQTable aQTable[KJpgMaxNumberOfTables])
+ {
+ CSequentialJpgReadCodec* self = new(ELeave) CSequentialJpgReadCodec(
+ aFrameInfo,
+ aScanInfo,
+ aDCHuffmanTable,
+ aACHuffmanTable,
+ aQTable);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+//
+//
+//
+void CSequentialJpgReadCodec::ConstructL(TBool aUseCache)
+ {
+ CJpgReadCodec::ConstructL();
+
+ ASSERT(iMCULookup == NULL);
+
+ iTotalMCUCount = iFrameInfo.TotalMCUCount();
+
+ // Make sure iFrameInfo has had its members set.
+ JPEG_ASSERT(iTotalMCUCount >= 1);
+
+ iMCUStore = CMCUStore::NewL(iFrameInfo);
+
+ // If the allocation of the lookup table fails, continue
+ // to decode the image as it's not needed for some decodes
+ // and others should be able to continue without it (with a
+ // performance hit).
+ if (aUseCache)
+ {
+ JPEG_DEBUG2(" - Cache for %d elements", iTotalMCUCount);
+ TInt allocSize = iTotalMCUCount * sizeof(TMCUEntry);
+ iMCULookup = reinterpret_cast<TMCUEntry*>(User::AllocZ(allocSize));
+ }
+ }
+
+//
+// Nothing is guaranteed to be known about the image structure at this point.
+//
+void CSequentialJpgReadCodec::PreInitFrameL()
+ {
+ CJpgReadCodec::PreInitFrameL();
+ iMCUStore->Reset();
+ }
+
+
+//
+// This function is called after the various variants of
+// InitFrameL have been called. At this stage enough should
+// be known about the image to set several properties.
+//
+void CSequentialJpgReadCodec::PostInitFrameL()
+ {
+ iHorzMCUBlkCount = 0;
+ iStreamMCU = 0;
+ iMCUStore->SetMCUsPerBuffer(iMCUsPerBuffer);
+ iMCUStore->SetOperation(iOperation);
+ iNeededMCU = iMCUStore->GetNextMCU();
+
+ // Make sure iScanInfo isn't used before its members have been set.
+ JPEG_ASSERT(iScanInfo.iImageOffset > 0);
+
+ iCurrentMCUBitOffset = (iScanInfo.iImageOffset * 8);
+
+ CJpgReadCodec::PostInitFrameL();
+ }
+
+//
+// This is called by the framework whenever DoProcessL returns EFrameIncompleteRepositionRequest.
+// After this function returns the framework should call DoProcessL again.
+//
+void CSequentialJpgReadCodec::GetNewDataPosition(TInt& aPosition, TInt& /*aLength*/)
+ {
+ if (!iMCULookup)
+ {
+ // Seek to the start of the image data.
+ aPosition = iScanInfo.iImageOffset;
+ iSeekDone = ETrue;
+ return;
+ }
+
+ if ((iNeededMCU >= 0) && (iNeededMCU < iTotalMCUCount))
+ {
+ TMCUEntry& entry = iMCULookup[iNeededMCU];
+
+ aPosition = (entry.iPosition >> 3);
+ iSeekDone = ETrue;
+ return;
+ }
+
+ // The seek shouldn't have been made if iNeededMCU is outside
+ // the bounds of the lookup table.
+ ASSERT(EFalse);
+ }
+
+#if defined(__ARMCC__)
+#pragma pop
+#endif
+
+#if defined(__ARMCC__)
+// use ARM instruction for performance-critical code
+#pragma push
+#pragma arm
+#pragma O3
+#pragma Otime
+#endif
+//
+// This will be called by CJpgReadCodec once enough
+// data is available to do correct calculations.
+//
+void CSequentialJpgReadCodec::CalculateMCUBoundingRectL(TInt aMCUsPerLine)
+ {
+ JPEG_DEBUG1("CalculateMCUBoundingRectL()");
+
+ ASSERT(iExtensionManager);
+
+ JPEG_ASSERT(!iIsBlockStreaming);
+ JPEG_ASSERT(!iProgressive);
+ JPEG_ASSERT(aMCUsPerLine > 0);
+
+ TRect clipRect;
+ clipRect = iExtensionManager->ClippingRect();
+
+ // This function shouldn't be called by InitFrameL
+ // if no clipping rect has been set.
+ JPEG_ASSERT(!clipRect.IsEmpty());
+
+ TInt mcuWidthInPixels = iFrameInfo.MCUWidthInPixels();
+ TInt mcuHeightInPixels = iFrameInfo.MCUHeightInPixels();
+ JPEG_DEBUG2(" - MCU pixel width: %d", mcuWidthInPixels);
+ JPEG_DEBUG2(" - MCU pixel height: %d", mcuHeightInPixels);
+
+ // The clipping rect is specified in pixels. We need to
+ // find out which MCUs contain these pixels.
+ TInt left = clipRect.iTl.iX / mcuWidthInPixels;
+ TInt top = clipRect.iTl.iY / mcuHeightInPixels;
+ TInt right = (clipRect.iBr.iX - 1) / mcuWidthInPixels;
+ TInt bottom = (clipRect.iBr.iY - 1) / mcuHeightInPixels;
+
+ TInt firstMCU = (top * aMCUsPerLine) + left;
+
+ iMCUClipRect.SetRect(left, top, right + 1, bottom + 1);
+
+ iMCUStore->SetClippingRect(firstMCU, iMCUClipRect.Width() * iMCUClipRect.Height());
+
+ // Convert back to pixels.
+ left *= mcuWidthInPixels;
+ top *= mcuHeightInPixels;
+ right = (right * mcuWidthInPixels) + mcuWidthInPixels;
+ bottom = (bottom * mcuHeightInPixels) + mcuHeightInPixels;
+
+ iMCUClipRect.SetRect(left, top, right, bottom);
+ JPEG_DEBUG5("iMCUClipRect: (%d, %d) - (%d x %d)",
+ iMCUClipRect.iTl.iX,
+ iMCUClipRect.iTl.iY,
+ iMCUClipRect.Width(),
+ iMCUClipRect.Height());
+ }
+
+//
+//
+//
+void CSequentialJpgReadCodec::CacheMCULocation()
+ {
+ JPEG_ASSERT(iStreamMCU >= 0);
+ JPEG_ASSERT(!iFrameInfo.iMultiScan);
+
+ if (!iMCULookup || (iStreamMCU >= iTotalMCUCount))
+ {
+ return;
+ }
+
+ TMCUEntry& entry = iMCULookup[iStreamMCU];
+
+ if (entry.iPosition != 0)
+ {
+ for (TInt i = 0; i < 3; i++)
+ {
+ JPEG_ASSERT(entry.iDCPredictor[i] == iDCPredictor[i]);
+ }
+ }
+ else
+ {
+ entry.iPosition = iCurrentMCUBitOffset;
+ if (iEscapeAtEnd)
+ {
+ entry.iPosition -= 8; // Go back a byte.
+ }
+
+ entry.iDCPredictor[0] = iDCPredictor[0];
+ entry.iDCPredictor[1] = iDCPredictor[1];
+ entry.iDCPredictor[2] = iDCPredictor[2];
+ entry.iRestartMCUCount = iRestartMCUCount;
+ }
+ }
+
+//
+//
+//
+#ifdef JPEG_DEBUG_OUTPUT
+void CSequentialJpgReadCodec::DumpCache()
+ {
+ JPEG_DEBUG1("CACHE DUMP");
+
+ for (TInt i = 0; i < iTotalMCUCount; i++)
+ {
+ TMCUEntry& entry = iMCULookup[i];
+ if (entry.iPosition == 0)
+ {
+ return;
+ }
+
+ JPEG_DEBUG7("Entry[%6d] location=%8d; predictors[%4d, %4d, %4d] restart=%d",
+ i,
+ entry.iPosition,
+ entry.iDCPredictor[0],
+ entry.iDCPredictor[1],
+ entry.iDCPredictor[2],
+ entry.iRestartMCUCount);
+ }
+ }
+#endif
+
+//
+// This is called after a seek has been performed.
+// It sets everything up so that we're decoding from
+// the correct position in the bitstream and does
+// some other housekeeping.
+//
+void CSequentialJpgReadCodec::RestoreAfterSeekL()
+ {
+ // Reset the bitstream.
+ TInt bitOffset = 0;
+ iBitsLeft = 0;
+ iBitBufferPtrLimit = 0;
+ iDataValue = 0;
+
+ if (iMCULookup)
+ {
+ TMCUEntry& entry = iMCULookup[iNeededMCU];
+
+ // Divide entry.iPosition into byte and bit offsets.
+ bitOffset = (entry.iPosition & 0x07);
+
+ // Make sure re-caching will work.
+ iCurrentMCUBitOffset = entry.iPosition;
+ iDCPredictor[0] = entry.iDCPredictor[0];
+ iDCPredictor[1] = entry.iDCPredictor[1];
+ iDCPredictor[2] = entry.iDCPredictor[2];
+ iRestartMCUCount = entry.iRestartMCUCount;
+
+ iStreamMCU = iNeededMCU;
+ }
+ else
+ {
+ // The seek was to the start of the image.
+ iRestartMCUCount = iFrameInfo.iRestartInterval;
+ iDCPredictor[0] = 0;
+ iDCPredictor[1] = 0;
+ iDCPredictor[2] = 0;
+ iStreamMCU = 0;
+ iCurrentMCUBitOffset = iScanInfo.iImageOffset * 8;
+ }
+
+ //FetchNext3BytesL();
+ FillBitBufferL(iBitsLeft);
+ iBitsLeft -= bitOffset;
+ iSeekDone = EFalse;
+ }
+
+
+//
+// Check's if the location of iNeededMCU is known in advance.
+//
+TBool CSequentialJpgReadCodec::QueryCache()
+ {
+ // Prevent an infinite seeking loop.
+ if (iNeededMCU == iStreamMCU)
+ {
+ return EFalse;
+ }
+
+ if (!iMCULookup)
+ {
+ if (iNeededMCU < iStreamMCU)
+ {
+ // We can seek to the start of the image and start
+ // decoding again from there.
+ iDataPtr++;
+ return ETrue;
+ }
+
+ return EFalse;
+ }
+
+ if ((iNeededMCU < 0) || (iNeededMCU >= iTotalMCUCount))
+ {
+ // Out of bounds.
+ return EFalse;
+ }
+
+ TMCUEntry& entry = iMCULookup[iNeededMCU];
+ if (entry.iPosition != 0)
+ {
+ // This is in order to get the framework to do the seek.
+ iDataPtr++;
+ return ETrue;
+ }
+
+ return EFalse;
+ }
+
+
+#if defined(__ARMCC__)
+#pragma pop
+#endif
+
+//
+//
+//
+TFrameState CSequentialJpgReadCodec::DoProcessL()
+ {
+ if (iSeekDone)
+ {
+ RestoreAfterSeekL();
+ }
+
+ while (iDataPtr < iDataPtrLimit)
+ {
+ if (iNeededMCU > iTotalMCUCount)
+ {
+ JPEG_LEAVE(KErrOverflow, "iNeededMCU is out of bounds");
+ }
+
+ // See if we're done.
+ if (iNeededMCU == KErrCompletion)
+ {
+ return EFrameComplete;
+ }
+ else if (QueryCache())
+ {
+ return EFrameIncompleteRepositionRequest;
+ }
+
+ StoreState();
+ if (iRestartMCUCount == 0)
+ {
+ TInt skipped = RestartStateL();
+ iCurrentMCUBitOffset += (skipped * 8);
+ }
+
+ CacheMCULocation();
+
+ TInt error = KErrNone;
+ TInt mcuBitSize = 0;
+ // we do that "if" in order to bypass exception handling which can
+ // affect performance of the decoder
+ if (iPreviousDataLeft < KMCUDataLeftThreshhold)
+ {
+ const TUint8* const latestDataPtr = iDataPtr;
+ TRAP(error, mcuBitSize = ProcessMCUL());
+
+ // leave if it wasn't a partial MCU
+ if ((error != KErrNone) &&
+ (error != KErrEof ||
+ latestDataPtr == iDataPtr ||
+ (latestDataPtr + sizeof(TUint16) <= iDataPtrLimit &&
+ PtrReadUtil::ReadBigEndianUint16(latestDataPtr) == KJpgEOISignature)))
+ {
+ User::Leave(error);
+ }
+ }
+ else
+ {
+ mcuBitSize = ProcessMCUL();
+ }
+
+ iCurrentMCUBitOffset += mcuBitSize;
+
+ // we would try to render the partially decoded MCU
+ // in case if there is a partial MCU/incompelete image
+ // and do leave with original error code later
+ if (iStreamMCU == iNeededMCU)
+ {
+ if (iIsBlockStreaming &&
+ (iNavigation == TDecodeStreamCaps::ENavigationRandomForward ||
+ iNavigation == TDecodeStreamCaps::ENavigationRandomBackwards) &&
+ iStreamMCU < iStreamDecodeConfig.iSeqPosition)
+ {
+ iNeededMCU = iMCUStore->GetNextMCU();
+ }
+ else
+ {
+ PostProcessMCUL(error != KErrNone);
+
+ User::LeaveIfError(error);
+
+ if (iIsBlockStreaming)
+ {
+ iNeededMCU++;
+ if (EBlockComplete == ProcessStreaming())
+ {
+ iStreamMCU++;
+ iRestartMCUCount--;
+ return EBlockComplete;
+ }
+ }
+ else
+ {
+ iNeededMCU = iMCUStore->GetNextMCU();
+ }
+ }
+ }
+
+ iStreamMCU++;
+ iRestartMCUCount--;
+
+ TInt dataLeft = iDataPtrLimit - iDataPtr;
+ if (dataLeft < KMCUDataLeftThreshhold)
+ {
+ TBool needLeave = (iPreviousDataLeft > dataLeft);
+ iPreviousDataLeft = dataLeft;
+ if (needLeave)
+ {
+ StoreState();
+ User::Leave(KErrCompletion);
+ }
+ }
+ }
+
+ return EFrameIncomplete;
+ }
+
+TFrameState CSequentialJpgReadCodec::ProcessStreaming()
+ {
+ if(iNavigation == TDecodeStreamCaps::ENavigationSequentialForward)
+ {
+ if(iTotalMCUCount > iNeededMCU)
+ {
+ *(iStreamDecodeConfig.iHaveMoreBlocks) = ETrue;
+ }
+ else
+ {
+ *(iStreamDecodeConfig.iHaveMoreBlocks) = EFalse;
+ }
+ }
+
+ *(iStreamDecodeConfig.iNumBlocksRead) += 1;
+
+ if(*(iStreamDecodeConfig.iNumBlocksRead) >= iStreamDecodeConfig.iNumBlocksToGet)
+ {
+ return EBlockComplete;
+ }
+ else
+ {
+ return EFrameIncomplete;
+ }
+ }
+
+//
+// This functions turns the MCU data into pixel data.
+// The pixel data is written into an intermediate buffer,
+// iRgbBuffer, by WriteMCU() and then if this intermediate
+// buffer is full this is copied to the output bitmap by
+// SetPixelBlock().
+//
+void CSequentialJpgReadCodec::PostProcessMCUL(TBool aForceBufferFlush)
+ {
+ if (!iImageFrameCodecPtr)
+ {
+ TBool copyIt = aForceBufferFlush;
+
+ WriteMCU();
+ iHorzMCUBlkCount++;
+
+ // Only copy the buffer if we have rendered a row of MCUs or are forced to.
+ copyIt |= (iHorzMCUBlkCount == iMCUsPerBuffer);
+ if (copyIt)
+ {
+ CImageProcessor* proc = ImageProcessor();
+ ASSERT(proc != NULL);
+ proc->SetPixelBlock(iRgbBuffer);
+ iMCUStore->NextLine();
+ iHorzMCUBlkCount = 0;
+ }
+ }
+ else
+ {
+ RArray<const TDataUnit*> dataUnits;
+ CleanupClosePushL(dataUnits);
+ for(TInt i = 0; i < iFrameInfo.iNumberOfComponents; i++)
+ {
+ TDataUnit* compPtr = iComponent[i];
+ TInt numSamples = iMCUDataUnitCount[i];
+ while (numSamples > 0)
+ {
+ numSamples--;
+ User::LeaveIfError(dataUnits.Append(compPtr++));
+ }
+ }
+ iImageFrameCodecPtr->ProcessL(dataUnits);
+ CleanupStack::PopAndDestroy(1, &dataUnits);
+ }
+ }
+
+
+TInt CSequentialJpgReadCodec::ProcessMCUL()
+ {
+ TDataUnit temp;
+ TInt numValues;
+ TInt bitsBefore = iBitsLeft;
+ const TUint8* startPtr = iDataPtr;
+
+ iEscapeAtEnd = EFalse;
+ for (TInt i = 0; i < iScanInfo.iNumberOfComponents; i++)
+ {
+ TJpgScanInfo::TScanComponentInfo& scanInfo = iScanInfo.iComponent[i];
+ TInt compIndex = ComponentIndexL(scanInfo.iId);
+ TJpgFrameInfo::TComponentInfo& compInfo = iFrameInfo.iComponent[compIndex];
+ const TDecHuffmanTable& dcTable = iDCHuffmanTable[scanInfo.iDCCodingTable];
+ const TDecHuffmanTable& acTable = iACHuffmanTable[scanInfo.iACCodingTable];
+ const TQTable& qTable = iQTable[compInfo.iQTable];
+ TDataUnit* compPtr = iComponent[compIndex];
+ if (compPtr==NULL)
+ {
+ compPtr = &temp; // we'd throw it away in case of Mono mode
+ }
+ TInt numSamples = iMCUDataUnitCount[compIndex];
+
+ while (numSamples > 0)
+ {
+ GetComponentBlockL(*compPtr, numValues, iDCPredictor[compIndex], dcTable, acTable);
+ if (compIndex == 0 || !iMonochrome)
+ {
+ (qTable.*iCompConf[compIndex].iDequantFunc)(temp, *compPtr, numValues);
+ iCompConf[compIndex].iDCT->InverseTransform(*compPtr, temp);
+ compPtr++;
+ }
+ numSamples--;
+ }
+ }
+
+ if (iRestartMCUCount == 1)
+ {
+ iBitsLeft = 0;
+ }
+ else
+ {
+ iEscapeAtEnd = ((iDataValue & 0xFF) == 0xFF);
+ }
+
+ TInt bytesRead = iDataPtr - startPtr;
+ return bitsBefore + (bytesRead * 8) - iBitsLeft;
+ }
+
+
+#if defined(__ARMCC__)
+// use ARM instruction for performance-critical code
+#pragma push
+#pragma thumb
+#endif
+
+// CProgressiveJpgReadCodec
+CProgressiveJpgReadCodec::CProgressiveJpgReadCodec(const TJpgFrameInfo& aFrameInfo,const TJpgScanInfo& aScanInfo,const TDecHuffmanTable aDCHuffmanTable[KJpgMaxNumberOfTables],const TDecHuffmanTable aACHuffmanTable[KJpgMaxNumberOfTables],const TQTable aQTable[KJpgMaxNumberOfTables])
+: CJpgReadCodec(aFrameInfo,aScanInfo),
+ iOriginalFrameInfo(aFrameInfo),
+ iOriginalScanInfo(aScanInfo)
+ {
+ iProgressive = ETrue;
+ Mem::Copy(iDCHuffmanTable,aDCHuffmanTable,sizeof(TDecHuffmanTable) * KJpgMaxNumberOfTables);
+ Mem::Copy(iACHuffmanTable,aACHuffmanTable,sizeof(TDecHuffmanTable) * KJpgMaxNumberOfTables);
+ Mem::Copy(iQTable,aQTable,sizeof(TQTable) * KJpgMaxNumberOfTables);
+ }
+
+CProgressiveJpgReadCodec* CProgressiveJpgReadCodec::NewL(const TJpgFrameInfo& aFrameInfo,const TJpgScanInfo& aScanInfo,const TDecHuffmanTable aDCHuffmanTable[KJpgMaxNumberOfTables],const TDecHuffmanTable aACHuffmanTable[KJpgMaxNumberOfTables],const TQTable aQTable[KJpgMaxNumberOfTables])
+ {
+ CProgressiveJpgReadCodec* self = new(ELeave) CProgressiveJpgReadCodec(aFrameInfo, aScanInfo, aDCHuffmanTable, aACHuffmanTable, aQTable);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+CProgressiveJpgReadCodec::~CProgressiveJpgReadCodec()
+ {
+ CleanupBuffers();
+ }
+
+void CProgressiveJpgReadCodec::DoInitFrameL()
+ {
+ iFrameInfo = iOriginalFrameInfo;
+ iScanInfo = iOriginalScanInfo;
+
+ TInt maxMCUWidth = KJpgDCTBlockWidth * iMaxHorzSampleFactor;
+ TInt maxMCUHeight = KJpgDCTBlockWidth * iMaxVertSampleFactor;
+ iHorzMCUCount = (iFrameInfo.iSizeInPixels.iWidth + maxMCUWidth - 1) / maxMCUWidth;
+ iVertMCUCount = (iFrameInfo.iSizeInPixels.iHeight + maxMCUHeight - 1) / maxMCUHeight;
+ iTotalMCUCount = iHorzMCUCount * iVertMCUCount;
+ iCurrentMCUCount = 0;
+ iCurrentMCUHorzCount = 0;
+ iCurrentMCUVertCount = 0;
+ iMCUChunkAllocated = EFalse;
+
+ // We calculate how many data units we'll need in total
+ TInt totalDataUnitCount = 0;
+ for (TInt compIndex = 0; compIndex < iFrameInfo.iNumberOfComponents; compIndex++)
+ {
+ totalDataUnitCount += iMCUDataUnitCount[compIndex] * iTotalMCUCount;
+ }
+
+ TUint8* offset = NULL;
+ if(totalDataUnitCount > KMCUMaxTotalDataUnits)
+ {
+ iMCUChunk.Close();
+ User::LeaveIfError(iMCUChunk.CreateLocal(totalDataUnitCount * sizeof(TDataUnit), totalDataUnitCount * sizeof(TDataUnit)));
+ offset = iMCUChunk.Base();
+ iMCUChunkAllocated = ETrue;
+ }
+ else
+ {
+ delete iMCUMemoryBuffer;
+ iMCUMemoryBuffer = NULL;
+ iMCUMemoryBuffer = new (ELeave) TUint8 [ totalDataUnitCount * sizeof(TDataUnit) ];
+ offset = iMCUMemoryBuffer;
+ }
+
+
+ for (TInt compIndex = 0; compIndex < iFrameInfo.iNumberOfComponents; compIndex++)
+ {
+ TInt dataUnitCount = iMCUDataUnitCount[compIndex] * iTotalMCUCount;
+
+ iMCUBuffer[compIndex] = new(offset) TDataUnit[dataUnitCount];
+ offset += dataUnitCount * sizeof(TDataUnit);
+
+ Mem::FillZ(iMCUBuffer[compIndex],dataUnitCount * sizeof(TDataUnit));
+ iMCUBufferPtr[compIndex] = iMCUBuffer[compIndex];
+ iMCUBufferPtrLimit[compIndex] = iMCUBuffer[compIndex] + dataUnitCount;
+
+ iIndividualHorzMCUCount[compIndex] = ((iFrameInfo.iSizeInPixels.iWidth*iHorzSampleFactor[compIndex]) + maxMCUWidth - 1) / maxMCUWidth;
+ iIndividualVertMCUCount[compIndex] = ((iFrameInfo.iSizeInPixels.iHeight*iVertSampleFactor[compIndex]) + maxMCUHeight - 1) / maxMCUHeight;
+ }
+
+ iProcessing = ETrue;
+ iRefinedDCValue = 0;
+ }
+
+//
+// Progressive supports scaling and normal decode only.
+//
+void CProgressiveJpgReadCodec::PreInitFrameL()
+ {
+ CJpgReadCodec::PreInitFrameL();
+
+ if (iOperation != EDecodeNormal)
+ {
+ JPEG_LEAVE(KErrNotSupported, "No operations on Progressive");
+ }
+
+ if (iUseClipRect)
+ {
+ JPEG_LEAVE(KErrNotSupported, "No clipping on Progressive");
+ }
+ }
+
+void CProgressiveJpgReadCodec::InitFrameL(TFrameInfo& aFrameInfo, CFrameImageData& aFrameImageData, TBool aDisableErrorDiffusion, CFbsBitmap& aFrame, CFbsBitmap* aFrameMask)
+ {
+ CJpgReadCodec::InitFrameL(aFrameInfo, aFrameImageData, aDisableErrorDiffusion, aFrame, aFrameMask);
+
+ ClearBitmapL(aFrame, KRgbWhite); // clear bitmap so sensibly draw partial decodes
+
+ DoInitFrameL();
+ }
+
+void CProgressiveJpgReadCodec::InitFrameL(CImageFrame& aFrame)
+ {
+ CJpgReadCodec::InitFrameL(aFrame);
+
+ DoInitFrameL();
+ }
+
+#if defined(__ARMCC__)
+#pragma pop
+#endif
+
+TFrameState CProgressiveJpgReadCodec::DoProcessL()
+ {
+ FOREVER
+ {
+ if (iProcessing)
+ ProcessFrameL();
+ else
+ {
+ StoreState();
+ TInt dataRemaining = iDataPtrLimit - iDataPtr;
+ if (dataRemaining < 2)
+ return EFrameIncomplete;
+
+ TInt sig = (iDataPtr[0] << 8) | iDataPtr[1];
+ switch (sig)
+ {
+ case KJpgDHTSignature:
+ LoadHuffmanTableL();
+ break;
+ case KJpgDQTSignature:
+ break;
+ case KJpgSOSSignature:
+ LoadSOSL();
+ iProcessing = ETrue;
+ break;
+ case KJpgRestartIntervalSignature:
+ LoadRestartIntervalL();
+ break;
+ case KJpgEOISignature:
+ iDataPtr += 2;
+ return EFrameComplete;
+ default:
+#if defined(RELAX_JPEG_STRICTNESS)
+ iDataPtr += 1;
+ dataRemaining = iDataPtrLimit - iDataPtr;
+ if (dataRemaining < 2)
+ {
+ return EFrameIncomplete;
+ }
+#else
+ User::Leave(KErrCorrupt);
+#endif
+ break;
+ }
+ }
+ }
+ }
+
+void CProgressiveJpgReadCodec::ProcessFrameL()
+ {
+ if (iScanInfo.iEndSpectralSelection == 0)
+ {
+ if (iScanInfo.iSuccessiveApproximationBitsHigh == 0)
+ InitDCL();
+ else
+ RefineDCL();
+ }
+ else
+ {
+ if (iScanInfo.iSuccessiveApproximationBitsHigh == 0)
+ InitACL();
+ else
+ RefineACL();
+ }
+ }
+
+void CProgressiveJpgReadCodec::InitDCL()
+ {
+ if(iScanInfo.iNumberOfComponents == 1)
+ { //Non interleaved scan
+ for (TInt scanInfoIndex = 0; scanInfoIndex < iScanInfo.iNumberOfComponents; scanInfoIndex++)
+ {
+ TJpgScanInfo::TScanComponentInfo& scanInfo = iScanInfo.iComponent[scanInfoIndex];
+ TInt compIndex = ComponentIndexL(scanInfo.iId);
+ const TDecHuffmanTable& dcTable = iDCHuffmanTable[scanInfo.iDCCodingTable];
+ TInt& dcPredictor = iDCPredictor[compIndex];
+ TInt dataUnitCount = iMCUDataUnitCount[compIndex];
+
+ TInt horzSamples = iHorzSampleFactor[compIndex];
+ TInt vertSamples = iVertSampleFactor[compIndex];
+ TDataUnit* dataUnit = iMCUBuffer[compIndex];
+ for (; iCurrentMCUVertCount < iIndividualVertMCUCount[compIndex]; iCurrentMCUVertCount++)
+ {
+ TInt blockBase = (iCurrentMCUVertCount / vertSamples) * iHorzMCUCount;
+ TInt unitBase = (iCurrentMCUVertCount % vertSamples) * horzSamples;
+ for (; iCurrentMCUHorzCount < iIndividualHorzMCUCount[compIndex]; iCurrentMCUHorzCount++)
+ {
+ StoreState();
+ if (iRestartMCUCount == 0)
+ RestartStateL();
+
+ TInt blockOffset = blockBase + (iCurrentMCUHorzCount / horzSamples);
+ TInt unitOffset = unitBase + (iCurrentMCUHorzCount % horzSamples);
+
+ GetDCValueL(dataUnit[(blockOffset * dataUnitCount) + unitOffset],dcTable,dcPredictor);
+
+ iRestartMCUCount--;
+ }
+
+ iCurrentMCUHorzCount = 0;
+ }
+ iCurrentMCUVertCount = 0;
+ }
+ }
+ else
+ { // Interleaved scan
+ for(; iCurrentMCUCount < iTotalMCUCount; iCurrentMCUCount++)
+ {
+ StoreState();
+ if (iRestartMCUCount == 0)
+ RestartStateL();
+
+ for (TInt scanInfoIndex = 0; scanInfoIndex < iScanInfo.iNumberOfComponents; scanInfoIndex++)
+ {
+ TJpgScanInfo::TScanComponentInfo& scanInfo = iScanInfo.iComponent[scanInfoIndex];
+ TInt compIndex = ComponentIndexL(scanInfo.iId);
+ const TDecHuffmanTable& dcTable = iDCHuffmanTable[scanInfo.iDCCodingTable];
+ TInt& dcPredictor = iDCPredictor[compIndex];
+ TDataUnit* tempMCUBufferPtr = iMCUBufferPtr[compIndex];
+
+ for (TInt count = iMCUDataUnitCount[compIndex]; count > 0; count--)
+ {
+ GetDCValueL(*tempMCUBufferPtr,dcTable,dcPredictor);
+ tempMCUBufferPtr++;
+ }
+ }
+
+ for (TInt count = 0; count < iFrameInfo.iNumberOfComponents; count++)
+ iMCUBufferPtr[count] += iMCUDataUnitCount[count];
+
+ iRestartMCUCount--;
+ }
+ }
+
+ for (TInt count = 0; count < iFrameInfo.iNumberOfComponents; count++)
+ iMCUBufferPtr[count] = iMCUBuffer[count];
+
+ ResetState();
+ iCurrentMCUCount = 0;
+ iProcessing = EFalse;
+ }
+
+void CProgressiveJpgReadCodec::RefineDCL()
+ {
+ for(; iCurrentMCUCount < iTotalMCUCount; iCurrentMCUCount++)
+ {
+ StoreState();
+ if (iRestartMCUCount == 0)
+ RestartStateL();
+
+ for (TInt scanInfoIndex = 0; scanInfoIndex < iScanInfo.iNumberOfComponents; scanInfoIndex++)
+ {
+ TJpgScanInfo::TScanComponentInfo& scanInfo = iScanInfo.iComponent[scanInfoIndex];
+ TInt compIndex = ComponentIndexL(scanInfo.iId);
+ TDataUnit* tempMCUBufferPtr = iMCUBufferPtr[compIndex];
+
+ for (TInt count = iMCUDataUnitCount[compIndex]; count > 0; count--)
+ {
+ if (NextBitL())
+ tempMCUBufferPtr->iCoeff[0] |= iRefinedDCValue;
+ tempMCUBufferPtr++;
+ }
+ }
+
+ for (TInt count = 0; count < iFrameInfo.iNumberOfComponents; count++)
+ iMCUBufferPtr[count] += iMCUDataUnitCount[count];
+
+ iRestartMCUCount--;
+ }
+
+ for (TInt count = 0; count < iFrameInfo.iNumberOfComponents; count++)
+ iMCUBufferPtr[count] = iMCUBuffer[count];
+
+ ResetState();
+ iCurrentMCUCount = 0;
+ iProcessing = EFalse;
+ }
+
+void CProgressiveJpgReadCodec::InitACL()
+ {
+ if (iScanInfo.iNumberOfComponents > 1)
+ User::Leave(KErrCorrupt);
+
+ TInt compIndex = ComponentIndexL(iScanInfo.iComponent[0].iId);
+ const TDecHuffmanTable& acTable = iACHuffmanTable[iScanInfo.iComponent[0].iACCodingTable];
+ TInt dataUnitCount = iMCUDataUnitCount[compIndex];
+
+ if (dataUnitCount == 1)
+ {
+ for(; iCurrentMCUCount < iTotalMCUCount; iCurrentMCUCount++)
+ {
+ StoreState();
+ if (iRestartMCUCount == 0)
+ RestartStateL();
+
+ if (iSkipCount == 0)
+ iSkipCount = GetACValuesL(*iMCUBufferPtr[compIndex],acTable);
+ else
+ iSkipCount--;
+
+ iMCUBufferPtr[compIndex]++;
+ iRestartMCUCount--;
+ }
+
+ iCurrentMCUCount = 0;
+ }
+ else
+ {
+ TInt horzSamples = iHorzSampleFactor[compIndex];
+ TInt vertSamples = iVertSampleFactor[compIndex];
+ TDataUnit* dataUnit = iMCUBuffer[compIndex];
+ for (; iCurrentMCUVertCount < iIndividualVertMCUCount[compIndex]; iCurrentMCUVertCount++)
+ {
+ TInt blockBase = (iCurrentMCUVertCount / vertSamples) * iHorzMCUCount;
+ TInt unitBase = (iCurrentMCUVertCount % vertSamples) * horzSamples;
+ for (; iCurrentMCUHorzCount < iIndividualHorzMCUCount[compIndex]; iCurrentMCUHorzCount++)
+ {
+ StoreState();
+ if (iRestartMCUCount == 0)
+ RestartStateL();
+
+ if (iSkipCount == 0)
+ {
+ TInt blockOffset = blockBase + (iCurrentMCUHorzCount / horzSamples);
+ TInt unitOffset = unitBase + (iCurrentMCUHorzCount % horzSamples);
+ iSkipCount = GetACValuesL(dataUnit[(blockOffset * dataUnitCount) + unitOffset],acTable);
+ }
+ else
+ iSkipCount--;
+
+ iRestartMCUCount--;
+ }
+
+ iCurrentMCUHorzCount = 0;
+ }
+ iCurrentMCUVertCount = 0;
+ }
+
+ for (TInt count = 0; count < iFrameInfo.iNumberOfComponents; count++)
+ iMCUBufferPtr[count] = iMCUBuffer[count];
+
+ iSkipCount = 0;
+ ResetState();
+ iProcessing = EFalse;
+ }
+
+void CProgressiveJpgReadCodec::RefineACL()
+ {
+ if (iScanInfo.iNumberOfComponents > 1)
+ User::Leave(KErrCorrupt);
+
+ TInt compIndex = ComponentIndexL(iScanInfo.iComponent[0].iId);
+ const TDecHuffmanTable& acTable = iACHuffmanTable[iScanInfo.iComponent[0].iACCodingTable];
+ TInt dataUnitCount = iMCUDataUnitCount[compIndex];
+ if (dataUnitCount == 1)
+ {
+ for(; iCurrentMCUCount < iTotalMCUCount; iCurrentMCUCount++)
+ {
+ StoreState();
+ if (iRestartMCUCount == 0)
+ RestartStateL();
+
+ RefineACValuesL(*iMCUBufferPtr[compIndex],acTable);
+ iMCUBufferPtr[compIndex]++;
+
+ iRestartMCUCount--;
+ }
+
+ iCurrentMCUCount = 0;
+ }
+ else
+ {
+ TInt horzSamples = iHorzSampleFactor[compIndex];
+ TInt vertSamples = iVertSampleFactor[compIndex];
+ TDataUnit* dataUnit = iMCUBuffer[compIndex];
+ for (; iCurrentMCUVertCount < iIndividualVertMCUCount[compIndex]; iCurrentMCUVertCount++)
+ {
+ TInt blockBase = (iCurrentMCUVertCount / vertSamples) * iHorzMCUCount;
+ TInt unitBase = (iCurrentMCUVertCount % vertSamples) * horzSamples;
+ for (; iCurrentMCUHorzCount < iIndividualHorzMCUCount[compIndex]; iCurrentMCUHorzCount++)
+ {
+ StoreState();
+ if (iRestartMCUCount == 0)
+ RestartStateL();
+
+ TInt blockOffset = blockBase + (iCurrentMCUHorzCount / horzSamples);
+ TInt unitOffset = unitBase + (iCurrentMCUHorzCount % horzSamples);
+ RefineACValuesL(dataUnit[(blockOffset * dataUnitCount) + unitOffset],acTable);
+
+ iRestartMCUCount--;
+ }
+
+ iCurrentMCUHorzCount = 0;
+ }
+ iCurrentMCUVertCount = 0;
+ }
+
+ for (TInt count = 0; count < iFrameInfo.iNumberOfComponents; count++)
+ iMCUBufferPtr[count] = iMCUBuffer[count];
+
+ ResetState();
+ iProcessing = EFalse;
+ }
+
+void CProgressiveJpgReadCodec::InitDrawFrame()
+ {
+ iCurrentDrawMCU = 0;
+ TInt numberOfComponents = iMonochrome ? 1 : iFrameInfo.iNumberOfComponents;
+ for (TInt count = 0; count < numberOfComponents; count++)
+ {
+ iDrawMCUPtr[count] = iMCUBuffer[count];
+ }
+
+ if (!iImageFrameCodecPtr)
+ {
+ CImageProcessor* const imageProc = ImageProcessor();
+ imageProc->SetPos(TPoint(0,0));
+ }
+ }
+
+TBool CProgressiveJpgReadCodec::DrawFrameL()
+ {
+ TInt numberOfComponents = iMonochrome ? 1 : iFrameInfo.iNumberOfComponents;
+
+ TDataUnit dataUnit;
+ CImageProcessor* const imageProc = ImageProcessor();
+
+ const TInt drawMCULimit = Min(iCurrentDrawMCU + KMaxMCUPerDraw, iTotalMCUCount);
+
+ while (iCurrentDrawMCU < drawMCULimit)
+ {
+ for (TInt compIndex = 0; compIndex < numberOfComponents; compIndex++)
+ {
+ TQTable& qTable = iQTable[iFrameInfo.iComponent[compIndex].iQTable];
+ TDataUnit* compPtr = iComponent[compIndex];
+ for (TInt dataUnitCount = iMCUDataUnitCount[compIndex]; dataUnitCount > 0; dataUnitCount--)
+ {
+ qTable.DeQuantize(dataUnit, *iDrawMCUPtr[compIndex], KJpgDCTBlockSize);
+ iCompConf[compIndex].iDCT->InverseTransform(*compPtr, dataUnit);
+ iDrawMCUPtr[compIndex]++;
+ compPtr++;
+ }
+ }
+
+ if (imageProc)
+ {
+ WriteMCU();
+ imageProc->SetPixelBlock(iRgbBuffer);
+ }
+ else
+ {
+ RArray<const TDataUnit*> dataUnits;
+ CleanupClosePushL(dataUnits);
+ for (TInt compIndex = 0; compIndex < iFrameInfo.iNumberOfComponents; compIndex++)
+ {
+ TDataUnit* compPtr = iComponent[compIndex];
+ TInt numSamples = iMCUDataUnitCount[compIndex];
+ while (numSamples > 0)
+ {
+ numSamples--;
+ User::LeaveIfError(dataUnits.Append(compPtr++));
+ }
+ }
+ iImageFrameCodecPtr->ProcessL(dataUnits);
+ CleanupStack::PopAndDestroy(1, &dataUnits);
+ }
+
+ iCurrentDrawMCU++;
+ }
+
+ if (iCurrentDrawMCU < iTotalMCUCount)
+ {
+ return EFalse;
+ }
+
+ return ETrue;
+ }
+
+void CProgressiveJpgReadCodec::GetDCValueL(TDataUnit& aDataUnit,const TDecHuffmanTable& aHuffmanTable,TInt& aDCPredictor)
+ {
+ TInt size = GetHuffmanCodeL(aHuffmanTable);
+ TInt amplitude = (size > 0) ? GetBinaryNumberL(size) : 0;
+ aDCPredictor += amplitude;
+ ASSERT(aDCPredictor <= KMaxTInt16 && aDCPredictor >= KMinTInt16);
+ aDataUnit.iCoeff[0] = TInt16(aDCPredictor << iScanInfo.iSuccessiveApproximationBitsLow);
+ }
+
+TInt CProgressiveJpgReadCodec::GetACValuesL(TDataUnit& aDataUnit,const TDecHuffmanTable& aHuffmanTable)
+ {
+ TInt16* valuePtr = &aDataUnit.iCoeff[iScanInfo.iStartSpectralSelection];
+ TInt16* valuePtrLimit = &aDataUnit.iCoeff[iScanInfo.iEndSpectralSelection + 1];
+
+ while (valuePtr < valuePtrLimit)
+ {
+ TInt s = GetHuffmanCodeL(aHuffmanTable);
+ const TInt r = s >> 4;
+ s &= 0x0f;
+ if (s > 0)
+ {
+ valuePtr += r;
+
+ if (valuePtr < valuePtrLimit)
+ *valuePtr++ |= TInt16(GetBinaryNumberL(s) << iScanInfo.iSuccessiveApproximationBitsLow);
+ }
+ else
+ {
+ if (r == 15) // Zero run length
+ valuePtr += 16;
+ else
+ {
+ TInt eobRun = 1 << r;
+ if (r > 0)
+ eobRun += GetPositiveBinaryNumberL(r);
+ return eobRun - 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+void CProgressiveJpgReadCodec::RefineACValuesL(TDataUnit& aDataUnit,const TDecHuffmanTable& aHuffmanTable)
+ {
+ TInt s = 0;
+ TInt r;
+ TInt16* valuePtr = &aDataUnit.iCoeff[iScanInfo.iStartSpectralSelection];
+ TInt16* valuePtrLimit = &aDataUnit.iCoeff[iScanInfo.iEndSpectralSelection];
+
+ TInt err;
+ TInt numNewNonZero = 0;
+ TInt16* newNonZeroPosition[KJpgDCTBlockSize];
+ TInt oldEobRun = iEobRun;
+ if (iEobRun == 0)
+ {
+ for (; valuePtr <= valuePtrLimit; valuePtr++)
+ {
+ TRAP(err,s = GetHuffmanCodeL(aHuffmanTable));
+ if (err == KErrCompletion)
+ goto OutOfData;
+ User::LeaveIfError(err);
+ r = s >> 4;
+ s &= 0x0f;
+ if (s > 0)
+ {
+ TBool nextBit = EFalse;
+ TRAP(err,nextBit = NextBitL());
+ if (err == KErrCompletion)
+ goto OutOfData;
+ User::LeaveIfError(err);
+ if (nextBit)
+ s = iP1;
+ else
+ s = iM1;
+ }
+ else
+ {
+ if (r != 15)
+ {
+ iEobRun = 1 << r;
+ if (r > 0)
+ {
+ TRAP(err,iEobRun += GetPositiveBinaryNumberL(r));
+ if (err == KErrCompletion)
+ goto OutOfData;
+ User::LeaveIfError(err);
+ }
+ break;
+ }
+ }
+
+ do
+ {
+ TInt16* coef = valuePtr;
+ if (*coef != 0)
+ {
+ TBool nextBit = EFalse;
+ TRAP(err,nextBit = NextBitL());
+ if (err == KErrCompletion)
+ goto OutOfData;
+ User::LeaveIfError(err);
+ if (nextBit)
+ {
+ if ((*coef & iP1) == 0)
+ {
+ if (*coef >= 0)
+ *coef = TInt16(*coef + iP1);
+ else
+ *coef = TInt16(*coef + iM1);
+ }
+ }
+ }
+ else
+ {
+ if (--r < 0)
+ break;
+ }
+ valuePtr++;
+ }
+ while (valuePtr <= valuePtrLimit);
+
+ if (s != 0)
+ {
+ *valuePtr = TInt16(s);
+ newNonZeroPosition[numNewNonZero++] = valuePtr;
+ }
+ }
+ }
+
+ if (iEobRun > 0)
+ {
+ for (; valuePtr <= valuePtrLimit; valuePtr++)
+ {
+ TInt16* coef = valuePtr;
+ if (*coef != 0)
+ {
+ TBool nextBit = EFalse;
+ TRAP(err,nextBit = NextBitL());
+ if (err == KErrCompletion)
+ goto OutOfData;
+ User::LeaveIfError(err);
+ if (nextBit)
+ {
+ if ((*coef & iP1) == 0)
+ {
+ if (*coef >= 0)
+ *coef = TInt16(*coef + iP1);
+ else
+ *coef = TInt16(*coef + iM1);
+ }
+ }
+ }
+ }
+ iEobRun--;
+ }
+ return;
+
+OutOfData:
+ while (numNewNonZero > 0)
+ *(newNonZeroPosition[--numNewNonZero]) = 0;
+ iEobRun = oldEobRun;
+
+ User::Leave(KErrCompletion);
+ }
+
+#if defined(__ARMCC__)
+#pragma push
+#pragma thumb
+#endif
+
+void CProgressiveJpgReadCodec::LoadHuffmanTableL()
+ {
+ if (iDataPtr + 4 > iDataPtrLimit)
+ {
+ User::Leave(KErrCompletion);
+ }
+
+ TInt length = (iDataPtr[2] << 8) | iDataPtr[3];
+
+ const TUint8* dataPtrLimit = iDataPtr + length + 2;
+ if (dataPtrLimit > iDataPtrLimit)
+ User::Leave(KErrCompletion);
+
+ iDataPtr += 4;
+ while (iDataPtr < dataPtrLimit)
+ {
+ TInt index = *iDataPtr++;
+ TBool dcTable = !(index & 0x10);
+ index &= 0x0f;
+ if (index >= KJpgMaxNumberOfTables)
+ User::Leave(KErrCorrupt);
+ TDecHuffmanTable& table = dcTable ? iDCHuffmanTable[index] : iACHuffmanTable[index];
+ iDataPtr += table.SetL(iDataPtr,dataPtrLimit);
+ }
+ }
+
+void CProgressiveJpgReadCodec::LoadSOSL()
+ {
+ if (iDataPtr + 4 > iDataPtrLimit)
+ User::Leave(KErrCompletion);
+ TInt length = (iDataPtr[2] << 8) | iDataPtr[3];
+
+ if (iDataPtr + length + 2 > iDataPtrLimit)
+ User::Leave(KErrCompletion);
+
+ iDataPtr += 4;
+ iScanInfo.iNumberOfComponents = *iDataPtr++;
+
+ // We ony support up to 3 components, even though spec supports up to 4.
+ // Additionaly, number must not be greater than in original frame SOS header
+ if (iScanInfo.iNumberOfComponents < KJpgMinNumberOfComponents ||
+ iScanInfo.iNumberOfComponents > iOriginalFrameInfo.iNumberOfComponents)
+ User::Leave(KErrCorrupt);
+
+ for (TInt count = 0; count < iScanInfo.iNumberOfComponents; count++)
+ {
+ iScanInfo.iComponent[count].iId = *iDataPtr++;
+
+ TUint8 table = *iDataPtr++;
+ TUint8 DCTable = static_cast<TUint8>(table >> 4);
+ TUint8 ACTable = static_cast<TUint8>(table & 0x0f);
+
+ if(DCTable >= KJpgMaxNumberOfTables || ACTable >= KJpgMaxNumberOfTables)
+ User::Leave(KErrCorrupt);
+
+ iScanInfo.iComponent[count].iDCCodingTable = DCTable;
+ iScanInfo.iComponent[count].iACCodingTable = ACTable;
+
+ iDCHuffmanTable[DCTable].MakeDerivedTableL();
+ iACHuffmanTable[ACTable].MakeDerivedTableL();
+ }
+
+ iScanInfo.iStartSpectralSelection = *iDataPtr++;
+ iScanInfo.iEndSpectralSelection = *iDataPtr++;
+ TUint8 successiveApproximation = *iDataPtr++;
+ iScanInfo.iSuccessiveApproximationBitsHigh = successiveApproximation >> 4;
+ iScanInfo.iSuccessiveApproximationBitsLow = successiveApproximation & 0x0f;
+ iP1 = 1 << iScanInfo.iSuccessiveApproximationBitsLow;
+ iM1 = (-1) << iScanInfo.iSuccessiveApproximationBitsLow;
+
+ iRefinedDCValue = TInt16(1 << iScanInfo.iSuccessiveApproximationBitsLow);
+
+ // if iRestartInterval is 0, iRestartMCUCount is set to a negative value (KErrNotFound) to skip the 0 trigger points that would call RestartStateL
+ // This is done to solve the problem that on some images the iRestartInterval marker is 0 on every frame
+ iRestartMCUCount = iFrameInfo.iRestartInterval > 0 ? iFrameInfo.iRestartInterval : KErrNotFound;
+ }
+
+void CProgressiveJpgReadCodec::LoadRestartIntervalL()
+ {
+ if (iDataPtr + 6 > iDataPtrLimit)
+ {
+ User::Leave(KErrCompletion);
+ }
+
+ iFrameInfo.iRestartInterval = iDataPtr[5] | (iDataPtr[4] << 8);
+ iDataPtr += 6;
+
+ // if iRestartInterval is 0, iRestartMCUCount is set to a negative value (KErrNotFound) to skip the 0 trigger points that would call RestartStateL
+ // This is done to solve the problem that on some images the iRestartInterval marker is 0 on every frame
+ iRestartMCUCount = iFrameInfo.iRestartInterval > 0 ? iFrameInfo.iRestartInterval : KErrNotFound;
+ }
+
+// 03/12/03 - function added as a result of INC037134
+// needed a way to cleanup buffers when decoding complete
+void CProgressiveJpgReadCodec::CleanupBuffers()
+ {
+ if(!iMCUChunkAllocated)
+ {
+ delete iMCUMemoryBuffer;
+ iMCUMemoryBuffer = NULL;
+ }
+ else
+ {
+ iMCUChunk.Close();
+ }
+ for (TInt count = 0; count < KJpgNumberOfComponents; count++)
+ {
+ iMCUBuffer[count] = NULL;
+ }
+ iMCUChunkAllocated = EFalse;
+ }
+
+// CJpgImageFrameReadCodec
+CJpgImageFrameReadCodec::CJpgImageFrameReadCodec(CImageFrame* aFrame)
+ {
+ iDestination = aFrame;
+ }
+
+CJpgImageFrameReadCodec::~CJpgImageFrameReadCodec()
+ {
+ delete iImageFrameProcessorPtr;
+ }
+
+CJpgImageFrameReadCodec* CJpgImageFrameReadCodec::NewL(CImageFrame* aFrame)
+ {
+ CJpgImageFrameReadCodec* self = new(ELeave) CJpgImageFrameReadCodec(aFrame);
+ return self;
+ }
+
+void CJpgImageFrameReadCodec::CreateImageProcessorL(const TJpgFrameInfo& aFrameInfo)
+ {
+ ASSERT(iImageFrameProcessorPtr==NULL);
+
+ iDestination->SetFrameSizeInPixels(aFrameInfo.iSizeInPixels);
+ CJpgImageFrameProcessorUtility::PrepareImageFrameL(aFrameInfo,*iDestination);
+ iImageFrameProcessorPtr = CJpgImageFrameProcessorUtility::NewL(*iDestination);
+ }
+
+void CJpgImageFrameReadCodec::ProcessL(const RArray<const TDataUnit*>& aDataUnits)
+ {
+ iImageFrameProcessorPtr->WriteBlockL(aDataUnits);
+ }
+
+CImageFrame* CJpgImageFrameReadCodec::Destination()
+ {
+ return iDestination;
+ }
+
+void CJpgImageFrameReadCodec::SetImageFrameBlocksL(CImageFrame* aFrame, const TJpgFrameInfo& aFrameInfo)
+ {
+ iDestination = aFrame;
+
+ if(iImageFrameProcessorPtr)
+ {
+ delete iImageFrameProcessorPtr;
+ iImageFrameProcessorPtr = NULL;
+ }
+
+ CJpgImageFrameProcessorUtility::PrepareImageFrameL(aFrameInfo, *iDestination);
+ iImageFrameProcessorPtr = CJpgImageFrameProcessorUtility::NewL(*iDestination);
+ }
+
+//
+// Multiscan sequential read codec.
+//
+CMultiScanSequentialJpgReadCodec* CMultiScanSequentialJpgReadCodec::NewL(
+ const TJpgFrameInfo& aFrameInfo,
+ const TJpgScanInfo& aScanInfo,
+ TDecHuffmanTable aDCHuffmanTable[KJpgMaxNumberOfTables],
+ TDecHuffmanTable aACHuffmanTable[KJpgMaxNumberOfTables],
+ const TQTable aQTable[KJpgMaxNumberOfTables])
+ {
+ CMultiScanSequentialJpgReadCodec* self = new(ELeave) CMultiScanSequentialJpgReadCodec(
+ aFrameInfo,
+ aScanInfo,
+ aDCHuffmanTable,
+ aACHuffmanTable,
+ aQTable);
+ CleanupStack::PushL(self);
+ self->ConstructL(EFalse); // No cache needed
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+
+//
+//
+//
+CMultiScanSequentialJpgReadCodec::CMultiScanSequentialJpgReadCodec(
+ const TJpgFrameInfo& aFrameInfo,
+ const TJpgScanInfo& aScanInfo,
+ TDecHuffmanTable aDCHuffmanTable[KJpgMaxNumberOfTables],
+ TDecHuffmanTable aACHuffmanTable[KJpgMaxNumberOfTables],
+ const TQTable aQTable[KJpgMaxNumberOfTables])
+ : CSequentialJpgReadCodec(aFrameInfo,
+ aScanInfo,
+ aDCHuffmanTable,
+ aACHuffmanTable,
+ aQTable)
+ {
+ Mem::Copy(&iFirstScan, &iScanInfo, sizeof(TJpgScanInfo));
+ }
+
+
+//
+//
+//
+CMultiScanSequentialJpgReadCodec::~CMultiScanSequentialJpgReadCodec()
+ {
+ iMCUChunk.Close();
+ }
+
+
+//
+//
+//
+void CMultiScanSequentialJpgReadCodec::PreInitFrameL()
+ {
+ CSequentialJpgReadCodec::PreInitFrameL();
+
+ // Restore the first scan.
+ Mem::Copy(&iScanInfo, &iFirstScan, sizeof(TJpgScanInfo));
+
+ for (TInt i = 0; i < KJpgNumberOfComponents; i++)
+ {
+ iCompAvailable[i] = EFalse;
+ }
+
+ iMCUChunk.Close();
+ iMCUMemoryOffset = NULL;
+ }
+
+
+//
+//
+//
+void CMultiScanSequentialJpgReadCodec::InitFrameL(TFrameInfo& aFrameInfo, CFrameImageData& aFrameImageData, TBool aDisableErrorDiffusion, CFbsBitmap& aFrame, CFbsBitmap* aFrameMask)
+ {
+ CJpgReadCodec::InitFrameL(aFrameInfo, aFrameImageData, aDisableErrorDiffusion, aFrame, aFrameMask);
+
+ iTotalMCUBlocks = GetHorzMCUCount() * GetVertMCUCount();
+
+ // Prepare RChunk memory for all components
+ TInt mcuMemory = 0;
+ for (TInt i = 0; i < iFrameInfo.iNumberOfComponents; i++)
+ {
+ mcuMemory += iTotalMCUBlocks * iMCUDataUnitCount[i] * sizeof(TDataUnit);
+ }
+
+ TInt err = iMCUChunk.CreateLocal(mcuMemory, mcuMemory);
+ JPEG_LEAVE_IF_ERROR(err, "Chunk creation");
+
+ iMCUMemoryOffset = iMCUChunk.Base();
+
+ // Allocate memory for individual components in scan.
+ for (TInt scanInfoIndex = 0; scanInfoIndex < iScanInfo.iNumberOfComponents; scanInfoIndex++)
+ {
+ TJpgScanInfo::TScanComponentInfo& scanInfo = iScanInfo.iComponent[scanInfoIndex];
+ TInt compIndex = ComponentIndexL(scanInfo.iId);
+ TInt unitCount = iTotalMCUBlocks * iMCUDataUnitCount[compIndex];
+
+ iCompBuf[compIndex] = reinterpret_cast<TDataUnit*>(iMCUMemoryOffset);
+ iCompAvailable[compIndex] = ETrue;
+
+ iMCUMemoryOffset += unitCount * sizeof(TDataUnit);
+ }
+
+ iCurrentMcuIdx = 0;
+ }
+
+#if defined(__ARMCC__)
+#pragma pop
+#endif
+
+//
+//
+//
+TFrameState CMultiScanSequentialJpgReadCodec::DoProcessL()
+ {
+ while (iDataPtr < iDataPtrLimit)
+ {
+ StoreState();
+ if (iRestartMCUCount == 0)
+ {
+ RestartStateL();
+ }
+
+ const TUint8* const latestDataPtr = iDataPtr;
+ TInt error = KErrNone;
+ // we do that "if" in order to bypass exception handling which can
+ // affect performance of the decoder
+ if (iPreviousDataLeft < KMCUDataLeftThreshhold)
+ {
+ TRAP(error, ProcessMCUL());
+ // leave if it wasn't a partial MCU
+ if (error != KErrNone && (error != KErrEof || latestDataPtr == iDataPtr ||
+ (latestDataPtr + sizeof(TUint16) <= iDataPtrLimit
+ && PtrReadUtil::ReadBigEndianUint16(latestDataPtr)==KJpgEOISignature
+ )
+ )
+ )
+ {
+ JPEG_LEAVE(error, "From ProcessMCUL_1");
+ }
+ }
+ else
+ {
+ ProcessMCUL();
+ }
+
+ JPEG_LEAVE_IF_ERROR(error, "From slow ProcessMCUL");
+
+ //in case of corrupt image, if MCUs exceed total MCUs then leave
+ if (iCurrentMcuIdx >= iTotalMCUBlocks)
+ {
+ JPEG_LEAVE(KErrEof, "Too many MCUs in image");
+ }
+
+ for (TInt scanInfoIndex = 0; scanInfoIndex < iScanInfo.iNumberOfComponents; scanInfoIndex++)
+ {
+ TJpgScanInfo::TScanComponentInfo& scanInfo = iScanInfo.iComponent[scanInfoIndex];
+ TInt compIndex = ComponentIndexL(scanInfo.iId);
+ CopyMCUs(compIndex);
+ }
+
+ iCurrentMcuIdx++;
+ iRestartMCUCount--;
+
+ if (iDataPtrLimit - iDataPtr < KMCUDataLeftThreshhold)
+ {
+ TBool needLeave = (iPreviousDataLeft > iDataPtrLimit - iDataPtr);
+ iPreviousDataLeft = iDataPtrLimit - iDataPtr;
+ if (needLeave && !iNewTableOrScan)
+ {
+ StoreState();
+ User::Leave(KErrCompletion);
+ }
+ }
+ }
+ return EFrameIncomplete;
+ }
+
+
+//
+// Searches Start of Scan marker(0xffda) or Huffman table marker(0xffc4) and gives the position in aPos.
+//
+TBool CMultiScanSequentialJpgReadCodec::SearchTableOrSosMarker(TBufPtr8& aSourceData, TInt& aPos, TUint16& aMarker)
+ {
+ const TUint8* ptr = aSourceData.Ptr();
+ const TUint8* limit = ptr + aSourceData.Length() - 1;
+
+ aPos = 0;
+ while ((ptr + 1) <= limit)
+ {
+ // Mem::Copy must be used to avoid KERN:EXEC 3 access alignment panics on hardware.
+ TUint16 data = 0;
+ Mem::Copy(&data, ptr++, sizeof(TUint16));
+
+ switch (data)
+ {
+ case 0xDAFF: // Big-endian version of KJpgSOSSignature.
+ aMarker = KJpgSOSSignature;
+ return ETrue;
+
+ case 0xC4FF: // Big-endian version of KJpgDHTSignature.
+ aMarker = KJpgDHTSignature;
+ return ETrue;
+
+ default:
+ aPos++;
+ }
+ }
+
+ return EFalse;
+ }
+
+
+//
+// Processes frame data. Renders only on decoding all MCUs.
+//
+TFrameState CMultiScanSequentialJpgReadCodec::ProcessFrameL(TBufPtr8& aSrc)
+ {
+ TUint16 marker = 0;
+ TInt markerPos = -1;
+ iDataPtr = const_cast<TUint8*>(aSrc.Ptr());
+ TInt dataLen = aSrc.Length();
+ const TUint8* newMarkerAddr = NULL;
+ const TUint8* bufferLimit = iDataPtr + dataLen;
+ TBool switched = EFalse;
+
+ // Exclude table or next scan.
+ iNewTableOrScan = SearchTableOrSosMarker(aSrc, markerPos, marker);
+ if (iNewTableOrScan)
+ {
+ newMarkerAddr = iDataPtr + markerPos;
+ iDataPtrLimit = const_cast<TUint8*>(newMarkerAddr);
+ }
+ else
+ {
+ // If marker 0xff is in the last byte, exclude it.
+ iDataPtrLimit = iDataPtr + dataLen;
+ if (*(iDataPtrLimit - 1) == 0xFF)
+ {
+ iDataPtrLimit--;
+ }
+ }
+
+ iPreviousDataLeft = iDataPtrLimit - iDataPtr;
+
+ TFrameState frameState = EFrameComplete;
+ TRAPD(err, frameState = DoProcessL());
+ // If there is new scan or table, switch to it.
+ if (iNewTableOrScan)
+ {
+ if (marker == KJpgDHTSignature)
+ {
+ switched = ProcessHuffmanTableL(newMarkerAddr, bufferLimit);
+ }
+ else if (marker == KJpgSOSSignature)
+ {
+ switched = SwitchScanL(newMarkerAddr, bufferLimit);
+ }
+ }
+
+ if (err != KErrNone)
+ {
+ if (err == KErrCompletion)
+ {
+ RestoreState();
+ frameState = EFrameIncomplete;
+ }
+ else if (err == KErrEof)
+ {
+ frameState = EFrameComplete;
+ // Render all MCUs.
+ RenderMCUsL();
+ }
+ else
+ {
+ JPEG_LEAVE(err, "From DoProcessL");
+ }
+ }
+
+ if (switched)
+ {
+ iDataPtr = const_cast<TUint8*>(newMarkerAddr);
+ if (marker == KJpgSOSSignature)
+ {
+ ResetOnNewScan();
+ }
+ }
+
+ aSrc.Shift(iDataPtr - aSrc.Ptr()); // Shift out used data.
+
+ return frameState;
+ }
+
+
+//
+// Switches to new scan if sufficient header information is available.
+//
+TBool CMultiScanSequentialJpgReadCodec::SwitchScanL(const TUint8*& aScan, const TUint8* aDataLimit)
+ {
+ const TInt KSosSizeFieldLength = 2;
+ if (aScan + sizeof(KJpgSOSSignature) + KSosSizeFieldLength > aDataLimit)
+ {
+ return EFalse;
+ }
+
+ // Check for SOS marker.
+ TUint16 value = ReadBigEndianUint16(aScan);
+ if (value != KJpgSOSSignature)
+ {
+ return EFalse;
+ }
+
+ // Read length and check if entire scan is available.
+ value = ReadBigEndianUint16(aScan);
+ if (aScan + value - KSosSizeFieldLength > aDataLimit)
+ {
+ return EFalse;
+ }
+
+ // Keep track of MCUs available for components.
+ for (TInt i = 0; i < iScanInfo.iNumberOfComponents; i++)
+ {
+ TInt compIndex = ComponentIndexL(iScanInfo.iComponent[i].iId);
+ iCompMcuCount[compIndex] = iCurrentMcuIdx;
+ }
+
+ iScanInfo.iNumberOfComponents = *aScan;
+ // Component id, table selector bytes.
+ const TInt KCompBytes = 2;
+ // Scan start, scan end, successive approx bytes.
+ const TInt KScanBytes = 3;
+ if (aScan + (iScanInfo.iNumberOfComponents * KCompBytes) + KScanBytes > aDataLimit)
+ {
+ // Header length is wrong. Entire header is not available in the buffer.
+ return EFalse;
+ }
+
+ TJpgScanInfoProcessor::ProcessStartOfScanL(aScan, iFrameInfo, iScanInfo, iDCHuffmanTable, iACHuffmanTable);
+
+ // Move to next address after scan.
+ aScan++;
+ for (TInt i = 0; i < iScanInfo.iNumberOfComponents; i++)
+ {
+ TJpgScanInfo::TScanComponentInfo& scanInfo = iScanInfo.iComponent[i];
+ TInt compIndex = ComponentIndexL(scanInfo.iId);
+ if (iCompAvailable[compIndex])
+ {
+ // If a same component repeats, then the file is corrupt. Stop further decoding.
+ JPEG_LEAVE(KErrEof, "Repeated component");
+ }
+
+ TInt unitCount = iTotalMCUBlocks * iMCUDataUnitCount[compIndex];
+ iCompBuf[compIndex] = reinterpret_cast<TDataUnit*>(iMCUMemoryOffset);
+ iCompAvailable[compIndex] = ETrue;
+
+ iMCUMemoryOffset += unitCount * sizeof(TDataUnit);
+ }
+
+ iCurrentMcuIdx = 0;
+ if (iFrameInfo.iRestartInterval > 0)
+ {
+ iRestartMCUCount = iFrameInfo.iRestartInterval;
+ }
+ else
+ {
+ iRestartMCUCount = KErrNotFound;
+ }
+
+ ResetState();
+ return ETrue;
+ }
+
+
+//
+//
+//
+TBool CMultiScanSequentialJpgReadCodec::ProcessHuffmanTableL(const TUint8*& aData, const TUint8* aBufferLimit)
+ {
+ // Length of size field for table marker.
+ const TInt KTableMarkerSizeField = 2;
+ if (aData + sizeof(KJpgDHTSignature) + KTableMarkerSizeField > aBufferLimit)
+ {
+ return EFalse;
+ }
+
+ // Read marker.
+ TUint16 value = ReadBigEndianUint16(aData);
+ if (value != KJpgDHTSignature)
+ {
+ return EFalse;
+ }
+
+ // Read length.
+ value = ReadBigEndianUint16(aData);
+
+ // Check if entire table data is available in buffer.
+ const TUint8* dataLimit = aData + value - KTableMarkerSizeField;
+ if (dataLimit > aBufferLimit)
+ {
+ return EFalse;
+ }
+
+ THuffmanTableProcessor::ProcessHuffmanTableL(aData, dataLimit, iDCHuffmanTable, iACHuffmanTable);
+
+ return ETrue;
+ }
+
+
+//
+// Copies MCUs to component buffers, where they are accumulated.
+//
+void CMultiScanSequentialJpgReadCodec::CopyMCUs(TInt aCompIdx)
+ {
+ if (iMonochrome && (aCompIdx != KYComp))
+ {
+ return;
+ }
+
+ // Assumes that iCurrentMcuIdx value start from 0.
+ const TDataUnit* src = iComponent[aCompIdx];
+ TDataUnit* des = iCompBuf[aCompIdx];
+
+ // If Y with 4 DUs.
+ if ((aCompIdx == KYComp) && (iMCUDataUnitCount[KYComp] == 4))
+ {
+ // Rearranges Y Data Units, so that they are rendered sequentially.
+ TInt mcusPerLine = GetHorzMCUCount();
+ TInt dusPerLine = mcusPerLine * iMCUDataUnitCount[KYComp];
+ TInt line = iCurrentMcuIdx / mcusPerLine;
+ TDataUnit* lineAddr = des + (line * dusPerLine);
+ TInt mcuIdxInLine = iCurrentMcuIdx - (line * mcusPerLine);
+
+ // Copy offset.
+ TInt offset = 0;
+ if ((mcuIdxInLine + 1) <= mcusPerLine / 2)
+ {
+ offset = mcuIdxInLine * 8;
+ }
+ else
+ {
+ TInt idx = (mcuIdxInLine + 1) - (mcusPerLine / 2);
+ offset = (idx - 1) * 8 + 2;
+ }
+
+ // Copy 2 DUs at a time, giving 2 DU space.
+ Mem::Copy(lineAddr + offset, src, 2 * sizeof(TDataUnit));
+ Mem::Copy(lineAddr + offset + 4, src + 2, 2 * sizeof(TDataUnit));
+ }
+ else
+ {
+ des += iCurrentMcuIdx * iMCUDataUnitCount[aCompIdx];
+ Mem::Copy(des, src, iMCUDataUnitCount[aCompIdx] * sizeof(TDataUnit));
+ }
+ }
+
+
+//
+// Sets up iComponent to point to the right parts of the RChunk memory.
+//
+void CMultiScanSequentialJpgReadCodec::PrepareToRenderMCU(TInt aMCUIndex)
+ {
+ JPEG_ASSERT(aMCUIndex >= 0);
+
+ for (TInt i = 0; i < KJpgNumberOfComponents; i++)
+ {
+ if (iCompAvailable[i])
+ {
+ iComponent[i] = &iCompBuf[i][aMCUIndex * iMCUDataUnitCount[i]];
+ }
+ }
+ }
+
+
+//
+// Sends all MCUs of components to rendering.
+//
+void CMultiScanSequentialJpgReadCodec::RenderMCUsL()
+ {
+ for (TInt i = 0; i < iScanInfo.iNumberOfComponents; i++)
+ {
+ // Keep track of number of MCUs available for components.
+ TInt compIndex = ComponentIndexL(iScanInfo.iComponent[i].iId);
+ iCompMcuCount[compIndex] = iCurrentMcuIdx;
+ }
+
+ FillEmptyMCUs();
+
+ while (iNeededMCU != KErrCompletion)
+ {
+ iStreamMCU = iNeededMCU; // Maintain behaviour of CSequentialJpgReadCodec.
+
+ Mem::Copy(&iComponentCpy, &iComponent, sizeof(TDataUnit*) * KJpgNumberOfComponents);
+ PrepareToRenderMCU(iNeededMCU);
+ PostProcessMCUL(EFalse);
+ Mem::Copy(&iComponent, &iComponentCpy, sizeof(TDataUnit*) * KJpgNumberOfComponents);
+
+
+ iNeededMCU = iMCUStore->GetNextMCU();
+ }
+ }
+
+
+//
+// If the number of available MCUs for Cb and Cr is less than that of Y, those MCUs are filled with 0x7f.
+//
+void CMultiScanSequentialJpgReadCodec::FillEmptyMCUs()
+ {
+ // MCU index to start filling. This may involve overwriting existing U or V data.
+ TInt startIdx = Min(iCompMcuCount[KUComp], iCompMcuCount[KVComp]);
+ for (TInt i = KUComp; i <= KVComp; i++)
+ {
+ if (iComponent[i] == NULL)
+ {
+ // If monochrome or if the component is not part of image.
+ continue;
+ }
+
+ TDataUnit* des = NULL;
+ if (iCompAvailable[i])
+ {
+ des = iCompBuf[i];
+ des += startIdx * iMCUDataUnitCount[i];
+ TInt dus = (iCompMcuCount[KYComp] - startIdx) * iMCUDataUnitCount[i];
+ for (TInt k = 0; k < dus; k++)
+ {
+ FillEmptyDU(&des[k]);
+ }
+ }
+ else
+ {
+ des = iComponent[i];
+ // If component is not at all available, fill iComponent[] buffer with 0x7f.
+ for (TInt k = 0; k < iMCUDataUnitCount[i]; k++)
+ {
+ FillEmptyDU(&des[k]);
+ }
+ }
+ }
+ }
+
+
+//
+// Resets the data members so that new scan can be started
+//
+void CMultiScanSequentialJpgReadCodec::ResetOnNewScan()
+ {
+ iInitialDataPtr = NULL;
+ iBitBufferPtrLimit = NULL;
+ iDataValue = 0;
+ iInitialDataValue = 0;
+ iBitsLeft = 0;
+ iInitialBitsLeft = 0;
+
+ for (TInt i = 0; i < KJpgNumberOfComponents; i++)
+ {
+ iDCPredictor[i] = 0;
+ iInitialDCPredictor[i] = 0;
+ }
+ }
+
+
+//
+// Fills the given Data Unit with 0x7f.
+//
+void CMultiScanSequentialJpgReadCodec::FillEmptyDU(TDataUnit* pDU)
+ {
+ JPEG_ASSERT(pDU);
+ JPEG_ASSERT(pDU->iCoeff);
+
+ TDataUnit::TDataUnitElemType* ptr = pDU->iCoeff;
+ for(TInt i = 0; i < KJpgDCTBlockSize; i++)
+ {
+ *ptr++ = 0x7f;
+ }
+ }
+
+
+