/*
* Copyright (c) 2008 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: Implementation of the resource pool
*
*/
// From this component
#include "alfresourcepoolimpl.h"
// From the same subsystem
#include <osn/osnnew.h>
#include <alf/alfimageloaderutil.h>
#include <alf/alfutil.h>
#include <alf/alfdisplay.h>
#include <alf/alfwidgetenvextension.h>
// Outside the same subsystem
#include <AknsItemID.h>
#include <utf.h>
// standard C++/STL
#include <algorithm>
#include <cstdlib>
#include <stdexcept>
namespace Alf
{
#define KResourcePoolHexadecimalPrefix "0x"
const unsigned int INITIAL_IMAGE_WIDTH = 0; // no magic
const unsigned int INITIAL_IMAGE_HEIGHT = 0; // no magic
const TAlfTextureFlags IMAGE_RESOURCE_DEFAULT_TEXTURE_FLAGS = TAlfTextureFlags(EAlfTextureFlagAutoSize);
const TAlfTextureFlags IMAGE_RESOURCE_SKINNED_TEXTURE_FLAGS = TAlfTextureFlags(EAlfTextureFlagAutoSize | EAlfTextureFlagSkinContent);
// Function is Not used.
/**********************************
Utf8* alloc8BitBuffer( const Utf8* aSource )
{
int length( 0 );
while ( aSource && aSource[length] != '\0' )
{
length++;
}
length++; // for the NULL characher
Utf8* result = new (EMM) Utf8[ length ];
for ( int c = 0; c < ( length - 1 ); c++ )
{
result[c] = aSource[c];
}
result[length - 1] = '\0'; // add the NULL character at the end
return result;
}
********************************************/
Utf8* allocLowerCase( const Utf8* aTag )
{
const unsigned int length = aTag ? strlen(aTag) : 0;
Utf8* result = new (EMM) Utf8[ length+1 ];
for ( int s = 0 ; s < length ; s++ )
{
result[s] = tolower( aTag[s]);
}
result[length] = '\0'; // add the NULL character at the end
return result;
}
TInt RoundFloatToInt(TReal32 aVal)
{
return (aVal < 0 ? (TInt)(aVal - 0.5f) : (TInt)(aVal + 0.5f));
}
// ======== MEMBER FUNCTIONS ========
ResourcePoolImpl::ImageResource::ImageResource()
: mTag(0), mAspectRatio( ResourcePool::aspectRatioPreserved ), mReferenceCount(1)
{
mInitialSizeHint.iX.iMagnitude = INITIAL_IMAGE_WIDTH;
mInitialSizeHint.iX.iUnit = EAlfUnitPixel;
mInitialSizeHint.iY.iMagnitude = INITIAL_IMAGE_HEIGHT;
mInitialSizeHint.iY.iUnit = EAlfUnitPixel;
}
ResourcePoolImpl::ImageResource::~ImageResource()
{
delete mTag;
}
// ---------------------------------------------------------------------------
// ?description_if_needed
// ---------------------------------------------------------------------------
//
ResourcePoolImpl::ResourcePoolImpl(
CAlfTextureManager& aTextureManager,
ResourcePool* aParentPool )
: mTextureManager( aTextureManager ),
mParentPool( aParentPool )
{
//This may throw an exception leading to object creation failure.
}
// ---------------------------------------------------------------------------
// ?description_if_needed
// ---------------------------------------------------------------------------
//
ResourcePoolImpl::~ResourcePoolImpl()
{
// delete the texture instances
for ( int resourceIndex = 0 ; resourceIndex < mResources.count() ; resourceIndex++ )
{
ImageResource* resource = mResources[resourceIndex];
for ( int textureIndex = 0 ; textureIndex < resource->mLoadedTextures.count() ; textureIndex++ )
{
const CAlfTexture* texture = mTextureManager.Texture( resource->mLoadedTextures[textureIndex]->mTextureId);
if ( texture->Id() == resource->mLoadedTextures[textureIndex]->mTextureId )
{
// make sure we do not delete blank texure
delete texture;
if (resource->mLoadedTextures[textureIndex]->mAutoSizeTexture)
{
mTextureManager.RemoveAutoSizeObserver(resource->mLoadedTextures[textureIndex]->mImageLoaderUtil);
}
}
}
}
// items in mResources will be automatically deleted in the destructor of
// the AlfPtrVector
}
void ResourcePoolImpl::createLogicalImageResource( const Utf8* aTag )
{
}
void ResourcePoolImpl::createThemeImageResource(
const Utf8* aTag,
const UString& aThemeDefinition )
{
}
void ResourcePoolImpl::createFileImageResource(
const Utf8* aTag,
const UString& aFileName , TAlfTextureFlags aFlag)
{
if ( !aTag )
{
throw invalid_argument("NULL ptr");
}
auto_ptr<Utf8> lowerCaseTag( allocLowerCase( aTag ) );
ImageResource* existingResource = findResource( lowerCaseTag.get() );
if ( existingResource )
{
existingResource->mReferenceCount++;
return;
}
// : check that the file exists.
auto_ptr<FileImageResource> newFileResource( new (EMM) FileImageResource );
newFileResource->mTag = lowerCaseTag.get();
lowerCaseTag.release();
newFileResource->mType = imageResourceTypeFileOther;
newFileResource->mFileName = aFileName;
newFileResource->mFlag = aFlag;
// check the .svg prefix
const long length = aFileName.getCharLength();
if ( length > 4 )
{
auto_ptr<Utf8> lowerCaseTag( allocLowerCase( aFileName.getUtf8() ) );
if ( lowerCaseTag.get()[length-4] == '.' &&
lowerCaseTag.get()[length-3] == 's' &&
lowerCaseTag.get()[length-2] == 'v' &&
lowerCaseTag.get()[length-1] == 'g' )
{
newFileResource->mType = imageResourceTypeFileSVG;
}
}
mResources.resize(mResources.count()+1);
mResources.insert(mResources.count(),newFileResource.get());
newFileResource.release();
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void ResourcePoolImpl::deleteImageResource( const Utf8* aTag )
{
auto_ptr<Utf8> lowerCaseTag( allocLowerCase( aTag ) );
ImageResource* existingResource = findResource( lowerCaseTag.get() );
if ( !existingResource )
{
return;
}
existingResource->mReferenceCount--;
if ( existingResource->mReferenceCount > 0 )
{
// not time to delete yet.
return;
}
const int resouceIndex = findResourceIndex( lowerCaseTag.get() );
// delete CAlfTexture instances created by this resource
if ( resouceIndex != -1 )
{
ImageResource* resource = mResources[resouceIndex];
for ( int textureIndex = 0 ; textureIndex < resource->mLoadedTextures.count() ; textureIndex++ )
{
const CAlfTexture* texture = mTextureManager.Texture( resource->mLoadedTextures[textureIndex]->mTextureId);
if ( texture->Id() == resource->mLoadedTextures[textureIndex]->mTextureId )
{
// make sure we do not delete blank texure
delete texture;
if (resource->mLoadedTextures[textureIndex]->mAutoSizeTexture)
{
mTextureManager.RemoveAutoSizeObserver(resource->mLoadedTextures[textureIndex]->mImageLoaderUtil);
}
}
}
mResources.remove( resouceIndex );
}
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
bool ResourcePoolImpl::hasImageResource( const Utf8* aTag ) const
{
auto_ptr<Utf8> lowerCaseTag( allocLowerCase( aTag ) );
int resouceIndex = findResourceIndex( lowerCaseTag.get() );
if ( resouceIndex != -1 )
{
return true;
}
return false;
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void ResourcePoolImpl::setInitialSize(
const Utf8* aTag,
const TAlfXYMetric& aInitialSizeHint )
{
auto_ptr<Utf8> lowerCaseTag( allocLowerCase( aTag ) );
ImageResource* imageResource = findResource( lowerCaseTag.get() );
if ( imageResource )
{
imageResource->mInitialSizeHint = aInitialSizeHint;
}
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void ResourcePoolImpl::setAspectRatio(
const Utf8* aTag,
ResourcePool::AspectRatio aAspectRatio )
{
auto_ptr<Utf8> lowerCaseTag( allocLowerCase( aTag ) );
ImageResource* imageResource = findResource( lowerCaseTag.get() );
if ( imageResource )
{
imageResource->mAspectRatio = aAspectRatio;
}
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TAlfImage ResourcePoolImpl::getImageResource( const Utf8* aTag )
{
auto_ptr<Utf8> lowerCaseTag( allocLowerCase( aTag ) );
ImageResource* imageResource = findResource( lowerCaseTag.get() );
if ( imageResource )
{
return getImageResource(
lowerCaseTag.get() ,
imageResource->mInitialSizeHint );
}
return TAlfImage(); // return empty
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TAlfImage ResourcePoolImpl::getImageResource(
const Utf8* aTag,
const TAlfXYMetric& aSizeHint )
{
auto_ptr<Utf8> lowerCaseTag( allocLowerCase( aTag ) );
ImageResource* imageResource = findResource( lowerCaseTag.get() );
if ( imageResource )
{
// check if the texture ID with the given size hint is already generated
if ( imageResource->mLoadedTextures.count() )
{
const TSize requestedSize( determineSizeInPixels(aSizeHint) );
const CAlfTexture* texture = NULL;
for ( TInt idIndex = 0 ; idIndex < imageResource->mLoadedTextures.count() ; idIndex++ )
{
texture = mTextureManager.Texture(imageResource->mLoadedTextures[idIndex]->mTextureId);
if ( texture->Id() != imageResource->mLoadedTextures[idIndex]->mTextureId )
{
// if the texture manager returns blank, return empty.
return TAlfImage(); // return empty
}
TSize textureSize( const_cast<CAlfTexture*>(texture)->MaxTextureSize() );
if ( textureSize.iWidth == 0 || textureSize.iHeight == 0 )
{
// texture loaded directly with the size, use that.
textureSize = texture->Size();
}
const bool autosize = imageResource->mLoadedTextures[idIndex]->mAutoSizeTexture;
if ( requestedSize == TSize(0,0) && autosize )
{
// if zero size is requested, we think autosizing image is requested
// and if we find one autosizing texture we return it.
return TAlfImage( *texture );
}
else if ( areSizesCloseEnough( requestedSize, textureSize ) && !autosize )
{
// we found existing texure -> use that
return TAlfImage( *texture );
}
}
}
// If we have not found a suitable resource, create one.
TAlfImage result;
TRAPD( sError ,
{
// Create new one.
switch( imageResource->mType )
{
case imageResourceTypeSkin:
result = CreateSkinImageResourceL(
static_cast<SkinImageResource&>( *imageResource ), aSizeHint );
break;
case imageResourceTypeFileOther:
result = CreateFileImageResourceL(
static_cast<FileImageResource&>( *imageResource ), aSizeHint , imageResource->mFlag );
break;
case imageResourceTypeFileSVG:
result = CreateSVGImageResourceL(
static_cast<FileImageResource&>( *imageResource ), aSizeHint );
break;
default:
break;
}
}); // TRAP
if ( sError != KErrNone )
{
throw invalid_argument("cannot create image resource");
}
return result;
}
else
{
// this pool does not contain the resource, check the parent pool
if ( mParentPool )
{
return mParentPool->getImageResource( lowerCaseTag.get(), aSizeHint );
}
}
return TAlfImage(); // return empty
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
ResourcePoolImpl::ImageResource* ResourcePoolImpl::findResource( const Utf8* aTag ) const
{
const int index = findResourceIndex( aTag );
if ( index != -1 )
{
return mResources.at(index);
}
return NULL;
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
int ResourcePoolImpl::findResourceIndex( const Utf8* aTag ) const
{
for ( int i = 0 ; i < mResources.count() ; i++ )
{
// : find a better way to compare
int c = 0;
while ( mResources.at(i)->mTag[c] != '\0' )
{
if ( mResources.at(i)->mTag[c] != aTag[c] )
{
break;
}
c++;
}
if ( mResources.at(i)->mTag[c] == '\0' && aTag[c] == '\0' )
{
return i;
}
}
return -1;
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void ResourcePoolImpl::DetermineSkinInstanceL(
const Utf8* aTag, TAknsItemID& aSkinItemID ) const
{
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TSize ResourcePoolImpl::determineSizeInPixels( const TAlfXYMetric& aSize )
{
TSize result(0,0);
TRect displayRect(0,0,0,0);
// check X
if ( aSize.iX.iUnit == EAlfUnitPixel )
{
result.iWidth = aSize.iX.iMagnitude;
}
else if ( aSize.iX.iUnit == EAlfUnitDisplaySize )
{
// check the primary display
if ( mTextureManager.Env().DisplayCount() )
{
const CAlfDisplay& display = mTextureManager.Env().PrimaryDisplay();
displayRect = display.VisibleArea();
result.iWidth = RoundFloatToInt( TReal32(displayRect.Width())*aSize.iX.iUnit );
}
}
// check Y
if ( aSize.iY.iUnit == EAlfUnitPixel )
{
result.iHeight = aSize.iY.iMagnitude;
}
else if ( aSize.iY.iUnit == EAlfUnitDisplaySize )
{
// check the primary display (if not checked already
if ( !displayRect.Height() && mTextureManager.Env().DisplayCount() )
{
const CAlfDisplay& display = mTextureManager.Env().PrimaryDisplay();
displayRect = display.VisibleArea();
}
result.iHeight = RoundFloatToInt( TReal32(displayRect.Height())*aSize.iY.iUnit );
}
return result;
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void ResourcePoolImpl::determineSkinId( const UString& aNumberString, int& aSkinItem )
{
aSkinItem = -1;
const unsigned int charLength = aNumberString.getCharLength();
if ( charLength )
{
// there is something
// check if we are dealing with hexadecimal entry
const string strNumber = aNumberString.getUtf8();
if ( charLength > 3 && strNumber.substr(0,2).compare( KResourcePoolHexadecimalPrefix ) == 0 )
{
// hexa it is
aSkinItem = strtol( aNumberString.getUtf8(), NULL, 16 );
}
else
{
// try normal decimal number
aSkinItem = atoi( aNumberString.getUtf8() );
// if the 'atoi' returns 0, make sure it is that
if ( aSkinItem == 0 )
{
if ( aNumberString.getUtf8()[0] != '0' )
{
aSkinItem = -1;
}
}
}
}
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TAlfImage ResourcePoolImpl::CreateSkinImageResourceL(
SkinImageResource& aSkinImageResource,
const TAlfXYMetric& aSizeHint)
{
CAlfAutoSizeImageLoaderUtil* imageLoaderUtil = new (ELeave) CAlfAutoSizeImageLoaderUtil;
CleanupStack::PushL( imageLoaderUtil );
CAlfTexture* texture = NULL;
MAlfBitmapProvider* bitmapProvider = NULL;
TAknsItemID skinId;
skinId.Set( aSkinImageResource.mSkinIdMajor, aSkinImageResource.mSkinIdMinor );
TSize size( determineSizeInPixels(aSizeHint) );
imageLoaderUtil->SetSize( size, static_cast<TScaleMode>(aSkinImageResource.mAspectRatio));
TInt textureFlags = IMAGE_RESOURCE_SKINNED_TEXTURE_FLAGS;
if (size != TSize(0,0))
{
// If sizehint was provided, turn autosize off at least for now.
textureFlags &= ~EAlfTextureFlagAutoSize;
}
if ( aSkinImageResource.mFallBackFileName.getCharLength() )
{
HBufC* sUnicodeBuffer =
CnvUtfConverter::ConvertToUnicodeFromUtf8L(
TPtrC8((TUint8*)aSkinImageResource.mFallBackFileName.getUtf8()));
CleanupStack::PushL( sUnicodeBuffer );
bitmapProvider =
imageLoaderUtil->CreateImageLoaderL(
skinId,
*sUnicodeBuffer,
aSkinImageResource.mFallBackIndex,
aSkinImageResource.mFallBackMaskIndex );
CleanupStack::PopAndDestroy( sUnicodeBuffer );
}
else
{
bitmapProvider =
imageLoaderUtil->CreateImageLoaderL(
skinId,
KNullDesC(),
-1,
-1 );
}
texture = &mTextureManager.CreateTextureL( KAlfAutoGeneratedTextureId, bitmapProvider, TAlfTextureFlags(textureFlags));
CleanupStack::PushL( texture );
// Enable ref counting
texture->EnableRefCounting();
// store texture ID and create image
ResourceInstanceData* newResourceInstance = new (ELeave) ResourceInstanceData;
newResourceInstance->mTextureId = texture->Id();
newResourceInstance->mImageLoaderUtil = imageLoaderUtil;
newResourceInstance->mAutoSizeTexture = (textureFlags & EAlfTextureFlagAutoSize);
try
{
aSkinImageResource.mLoadedTextures.resize(aSkinImageResource.mLoadedTextures.count()+1);
aSkinImageResource.mLoadedTextures.insert(aSkinImageResource.mLoadedTextures.count(),newResourceInstance);
}
catch ( ... )
{
delete newResourceInstance;
User::Leave( KErrNoMemory );
}
CleanupStack::Pop( texture );
CleanupStack::Pop( imageLoaderUtil ); // mLoadedTextures has taken the ownership
if (textureFlags & EAlfTextureFlagAutoSize)
{
mTextureManager.AddAutoSizeObserverL(imageLoaderUtil);
}
return TAlfImage( *texture );
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TAlfImage ResourcePoolImpl::CreateFileImageResourceL(
FileImageResource& aFileImageResource,
const TAlfXYMetric& aSizeHint , TAlfTextureFlags aFlag)
{
CAlfTexture* texture = NULL;
HBufC* sUnicodeBuffer =
CnvUtfConverter::ConvertToUnicodeFromUtf8L(
TPtrC8((TUint8*)aFileImageResource.mFileName.getUtf8()));
CleanupStack::PushL( sUnicodeBuffer );
TSize size(determineSizeInPixels( aSizeHint ) );
TInt textureFlags = IMAGE_RESOURCE_DEFAULT_TEXTURE_FLAGS;
textureFlags |= aFlag;
if (size != TSize(0,0))
{
// If sizehint was provided, turn autosize off at least for now.
textureFlags &= ~EAlfTextureFlagAutoSize;
}
if ( size.iWidth && size.iHeight )
{
texture = &mTextureManager.LoadTextureL(
*sUnicodeBuffer,
size,
TAlfTextureFlags(textureFlags),
KAlfAutoGeneratedTextureId );
}
else
{
texture = &mTextureManager.LoadTextureL(
*sUnicodeBuffer,
TAlfTextureFlags(textureFlags),
KAlfAutoGeneratedTextureId );
}
CleanupStack::PopAndDestroy( sUnicodeBuffer );
CleanupStack::PushL( texture );
// Enable ref counting
texture->EnableRefCounting();
// store texture ID and create image
ResourceInstanceData* newResourceInstance = new (ELeave) ResourceInstanceData;
newResourceInstance->mTextureId = texture->Id();
newResourceInstance->mAutoSizeTexture = (textureFlags & EAlfTextureFlagAutoSize);
try
{
aFileImageResource.mLoadedTextures.resize(aFileImageResource.mLoadedTextures.count()+1);
aFileImageResource.mLoadedTextures.insert(aFileImageResource.mLoadedTextures.count(),newResourceInstance);
}
catch ( ... )
{
delete newResourceInstance;
User::Leave( KErrNoMemory );
}
CleanupStack::Pop( texture );
return TAlfImage( *texture );
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TAlfImage ResourcePoolImpl::CreateSVGImageResourceL(
FileImageResource& aSVGImageResource,
const TAlfXYMetric& aSizeHint)
{
CAlfAutoSizeImageLoaderUtil* imageLoaderUtil = new (ELeave) CAlfAutoSizeImageLoaderUtil;
CleanupStack::PushL( imageLoaderUtil );
MAlfBitmapProvider* bitmapProvider = NULL;
CAlfTexture* texture = NULL;
TSize size( determineSizeInPixels(aSizeHint) );
imageLoaderUtil->SetSize( size, static_cast<TScaleMode>(aSVGImageResource.mAspectRatio) );
TInt textureFlags = IMAGE_RESOURCE_SKINNED_TEXTURE_FLAGS;
if (size != TSize(0,0))
{
// If sizehint was provided, turn autosize off at least for now.
textureFlags &= ~EAlfTextureFlagAutoSize;
}
HBufC* sUnicodeBuffer =
CnvUtfConverter::ConvertToUnicodeFromUtf8L(
TPtrC8((TUint8*)aSVGImageResource.mFileName.getUtf8()));
CleanupStack::PushL( sUnicodeBuffer );
bitmapProvider = imageLoaderUtil->CreateSVGImageLoaderL( *sUnicodeBuffer );
CleanupStack::PopAndDestroy( sUnicodeBuffer );
texture = &mTextureManager.CreateTextureL( KAlfAutoGeneratedTextureId, bitmapProvider, TAlfTextureFlags(textureFlags));
CleanupStack::PushL( texture );
// Enable ref counting
texture->EnableRefCounting();
// store texture ID and create image
ResourceInstanceData* newResourceInstance = new (ELeave) ResourceInstanceData;
newResourceInstance->mTextureId = texture->Id();
newResourceInstance->mImageLoaderUtil = imageLoaderUtil;
newResourceInstance->mAutoSizeTexture = (textureFlags & EAlfTextureFlagAutoSize);
try
{
aSVGImageResource.mLoadedTextures.resize(aSVGImageResource.mLoadedTextures.count()+1);
aSVGImageResource.mLoadedTextures.insert(aSVGImageResource.mLoadedTextures.count(),newResourceInstance);
}
catch ( ... )
{
delete newResourceInstance;
User::Leave( KErrNoMemory );
}
CleanupStack::Pop( texture );
CleanupStack::Pop( imageLoaderUtil ); // mLoadedTextures has taken the ownership
if (textureFlags & EAlfTextureFlagAutoSize)
{
mTextureManager.AddAutoSizeObserverL(imageLoaderUtil);
}
return TAlfImage( *texture );
}
bool ResourcePoolImpl::areSizesCloseEnough( const TSize& aSize1, const TSize& aSize2 )
{
// this is the inteligent part:)
// the function tries to determine do we need to rasterize a new texture or use the existing one.
// check if the other one is zero size
if ( !aSize1.iWidth || !aSize1.iHeight || !aSize2.iWidth || !aSize2.iHeight )
{
return false;
}
// check if both are smaller than 10px
if ( aSize1.iWidth < 10 && aSize1.iHeight < 10 && aSize2.iWidth < 10 && aSize2.iHeight < 10 )
{
return true;
}
// check if both dimensions fit in 10% range.
bool result = false;
// check width
if ( aSize1.iWidth <= aSize2.iWidth*1.1f && aSize1.iWidth >= aSize2.iWidth*0.9f )
{
// width in the range, chech height
if ( aSize1.iHeight <= aSize2.iHeight*1.1f && aSize1.iHeight >= aSize2.iHeight*0.9f )
{
// height in the range
result = true;
}
}
return result;
}
} // namespace Alf