diff -r 000000000000 -r 40261b775718 mmplugins/imagingplugins/codecs/JPEGCodec/jpgimageframeprocessor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmplugins/imagingplugins/codecs/JPEGCodec/jpgimageframeprocessor.cpp Tue Feb 02 01:56:55 2010 +0200 @@ -0,0 +1,1211 @@ +// Copyright (c) 2005-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: +// + +/** + @file + @internalTechnology +*/ + +#include "jpgimageframeprocessor.h" +#include +#include +#include "JpegConsts.h" +#include "JPEGCodec.h" + +const TInt KLayoutIndex0 = 0; +const TInt KLayoutIndex1 = 1; +const TInt KLayoutIndex2 = 2; +const TInt KErrValue = -1; + +// these are symmetrical values +const TInt KYUVMonoSampleRatio[] = {1, -1}; +const TInt KYUV422SampleRatio[] = {1, -1}; +const TInt KYUV420SampleRatio[] = {1, 2, 2, -1}; + +CImageFrame& CJpgImageFrameProcessor::ImageFrame() const + { + return *iFrame; + } + +void CJpgImageFrameProcessor::GetCurrentPosition(TPoint& aPosition) const + { + aPosition = iCurrentPosition; + } + +void CJpgImageFrameProcessor::SetCurrentPosition(const TPoint& aPosition) + { + iCurrentPosition = aPosition; + } + +CJpgImageFrameProcessor::CJpgImageFrameProcessor(CImageFrame& aFrame) : iFrame(&aFrame), + iIndexY(KErrValue), + iIndexU(KErrValue), + iIndexV(KErrValue) + { + } + +CJpgImageFrameProcessor::~CJpgImageFrameProcessor() + { + delete iLayout; + } + +void CJpgImageFrameProcessor::ConstructL(const TFrameLayoutBase& aLayout) + { + iLayout = static_cast(aLayout.DuplicateL()); + } + +TBool CJpgImageFrameProcessor::MoreData() + { + TSize remain = iMaxPlaneSize - iCurrentPosition; + return (remain.iHeight > 0 && remain.iWidth > 0); + } + +void CJpgImageFrameProcessor::CalculateCorrections() + { + iWCorrection = (iMaxPlaneSize.iWidth - (iCurrentPosition.iX + iBlockSize.iWidth)); + iHCorrection = (iMaxPlaneSize.iHeight - (iCurrentPosition.iY + iBlockSize.iHeight)); + + //compensations are negative values + if (iWCorrection > 0) + { + iWCorrection = 0; + } + + if (iHCorrection > 0) + { + iHCorrection = 0; + } + } + +void CJpgImageFrameProcessor::CalculateNextBlockStart(TInt aScanLine) + { + iCurrentPosition.iX += iBlockSize.iWidth; + if (iCurrentPosition.iX >= aScanLine) + { + // move to the next row + iCurrentPosition.iX = 0; + iCurrentPosition.iY += iBlockSize.iHeight; + } + } + +void CJpgImageFrameProcessor::ValidateCurrentBlockL() + { + const TFrameLayout& layout = static_cast(iFrame->FrameLayout()); + + // for all indexes check + for (TInt index = 0; index < layout.Planes(); index++) + { + // whether lengths are correct + if ((layout.Length(index) < layout.CurrentLength(index)) || (layout.CurrentLength(index) < 0) ) + { + User::Leave(KErrArgument); + } + + // then calculate offsets from start + TInt scanLength = layout.ScanLength(index); + TInt offset = iCurrentPosition.iY / iVertSampleRatio[index] * scanLength + + iCurrentPosition.iX / iHorzSampleRatio[index]; + // and check whether the starting point is within the proper range + if (offset < layout.Length(index)) + { + iOffsets[index] = offset; + } + else + { + // position is incorrect - exceeding the allocated memory size + User::Leave(KErrOverflow); + } + } + + CalculateCorrections(); + } + + +void CJpgImageFrameProcessor::DoWriteBlock(TUint8* aDest, const TDataUnit& aSrc, + TInt aScanline, TInt aXComp, TInt aYComp) + { + for (TInt m = 0, rowStart = 0, i = 0; i < KJpgDCTBlockWidth + aYComp; i++, rowStart += aScanline) + { + for (TInt j = 0; j < KJpgDCTBlockWidth + aXComp; j++, m++) + { + *(aDest+rowStart+j) = Clip(aSrc.iCoeff[m]); + } + m -= aXComp; //compensations are negative values + } + } + +void CJpgImageFrameProcessor::DoReadBlock(TDataUnit& aDest, const TUint8* aSrc, + TInt aScanline, TInt aXComp, TInt aYComp) + { + TInt rowStart = 0; + TInt i; + TInt j; + TInt m=0; + + for (i = 0; i < KJpgDCTBlockWidth + aYComp; i++, rowStart += aScanline) + { + for (j = 0; j < KJpgDCTBlockWidth + aXComp; j++, m++) + { + aDest.iCoeff[m]= *(aSrc+rowStart+j); + } + // need to repeat the last data if position falls outside of the image + if (aXComp) + { + TInt16 data = aDest.iCoeff[m-1]; + for ( TInt k = j; k < KJpgDCTBlockWidth; k++, m++) + { + aDest.iCoeff[m] = data; + } + } + } + + // if there a rows otside of the image, repeat the last row which falls in the image + if (aYComp) + { + for (TInt k = i; k < KJpgDCTBlockWidth; k++) + { + Mem::Copy(&aDest.iCoeff[(k) * KJpgDCTBlockWidth], + &aDest.iCoeff[(i-1) * KJpgDCTBlockWidth], + KJpgDCTBlockWidth * sizeof(TInt16)); + } + } + } + +/*********************************Monochrome*******************************************/ +CJpgImageFrameYUVMonoProcessor::CJpgImageFrameYUVMonoProcessor(CImageFrame& aFrame) : CJpgImageFrameProcessor(aFrame) + { + iIndexY = 0; + + iMaxHorzPlaneFactor = 1; + iMaxVertPlaneFactor = 1; + + iHorzSampleRatio = KYUVMonoSampleRatio; + iVertSampleRatio = KYUVMonoSampleRatio; + + iBlockSize.iWidth = KJpgDCTBlockWidth * iMaxHorzPlaneFactor; + iBlockSize.iHeight = KJpgDCTBlockWidth * iMaxVertPlaneFactor; + + iMaxPlaneSize = TSize(iMaxHorzPlaneFactor * iFrame->FrameSizeInPixels().iWidth, + iMaxVertPlaneFactor * iFrame->FrameSizeInPixels().iHeight); + } + +TDataUnit* CJpgImageFrameYUVMonoProcessor::ReadBlockL() + { + ValidateCurrentBlockL(); + + TUint8* p = &(iFrame->Data()[iLayout->Start(KLayoutIndex0) + iOffsets[KLayoutIndex0]]); + + // set the data to zero + Mem::FillZ(&iDataUnit, sizeof(TDataUnit)); + DoReadBlock(iDataUnit[0], p, iLayout->ScanLength(KLayoutIndex0), iWCorrection, iHCorrection); + + CalculateNextBlockStart(iLayout->ScanLength(KLayoutIndex0)); + return iDataUnit; + } + +void CJpgImageFrameYUVMonoProcessor::WriteBlockL(const RArray& aDataUnit) + { + ValidateCurrentBlockL(); + + TUint8* p = &(iFrame->Data()[iLayout->Start(KLayoutIndex0) + iOffsets[KLayoutIndex0]]); + + DoWriteBlock(p, *aDataUnit[0], iLayout->ScanLength(KLayoutIndex0), + iWCorrection, iHCorrection); + + UpdateCurrentLengthL(); + + CalculateNextBlockStart(iLayout->ScanLength(KLayoutIndex0)); + } + +void CJpgImageFrameYUVMonoProcessor::UpdateCurrentLengthL() + { + TInt currentLength = iOffsets[KLayoutIndex0] + (iBlockSize.iHeight + iHCorrection - 1) * iLayout->ScanLength(KLayoutIndex0) + iBlockSize.iWidth + iWCorrection; + iLayout->SetCurrentLength(KLayoutIndex0, currentLength); + iFrame->SetFrameLayoutL(*iLayout); + } + +/********************************CJpgImageFrameYUV422InterleavedProcessor********************/ +CJpgImageFrameYUV422InterleavedProcessor::CJpgImageFrameYUV422InterleavedProcessor(CImageFrame& aFrame) : CJpgImageFrameProcessor(aFrame) + { + iIndexY = 0; + iIndexU = 2; + iIndexV = 3; + + iHorzSampleRatio = KYUV422SampleRatio; + iVertSampleRatio = KYUV422SampleRatio; + + iMaxHorzPlaneFactor = 2; + iMaxVertPlaneFactor = 1; + + iBlockSize.iWidth = KJpgDCTBlockWidth * iMaxHorzPlaneFactor * 2; + iBlockSize.iHeight = KJpgDCTBlockWidth * iMaxVertPlaneFactor; + + iMaxPlaneSize = TSize(iMaxHorzPlaneFactor * iFrame->FrameSizeInPixels().iWidth, + iMaxVertPlaneFactor * iFrame->FrameSizeInPixels().iHeight); + } + +TDataUnit* CJpgImageFrameYUV422InterleavedProcessor::ReadBlockL() + { + ValidateCurrentBlockL(); + + TUint8* p = &(iFrame->Data()[iLayout->Start(KLayoutIndex0) + iOffsets[KLayoutIndex0]]); + + // set the data to zero + Mem::FillZ(&iDataUnit, KUnitsYCbCr422 * sizeof(TDataUnit)); + + TInt rowStart = 0; + TInt i; + TInt j; + TInt m=0; + TInt l=0; + + for (i = 0; i < iBlockSize.iHeight + iHCorrection; i++, rowStart += iLayout->ScanLength(KLayoutIndex0)) + { + iIndexY = 0; + l = KJpgDCTBlockWidth * i; + + for (j = 0; j < iBlockSize.iWidth + iWCorrection; j+=4, m++, l+=2) + { + if (j == KJpgDCTBlockWidth * 2) + { + iIndexY = 1; + l = KJpgDCTBlockWidth * i; + } + // U plane + iDataUnit[iIndexU].iCoeff[m]= *(p+rowStart+j); + // Y plane + iDataUnit[iIndexY].iCoeff[l]= *(p+rowStart+j+1); + // V plane + iDataUnit[iIndexV].iCoeff[m]= *(p+rowStart+j+2); + // Y1 plane + iDataUnit[iIndexY].iCoeff[l+1]= *(p+rowStart+j+3); + } + + if (iWCorrection) + { + TInt16 data = iDataUnit[iIndexY].iCoeff[l - 1]; + for (TInt k = j; k < iBlockSize.iWidth; k += 4, m++, l += 2) + { + if (k == KJpgDCTBlockWidth * 2) + { + iIndexY = 1; + l = KJpgDCTBlockWidth * i; + } + // U plane + iDataUnit[iIndexU].iCoeff[m] = iDataUnit[iIndexU].iCoeff[m-1]; + // Y plane + iDataUnit[iIndexY].iCoeff[l] = data; + // V plane + iDataUnit[iIndexV].iCoeff[m] = iDataUnit[iIndexV].iCoeff[m-1]; + // Y1 plane + iDataUnit[iIndexY].iCoeff[l+1] = data; + } + } + } + + if (iHCorrection) + { + for (TInt k = i; k < iBlockSize.iHeight; k++) + { + for (TInt block = 0; block < KUnitsYCbCr422; block ++ ) + { + Mem::Copy(&iDataUnit[block].iCoeff[(k) * KJpgDCTBlockWidth], + &iDataUnit[block].iCoeff[(k-1) * KJpgDCTBlockWidth], + KJpgDCTBlockWidth * sizeof(TInt16)); + } + } + } + + CalculateNextBlockStart(iLayout->ScanLength(KLayoutIndex0)); + return iDataUnit; + } + +void CJpgImageFrameYUV422InterleavedProcessor::WriteBlockL(const RArray& aDataUnit) + { + ValidateCurrentBlockL(); + + TUint8* p = &(iFrame->Data()[iLayout->Start(KLayoutIndex0) + iOffsets[KLayoutIndex0]]); + + TInt rowStart = 0; + + TInt i; + TInt j; + TInt m = 0; + TInt l = 0; + + for (i = 0; i < iBlockSize.iHeight + iHCorrection; i++, rowStart += iLayout->ScanLength(KLayoutIndex0)) + { + iIndexY = 0; + l = KJpgDCTBlockWidth * i; + + for (j = 0; j < iBlockSize.iWidth + iWCorrection; j += 4, m++, l += 2) + { + // switch between Y blocks using the current index as a guidance + if (j == KJpgDCTBlockWidth * 2) + { + iIndexY = 1; + l = KJpgDCTBlockWidth * i; + } + + // U plane + *(p+rowStart+j) = Clip(aDataUnit[iIndexU]->iCoeff[m]); + // Y plane + *(p+rowStart+j+1) = Clip(aDataUnit[iIndexY]->iCoeff[l]); + // V plane + *(p+rowStart+j+2) = Clip(aDataUnit[iIndexV]->iCoeff[m]); + // Y1 plane + *(p+rowStart+j+3) = Clip(aDataUnit[iIndexY]->iCoeff[l+1]); + } + m = l = KJpgDCTBlockWidth * (i + 1); + } + + UpdateCurrentLengthL(); + + // where is the update for the current frame + CalculateNextBlockStart(iLayout->ScanLength(KLayoutIndex0)); + } + +void CJpgImageFrameYUV422InterleavedProcessor::UpdateCurrentLengthL() + { + TInt currentLength = iOffsets[KLayoutIndex0] + (iBlockSize.iHeight + iHCorrection - 1) * iLayout->ScanLength(KLayoutIndex0) + iBlockSize.iWidth + iWCorrection; + iLayout->SetCurrentLength(KLayoutIndex0, currentLength); + iFrame->SetFrameLayoutL(*iLayout); + } + +/***********************CJpgImageFrameYUV420PlanarProcessor********************/ +CJpgImageFrameYUV420PlanarProcessor::CJpgImageFrameYUV420PlanarProcessor(CImageFrame& aFrame) : CJpgImageFrameProcessor(aFrame) + { + iIndexY = 0; + if (static_cast(aFrame.FrameFormat()).FormatCode() == KUidFormatYUV420Planar) + { + iIndexU = 4; + iIndexV = 5; + } + else + { + iIndexU = 5; + iIndexV = 4; + } + + iMaxHorzPlaneFactor = 1; + iMaxVertPlaneFactor = 1; + + iHorzSampleRatio = KYUV420SampleRatio; + iVertSampleRatio = KYUV420SampleRatio; + + iBlockSize.iWidth = KJpgDCTBlockWidth * 2; + iBlockSize.iHeight = KJpgDCTBlockWidth * 2; + + iMaxPlaneSize = TSize(iMaxHorzPlaneFactor * iFrame->FrameSizeInPixels().iWidth, iMaxVertPlaneFactor * iFrame->FrameSizeInPixels().iHeight); + } + +TDataUnit* CJpgImageFrameYUV420PlanarProcessor::ReadBlockL() + { + ValidateCurrentBlockL(); + + // set the data to zero + Mem::FillZ(&iDataUnit[0], KUnitsYCbCr420 * sizeof(TDataUnit)); + + TUint8* p = &(iFrame->Data()[iLayout->Start(KLayoutIndex0) + iOffsets[KLayoutIndex0]]); + + // Y plane + TInt i; + TInt j; + TInt l=0; + TInt rowStart = 0; + + // initially copy the the data from the image + // however when MCU is partially outside the image the remaining elements are filled in + // using replication, along rows and copying rows afterwards + for (i = 0; i < iBlockSize.iHeight + iHCorrection; i++, rowStart += iLayout->ScanLength(KLayoutIndex0)) + { + iIndexY = (i < KJpgDCTBlockWidth)? 0: 2; + l = (iIndexY == 0) ? (i * KJpgDCTBlockWidth):(i * KJpgDCTBlockWidth - KJpgDCTBlockSize); + + for (j = 0; j < iBlockSize.iWidth + iWCorrection; j++, l++) + { + if (j == KJpgDCTBlockWidth) + { + l = (iIndexY == 0)?(i * KJpgDCTBlockWidth) : (i * KJpgDCTBlockWidth - KJpgDCTBlockSize); + iIndexY++; + } + + iDataUnit[iIndexY].iCoeff[l] = *(p+rowStart+j); + } + + // need to repeat the last data + if (iWCorrection) + { + TInt16 data = iDataUnit[iIndexY].iCoeff[l - 1]; + + for ( TInt k = j; k < iBlockSize.iWidth; k++, l++) + { + if (k == KJpgDCTBlockWidth) + { + l = (iIndexY == 0)?(i * KJpgDCTBlockWidth):(i * KJpgDCTBlockWidth - KJpgDCTBlockSize); + iIndexY++; + } + iDataUnit[iIndexY].iCoeff[l] = data; + } + } + } + + // if there are rows outside the image, use the last row to fill them in + if (iHCorrection) + { + TInt iIndexYO = 0; + iIndexY = 0; + TInt line; + TInt origline = iBlockSize.iHeight + iHCorrection - 1; + + if (origline >= KJpgDCTBlockWidth) + { + iIndexYO = 2; + origline -= KJpgDCTBlockWidth; + } + for (TInt k = iBlockSize.iHeight + iHCorrection; k < iBlockSize.iHeight; k++) + { + line = k; + if (k>=KJpgDCTBlockWidth) + { + iIndexY = 2; + line = k - KJpgDCTBlockWidth; + } + Mem::Copy(&iDataUnit[iIndexY].iCoeff[(line) * KJpgDCTBlockWidth], + &iDataUnit[iIndexYO].iCoeff[origline * KJpgDCTBlockWidth], + KJpgDCTBlockWidth * sizeof(TInt16)); + + Mem::Copy(&iDataUnit[iIndexY+1].iCoeff[(line) * KJpgDCTBlockWidth], + &iDataUnit[iIndexYO+1].iCoeff[origline * KJpgDCTBlockWidth], + KJpgDCTBlockWidth * sizeof(TInt16)); + } + } + + // U plane + p = &(iFrame->Data()[iLayout->Start(KLayoutIndex1) + iOffsets[KLayoutIndex1]]); + DoReadBlock(iDataUnit[iIndexU], p, iLayout->ScanLength(KLayoutIndex1), + iWCorrection / iHorzSampleRatio[KLayoutIndex1], + iHCorrection / iVertSampleRatio[KLayoutIndex1]); + + // V plane + p = &(iFrame->Data()[iLayout->Start(KLayoutIndex2) + iOffsets[KLayoutIndex2]]); + DoReadBlock(iDataUnit[iIndexV], p, iLayout->ScanLength(KLayoutIndex2), + iWCorrection / iHorzSampleRatio[KLayoutIndex2], + iHCorrection / iVertSampleRatio[KLayoutIndex2]); + + CalculateNextBlockStart(iLayout->ScanLength(KLayoutIndex0)); + + return iDataUnit; + } + +void CJpgImageFrameYUV420PlanarProcessor::WriteBlockL(const RArray& aDataUnit) + { + ValidateCurrentBlockL(); + + TUint8* p = &(iFrame->Data()[iLayout->Start(KLayoutIndex0) + iOffsets[KLayoutIndex0]]); + + TInt l=0; + TInt rowStart = 0; + + // Y plane - 4 blocks - iIndexY is used to select the correct data unit depending on the position + // they are organised 0, 1 + // 2, 3 + for (TInt i = 0; i < iBlockSize.iHeight + iHCorrection; i++, rowStart += iLayout->ScanLength(KLayoutIndex0)) + { + iIndexY = (i < KJpgDCTBlockWidth) ? 0: 2; + l = (iIndexY == 0) ? (i * KJpgDCTBlockWidth) : (i * KJpgDCTBlockWidth - KJpgDCTBlockSize); + + for (TInt j = 0; j < iBlockSize.iWidth + iWCorrection; j++, l++) + { + if (j == KJpgDCTBlockWidth) + { + l = (iIndexY == 0) ? (i * KJpgDCTBlockWidth) : (i * KJpgDCTBlockWidth - KJpgDCTBlockSize); + iIndexY++; + } + *(p+rowStart+j) = Clip(aDataUnit[iIndexY]->iCoeff[l]); + } + } + + // U plane + p = &(iFrame->Data()[iLayout->Start(KLayoutIndex1) + iOffsets[KLayoutIndex1]]); + DoWriteBlock(p, *aDataUnit[iIndexU], iLayout->ScanLength(KLayoutIndex1), + iWCorrection / iHorzSampleRatio[KLayoutIndex1], + iHCorrection / iVertSampleRatio[KLayoutIndex1]); + // V plane + p = &(iFrame->Data()[iLayout->Start(KLayoutIndex2) + iOffsets[KLayoutIndex2]]); + DoWriteBlock(p, *aDataUnit[iIndexV], iLayout->ScanLength(KLayoutIndex2), + iWCorrection / iHorzSampleRatio[KLayoutIndex2], + iHCorrection / iVertSampleRatio[KLayoutIndex2]); + + UpdateCurrentLengthL(); + CalculateNextBlockStart(iLayout->ScanLength(KLayoutIndex0)); + } + +void CJpgImageFrameYUV420PlanarProcessor::UpdateCurrentLengthL() + { + TInt currentLength; + currentLength = iOffsets[KLayoutIndex0] + (iBlockSize.iHeight + iHCorrection - 1) * iLayout->ScanLength(KLayoutIndex0) + iBlockSize.iWidth + iWCorrection; + iLayout->SetCurrentLength(KLayoutIndex0, currentLength); + + currentLength = (iOffsets[KLayoutIndex1] + (KJpgDCTBlockWidth + iHCorrection/2 - 1) * iLayout->ScanLength(KLayoutIndex1) + KJpgDCTBlockWidth + iWCorrection/2)/2; + iLayout->SetCurrentLength(KLayoutIndex1, currentLength); + + currentLength = (iOffsets[KLayoutIndex2] + (KJpgDCTBlockWidth + iHCorrection/2 - 1) * iLayout->ScanLength(KLayoutIndex2) + KJpgDCTBlockWidth + iWCorrection/2)/2; + iLayout->SetCurrentLength(KLayoutIndex2, currentLength); + + iFrame->SetFrameLayoutL(*iLayout); + } + +// +// all the code down there is not performance-critical so use thumb +// instruction set to save on some ROM footprint +// +#if defined(__ARMCC__) +#pragma thumb +#endif + +/****************************CJpgImageFrameProcessorUtility*********************/ +CJpgImageFrameProcessor* CJpgImageFrameProcessorUtility::NewL(CImageFrame& aFrame) + { + // verify that the correct format and layout objects are used. + if (!CJpgImageFrameProcessorUtility::IsRecognisedFormatType(aFrame)) + { + User::Leave(KErrNotSupported); + } + + if (!CJpgImageFrameProcessorUtility::IsRecognisedLayoutType(aFrame)) + { + User::Leave(KErrNotSupported); + } + + CJpgImageFrameProcessor *processor = NULL; + const TFrameFormat& format = static_cast(aFrame.FrameFormat()); + + // this frame processor supports only YCbCr or YUV colour spaces + if ((format.ColourSpace() != KUidColourSpaceYCbCr) && + (format.ColourSpace() != KUidColourSpaceYUV)) + { + User::Leave(KErrNotSupported); + } + + switch(format.FormatCode().iUid) + { + case KFormatYUVMonochromeUidValue: + { + CJpgImageFrameYUVMonoProcessor* self = new(ELeave) CJpgImageFrameYUVMonoProcessor(aFrame); + CleanupStack::PushL(self); + self->ConstructL(aFrame.FrameLayout()); + CleanupStack::Pop(); + processor = self; + break; + } + + case KFormatYUV422InterleavedUidValue: + { + CJpgImageFrameYUV422InterleavedProcessor* self = new(ELeave) CJpgImageFrameYUV422InterleavedProcessor(aFrame); + CleanupStack::PushL(self); + self->ConstructL(aFrame.FrameLayout()); + CleanupStack::Pop(); + processor = self; + break; + } + // the two formats differ just in the ordering of the UV planes + // one is UV and the other is VU. + // so just use one processor. + case KFormatYUV420PlanarReversedUidValue: + case KFormatYUV420PlanarUidValue: + { + CJpgImageFrameYUV420PlanarProcessor* self = new(ELeave) CJpgImageFrameYUV420PlanarProcessor(aFrame); + CleanupStack::PushL(self); + self->ConstructL(aFrame.FrameLayout()); + CleanupStack::Pop(); + + processor = self; + break; + } + + default: + User::Leave(KErrNotSupported); + break; + } + + return processor; + } + +void CJpgImageFrameProcessorUtility::PrepareImageFrameL(const TJpgFrameInfo& aFrameInfo, CImageFrame& aFrame) + { + // This routine checks the validity of the destination image frame and writes: + // - the frame format based on the jpg frame info obtained from the jpeg header information. + // - the frame layout based on the capabilities of this codec. + + // First, validate image frame + CJpgImageFrameProcessorUtility::ValidateImageFrameL(aFrame, EFalse); + + + TInt dataUnitCount; + + 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; + } + } + + const TFrameFormat& format = static_cast(aFrame.FrameFormat()); + + TUid imageFrameSampleScheme = format.Sampling(); + + // Check that there is enough memory allocated before starting decode: + TInt maxBufferSize = aFrame.MaxBufferSize(); + + switch (dataUnitCount) + { + + case 1: // Monochrome + { + if (imageFrameSampleScheme != KUidSamplingMonochrome ) + { + if (imageFrameSampleScheme == KNullUid) + { + // this monochrome specific format + TFrameFormat monochromeFrameFormat = TFrameFormat(KUidFormatYUVMonochrome); + aFrame.SetFrameFormatL(monochromeFrameFormat); + } + else + { + // error transcoding not supported + User::Leave(KErrNotSupported); + } + } + + // this monochrome specific layout + TInt bufferSize = aFrame.FrameSizeInPixels().iWidth * + aFrame.FrameSizeInPixels().iHeight; + if (bufferSize > maxBufferSize) + { + User::Leave(KErrOverflow); + } + + TInt scanlineLength = aFrame.FrameSizeInPixels().iWidth; + TInt numberOfPlanes = 1; + + TFrameLayout monochromeFrameLayout = TFrameLayout(numberOfPlanes); + monochromeFrameLayout.SetStart(0, 0); + monochromeFrameLayout.SetLength(0, bufferSize); + monochromeFrameLayout.SetCurrentLength(0, 0); + monochromeFrameLayout.SetScanLength(0, scanlineLength); + + aFrame.SetFrameLayoutL(monochromeFrameLayout); + + break; + } + + case 3: // 4:4:4 + { + if (imageFrameSampleScheme != KUidSamplingColor444 ) + { + // This format is not supported by this codec + User::Leave(KErrNotSupported); + } + break; + } + + case 4: // 4:2:2 + { + if (imageFrameSampleScheme != KUidSamplingColor422 ) + { + if (imageFrameSampleScheme == KNullUid) + { + // this specific 4:2:2 format + TFrameFormat this422FrameFormat = TFrameFormat(KUidFormatYUV422Interleaved); + aFrame.SetFrameFormatL(this422FrameFormat); + } + else + { + // error transcoding not supported + User::Leave(KErrNotSupported); + } + } + + // this is specific for 4:2:2 interleaved layout - width have to be a multiple of 4 + TInt width = (aFrame.FrameSizeInPixels().iWidth + 3) & (KMaxTInt - 3); + TInt height = aFrame.FrameSizeInPixels().iHeight; + TInt bufferSize = width * height * 2; + if (bufferSize > maxBufferSize) + { + User::Leave(KErrOverflow); + } + TInt scanlineLength = width * 2; + TInt numberOfPlanes = 1; + + TFrameLayout this422FrameLayout = TFrameLayout(numberOfPlanes); + this422FrameLayout.SetStart(0, 0); + this422FrameLayout.SetLength(0, bufferSize); + this422FrameLayout.SetCurrentLength(0, 0); + this422FrameLayout.SetScanLength(0, scanlineLength); + + aFrame.SetFrameLayoutL(this422FrameLayout); + + break; + } + + case 6: // 4:2:0 + { + if (imageFrameSampleScheme != KUidSamplingColor420) + { + if (imageFrameSampleScheme == KNullUid) + { + // this specific 4:2:0 format + TFrameFormat this420FrameFormat = TFrameFormat(KUidFormatYUV420Planar); + aFrame.SetFrameFormatL(this420FrameFormat); + } + else + { + // error transcoding not supported + User::Leave(KErrNotSupported); + } + } + + // this specific 4:2:0 layout + TInt bufferSizeY = aFrame.FrameSizeInPixels().iWidth * + aFrame.FrameSizeInPixels().iHeight; + + + // Round width and height of UV planes to nearest multiple of 2 + // to avoid calculating incorrect buffer size + TInt widthUV = (aFrame.FrameSizeInPixels().iWidth + 1) & (KMaxTInt - 1); + TInt heightUV = (aFrame.FrameSizeInPixels().iHeight + 1) & (KMaxTInt - 1); + + TInt bufferSizeUV = (widthUV * heightUV) / 4; + + TInt bufferSize = bufferSizeY + 2 * bufferSizeUV; + if (bufferSize > maxBufferSize) + { + User::Leave(KErrOverflow); + } + + TInt scanlineLengthY = aFrame.FrameSizeInPixels().iWidth; + TInt scanlineLengthUV = aFrame.FrameSizeInPixels().iWidth / 2; + TInt numberOfPlanes = 3; + + TFrameLayout this420FrameLayout = TFrameLayout(numberOfPlanes); + this420FrameLayout.SetStart(0, 0); + this420FrameLayout.SetLength(0, bufferSizeY); + this420FrameLayout.SetCurrentLength(0, 0); + this420FrameLayout.SetScanLength(0, scanlineLengthY); + this420FrameLayout.SetStart(1, bufferSizeY); + this420FrameLayout.SetLength(1, bufferSizeUV); + this420FrameLayout.SetCurrentLength(1, 0); + this420FrameLayout.SetScanLength(1, scanlineLengthUV); + this420FrameLayout.SetStart(2, bufferSizeY + bufferSizeUV); + this420FrameLayout.SetLength(2, bufferSizeUV); + this420FrameLayout.SetCurrentLength(2, 0); + this420FrameLayout.SetScanLength(2, scanlineLengthUV); + + aFrame.SetFrameLayoutL(this420FrameLayout); + break; + } + + default: + { + User::Leave(KErrNotSupported); + break; + } + } + + } + +void CJpgImageFrameProcessorUtility::ValidateImageFrameL(CImageFrame& aFrame, TBool aFullFrame) + { + // This routine validates the image frame. It leaves if inconsistencies are found. + + // Encoder expects a CImageFrame object with the reference to the memory + // (RChunk/Descriptor) containing the data plus a full description of the data, + // whereas the decoder may have instantiated the CImageFrame object with just + // the reference to the RChunk/Descriptor (without data). + // - aFullFrame = True means validation of the source frame (encoder). + // - aFullFrame = False means validation of the destination frame (decoder). + if (!CJpgImageFrameProcessorUtility::IsRecognisedFormatType(aFrame)) + { + User::Leave(KErrNotSupported); + } + if (!CJpgImageFrameProcessorUtility::IsRecognisedLayoutType(aFrame)) + { + User::Leave(KErrNotSupported); + } + const TFrameFormat& format = static_cast(aFrame.FrameFormat()); + + TUid imageFrameSampleScheme = format.Sampling(); + TUid imageFrameColourSpace = format.ColourSpace(); + TUid formatCode = format.FormatCode(); + + // The format code in an image frame defines unequivocably the data layout and sampling format. + // Check for inconsistencies and fill in the rest of the image frame fields if necessary + switch(formatCode.iUid) + { + case KFormatYUVMonochromeUidValue: + { + if (((imageFrameSampleScheme == KUidSamplingMonochrome) || + (imageFrameSampleScheme == KNullUid)) + && + ((imageFrameColourSpace == KUidColourSpaceYCbCr) || + (imageFrameColourSpace == KNullUid))) + { + TFrameFormat thismonochromeFrameFormat = TFrameFormat(KUidFormatYUVMonochrome); + aFrame.SetFrameFormatL(thismonochromeFrameFormat); + } + else + { + User::Leave(KErrNotSupported); + } + break; + } + + case KFormatYUV422InterleavedUidValue: + { + if (((imageFrameSampleScheme == KUidSamplingColor422) || + (imageFrameSampleScheme == KNullUid)) + && + ((imageFrameColourSpace == KUidColourSpaceYCbCr) || + (imageFrameColourSpace == KNullUid))) + { + TFrameFormat this422FrameFormat = TFrameFormat(KUidFormatYUV422Interleaved); + aFrame.SetFrameFormatL(this422FrameFormat); + } + else + { + User::Leave(KErrNotSupported); + } + break; + } + + case KFormatYUV420PlanarReversedUidValue: + case KFormatYUV420PlanarUidValue: + { + if (((imageFrameSampleScheme == KUidSamplingColor420) || + (imageFrameSampleScheme == KNullUid)) + && + ((imageFrameColourSpace == KUidColourSpaceYCbCr) || + (imageFrameColourSpace == KNullUid))) + + { + if (formatCode == KUidFormatYUV420PlanarReversed) + { + TFrameFormat this420FrameFormat = TFrameFormat(KUidFormatYUV420PlanarReversed); + aFrame.SetFrameFormatL(this420FrameFormat); + } + else if (formatCode == KUidFormatYUV420Planar) + { + TFrameFormat this420FrameFormat = TFrameFormat(KUidFormatYUV420Planar); + aFrame.SetFrameFormatL(this420FrameFormat); + } + else + { + User::Leave(KErrNotSupported); + } + } + break; + } + + case KNullUidValue: + { + // This is the case where the application passes an empty imageFrame + // object to the decoder (no data, no description of frame, just the + // reference to an empty RChunk/Descriptor) + if (aFullFrame != EFalse) + { + User::Leave(KErrNotSupported); + } + break; + } + + default: + { + User::Leave(KErrNotSupported); + break; + } + } + } + +void CJpgImageFrameProcessorUtility::ValidateFrameImageDataL(const TJpegImageData::TColorSampling aSampleScheme, CImageFrame& aSource) + { + // Optional Frame Image data provided when calling ConvertFrame() needs to be validated + // against the source format since transcoding is not supported by this codec. + const TFrameFormat& format = static_cast(aSource.FrameFormat()); + TUid imageFrameSampleScheme = format.Sampling(); + + switch (aSampleScheme) + { + case TJpegImageData::EMonochrome: + { + if (imageFrameSampleScheme != KUidSamplingMonochrome) + { + User::Leave(KErrNotSupported); + } + break; + } + + case TJpegImageData::EColor420: + { + if (imageFrameSampleScheme != KUidSamplingColor420) + { + User::Leave(KErrNotSupported); + } + break; + } + + case TJpegImageData::EColor422: + { + if (imageFrameSampleScheme != KUidSamplingColor422) + { + User::Leave(KErrNotSupported); + } + break; + } + + case TJpegImageData::EColor444: + { + // YUV 4:4:4 sampling scheme is not supported by this codec + User::Leave(KErrNotSupported); + break; + } + + default: + { + User::Leave(KErrNotSupported); + break; + } + } + } +//Get the size of the memory buffer to hold the returned data. calculates teh buffersize based on the format and numbe of blocks +TInt CJpgImageFrameProcessorUtility::RecommendedStreamBufferSizeL(const TJpgFrameInfo& aFrameInfo, TUid aFormatCode, TSize& aBlockSizeInPixels, TInt aNumBlocks) + { + TInt dataUnitCount = 0; + TInt bufferSize = 0; + + if (aFrameInfo.iNumberOfComponents == 1) + { + dataUnitCount = 1; + } + else + { + 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) + { + aBlockSizeInPixels = TSize((aNumBlocks * KSamplingMonoMCUWidthInPixels), (KSamplingMonoMCUHeightInPixels)); + bufferSize = aBlockSizeInPixels.iWidth * aBlockSizeInPixels.iHeight; + } + else + { + // error transcoding not supported + User::Leave(KErrNotSupported); + } + break; + } + + case 4: // 4:2:2 + { + if (aFormatCode == KUidFormatYUV422Interleaved) + { + aBlockSizeInPixels = TSize((aNumBlocks * KSampling422MCUWidthInPixels), (KSampling422MCUHeightInPixels)); + // this is specific for 4:2:2 interleaved layout - width have to be a multiple of 4 + TInt width = (aBlockSizeInPixels.iWidth + 3) & (KMaxTInt - 3); + TInt height = aBlockSizeInPixels.iHeight; + bufferSize = width * height * 2; //in 422 sampling scheme width is twice the height + } + else + { + // error transcoding not supported + User::Leave(KErrNotSupported); + } + break; + } + + case 6: // 4:2:0 + { + if ((aFormatCode == KUidFormatYUV420Planar) || + (aFormatCode == KUidFormatYUV420PlanarReversed)) + { + aBlockSizeInPixels = TSize((aNumBlocks * KSampling420MCUWidthInPixels), (KSampling420MCUHeightInPixels)); + // this specific 4:2:0 layout + TInt bufferSizeY = aBlockSizeInPixels.iWidth * + aBlockSizeInPixels.iHeight; + + // Round width and height of UV planes to nearest multiple of 2 + // to avoid calculating incorrect recommended buffer size + TInt widthUV = (aBlockSizeInPixels.iWidth + 1) & (KMaxTInt - 1); + TInt heightUV = (aBlockSizeInPixels.iHeight + 1) & (KMaxTInt - 1); + + TInt bufferSizeUV = (widthUV * heightUV) / 4; + + bufferSize = bufferSizeY + 2 * bufferSizeUV + 15; + } + else + { + // error transcoding not supported + User::Leave(KErrNotSupported); + } + break; + } + + default: + { + User::Leave(KErrNotSupported); + break; + } + } + + return bufferSize; + } + +TInt CJpgImageFrameProcessorUtility::RecommendedBufferSizeL(const TJpgFrameInfo& aFrameInfo, TUid formatCode) + { + TInt dataUnitCount=0; + TInt bufferSize = 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 ((formatCode == KUidFormatYUVMonochrome) || + (formatCode == KNullUid)) + { + bufferSize = aFrameInfo.iSizeInPixels.iWidth * aFrameInfo.iSizeInPixels.iHeight; + } + else + { + // error transcoding not supported + User::Leave(KErrNotSupported); + } + break; + } + + case 4: // 4:2:2 + { + if ((formatCode == KUidFormatYUV422Interleaved) || + (formatCode == KNullUid)) + { + // this is specific for 4:2:2 interleaved layout - width have to be a multiple of 4 + TInt width = (aFrameInfo.iSizeInPixels.iWidth + 3) & (KMaxTInt - 3); + TInt height = aFrameInfo.iSizeInPixels.iHeight; + bufferSize = width * height * 2; + } + else + { + // error transcoding not supported + User::Leave(KErrNotSupported); + } + break; + } + + case 6: // 4:2:0 + { + if ((formatCode == KUidFormatYUV420Planar) || + (formatCode == KUidFormatYUV420PlanarReversed) || + (formatCode == KNullUid)) + { + // this specific 4:2:0 layout + TInt bufferSizeY = aFrameInfo.iSizeInPixels.iWidth * + aFrameInfo.iSizeInPixels.iHeight; + + // Round width and height of UV planes to nearest multiple of 2 + // to avoid calculating incorrect recommended buffer size + TInt widthUV = (aFrameInfo.iSizeInPixels.iWidth + 1) & (KMaxTInt - 1); + TInt heightUV = (aFrameInfo.iSizeInPixels.iHeight + 1) & (KMaxTInt - 1); + + TInt bufferSizeUV = (widthUV * heightUV) / 4; + + bufferSize = bufferSizeY + 2 * bufferSizeUV + 15; + } + else + { + // error transcoding not supported + User::Leave(KErrNotSupported); + } + break; + } + + default: + { + User::Leave(KErrNotSupported); + break; + } + } + + return bufferSize; + } + +TBool CJpgImageFrameProcessorUtility::IsRecognisedFormatType(CImageFrame& aFrame) + { + const TFrameFormat& format = static_cast(aFrame.FrameFormat()); + + if (&format != NULL) + { + return format.Type() == KUidIclImageFrameFormat; + } + else + { + return EFalse; + } + } + +TBool CJpgImageFrameProcessorUtility::IsRecognisedLayoutType(CImageFrame& aFrame) + { + const TFrameLayout& layout = static_cast(aFrame.FrameLayout()); + if (&layout != NULL) + { + return layout.Type()==KUidIclImageFrameLayout; + } + else + { + return EFalse; + } + } + + +