imageeditorengine/JpegScaler/src/CJpegScale.cpp
author Mikael Laine <mikael.laine@ixonos.com>
Fri, 29 Jan 2010 13:53:17 +0200
changeset 1 edfc90759b9f
permissions -rw-r--r--
Committing the Image Editor package under the Eclipse Public License

/*
* Copyright (c) 2010 Ixonos Plc.
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the "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:
* Ixonos Plc
*
* Description:  
*
*/


#include "CJpegScale.h"
#include "CJpeg.h"
#include "CJpegSave.h"
#include "CExifParser.h"


CJpegScale::CJpegScale( MJpegScaleCallBack* aCallBack )
	: iTargetScale( 0 )
	, iQuality( 95 )
	, iCallBack( aCallBack )
	{
	}
	

	
CJpegScale::~CJpegScale()
	{
	}
	

	
	
void CJpegScale::ScaleL()
	{
	const TInt KxShift = 14;
	const TInt KxMul = ( 1 << KxShift );
	const TInt KxAnd = KxMul - 1;	
	
	CJpeg* source;
	CJpegSave* target;
	
	// file server session
	RFs fs;
	User::LeaveIfError( fs.Connect() );
	CleanupClosePushL( fs );
	
	// encoded file
	RFile targetFile;
	User::LeaveIfError( targetFile.Replace( fs, iTargetName, EFileWrite ) );
	CleanupClosePushL( targetFile );
	
	// jpeg source
	source = CJpeg::NewL();
	CleanupStack::PushL( source );
	
	// jpeg target
	target = CJpegSave::NewL( &fs, &targetFile );
	CleanupStack::PushL( target );

	source->OpenL( iSourceName );
	
	TJpegData info = source->Info();

	TInt tw = iTargetSize.iWidth;
	TInt th = iTargetSize.iHeight;
	
	TInt sw = info.iSize.iWidth;
	TInt sh = info.iSize.iHeight;

	TInt xscale;
	TInt yscale;

	//
	// Decide scale or target size
	//
	if( iTargetScale != 0 )
		{
		xscale = (TInt)(iTargetScale * KxMul);
		yscale = xscale;
		tw = xscale * sw / KxMul;
		th = yscale * sh / KxMul;
		}
	else
		{
		yscale = KxMul * th / sh;
		xscale = KxMul * tw / sw;
		}

	if( tw>sw || th>sh )
		{
		// no upscaling yet.
		User::Leave( KErrNotSupported );
		}

	//
	// Hardscale is scaling which is done in jpeg decoder
	// possible hardscales are 1:1, 1:2, 1:4 and 1:8
	// hard scaling speeds up things a lot.
	//
	TInt hardScale = 1;
	source->SetScale( EScale1 );

	if( xscale <= KxMul/8 && yscale <= KxMul/8 )
		{
		hardScale = 8;
		source->SetScale( EScale8 );
		}

	else if( xscale <= KxMul/4 && yscale <= KxMul/4 )
		{
		hardScale = 4;
		source->SetScale( EScale4 );
		}

	else if( xscale <= KxMul/2 && yscale <= KxMul/2 )
		{
		hardScale = 2;
		source->SetScale( EScale2 );
		}
	// else hardscale = 1


	sw /= hardScale;
	sh /= hardScale;
	xscale *= hardScale;
	yscale *= hardScale;
	

	//
	// Jpeg store bitmap block
	// Jpeg encoder always uses block size of 16x8 pixels
	//
	TBitmapHandle blk;
	blk.iSize = TSize( 16,8 );
	blk.iData = new( ELeave )TUint32[ 16*8 ];
	CleanupStack::PushL( blk.iData );

	iTargetSize = TSize( tw,th );
		
	CExifParser* parser = CExifParser::NewLC();
	parser->ParseL(source->ExifData());
	
    // ImageWidth - Not used for JPEG images 
    if (parser->TagExist(CExifParser::EIfd0, 0x0100))
    {
        parser->DeleteTag ( CExifParser::EIfd0, 0x0100);
        //iExifParser->AddTagL ( CExifParser::EIfd0, 0x0100, (TUint16)iTargetSize.iWidth);        
    }

    // ImageHeight - Not used for JPEG images 
    if (parser->TagExist(CExifParser::EIfd0, 0x0101))
    {
        parser->DeleteTag ( CExifParser::EIfd0, 0x0101);
        //iExifParser->AddTagL ( CExifParser::EIfd0, 0x0101, (TUint16)iTargetSize.iHeight);
    }
	    
	// PixelXResolution 
    if (parser->TagExist(CExifParser::ESubIfd, 0xA002))
    {
        parser->DeleteTag ( CExifParser::ESubIfd, 0xA002);
        parser->AddTagL ( CExifParser::ESubIfd, 0xA002,(TUint32)iTargetSize.iWidth);
    }
    // PixelYResolution 
    if (parser->TagExist(CExifParser::ESubIfd, 0xA003))
    {
        parser->DeleteTag ( CExifParser::ESubIfd, 0xA003);
        parser->AddTagL ( CExifParser::ESubIfd, 0xA003, (TUint32)iTargetSize.iHeight);
    }

    TPtr8 exif = parser->SaveL(parser->ThumbData());
	
	//
	// Initialize jpeg encoder
	// no exif
	// save buffer ~16KB
	// use iQuality as quality ( values from 0 to 100 )
	//
	target->StartSaveL( iTargetSize, exif, 16000, iQuality );
	
	CleanupStack::PopAndDestroy(); // parser
    	
	TInt yStep = 8;		// jpeg encoder block height
	TInt yPos = 0;		// number of encoded lines
	
	//
	// Reserve scaled buffer
	//
	TInt pixels = tw * ( yStep*2 ); // room for 2 block lines

	TUint32* cr = new( ELeave )TUint32[ pixels ];
	CleanupStack::PushL( cr );
	TUint32* cg = new( ELeave )TUint32[ pixels ];
	CleanupStack::PushL( cg );
	TUint32* cb = new( ELeave )TUint32[ pixels ];
	CleanupStack::PushL( cb );
	TUint32* cm = new( ELeave )TUint32[ pixels ];
	CleanupStack::PushL( cm );

	//
	// Clear scaled buffer
	//
	Mem::FillZ( cr, pixels * sizeof( TUint32 ) );
	Mem::FillZ( cg, pixels * sizeof( TUint32 ) );
	Mem::FillZ( cb, pixels * sizeof( TUint32 ) );
	Mem::FillZ( cm, pixels * sizeof( TUint32 ) );
	

	TInt srcBlockHeight = info.iBlockSize.iHeight;
	
	TInt srcY1 = 0;
	TInt srcY2 = 0;
	TBitmapHandle bm;
	bm.iData = 0;

	for( TInt y=0; y<sh; y++ )
		{
		if( iCallBack )
			{
			iCallBack->JpegStatusCallBack( 100 * y / sh );
			}
		//
		// Fill source buffer when needed
		//
		if( srcY2 <= y )
			{
			delete bm.iData;
			TRect rect( 0,y*hardScale, info.iSize.iWidth, ( y + srcBlockHeight ) * hardScale );
			bm = source->LoadImageL( rect );
			srcY1 = y;
			srcY2 = srcY1 + bm.iSize.iHeight;
			}

		TUint32* srcRgb = (TUint32*)bm.iData;
		srcRgb += (y-srcY1) * bm.iSize.iWidth;

		TInt ty = y * yscale;
		TInt y2 = ty & KxAnd;
		TInt y1 = KxMul - y2;
		ty >>= KxShift;
		
		TInt oy = ( ty - yPos ) * tw;
		TUint32* pr = cr + oy;
		TUint32* pg = cg + oy;
		TUint32* pb = cb + oy;
		TUint32* pm = cm + oy;

		//
		// Add pixels to scaled buffer
		//
		for( TInt x=0; x<sw; x++ )
			{
			TInt tx = x * xscale;
			TInt x2 = tx & KxAnd;
			TInt x1 = KxMul - x2;
			tx >>= KxShift;
			
			TUint32 pixel = *srcRgb++;

			TUint32 m1 = x1*y1 / KxMul;
			TUint32 m2 = x2*y1 / KxMul;
			TUint32 m3 = x1*y2 / KxMul;
			TUint32 m4 = x2*y2 / KxMul;
			
			TInt red = pixel >> 16;
			pr[ tx ] += m1 * red;
			pr[ tx+1 ] += m2 * red;
			pr[ tx+tw ] += m3 * red;
			pr[ tx+tw+1 ] += m4 * red;

			TInt green = ( pixel >> 8 ) & 255;
			pg[ tx ] += m1 * green;
			pg[ tx+1 ] += m2 * green;
			pg[ tx+tw ] += m3 * green;
			pg[ tx+tw+1 ] += m4 * green;

			TInt blue = pixel & 255;
			pb[ tx ] += m1 * blue;
			pb[ tx+1 ] += m2 * blue;
			pb[ tx+tw ] += m3 * blue;
			pb[ tx+tw+1 ] += m4 * blue;

			pm[ tx ] += m1;
			pm[ tx+1 ] += m2;
			pm[ tx+tw ] += m3;
			pm[ tx+tw+1 ] += m4;
			}


		//
		// Transfer scaled buffer block-by-block to jpeg encoder
		//
		if( ty >= yPos + yStep || y == sh-1 )
			{
			yPos += yStep;

			TUint32* trgb = (TUint32*)blk.iData;
			
			for( TInt b=0; b<tw; b += 16 )
				{
				TUint32* p = trgb;
				for( TInt by=0; by<8; by++ )
				for( TInt bx=0; bx<16; bx++ )
					{
					TUint32 pos = b + bx + by*tw;
					TUint32 bcr = cr[ pos ];
					TUint32 bcg = cg[ pos ];
					TUint32 bcb = cb[ pos ];
					TUint32 m = cm[ pos ];
					if( m )
						{
						bcr /= m;
						bcg /= m;
						bcb /= m;
						}
					//trgb[ bx + by * 16 ] = bcr*65536 + bcg*256 + bcb;

					TUint32 c = bcr << 16;
					c += ( bcg << 8 );
					c += bcb;
					*p++ = c;
					}
				target->SaveBlock( blk );
				}

			//
			// move overflow line to first line in scaled buffer
			//
			TInt offset = tw * yStep;
			for( TInt rx=0; rx<tw; rx++ )
				{
				cr[ rx ] = cr[ rx + offset ];
				cg[ rx ] = cg[ rx + offset ];
				cb[ rx ] = cb[ rx + offset ];
				cm[ rx ] = cm[ rx + offset ];

				}

			//
			// clear all but first line from scaled buffer
			//
			TInt clrbytes = tw * yStep * sizeof( TUint32 );
			offset = tw;
			Mem::FillZ( cr+offset, clrbytes );
			Mem::FillZ( cg+offset, clrbytes );
			Mem::FillZ( cb+offset, clrbytes );
			Mem::FillZ( cm+offset, clrbytes );
			}
		}

	target->FinalizeSave();
	
	delete bm.iData;

	CleanupStack::PopAndDestroy( 4 ); // cm,cb,cg,cr
	CleanupStack::PopAndDestroy( blk.iData );
	CleanupStack::PopAndDestroy( target );
	CleanupStack::PopAndDestroy( source );
	CleanupStack::PopAndDestroy( 2 ); // file, fs

	if( iCallBack )
		{
		iCallBack->JpegStatusCallBack( 100 );
		}

	}