diff -r 000000000000 -r 469c91dae73b imagingmodules/jp2kcodec/Src/JP2KImageWriter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imagingmodules/jp2kcodec/Src/JP2KImageWriter.cpp Thu Dec 17 09:22:31 2009 +0200 @@ -0,0 +1,2187 @@ +/* +* Copyright (c) 2003, 2004 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: CJ2kImageWriter class used to perform inverse transformation and +* writing decoded image data to bitmap. +* +*/ + + +// INCLUDE FILES +#include +#include +#include "JP2KImageUtils.h" +#include "JP2KFormat.h" +#include "JP2KTileInfo.h" +#include "JP2KImageInfo.h" +#include "JP2KSubband.h" +#include "JP2KComponentInfo.h" +#include "JP2KImageWriter.h" + +// EXTERNAL DATA STRUCTURES + +// EXTERNAL FUNCTION PROTOTYPES + +// CONSTANTS + +// MACROS +#define INT2BYTE( n ) ( ( n ) < 0 ? (TUint8)0 : ( ( n ) > 255 ? (TUint8)255 : (TUint8)( n ) ) ) +#define CLIPINT( n,bitdepth ) ( ( n >= ( 1 << bitdepth ) ) ? ( ( 1< maxValue ) ? ( maxValue ) : ( n < 0 ) ? 0 : n ) +#define CLIP2RANGE( n, minValue, maxValue ) ( ( n > maxValue ) ? ( maxValue ) : ( n < minValue ) ? minValue : n ) + +// LOCAL CONSTANTS AND MACROS + +// MODULE DATA STRUCTURES + +// LOCAL FUNCTION PROTOTYPES + +// FORWARD DECLARATIONS + +// LOCAL CONSTANTS AND MACROS + + + +// ============================ MEMBER FUNCTIONS =============================== + +// Destructor +CJ2kWriterComponentInfo::~CJ2kWriterComponentInfo() + { + iTileStartList.Close(); + FreeData(); + } + +// ----------------------------------------------------------------------------- +// CJ2kWriterComponentInfo::AllocDataL +// Allocate 2-D data array with a size +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kWriterComponentInfo::AllocDataL( const TSize& aSize ) + { + FreeData(); + iData = TJ2kUtils::Alloc2DArrayL( aSize.iHeight, aSize.iWidth ); + } + +// ----------------------------------------------------------------------------- +// CJ2kWriterComponentInfo::FreeData +// Free the 2-D data array +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kWriterComponentInfo::FreeData() + { + if ( iData ) + { + TJ2kUtils::Free2DArray( iData ); + iData = 0; + } + } + +// ----------------------------------------------------------------------------- +// CJ2kWriterComponentInfo::Data +// Get the 2-D data array +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TPrecInt** CJ2kWriterComponentInfo::Data() + { + return iData; + } + +// ----------------------------------------------------------------------------- +// CJ2kWriterComponentInfo::TileStartAt +// Get the starting point of a tile +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TPoint& CJ2kWriterComponentInfo::TileStartAt( TUint16 aTileIndex ) + { + return iTileStartList[aTileIndex]; + } + +// ----------------------------------------------------------------------------- +// CJ2kWriterComponentInfo::UpdateNextTileStartAt +// Update the starting point of next tile +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kWriterComponentInfo::UpdateNextTileStartAt( TUint16 aTileIndex, + const TSize& aSize, + CJ2kImageInfo& aImageInfo ) + { + TUint16 numOfHorizTiles = aImageInfo.NumOfHorizTiles(); + TUint16 numOfVertTiles = aImageInfo.NumOfVertTiles(); + + // Calculate the p and q of a tile + TDiv tDiv = TJ2kUtils::Div( aTileIndex, numOfHorizTiles ); + if ( tDiv.rem != ( numOfHorizTiles - 1 ) ) + { + iTileStartList[aTileIndex + 1].iX = iTileStartList[aTileIndex].iX + aSize.iWidth; + } + + if ( tDiv.quot != ( numOfVertTiles - 1 ) ) + { + iTileStartList[aTileIndex + numOfHorizTiles].iY = iTileStartList[aTileIndex].iY + aSize.iHeight; + } + } + + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CJ2kImageWriter* CJ2kImageWriter::NewL( CImageProcessor* aImageProcessor, + CJ2kImageInfo& aImageInfo, + TJ2kInfo& aJ2kInfo ) + { + CJ2kImageWriter *self = new ( ELeave ) CJ2kImageWriter( aImageProcessor, aImageInfo, aJ2kInfo ); + + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop(); + + return self; + } + +// Destructor +CJ2kImageWriter::~CJ2kImageWriter() + { + iComponents.ResetAndDestroy(); + + delete iLinearsRGBLut; + iLinearsRGBLut = 0; + + User::Free( iGrayTRCLut ); + User::Free( iRedTRCLut ); + User::Free( iGreenTRCLut ); + User::Free( iBlueTRCLut ); + User::Free( iMonoPixelBlock ); + User::Free( iColorPixelBlock ); + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::WriterComponentAt +// Get the component of the image writer +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +const CJ2kWriterComponentInfo& CJ2kImageWriter::WriterComponentAt( TUint16 aIndex ) const + { + return *iComponents[aIndex]; + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::OutputImageL +// Output the image related to the component of the tile +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::OutputImageL( CJ2kTileInfo& aTile, TUint16 aComponentIndex ) + { + TUint8 bitdepth = 0; + TUint16 c = 0; + CJ2kComponentInfo& componentInfo = CONST_CAST( CJ2kComponentInfo&, aTile.ComponentAt( aComponentIndex ) ); + + TInt16 reducedLevels = (TInt16)( componentInfo.Levels() - iImageInfo.LevelDrop() ); + if ( reducedLevels < 0 ) + { + reducedLevels = 0; + } + + CJ2kSubband* subband = CONST_CAST( CJ2kSubband*, componentInfo.SubbandAt( (TUint8)reducedLevels ) ); + + if ( subband->SubbandResLevel() != 0 ) + { + subband = subband->Parent(); + } + + TSize subbandSize = subband->SubbandCanvasSize(); + + // Note that in case of component truncation color transform is not performed + if ( iImageInfo.ComponentDrop() ) + { + iNumComponents = 1; + } + else // Only perform color transform if we don't drop components + { + // Perform the color transfrom + if ( aComponentIndex == 2 && aTile.ColorTransformation() ) + { + // Perform the inverse color transform + if ( aTile.ComponentAt( 0 ).IsReversible() ) // If RCT is used + { + PerformInverseRCT( subbandSize ); + } + else + { + PerformInverseICT( subbandSize ); + } + } + } + + // Check if palettes are used. If so, get the number of output channels + if ( iJ2kInfo.iCMPList.Count() ) + { + TUint16 numCSComp = iImageInfo.NumOfComponents(); + + // Allocate more memory for data if needed + if ( iNumComponents > numCSComp ) + { + for ( c = numCSComp; c < iNumComponents; c++ ) + { + // Allocate memory for component data + iComponents[c]->AllocDataL( subbandSize ); + } + } + // Check if we have all the necessary channels for component mapping + if ( aComponentIndex == ( numCSComp - 1 ) ) + { + MapComponentsL( numCSComp, reducedLevels, subbandSize, aTile ); + } + } + + // We start the output of files: + if ( iSingleFileOutput ) + { + if ( iNumComponents == 3 ) // 3 comp to combine + { + // Compute the subbandSize from the first component since others might be downsampled. + TInt16 tempNumLevels = (TUint16)( aTile.ComponentAt( 0 ).Levels() - iImageInfo.LevelDrop() ); + + if ( tempNumLevels < 0 ) + { + tempNumLevels = 0; + } + + CJ2kSubband* tempSubband = CONST_CAST( CJ2kSubband*, aTile.ComponentAt( 0 ).SubbandAt( (TUint8)tempNumLevels ) ); + + if ( tempSubband->SubbandResLevel() != 0 ) + { + tempSubband = tempSubband->Parent(); + } + + subbandSize = tempSubband->SubbandCanvasSize(); + + if( !iColorPixelBlock ) + { + // Allocate memory for block of color pixels + iColorPixelBlock = STATIC_CAST( TRgb*, User::AllocL( 2 * KPixelsBlock * sizeof( TRgb ) ) ); + } + + CombineOutputFile( aTile, subbandSize ); + for ( c = 0; c < iNumComponents; c++ ) + { + iComponents[c]->FreeData(); + } + } + else // 1 comp to output + { + if ( iImageInfo.ComponentDrop() ) + { + c = ( TUint16 )( iImageInfo.ComponentDrop() - 1 ); + } + else + { + c = aComponentIndex; + } + + bitdepth = iImageInfo.DepthOfComponent( c ); + + if( !iMonoPixelBlock ) + { + // Allocate memory for block of grayscale pixels + iMonoPixelBlock = STATIC_CAST( TUint32*, User::AllocL( KPixelsBlock * sizeof( TUint32 ) ) ); + } + + // Output a single component + WriteOutputFile( aTile, c, subbandSize, bitdepth ); + iComponents[c]->FreeData(); + } + } + else + { + // Write out three independent output files + if( !iMonoPixelBlock ) + { + // Allocate memory for block of grayscale pixels + iMonoPixelBlock = STATIC_CAST( TUint32*, User::AllocL( KPixelsBlock * sizeof( TUint32 ) ) ); + } + + if ( aComponentIndex == 2 && aTile.ColorTransformation() ) + { + for ( c = 0; c < 3; c++ ) + { + bitdepth = iImageInfo.DepthOfComponent( c ); + + // Output single files + WriteOutputFile( aTile, c, subbandSize, bitdepth ); + iComponents[c]->FreeData(); + } + } + else if ( iJ2kInfo.iCMPList.Count() ) + { + for ( c = 0; c < iNumComponents; c++ ) + { + // Bitdepth is the lowest seven bits plus one for palettes + bitdepth = ( TUint8 )( ( iJ2kInfo.iPalette.iBList[0] & 0x7f )+1 ); + + // Output single files + WriteOutputFile( aTile, c, subbandSize, bitdepth ); + iComponents[c]->FreeData(); + } + } + else + { + bitdepth = iImageInfo.DepthOfComponent( aComponentIndex ); + + // Output only the first component to screen + if( aComponentIndex == 0 ) + { + WriteOutputFile( aTile, aComponentIndex, subbandSize, bitdepth ); + } + iComponents[aComponentIndex]->FreeData(); + } + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::OutputImageL +// Output the image related to the component of the tile +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::OutputImageL( CJ2kTileInfo& aTile, TUint16 aComponentIndex, + const TSize& aSize ) + { + TUint8 bitdepth = 0; + TUint16 c = 0; + CJ2kComponentInfo& componentInfo = CONST_CAST( CJ2kComponentInfo&, aTile.ComponentAt( aComponentIndex ) ); + + TInt16 reducedLevels = (TInt16)( componentInfo.Levels() - iImageInfo.LevelDrop() ); + if ( reducedLevels < 0 ) + { + reducedLevels = 0; + } + + CJ2kSubband* subband = CONST_CAST( CJ2kSubband*, componentInfo.SubbandAt( (TUint8)reducedLevels ) ); + if ( subband->SubbandResLevel() != 0 ) + { + subband = subband->Parent(); + } + + TSize subbandSize = aSize; + + // Note that in case of component truncation color transform is not performed + if ( iImageInfo.ComponentDrop() ) + { + iNumComponents = 1; + } + else // Only perform color transform if we don't drop components + { + // Perform the color transfrom + if ( aComponentIndex == 2 && aTile.ColorTransformation() ) + { + // Perform the inverse color transform + if ( aTile.ComponentAt( 0 ).IsReversible() ) // If RCT is used + { + PerformInverseRCT( subbandSize ); + } + else + { + PerformInverseICT( subbandSize ); + } + } + } + + // Check if palettes are used. If so, get the number of output channels + if ( iJ2kInfo.iCMPList.Count() ) + { + TUint16 numCSComp = iImageInfo.NumOfComponents(); + + // Allocate more memory for data if needed + if ( iNumComponents > numCSComp ) + { + for ( c = numCSComp; c < iNumComponents; c++ ) + { + // Allocate memory for component data + iComponents[c]->AllocDataL( subbandSize ); + } + } + // Check if we have all the necessary channels for component mapping + if ( aComponentIndex == ( numCSComp - 1 ) ) + { + MapComponentsL( numCSComp, reducedLevels, subbandSize, aTile ); + } + } + + // We start the output of files: + if ( iSingleFileOutput ) + { + if ( iNumComponents == 3 ) // 3 comp to combine + { + if( !iColorPixelBlock ) + { + // Allocate memory for block of color pixels + iColorPixelBlock = STATIC_CAST( TRgb*, User::AllocL( 2 * KPixelsBlock * sizeof( TRgb ) ) ); + } + + CombineOutputFile( aTile, subbandSize ); + for ( c = 0; c < iNumComponents; c++ ) + { + iComponents[c]->FreeData(); + } + } + else // 1 comp to output + { + if ( iImageInfo.ComponentDrop() ) + { + c = (TUint16)( iImageInfo.ComponentDrop() - 1 ); + } + else + { + c = aComponentIndex; + } + + bitdepth = iImageInfo.DepthOfComponent( c ); + + if( !iMonoPixelBlock ) + { + // Allocate memory for block of grayscale pixels + iMonoPixelBlock = STATIC_CAST( TUint32*, User::AllocL( KPixelsBlock * sizeof( TUint32 ) ) ); + } + + // Output a single component + WriteOutputFile( aTile, c, subbandSize, bitdepth ); + iComponents[c]->FreeData(); + } + } + else + { + // Write out three independent output files + if( !iMonoPixelBlock ) + { + // Allocate memory for block of grayscale pixels + iMonoPixelBlock = STATIC_CAST( TUint32*, User::AllocL( KPixelsBlock * sizeof( TUint32 ) ) ); + } + + if ( aComponentIndex == 2 && aTile.ColorTransformation() ) + { + for ( c = 0; c < 3; c++ ) + { + bitdepth = iImageInfo.DepthOfComponent( c ); + + // Output single files + WriteOutputFile( aTile, c, subbandSize, bitdepth ); + iComponents[c]->FreeData(); + } + } + else if ( iJ2kInfo.iCMPList.Count() ) + { + for ( c = 0; c < iNumComponents; c++ ) + { + // Bitdepth is the lowest seven bits plus one for palettes + bitdepth = (TUint8)( ( iJ2kInfo.iPalette.iBList[0] & 0x7f )+1 ); + + // Output single files + WriteOutputFile( aTile, c, subbandSize, bitdepth ); + iComponents[c]->FreeData(); + } + } + else + { + bitdepth = iImageInfo.DepthOfComponent( aComponentIndex ); + + // Output only the first component to screen + if( aComponentIndex == 0 ) + { + WriteOutputFile( aTile, aComponentIndex, subbandSize, bitdepth ); + } + + iComponents[aComponentIndex]->FreeData(); + } + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::OutputBlockL +// Output the image related to the component of the tile +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::OutputBlockL( CJ2kTileInfo& aTile, TUint16 aComponentIndex, + TInt32 aBlockXCoord, TInt32 aBlockYCoord, + TSize aFirstCompSize, TSize aThisCompSize ) + { + TUint32 tileIndex = 0; + CJ2kWriterComponentInfo* currentComponent = iComponents[aComponentIndex]; + TPoint tmpTileStart( 0, 0 ); + TPoint tmpTileStart1( 0, 0 ); + TPoint tmpTileStart2( 0, 0 ); + + CJ2kComponentInfo& componentInfo = CONST_CAST( CJ2kComponentInfo&, aTile.ComponentAt( aComponentIndex ) ); + TSize outputSize( 0, 0 ); + + tileIndex = aTile.SotMarker().iIsot; + + // Update the subband size ( which will be used to compute output size ) + TInt16 reducedLevels = (TInt16)( componentInfo.Levels() - iImageInfo.LevelDrop() ); + if ( reducedLevels < 0 ) + { + + TInt32 i; + TInt32 stepSize = 1; + + // Compute the output step size, the stepSize indicates how much more + // resolution has to be dropped if the image didn't have enough wavelet + // levels to begin with. One indicates no extra resolution drop (write + // each sample) and for each extra drop skip half of the samples, i.e. + // stepSize is 2^extraLevels in case extra drop is needed. + + // Compute the stepSize + for ( i = 0; i < (-reducedLevels); i++ ) + { + // Double the step size for every extra level dropped. + stepSize *= 2; + } + + // Adjust the block coordinates, so that next block is drawn to the right coordinates + aBlockXCoord /= stepSize; + aBlockYCoord /= stepSize; + + reducedLevels = 0; + } + + CJ2kSubband* subband = CONST_CAST( CJ2kSubband*, componentInfo.SubbandAt( (TUint8)reducedLevels ) ); + if ( subband->SubbandResLevel() != 0 ) + { + subband = subband->Parent(); + } + + // If we are going to combine the output into a single file we must use the first components size and + // tile-start values ( other components might be sub sampled ). + if( iSingleFileOutput && iNumComponents == 3 ) + { + // Update the tileStartCoordinates + tmpTileStart = iComponents[0]->TileStartAt( aTile.SotMarker().iIsot ); + iComponents[0]->iTileStartList[tileIndex].iX += aBlockXCoord; + iComponents[0]->iTileStartList[tileIndex].iY += aBlockYCoord; + + // Also store and update the tileStartCoordinates of the other two components, as they may be output to file + tmpTileStart1 = iComponents[1]->TileStartAt( aTile.SotMarker().iIsot ); + iComponents[1]->iTileStartList[tileIndex].iX += aBlockXCoord; + iComponents[1]->iTileStartList[tileIndex].iY += aBlockYCoord; + tmpTileStart2 = iComponents[2]->TileStartAt( aTile.SotMarker().iIsot ); + iComponents[2]->iTileStartList[tileIndex].iX += aBlockXCoord; + iComponents[2]->iTileStartList[tileIndex].iY += aBlockYCoord; + + outputSize = aFirstCompSize; + } + else + { + // Update the tileStartCoordinates + tmpTileStart = currentComponent->TileStartAt( aTile.SotMarker().iIsot ); + currentComponent->iTileStartList[tileIndex].iX += aBlockXCoord; + currentComponent->iTileStartList[tileIndex].iY += aBlockYCoord; + + outputSize = aThisCompSize; + } + + // Call OutputImageL with the changed parameters + OutputImageL( aTile, aComponentIndex, outputSize ); + + // Restore the original values + if( iSingleFileOutput && iNumComponents == 3 ) + { + iComponents[0]->iTileStartList[tileIndex] = tmpTileStart; + iComponents[1]->iTileStartList[tileIndex] = tmpTileStart1; + iComponents[2]->iTileStartList[tileIndex] = tmpTileStart2; + } + else + { + currentComponent->iTileStartList[tileIndex] = tmpTileStart; + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::SetNewImageProcessor +// Set the image processor of the image write +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::SetNewImageProcessor( CImageProcessor* aImageProcessor ) + { + iImageProcessor = aImageProcessor; + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::SingleFileOutput +// Get the single output file +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TUint8 CJ2kImageWriter::SingleFileOutput() const + { + return iSingleFileOutput; + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::CSCode +// Get the EnumCS +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TUint8 CJ2kImageWriter::CSCode() const + { + return (TUint8)iJ2kInfo.iEnumCS; + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::CJ2kImageWriter +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CJ2kImageWriter::CJ2kImageWriter( CImageProcessor* aImageProcessor, + CJ2kImageInfo& aImageInfo, + TJ2kInfo& aJ2kInfo ) : + iImageProcessor( aImageProcessor ), + iImageInfo( aImageInfo ), + iJ2kInfo( aJ2kInfo ) + { + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::ConstructL() + { + TInt32 i = 0; + + TSizMarker& sizMarker = CONST_CAST( TSizMarker&, iImageInfo.SizMarker() ); + + iNumComponents = iImageInfo.NumOfComponents(); + + if ( iJ2kInfo.iCMPList.Count() ) + { + iNumComponents = (TUint16)( iJ2kInfo.iCMPList.Count() ); + + if(iNumComponents < iImageInfo.NumOfComponents()) + { + // Every component in the codestream must have a mapping defined + User::Leave( KErrCorrupt ); + } + } + + for ( i = 0; i < iNumComponents; i++ ) + { + CJ2kWriterComponentInfo *info = new ( ELeave ) CJ2kWriterComponentInfo; + CleanupStack::PushL( info ); + User::LeaveIfError( iComponents.Append( info ) ); + CleanupStack::Pop(1); + } + + if ( iNumComponents == 3 ) + { + iSingleFileOutput = 1; + if ( sizMarker.iXRsiz[1] == 2 * sizMarker.iXRsiz[0] && + sizMarker.iXRsiz[2] == 2 * sizMarker.iXRsiz[0] ) + { + if ( sizMarker.iYRsiz[1] == 2 * sizMarker.iYRsiz[0] && + sizMarker.iYRsiz[2] == 2 * sizMarker.iYRsiz[0] ) + { + iFileType = KYUV420; + } + else + { + iFileType = KYUV422; + } + } + } + else if ( iNumComponents == 1 ) + { + iFileType = KRGB; + } + + if ( iImageInfo.ComponentDrop() ) //lint !e961 no else is needed here at the end of if...else if + { + iFileType = KRGB; + } + + if ( iJ2kInfo.iICCProfile ) + { + iICCProfile = ETrue; + InitializeICCProfileL(); + } + + // Initialize the output parameters + if ( iImageInfo.Crop() ) + { + // Currently do nothing. + } + else + { + InitializeOutputParametersL(); + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::PerformInverseRCT +// Perform the inverse reversible color transformation +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::PerformInverseRCT( const TSize& aSize ) + { + TInt32 col = 0; + TPrecInt red = 0; + TPrecInt green = 0; + TPrecInt blue = 0; + TPrecInt** block1 = iComponents[0]->iData; + TPrecInt** block2 = iComponents[1]->iData; + TPrecInt** block3 = iComponents[2]->iData; + + for ( TInt32 row = 0; row < aSize.iHeight; row++ ) + { + for ( col = 0; col < aSize.iWidth; col++ ) + { + red = block1[row][col]; + green = block2[row][col]; + blue = block3[row][col]; + + block2[row][col] = red - ( ( blue + green ) >> 2 ); //lint !e704 shifting is OK. + block1[row][col] = blue + block2[row][col]; + block3[row][col] = green + block2[row][col]; + } + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::PerformInverseICT +// Perform the inverse irreversible color transformation +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::PerformInverseICT( const TSize& aSize ) + { + TInt32 col = 0; + TPrecInt red = 0; + TPrecInt green = 0; + TPrecInt blue = 0; + TPrecInt** block1 = iComponents[0]->iData; + TPrecInt** block2 = iComponents[1]->iData; + TPrecInt** block3 = iComponents[2]->iData; + + for ( TInt32 row = 0; row < aSize.iHeight; row++ ) + { + for ( col = 0; col < aSize.iWidth; col++ ) + { + red = block1[row][col]; + green = block2[row][col]; + blue = block3[row][col]; + InverseICTTransform( red, green, blue, block1[row][col], block2[row][col], block3[row][col] ); + } + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::InverseICTTransform +// Inverse irreversible color transformation +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::InverseICTTransform( TPrecInt aY, TPrecInt aU, TPrecInt aV, + TPrecInt& aR, TPrecInt& aG, TPrecInt& aB ) + { + aR = ( TPrecInt )( ( ( ( aY << KFractionBits ) + + ( KIctCoefficient11 * aV ) ) + KOffset ) >> KFractionBits ); //lint !e704 shifting is OK. + aG = ( TPrecInt )( ( ( ( aY << KFractionBits ) + + ( KIctCoefficient21 * aU ) + ( KIctCoefficient22 * aV ) ) + KOffset ) >> KFractionBits ); //lint !e704 shifting is OK. + aB = ( TPrecInt )( ( ( ( aY << KFractionBits ) + + ( KIctCoefficient31 * aU ) ) + KOffset ) >> KFractionBits ); //lint !e704 shifting is OK. + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::InverseICTTransform +// Inverse irreversible color transformation. +// Performs fast transform and outputs even and odd samples at the same time +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::InverseICTTransform( TPrecInt aY1, TPrecInt aY2, TPrecInt aU, TPrecInt aV, + TPrecInt& aR1, TPrecInt& aG1, TPrecInt& aB1, + TPrecInt& aR2, TPrecInt& aG2, TPrecInt& aB2) + { + TInt32 y1Shifted; + TInt32 y2Shifted; + TInt32 rTempValue; + TInt32 gTempValue; + TInt32 bTempValue; + + y1Shifted = ( aY1 << KFractionBits ); + y2Shifted = ( aY2 << KFractionBits ); + rTempValue = ( KIctCoefficient11 * aV ) + KOffset; + gTempValue = ( KIctCoefficient21 * aU ) + ( KIctCoefficient22 * aV ) + KOffset; + bTempValue = ( KIctCoefficient31 * aU ) + KOffset; + + aR1 = ( TPrecInt )( ( y1Shifted + rTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aG1 = ( TPrecInt )( ( y1Shifted + gTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aB1 = ( TPrecInt )( ( y1Shifted + bTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aR2 = ( TPrecInt )( ( y2Shifted + rTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aG2 = ( TPrecInt )( ( y2Shifted + gTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aB2 = ( TPrecInt )( ( y2Shifted + bTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::InverseICTFastYUV420Transform +// Inverse irreversible color transformation +// Performs fast transform and outputs even and odd samples on even and odd +// rows (four samples) at the same time. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::InverseICTTransform( TPrecInt aY1, TPrecInt aY2, TPrecInt aY3, TPrecInt aY4, + TPrecInt aU, TPrecInt aV, + TPrecInt& aR1, TPrecInt& aG1, TPrecInt& aB1, + TPrecInt& aR2, TPrecInt& aG2, TPrecInt& aB2, + TPrecInt& aR3, TPrecInt& aG3, TPrecInt& aB3, + TPrecInt& aR4, TPrecInt& aG4, TPrecInt& aB4) + { + TInt32 y1Shifted; + TInt32 y2Shifted; + TInt32 y3Shifted; + TInt32 y4Shifted; + TInt32 rTempValue; + TInt32 gTempValue; + TInt32 bTempValue; + + y1Shifted = ( aY1 << KFractionBits ); + y2Shifted = ( aY2 << KFractionBits ); + y3Shifted = ( aY3 << KFractionBits ); + y4Shifted = ( aY4 << KFractionBits ); + rTempValue = ( KIctCoefficient11 * aV ) + KOffset; + gTempValue = ( KIctCoefficient21 * aU ) + ( KIctCoefficient22 * aV ) + KOffset; + bTempValue = ( KIctCoefficient31 * aU ) + KOffset; + + aR1 = ( TPrecInt )( ( y1Shifted + rTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aG1 = ( TPrecInt )( ( y1Shifted + gTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aB1 = ( TPrecInt )( ( y1Shifted + bTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aR2 = ( TPrecInt )( ( y2Shifted + rTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aG2 = ( TPrecInt )( ( y2Shifted + gTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aB2 = ( TPrecInt )( ( y2Shifted + bTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aR3 = ( TPrecInt )( ( y3Shifted + rTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aG3 = ( TPrecInt )( ( y3Shifted + gTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aB3 = ( TPrecInt )( ( y3Shifted + bTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aR4 = ( TPrecInt )( ( y4Shifted + rTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aG4 = ( TPrecInt )( ( y4Shifted + gTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + aB4 = ( TPrecInt )( ( y4Shifted + bTempValue ) >> KFractionBits ); //lint !e704 shifting is OK. + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::InitializeICCProfileL +// Initialize the ICC profile from JP2 file format ( iJ2kInfo ) +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::InitializeICCProfileL() + { + const TUint8* iterator = iJ2kInfo.iICCProfile->Ptr(); + TUint8* origin = CONST_CAST( TUint8*, iJ2kInfo.iICCProfile->Ptr() ); + + // Skip the first 128 bytes + iterator += KICCSkipBytes; + + // Get the tag count + TUint32 tagCount = PtrReadUtil::ReadBigEndianUint32Inc( iterator ); + + TUint32 tagSignature = 0; + TUint32 tagSize = 0; + TUint32 redXYZOffset = 0; + TUint32 greenXYZOffset = 0; + TUint32 blueXYZOffset = 0; + TUint32 trcOffset = 0; + TUint32 index = 0; + TInt32 i = 0; + TInt entries = 0; + HBufC16* redTRC = 0; + HBufC16* greenTRC = 0; + HBufC16* blueTRC = 0; + HBufC16* grayTRC = 0; + TUint8 isColor = EFalse; + TReal value = 0.0; + + for ( index = 0; index < tagCount; ++index ) + { + tagSignature = PtrReadUtil::ReadBigEndianUint32Inc( iterator ); + switch ( tagSignature ) + { + case KRedMatrixTag: + { + redXYZOffset = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) + 8; + iterator += 4; + break; + } + case KGreenMatrixTag: + { + greenXYZOffset = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) + 8; + iterator += 4; + break; + } + case KBlueMatrixTag: + { + blueXYZOffset = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) + 8; + iterator += 4; + break; + } + case KRedTrcTag: + { + isColor = ETrue; + trcOffset = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) + 12; + tagSize = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) - 12; + entries = tagSize >> 1; + redTRC = HBufC16::NewLC( entries ); + TPtr16 tmpPtr = ( redTRC->Des() ); + + // Read bytes into the TRC values array + for( i = 0; i < entries; i++ ) + { + tmpPtr.Append( (TUint16)( ( origin[trcOffset + 2 * i] << 8 ) | + origin[trcOffset + 2 * i + 1] ) ); + } + + break; + } + case KGreenTrcTag: + { + isColor = ETrue; + trcOffset = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) + 12; + tagSize = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) - 12; + entries = tagSize >> 1; + greenTRC = HBufC16::NewLC( entries ); + TPtr16 tmpPtr = ( greenTRC->Des() ); + + // Read bytes into the TRC values array + for( i = 0; i < entries; i++ ) + { + tmpPtr.Append( (TUint16)( ( origin[trcOffset + 2 * i] << 8 ) | + origin[trcOffset + 2 * i + 1] ) ); + } + + break; + } + case KBlueTrcTag: + { + isColor = ETrue; + trcOffset = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) + 12; + tagSize = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) - 12; + entries = tagSize >> 1; + blueTRC = HBufC16::NewLC( entries ); + TPtr16 tmpPtr = ( blueTRC->Des() ); + + // Read bytes into the TRC values array + for( i = 0; i < entries; i++ ) + { + tmpPtr.Append( (TUint16)( ( origin[trcOffset+2*i] << 8 ) | + origin[trcOffset+2*i+1] ) ); + } + + break; + } + case KGrayTrcTag: + { + trcOffset = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) + 12; + tagSize = PtrReadUtil::ReadBigEndianUint32Inc( iterator ) - 12; + entries = tagSize >> 1; + grayTRC = HBufC16::NewLC( entries ); + TPtr16 tmpPtr = ( grayTRC->Des() ); + + // Read bytes into the TRC values array + for( i = 0; i < entries; i++ ) + { + tmpPtr.Append( (TUint16)( ( origin[trcOffset + 2 * i] << 8 ) | + origin[trcOffset + 2 * i + 1] ) ); + } + + break; + } + default: + { + iterator += 8; + break; + } + } + } + + TReal gamma = 0.0; + TReal maxInput = 0.0; + TReal src = 0.0; + TUint32 numPerSample = 0; + TUint32 lutSize = 0; + if ( iJ2kInfo.iBPCList.Count() ) + { + lutSize = 1 << ( iJ2kInfo.iBPCList[0] & 0x7f + 1 ); + } + else + { + lutSize = 1 << iImageInfo.DepthOfComponent( 0 ); + } + + if ( isColor ) + { + // Read the RGB( lin. ) -> XYZ conversion matrix coefficients + // The matrix is the following: + // _ _ + // | redX greenX blueX | + // | redY greenY blueY | + // | redZ greenZ blueZ | + // |_ _| + // + TReal redX = ( TReal )PtrReadUtil::ReadBigEndianUint32( origin + redXYZOffset ) / KDivisor; + TReal redY = ( TReal )PtrReadUtil::ReadBigEndianUint32( origin + redXYZOffset + 4 ) / KDivisor; + TReal redZ = ( TReal )PtrReadUtil::ReadBigEndianUint32( origin + redXYZOffset + 8 ) / KDivisor; + + TReal greenX = ( TReal )PtrReadUtil::ReadBigEndianUint32( origin + greenXYZOffset ) / KDivisor; + TReal greenY = ( TReal )PtrReadUtil::ReadBigEndianUint32( origin + greenXYZOffset + 4 ) / KDivisor; + TReal greenZ = ( TReal )PtrReadUtil::ReadBigEndianUint32( origin + greenXYZOffset + 8 ) / KDivisor; + + TReal blueX = ( TReal )PtrReadUtil::ReadBigEndianUint32( origin + blueXYZOffset ) / KDivisor; + TReal blueY = ( TReal )PtrReadUtil::ReadBigEndianUint32( origin + blueXYZOffset + 4 ) / KDivisor; + TReal blueZ = ( TReal )PtrReadUtil::ReadBigEndianUint32( origin + blueXYZOffset + 8 ) / KDivisor; + + // Combine the RGB( lin. ) -> XYZ and the XYZ -> sRGB( lin. ) conversions, i.e. + // perform the matrix multiplication. Store the result in "iMatrix". + iMatrix[0] = ( TInt32 )( KSRGBMaxIntShifted * ( KSRGB00 * redX + KSRGB01 * redY + KSRGB02 * redZ ) ); + iMatrix[1] = ( TInt32 )( KSRGBMaxIntShifted * ( KSRGB00 * greenX + KSRGB01 * greenY + KSRGB02 * greenZ ) ); + iMatrix[2] = ( TInt32 )( KSRGBMaxIntShifted * ( KSRGB00 * blueX + KSRGB01 * blueY + KSRGB02 * blueZ ) ); + + iMatrix[3] = ( TInt32 )( KSRGBMaxIntShifted * ( KSRGB10 * redX + KSRGB11 * redY + KSRGB12 * redZ ) ); + iMatrix[4] = ( TInt32 )( KSRGBMaxIntShifted * ( KSRGB10 * greenX + KSRGB11 * greenY + KSRGB12 * greenZ ) ); + iMatrix[5] = ( TInt32 )( KSRGBMaxIntShifted * ( KSRGB10 * blueX + KSRGB11 * blueY + KSRGB12 * blueZ ) ); + + iMatrix[6] = ( TInt32 )( KSRGBMaxIntShifted * ( KSRGB20 * redX + KSRGB21 * redY + KSRGB22 * redZ ) ); + iMatrix[7] = ( TInt32 )( KSRGBMaxIntShifted * ( KSRGB20 * greenX + KSRGB21 * greenY + KSRGB22 * greenZ ) ); + iMatrix[8] = ( TInt32 )( KSRGBMaxIntShifted * ( KSRGB20 * blueX + KSRGB21 * blueY + KSRGB22 * blueZ ) ); + + iRedTRCLut = STATIC_CAST( TInt32*, User::AllocL( lutSize * sizeof( TInt32 ) ) ); + if ( redTRC->Length() == 1 ) + { + gamma = (TReal)( *redTRC )[0] / KGamma; + maxInput = (TReal)( lutSize - 1 ); + for ( index = 0; index < lutSize; ++index ) + { + src = index / maxInput; + Math::Pow( value, src, gamma ); + iRedTRCLut[index] = (TInt32)( value * KTRCShiftMultiplier + 0.5 ); + } + } + else + { + numPerSample = lutSize / (TUint32)( redTRC->Length() ); + if ( numPerSample ) + { + for ( index = 0; index < lutSize; ++index ) + { + value = ( *redTRC )[index / numPerSample] / KDivisor; + iRedTRCLut[index] = (TInt32)( value * KTRCShiftMultiplier + 0.5 ); + } + } + } + + if ( iJ2kInfo.iBPCList.Count() ) + { + lutSize = 1 << ( iJ2kInfo.iBPCList[1] & 0x7f + 1 ); + } + else + { + lutSize = 1 << iImageInfo.DepthOfComponent( 1 ); + } + + iGreenTRCLut = STATIC_CAST( TInt32*, User::AllocL( lutSize * sizeof( TInt32 ) ) ); + if ( greenTRC->Length() == 1 ) + { + gamma = (TReal)( *greenTRC )[0] / KGamma; + maxInput = (TReal)( lutSize - 1 ); + for ( index = 0; index < lutSize; ++index ) + { + src = index / maxInput; + Math::Pow( value, src, gamma ); + iGreenTRCLut[index] = (TInt32)( value * KTRCShiftMultiplier + 0.5 ); + } + } + else + { + numPerSample = lutSize / (TUint32)( greenTRC->Length() ); + if ( numPerSample ) + { + for ( index = 0; index < lutSize; ++index ) + { + value = ( *greenTRC )[index / numPerSample] / KDivisor; + iGreenTRCLut[index] = (TInt32)( value * KTRCShiftMultiplier + 0.5 ); + } + } + } + + if ( iJ2kInfo.iBPCList.Count() ) + { + lutSize = 1 << ( iJ2kInfo.iBPCList[2] & 0x7f + 1 ); + } + else + { + lutSize = 1 << iImageInfo.DepthOfComponent( 2 ); + } + + iBlueTRCLut = STATIC_CAST( TInt32*, User::AllocL( lutSize * sizeof( TInt32 ) ) ); + if ( blueTRC->Length() == 1 ) + { + gamma = (TReal)( *blueTRC )[0] / KGamma; + maxInput = (TReal)( lutSize - 1 ); + for ( index = 0; index < lutSize; ++index ) + { + src = index / maxInput; + Math::Pow( value, src, gamma ); + iBlueTRCLut[index] = (TInt32)( value * KTRCShiftMultiplier + 0.5 ); + } + } + else + { + numPerSample = lutSize / (TUint32)( blueTRC->Length() ); + if ( numPerSample ) + { + for ( index = 0; index < lutSize; ++index ) + { + value = ( *blueTRC )[index / numPerSample] / KDivisor; + iBlueTRCLut[index] = (TInt32)( value * KTRCShiftMultiplier + 0.5 ); + } + } + } + } + else + { + + iGrayTRCLut = STATIC_CAST( TInt32*, User::AllocL( lutSize * sizeof( TInt32 ) ) ); + if ( grayTRC->Length() == 1 ) + { + + gamma = (TReal)( *grayTRC )[0] / KGamma; + maxInput = (TReal)( lutSize - 1 ); + for ( index = 0; index < lutSize; ++index ) + { + src = index / maxInput; + Math::Pow( value, src, gamma ); + iGrayTRCLut[index] = (TInt32)( value*KSRGBMax+0.5 ); + } + } + else + { + numPerSample = lutSize / (TUint32)( grayTRC->Length() ); + if ( numPerSample ) + { + for ( index = 0; index < lutSize; ++index ) + { + value = ( *grayTRC )[index / numPerSample] / KDivisor; + iGrayTRCLut[index] = (TInt32)( value*KSRGBMax + 0.5 ); + } + } + } + } + + TUint16 cutoffIdx = 0; // Cutoff index for linear portion of output LUT + TReal linearSlope = 0.0; // Slope of output LUT in the linear region + TReal normalisation = 0.0; // Scale factor to normalize sRGB linear value to [0,1] + + // Generate the final output LUT for converting linear sRGB to non-linear sRGB. + // Input values are in the range [0,KSRGBMax] and output will be 8-bit [0,255]. + // Conversation values can be obtained in e.g. "JPEG2000 - Image compression + // fundamentals, standards and practice" book by Taubman and Marcellin. + cutoffIdx = (TUint16)( KSRGB_CUTOFF * KSRGBMax ); + linearSlope = 255.0 * KSRGB_SLOPE / KSRGBMax; + normalisation = 1.0 / KSRGBMax; + + iLinearsRGBLut = HBufC8::NewL( KSRGBMaxInt + 1 ); + + // Generate the output lin.sRGB -> non-lin.sRGB LUT + // Linear part + for ( index = 0; index <= cutoffIdx; ++index ) + { + iLinearsRGBLut->Des().Append( (TUint8)( linearSlope * index ) ); + } + + // Non-linear part + for ( ; index <= (TUint32)KSRGBMaxInt; ++index ) + { + gamma = index * normalisation; + Math::Pow( src, gamma , KSRGB_EXPONENT ); + iLinearsRGBLut->Des().Append( (TUint8)( 255 * ( KSRGB_MULTIPLIER * src - KSRGB_SUBTRACT ) ) ); + } + + // We do not need iJ2kInfo.iICCProfile data anymore + delete iJ2kInfo.iICCProfile; + iJ2kInfo.iICCProfile = 0; + + if ( isColor ) + { + CleanupStack::PopAndDestroy( 3 ); + } + else + { + CleanupStack::PopAndDestroy( 1 ); + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::InitializeOutputParametersL +// Initialize the output parameters +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::InitializeOutputParametersL() + { + CJ2kWriterComponentInfo* component = 0; + TUint16 l = 0; + TUint16 m = 0; + TUint16 tileIndex = 0; + TUint16 tileYIndex = 0; + TPoint tileStart( 0, 0 ); + TUint16 numOfHorizTiles = iImageInfo.NumOfHorizTiles(); + TUint16 numOfVertTiles = iImageInfo.NumOfVertTiles(); + TRect tileCanvas( 0, 0, 0, 0 ); + TRect componentCanvas( 0, 0, 0, 0 ); + const TSizMarker &sizMarker = iImageInfo.SizMarker(); + TInt useHeight = 0; + TInt useWidth = 0; + + // Prepare the tile coordinates for output image + for ( TUint16 compIndex = 0; compIndex < iNumComponents; compIndex++ ) + { + component = iComponents[compIndex]; + + for ( l = 0; l < numOfVertTiles; l++ ) + { + tileYIndex = (TUint16)( l * numOfHorizTiles ); + + for ( m = 0; m < numOfHorizTiles; m++ ) + { + // Tile index + tileIndex = (TUint16)( tileYIndex + m ); + + // Tile canvas + tileCanvas.iTl = TPoint( Max( sizMarker.iXTOsiz + m * sizMarker.iXTsiz, sizMarker.iXOsiz ), + Max( sizMarker.iYTOsiz + l * sizMarker.iYTsiz, sizMarker.iYOsiz ) ); + + tileCanvas.iBr = TPoint( Min( sizMarker.iXTOsiz + ( m + 1 ) * sizMarker.iXTsiz, sizMarker.iXsiz ), + Min( sizMarker.iYTOsiz + ( l + 1 ) * sizMarker.iYTsiz, sizMarker.iYsiz ) ); + // Component canvas + componentCanvas.iTl = TPoint( TJ2kUtils::Ceil( tileCanvas.iTl.iX, sizMarker.iXRsiz[compIndex] ), + TJ2kUtils::Ceil( tileCanvas.iTl.iY, sizMarker.iYRsiz[compIndex] ) ); + + componentCanvas.iBr = TPoint( TJ2kUtils::Ceil( tileCanvas.iBr.iX, sizMarker.iXRsiz[compIndex] ), + TJ2kUtils::Ceil( tileCanvas.iBr.iY, sizMarker.iYRsiz[compIndex] ) ); + + if ( m ) + { + tileStart.iX = component->iTileStartList[tileIndex - 1].iX + useWidth; + } + else + { + tileStart.iX = 0; + } + + // Width to be used on the next horizontal tile + useWidth = componentCanvas.Width(); + + if ( l ) + { + tileStart.iY = component->iTileStartList[tileIndex - 1].iY + useHeight; + useHeight = 0; + } + else + { + tileStart.iY = 0; + } + + User::LeaveIfError( component->iTileStartList.Append( tileStart ) ); + } + // Height to be used on the next vertical tile + useHeight = componentCanvas.Height(); + } + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::DoICCConversion +// Perform XYZ to sRGB conversion with the ICC profile. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::DoICCConversion( TInt32 aX, + TInt32 aY, + TInt32 aZ, + TPrecInt& aR, + TPrecInt& aG, + TPrecInt& aB ) + { + TInt32 tmpX = 0; + TInt32 tmpY = 0; + TInt32 tmpZ = 0; + TInt32 tmpR = 0; + TInt32 tmpG = 0; + TInt32 tmpB = 0; + + // First perform the input linearization + tmpX = iRedTRCLut[aX]; + tmpY = iGreenTRCLut[aY]; + tmpZ = iBlueTRCLut[aZ]; + + // Then perform the matrix multiplication to obtain the nonlinear RGB + tmpR = ( iMatrix[0] * tmpX + iMatrix[1] * tmpY + iMatrix[2] * tmpZ ) >> KICCDownshift; //lint !e704 shifting is OK. + tmpG = ( iMatrix[3] * tmpX + iMatrix[4] * tmpY + iMatrix[5] * tmpZ ) >> KICCDownshift; //lint !e704 shifting is OK. + tmpB = ( iMatrix[6] * tmpX + iMatrix[7] * tmpY + iMatrix[8] * tmpZ ) >> KICCDownshift; //lint !e704 shifting is OK. + + // Get rid of values outside the range of legal values + tmpR = CLIP2RANGE( tmpR, 0, KSRGBMaxInt ); + tmpG = CLIP2RANGE( tmpG, 0, KSRGBMaxInt ); + tmpB = CLIP2RANGE( tmpB, 0, KSRGBMaxInt ); + + // De-linearize the output RGB values + aR = ( *iLinearsRGBLut )[tmpR]; + aG = ( *iLinearsRGBLut )[tmpG]; + aB = ( *iLinearsRGBLut )[tmpB]; + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::MapToEightBits +// Map data less than 8 bits to 8 bits data +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::MapToEightBits( CJ2kWriterComponentInfo& aComponent, + const TSize& aSize, + TUint16 aBitDepth ) + { + TPrecInt* imageRow; + TInt32 i = 0; + TInt32 j = 0; + + // DC-shift is always non zero, since we map signed data to unsigned + if ( aBitDepth < 8 ) + { + TPrecInt upshift = KByteBits - aBitDepth; + TPrecInt dcShiftUp = 1 << ( KByteBits - 1 ); + + for ( i = aSize.iHeight - 1; i >= 0; i-- ) + { + imageRow = aComponent.Data()[i]; + for ( j = aSize.iWidth - 1; j >= 0; j-- ) + { + imageRow[j] = ( imageRow[j] * ( 1 << upshift ) ) + dcShiftUp; + } + } + } + else // The original bitdepth is more than eight + { + TPrecInt dcShift = 1 << ( aBitDepth - 1 ); + TPrecInt downshift = aBitDepth - KByteBits; + for ( i = aSize.iHeight - 1; i >= 0; --i ) + { + imageRow = aComponent.Data()[i]; + for ( j = aSize.iWidth - 1; j >= 0; j-- ) + { + imageRow[j] = ( imageRow[j] + dcShift ) >> downshift; //lint !e704 shifting is OK. + } + } + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::MapComponentsL +// Map data using the component mapping box from JP2 file format +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::MapComponentsL( TUint16 aNumCSComp, + TUint16 aReducedLevels, + const TSize& aSize, + CJ2kTileInfo& aTile ) + { + TInt32 i = 0; + TInt32 j = 0; + TInt32 k = 0; // Indices + TInt32 compIndex = 0; // Index of the component in the codestream + TInt32 paletteIndex = 0; // Index of the component in the palette + TInt32 value = 0; + TInt32 dcShift = 0; + TPrecInt*** tempData = 0; // temporary storage for codestream data + TUint8 bitdepth = 0; + + CJ2kWriterComponentInfo* componentFrom = 0; + CJ2kWriterComponentInfo* componentTo = 0; + CJ2kSubband* subband = 0; + + TSize subbandSize( 0, 0 ); + + // First allocate the temporary storage + tempData = STATIC_CAST( TPrecInt***, User::Alloc( aNumCSComp * sizeof( TPrecInt** ) ) ); + if ( !tempData ) + { + User::Leave( KErrNoMemory ); + } + CleanupStack::PushL( tempData ); + for ( i = 0; i < aNumCSComp; i++ ) + { + subband = CONST_CAST( CJ2kSubband*, aTile.ComponentAt( (TUint16)i ).SubbandAt( (TUint8)aReducedLevels ) ); + + if( subband->SubbandResLevel() != 0 ) + { + subband = subband->Parent(); + } + subbandSize = subband->SubbandCanvasSize(); + + // Check that this component's size doesn't exceed the output size + if( subbandSize.iWidth > aSize.iWidth ) + { + subbandSize.iWidth = aSize.iWidth; + } + if( subbandSize.iHeight > aSize.iHeight ) + { + subbandSize.iHeight = aSize.iHeight; + } + + // Allocate the temporary storage + tempData[i] = TJ2kUtils::Alloc2DArrayL( subbandSize.iHeight, subbandSize.iWidth ); + CleanupStack::PushL( TCleanupItem( ( TCleanupOperation )&( TJ2kUtils::Free2DArray ), ( TAny* )tempData[i] ) ); + componentFrom = iComponents[i]; + + for ( j = subbandSize.iHeight - 1; j >= 0; j-- ) + { + Mem::Copy( tempData[i][j], componentFrom->Data()[j], subbandSize.iWidth * sizeof( TPrecInt ) ); + } + } + + for ( k = 0; k < iNumComponents; k++ ) + { + // Get the channel in the codestream from which to map + compIndex = iJ2kInfo.iCMPList[k].iCmp; + componentFrom = iComponents[compIndex]; + + subband = CONST_CAST( CJ2kSubband*, aTile.ComponentAt( (TUint16)compIndex ).SubbandAt( (TUint8)aReducedLevels ) ); + + if( subband->SubbandResLevel() != 0 ) + { + subband = subband->Parent(); + } + + subbandSize = subband->SubbandCanvasSize(); + + // Check that this component's size doesn't exceed the output size + if( subbandSize.iWidth > aSize.iWidth ) + { + subbandSize.iWidth = aSize.iWidth; + } + if( subbandSize.iHeight > aSize.iHeight ) + { + subbandSize.iHeight = aSize.iHeight; + } + + // Get the output channel + componentTo = iComponents[k]; + + // Check if palette is used for mapping + if ( iJ2kInfo.iCMPList[k].iMtyp == 1 ) + { + // Get the right palette channel for mapping + paletteIndex = iJ2kInfo.iCMPList[k].iPcol; + + // Bitdepth is the lowest seven bits plus one for palettes + bitdepth = (TUint8)( ( iJ2kInfo.iPalette.iBList[paletteIndex] & 0x7f ) + 1 ); + dcShift = 1 << ( bitdepth - 1 ); + + for ( i = 0; i < subbandSize.iHeight; i++ ) + { + for ( j = 0; j < subbandSize.iWidth; j++ ) + { + // Map the input codestream channel into the output component + // using the palette value. + value = tempData[compIndex][i][j] + dcShift; + if ( value < 0 ) + { + value = 0; + } + if ( value >= iJ2kInfo.iPalette.iC2DArray.Count() ) + { + value = iJ2kInfo.iPalette.iC2DArray.Count() - 1; + } + + componentTo->iData[i][j] = ( *iJ2kInfo.iPalette.iC2DArray[value] )[paletteIndex] - dcShift; + } + } + } + else // Direct mapping + { + for ( i = subbandSize.iHeight - 1; i >= 0; --i ) + { + for ( j = subbandSize.iWidth - 1; j >= 0; --j ) + { + // Map the input codestream channel into the output component directly. + componentTo->iData[i][j] = componentFrom->iData[i][j]; + } + } + } + } + + CleanupStack::PopAndDestroy( aNumCSComp + 1 ); + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::WriteOutputFile +// Write the component to the single output file +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::WriteOutputFile( CJ2kTileInfo& aTile, + TUint16 aCompIndex, + const TSize& aSize, + TUint16 aBitDepth ) + { + TPrecInt* imageRow = 0; + CJ2kWriterComponentInfo* currentComponent = iComponents[aCompIndex]; + TSize outputSize = aSize; + + TUint16 numLevels = aTile.ComponentAt( aCompIndex ).Levels(); + TInt16 tempNumLevels = (TUint16)( numLevels - iImageInfo.LevelDrop() ); + + TPoint tileStartCoord = currentComponent->TileStartAt( aTile.SotMarker().iIsot ); + currentComponent->UpdateNextTileStartAt( aTile.SotMarker().iIsot, aSize, iImageInfo ); + + if ( tempNumLevels < 0 ) + { + + TInt32 i; + TInt32 stepSize = 1; + + // Compute the output step size, the stepSize indicates how much more + // resolution has to be dropped if the image didn't have enough wavelet + // levels to begin with. One indicates no extra resolution drop (write + // each sample) and for each extra drop skip half of the samples, i.e. + // stepSize is 2^extraLevels in case extra drop is needed. + + // Adjust the tile starting points and the stepSize + for ( i = 0; i < (-tempNumLevels); i++ ) + { + // Double the step size for every extra level dropped. + stepSize *= 2; + } + + // Also take care of next tile's starting position + outputSize.iHeight /= stepSize; + outputSize.iWidth /= stepSize; + iComponents[0]->UpdateNextTileStartAt( aTile.SotMarker().iIsot, outputSize, iImageInfo ); + } + + TInt32 dcShift = 0; + TInt32 j = 0; + if ( aBitDepth == 8 ) + { + if ( !( iImageInfo.SignOfComponent( aCompIndex ) ) ) + { + dcShift = 1 << ( aBitDepth - 1 ); + } + } + + // Convert with an ICC profile if necessary + if( iICCProfile ) + { + TInt32 value = 0; + TInt32 outputValue = 0; + + // Compute the dcShift again, we must have dcShift != 0 for other than 8-bit input. + dcShift = 1 << ( iImageInfo.DepthOfComponent( 0 )-1 ); + + for ( TInt32 i = 0; i < outputSize.iHeight; i++ ) + { + imageRow = currentComponent->Data()[i]; + + SetPixelPos( tileStartCoord.iX, tileStartCoord.iY + i ); + for ( j = 0; j < outputSize.iWidth; j++ ) + { + // Use a clipped value of input sample for an index to the grayscale TRC LUT. + value = iGrayTRCLut[CLIPINT( ( imageRow[j]+dcShift ), aBitDepth )]; + + // Get rid of values outside the range of legal values. + value = CLIP2RANGE( value, 0, KSRGBMaxInt ); + + // De-linearize the output values. + outputValue = ( *iLinearsRGBLut )[value]; + + iMonoPixelBlock[j] = (TUint32)( INT2BYTE( outputValue ) ); + } + + // Flush the row of grayscale pixels to image processor + iImageProcessor->SetMonoPixels( iMonoPixelBlock, outputSize.iWidth ); + } + + SetPixelPos( tileStartCoord ); + } + else // No ICC profile was used. + { + + // Take care of bitdepths != 8 + if ( aBitDepth != 8 ) + { + MapToEightBits( *currentComponent, outputSize, aBitDepth ); + } + + SetPixelPos( tileStartCoord ); + + for ( TInt32 i = 0; i < outputSize.iHeight; i++ ) + { + imageRow = currentComponent->Data()[i]; + + + SetPixelPos( tileStartCoord.iX, tileStartCoord.iY + i ); + + for ( j = 0; j < outputSize.iWidth; j++ ) + { + // Store the value in the RGB "block" + iMonoPixelBlock[j] = (TUint32)( INT2BYTE( ( imageRow[j] + dcShift ) ) ); + } + + // Flush the row of grayscale pixels to image processor + iImageProcessor->SetMonoPixels( iMonoPixelBlock, outputSize.iWidth ); + } + + SetPixelPos( tileStartCoord ); + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::CombineOutputFile +// Write all components of the tile to the single output file +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::CombineOutputFile( CJ2kTileInfo& aTile, const TSize& aSize ) + { + TInt32 i = 0; + TInt32 j = 0; + TUint16 numLevels = aTile.ComponentAt( 0 ).Levels(); + TInt16 tempNumLevels = (TUint16)( numLevels - iImageInfo.LevelDrop() ); + + TSize outputSize = aSize; + TPoint& tileStartCoord = iComponents[0]->TileStartAt( aTile.SotMarker().iIsot ); + iComponents[0]->UpdateNextTileStartAt( aTile.SotMarker().iIsot, outputSize, iImageInfo ); + + if ( tempNumLevels < 0 ) + { + TInt32 stepSize = 1; + + // Compute the output step size, the stepSize indicates how much more + // resolution has to be dropped if the image didn't have enough wavelet + // levels to begin with. One indicates no extra resolution drop (write + // each sample) and for each extra drop skip half of the samples, i.e. + // stepSize is 2^extraLevels in case extra drop is needed. + + // Adjust the tile starting points and the stepSize + for ( i = 0; i < (-tempNumLevels); i++ ) + { + // Double the step size for every extra level dropped. + stepSize *= 2; + } + + // Also take care of next tile's starting position + outputSize.iHeight /= stepSize; + outputSize.iWidth /= stepSize; + iComponents[0]->UpdateNextTileStartAt( aTile.SotMarker().iIsot, outputSize, iImageInfo ); + + tempNumLevels = 0; + } + + TInt32 dcShift = 0; + if ( iImageInfo.DepthOfComponent( 0 ) == 8 ) + { + if ( !( iImageInfo.SignOfComponent( 0 ) ) ) + { + dcShift = 1 << ( iImageInfo.DepthOfComponent( 0 ) - 1 ); + } + } + + TSize subSamplingSize( 0, 0 ); + + // Due to subsampling we might have one more sample than the result of + // height/2 or width/2 will give thus we have to check whether we an + // extra row/column . + // + if ( iImageInfo.NumOfComponents() == 3 && iJ2kInfo.iEnumCS == 18 ) + { + CJ2kSubband* subband = CONST_CAST( CJ2kSubband*, aTile.ComponentAt( 1 ).SubbandAt( (TUint8)tempNumLevels ) ); + + if ( subband->SubbandResLevel() != 0 ) + { + subband = subband->Parent(); + } + + subSamplingSize = subband->SubbandCanvasSize(); + + if ( subSamplingSize.iWidth ) + { + --subSamplingSize.iWidth; + } + + if ( subSamplingSize.iHeight ) + { + --subSamplingSize.iHeight; + } + } + + TPrecInt** c1 = iComponents[0]->Data(); + TPrecInt** c2 = iComponents[1]->Data(); + TPrecInt** c3 = iComponents[2]->Data(); + + TPrecInt* c1Row = c1[0]; + TPrecInt* c2Row = c2[0]; + TPrecInt* c3Row = c3[0]; + + // For taking care of channel definition box order + if ( iJ2kInfo.iCNList.Count() > 0 ) + { + for ( i = 0; i < 3; i++ ) + { + if ( iJ2kInfo.iCNList[i].iAsoc == 1 ) + { + c1 = iComponents[i]->Data(); + } + else if ( iJ2kInfo.iCNList[i].iAsoc == 2 ) + { + c2 = iComponents[i]->Data(); + } + else if( iJ2kInfo.iCNList[i].iAsoc == 3 ) + { + c3 = iComponents[i]->Data(); + } + } //lint !e961 no else is needed here at the end of if...else if + } + + // If dithering is needed the following lines should be used: + // TInt32 A = KDitherCoeffcient11 + dcShift; + // TInt32 B = KDitherCoeffcient12 + dcShift; + // TInt32 C = KDitherCoeffcient21 + dcShift; + // TInt32 D = KDitherCoeffcient22 + dcShift; + // Without dithering, use dcShift only. + // + TInt32 ditherA = dcShift; + TInt32 ditherB = dcShift; + TInt32 ditherC = dcShift; + TInt32 ditherD = dcShift; + + TUint8 values[6]; + + // Convert with the ICC profile values if necessary. + if( iICCProfile ) + { + TInt32 x = 0; + TInt32 y = 0; + TInt32 z = 0; + TPrecInt sR = 0; + TPrecInt sG = 0; + TPrecInt sB = 0; + TInt32 maxValue = 0; + + // Compute the dcShift again, we must have dcShift != 0 for other than 8-bit input. + dcShift = 1 << ( iImageInfo.DepthOfComponent( 0 ) - 1 ); + maxValue = ( 1 << iImageInfo.DepthOfComponent( 0 ) ) - 1; // Maximum value for this bitdepth + + for( i = 0; i < outputSize.iHeight; i++ ) + { + + // Set the current coordinate in the output image + SetPixelPos( tileStartCoord.iX, tileStartCoord.iY + i ); + + for( j = 0; j < outputSize.iWidth; j++ ) + { + x = CLIP2BITDEPTH( ( c1[i][j] + dcShift ),maxValue ); + y = CLIP2BITDEPTH( ( c2[i][j] + dcShift ),maxValue ); + z = CLIP2BITDEPTH( ( c3[i][j] + dcShift ),maxValue ); + + DoICCConversion( x,y,z,sR,sG,sB ); + + values[0] = INT2BYTE( sB ); + values[1] = INT2BYTE( sG ); + values[2]= INT2BYTE( sR ); + + WritePixel( values[2], values[1], values[0] ); + } + } + + // Set the current coordinate in the output image + SetPixelPos( tileStartCoord ); + } + else + { + + // To speed up output writing (for subsampled samples), compute the number of half the samples + TInt32 halfOfWidth = outputSize.iWidth / 2; + + // If the bitdepth is not eight, shift values so that output is unsigned eight bit + for ( i = 0; i < iImageInfo.NumOfComponents(); i++ ) + { + if ( iImageInfo.DepthOfComponent( ( TUint16 )i ) != 8 ) + { + MapToEightBits( *iComponents[i], outputSize, iImageInfo.DepthOfComponent( (TUint16)i ) ); + } + } + + // Do the YUV to RGB conversion if needed + if ( iJ2kInfo.iEnumCS == 18 ) + { + TPrecInt y1 = 0; + TPrecInt y2 = 0; + TPrecInt u = 0; + TPrecInt v = 0; + TPrecInt r = 0; + TPrecInt g = 0; + TPrecInt b = 0; + TPrecInt r1 = 0; + TPrecInt g1 = 0; + TPrecInt b1 = 0; + TPrecInt r2 = 0; + TPrecInt g2 = 0; + TPrecInt b2 = 0; + + if ( iFileType == KYUV420 ) // YUV 4:2:0 + { + + TPrecInt* c1EvenRow = c1[0]; + TPrecInt* c1OddRow = c1[0]; + TPrecInt y3 = 0; + TPrecInt y4 = 0; + TPrecInt r3 = 0; + TPrecInt g3 = 0; + TPrecInt b3 = 0; + TPrecInt r4 = 0; + TPrecInt g4 = 0; + TPrecInt b4 = 0; + + for ( i = 0; i < outputSize.iHeight / 2; i++ ) + { + SetPixelPos( tileStartCoord.iX, ( tileStartCoord.iY + 2 * i ) ); + + c1EvenRow = c1[2 * i]; + c1OddRow = c1[2 * i + 1]; + c2Row = c2[i]; + c3Row = c3[i]; + + for ( j = 0; j < halfOfWidth; j++ ) + { + y1 = *c1EvenRow++; + y2 = *c1EvenRow++; + y3 = *c1OddRow++; + y4 = *c1OddRow++; + u = *c2Row++; + v = *c3Row++; + + InverseICTTransform(y1,y2,y3,y4,u,v,r1,g1,b1,r2,g2,b2,r3,g3,b3,r4,g4,b4); + + // Even rows, even columns + values[0] = INT2BYTE( ( r1 + ditherA ) ); + values[1] = INT2BYTE( ( g1 + ditherA ) ); + values[2] = INT2BYTE( ( b1 + ditherA ) ); + + // Even rows, odd columns: + values[3] = INT2BYTE( ( r2 + ditherB ) ); + values[4] = INT2BYTE( ( g2 + ditherB ) ); + values[5] = INT2BYTE( ( b2 + ditherB ) ); + + // Store the values in the RGB "block" + iColorPixelBlock[2 * j] = TRgb( values[0], values[1], values[2] ); + iColorPixelBlock[2 * j + 1] = TRgb( values[3], values[4], values[5] ); + + // Now the odd row: + // Odd rows, even column + values[0] = INT2BYTE( ( r3 + ditherC ) ); + values[1] = INT2BYTE( ( g3 + ditherC ) ); + values[2] = INT2BYTE( ( b3 + ditherC ) ); + + // Odd rows, odd columns: + values[3] = INT2BYTE( ( r4 + ditherD ) ); + values[4] = INT2BYTE( ( g4 + ditherD ) ); + values[5] = INT2BYTE( ( b4 + ditherD ) ); + + // Store the values in the RGB "block" + iColorPixelBlock[KPixelsBlock + 2 * j] = TRgb( values[0], values[1], values[2] ); + iColorPixelBlock[KPixelsBlock + 2 * j + 1] = TRgb( values[3], values[4], values[5] ); + } + + if ( outputSize.iWidth & 1 ) // If the width is odd: + { + // The even row: + y1 = c1[2 * i][outputSize.iWidth - 1]; + // And the odd row: + y2 = c1[2 * i + 1][outputSize.iWidth - 1]; + u = c2[i][subSamplingSize.iWidth]; + v = c3[i][subSamplingSize.iWidth]; + + InverseICTTransform(y1,y2,u,v,r1,g1,b1,r2,g2,b2); + + values[0] = INT2BYTE( ( r1 + ditherA ) ); + values[1] = INT2BYTE( ( g1 + ditherA ) ); + values[2] = INT2BYTE( ( b1 + ditherA ) ); + + // Store the value in the RGB "block" + iColorPixelBlock[outputSize.iWidth - 1] = TRgb( values[0], values[1], values[2] ); + + values[0] = INT2BYTE( ( r2 + ditherC ) ); + values[1] = INT2BYTE( ( g2 + ditherC ) ); + values[2] = INT2BYTE( ( b2 + ditherC ) ); + + // Store the value in the RGB "block" + iColorPixelBlock[KPixelsBlock + outputSize.iWidth - 1] = TRgb( values[0], values[1], values[2] ); + } + + // Flush the row of color pixels to image processor + iImageProcessor->SetPixels( iColorPixelBlock, outputSize.iWidth ); + + // Duplicated processing for the odd rows + SetPixelPos( tileStartCoord.iX, ( tileStartCoord.iY + 2 * i + 1 ) ); + + // Flush the row of color pixels to image processor + iImageProcessor->SetPixels( (iColorPixelBlock + KPixelsBlock), outputSize.iWidth ); + + } // End of loop on rows + + if ( outputSize.iHeight & 1 ) // If the height is odd: + { + SetPixelPos( tileStartCoord.iX, ( tileStartCoord.iY + outputSize.iHeight - 1 ) ); + + c1Row = c1[outputSize.iHeight - 1]; + c2Row = c2[subSamplingSize.iHeight]; + c3Row = c3[subSamplingSize.iHeight]; + + for ( i = 0; i < halfOfWidth; i++ ) + { + y1 = *c1Row++; + y2 = *c1Row++; + u = *c2Row++; + v = *c3Row++; + + InverseICTTransform(y1,y2,u,v,r1,g1,b1,r2,g2,b2); + + values[0] = INT2BYTE( ( r1 + ditherA ) ); + values[1] = INT2BYTE( ( g1 + ditherA ) ); + values[2] = INT2BYTE( ( b1 + ditherA ) ); + + values[3] = INT2BYTE( ( r2 + ditherB ) ); + values[4] = INT2BYTE( ( g2 + ditherB ) ); + values[5] = INT2BYTE( ( b2 + ditherB ) ); + + // Store the value in the RGB "block" + iColorPixelBlock[2 * i] = TRgb( values[0], values[1], values[2] ); + iColorPixelBlock[2 * i + 1] = TRgb( values[3], values[4], values[5] ); + } + + if ( outputSize.iWidth & 1 ) // if the width is odd: + { + y1 = c1[outputSize.iHeight - 1][outputSize.iWidth - 1]; + u = c2[subSamplingSize.iHeight][subSamplingSize.iWidth]; + v = c3[subSamplingSize.iHeight][subSamplingSize.iWidth]; + + InverseICTTransform( y1, u, v, r, g, b ); + values[0] = INT2BYTE( ( r + ditherA ) ); + values[1] = INT2BYTE( ( g + ditherA ) ); + values[2] = INT2BYTE( ( b + ditherA ) ); + + // Store the value in the RGB "block" + iColorPixelBlock[outputSize.iWidth - 1] = TRgb( values[0], values[1], values[2] ); + } + + // Flush the row of color pixels to image processor + iImageProcessor->SetPixels( iColorPixelBlock, outputSize.iWidth ); + + } + + SetPixelPos( tileStartCoord ); + } + else if ( iFileType == KYUV422 ) + { + + for ( i = 0; i < outputSize.iHeight; i++ ) + { + SetPixelPos( tileStartCoord.iX, ( tileStartCoord.iY + 2 * i ) ); + + c1Row = c1[i]; + c2Row = c2[i]; + c3Row = c3[i]; + + for ( j = 0; j < halfOfWidth; j++ ) + { + y1 = *c1Row++; + y2 = *c1Row++; + u = c2Row[j]; + v = c3Row[j]; + + InverseICTTransform(y1,y2,u,v,r1,g1,b1,r2,g2,b2); + + values[0] = INT2BYTE( ( r1 + dcShift ) ); + values[1] = INT2BYTE( ( g1 + dcShift ) ); + values[2] = INT2BYTE( ( b1 + dcShift ) ); + + values[3] = INT2BYTE( ( r2 + dcShift ) ); + values[4] = INT2BYTE( ( g2 + dcShift ) ); + values[5] = INT2BYTE( ( b2 + dcShift ) ); + + // Store the value in the RGB "block" + iColorPixelBlock[j] = TRgb( values[0], values[1], values[2] ); + iColorPixelBlock[j+1] = TRgb( values[3], values[4], values[5] ); + } + + if ( outputSize.iWidth & 1 ) // if the width is odd: + { + y1 = c1Row[outputSize.iWidth - 1]; + u = c2Row[subSamplingSize.iWidth]; + v = c3Row[subSamplingSize.iWidth]; + + InverseICTTransform( y1, u, v, r, g, b ); + + values[0] = INT2BYTE( ( r + dcShift ) ); + values[1] = INT2BYTE( ( g + dcShift ) ); + values[2] = INT2BYTE( ( b + dcShift ) ); + + // Store the value in the RGB "block" + iColorPixelBlock[outputSize.iWidth-1] = TRgb( values[0], values[1], values[2] ); + } + + // Flush the row of color pixels to image processor + iImageProcessor->SetPixels( iColorPixelBlock, outputSize.iWidth ); + } + + SetPixelPos( tileStartCoord ); + } + else // To take care of YUV 4:4:4 + { + + for ( i = 0; i < outputSize.iHeight; i++ ) + { + SetPixelPos( tileStartCoord.iX, ( tileStartCoord.iY + i ) ); + + c1Row = c1[i]; + c2Row = c2[i]; + c3Row = c3[i]; + + for ( j = 0; j < outputSize.iWidth; j++ ) + { + y1 = *c1Row++; + u = *c2Row++; + v = *c3Row++; + + InverseICTTransform( y1, u, v, r, g, b ); + + values[0] = INT2BYTE( ( r + dcShift ) ); + values[1] = INT2BYTE( ( g + dcShift ) ); + values[2] = INT2BYTE( ( b + dcShift ) ); + + // Store the value in the RGB "block" + iColorPixelBlock[j] = TRgb( values[0], values[1], values[2] ); + } + + // Flush the row of color pixels to image processor + iImageProcessor->SetPixels( iColorPixelBlock, outputSize.iWidth ); + } + + SetPixelPos( tileStartCoord ); + } + } + else // RGB + { + TInt32 r = 0; + TInt32 g = 0; + TInt32 b = 0; + + for ( i = 0; i < outputSize.iHeight; i++ ) + { + + SetPixelPos( tileStartCoord.iX, tileStartCoord.iY + i ); + + c1Row = c1[i]; + c2Row = c2[i]; + c3Row = c3[i]; + + for ( j = 0; j < outputSize.iWidth; j++ ) + { + r = *c1Row++; + g = *c2Row++; + b = *c3Row++; + + values[0] = INT2BYTE( r + dcShift ); + values[1] = INT2BYTE( g + dcShift ); + values[2] = INT2BYTE( b + dcShift ); + + // Store the value in the RGB "block" + iColorPixelBlock[j] = TRgb( values[0], values[1], values[2] ); + } + + // Flush the row of color pixels to image processor + iImageProcessor->SetPixels( iColorPixelBlock, outputSize.iWidth ); + } + + SetPixelPos( tileStartCoord ); + } + } + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::WritePixel +// Write out a color pixel +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::WritePixel( TUint8 aR, TUint8 aG, TUint8 aB ) + { + iImageProcessor->SetPixel( TRgb( aR, aG, aB ) ); + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::WritePixel +// Write out a grayscale pixel +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::WritePixel( TUint8 aGray256 ) + { + iImageProcessor->SetMonoPixel( aGray256 ); + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::SetPixelPos +// Set the position of the pixel +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::SetPixelPos( const TPoint& aPosition ) + { + iImageProcessor->SetPos( aPosition ); + } + +// ----------------------------------------------------------------------------- +// CJ2kImageWriter::SetPixelPos +// Set the position of the pixel +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CJ2kImageWriter::SetPixelPos( const TInt aX, const TInt aY ) + { + iImageProcessor->SetPos( TPoint( aX, aY ) ); + } +