/*
* Copyright (c) 2006-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:
*
*/
#include "huicanvastexturecache.h"
#include "uiacceltk/HuiImage.h"
#include "uiacceltk/HuiTexture.h"
#include "uiacceltk/HuiEnv.h"
#include "uiacceltk/HuiUtil.h"
#include "uiacceltk/HuiStatic.h"
#include "huicanvasgc.h"
#include "HuiRenderPlugin.h"
#include "huicanvasrenderbuffer.h"
#include <e32cmn.h>
#include <bitdev.h>
#include <AknLayoutFont.h>
#include <e32property.h>
// Enable this for debugging cache usage
//#define HUI_DEBUG_PRINT_CANVAS_TEXTURE_CACHE
//#define HUI_DEBUG_PRINT_CANVAS_TEXTURE_CACHE_EGL
//#define HUI_DEBUG_PRINT_CANVAS_TEXTURE_CACHE_EXTENDED
// Enable this for debugging performance
//#define HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
/** Magic S60 font constant */
const TInt KHuiFontVerticalShiftInPixels = 1;
/** Initial touch count value */
const TInt KHuiInitialBitmapTouchCount = -1;
/** Estimated bits per pixel for all textures */
const TReal32 KHuiTextureEstimatedBpp = 32;
/** Estimated bits per pixel for rasterized canvas texts */
const TReal32 KHuiCanvasTextEstimatedBpp = 8;
/** Estimated bits per pixel for canvas images */
const TReal32 KHuiCanvasImageEstimatedBpp = 32;
/** Estimated bits per pixel for canvas render buffers */
const TReal32 KHuiCanvasRenderBufferEstimatedBpp = 32;
/** Constant to define target how much memory UI textures should use,
this is not a hard limit but effects how long unused textures are cached */
const TInt KHuiMaxRecommendedTextureAmountInKBytes = 4096;
/** Constant to define target how much memory UI render buffers should use,
this is not a hard limit but effects how long unused render buffers are cached */
const TInt KHuiMaxRecommendedRenderBufferAmountInKBytes = 0;
/** Ratio how cache is divided between canvas texts and images */
const TReal32 KHuiCanvasUnusedTextImageCacheRatio = 0.1f;
/** Max amount of recycled texture objects */
const TInt KHuiMaxRecycledTextureCount = 100;
/** Use color modulation or not for setting text color */
const TBool KHuiCanvasTextUseColorModulation = ETrue;
/** Store bitmap pointers to avoid handle duplication */
const TBool KHuiCanvasKeepBitmapPointer = ETrue;
CHuiCanvasImage::CHuiCanvasImage() :
iTexture(NULL),
iCache(NULL)
{
}
CHuiCanvasImage::~CHuiCanvasImage()
{
if (iCache && iTexture)
{
iCache->DeleteRecycledTexture(iTexture);
}
else
{
delete iTexture;
}
iActiveUsers.Close();
}
void CHuiCanvasImage::RefreshUser(const CHuiCanvasVisual& aUser)
{
if (iActiveUsers.FindInAddressOrder(&aUser) == KErrNotFound)
{
iActiveUsers.InsertInAddressOrder(&aUser);
}
// Update last usage time
iLastUsed.UniversalTime();
}
void CHuiCanvasImage::RemoveUser(const CHuiCanvasVisual& aUser)
{
TInt index = iActiveUsers.FindInAddressOrder(&aUser);
if (index != KErrNotFound)
{
iActiveUsers.Remove(index);
}
}
TBool CHuiCanvasImage::IsUser(const CHuiCanvasVisual& aUser) const
{
TInt index = iActiveUsers.FindInAddressOrder(&aUser);
if (index != KErrNotFound)
{
return ETrue;
}
return EFalse;
}
TBool CHuiCanvasImage::IsAnyUser() const
{
return iActiveUsers.Count() != 0;
}
CHuiTexture* CHuiCanvasImage::Texture() const
{
return iTexture;
}
void CHuiCanvasImage::CopyAttributes(CHuiCanvasImage& aSrc)
{
iTexture = aSrc.iTexture;
iGcParams = aSrc.iGcParams;
iLastUsed = aSrc.iLastUsed;
iCache = aSrc.iCache;
iActiveUsers.Reset();
for (TInt i=0; i < aSrc.iActiveUsers.Count(); i++)
{
iActiveUsers.Append(aSrc.iActiveUsers[i]);
}
}
void CHuiCanvasImage::Reset()
{
// Reset does not delete owned items !
iTexture = NULL;
iGcParams = THuiCachedGcParams();
iActiveUsers.Reset();
}
CHuiCanvasGraphicImage::CHuiCanvasGraphicImage() :
iBitmapHandle(0),
iMaskHandle(0),
iBitmap(NULL),
iMask(NULL),
iInvertedMask(EFalse),
iMaskOriginPoint(TPoint(0,0)),
iBitmapTouchCount(-1),
iMaskTouchCount(-1),
iImageSize(TSize(0,0))
{
}
void CHuiCanvasGraphicImage::CopyAttributes(CHuiCanvasGraphicImage& aSrc)
{
CHuiCanvasImage::CopyAttributes(aSrc);
iBitmap = aSrc.iBitmap;
iMask = aSrc.iMask;
iBitmapHandle = aSrc.iBitmapHandle;
iMaskHandle = aSrc.iMaskHandle;
iInvertedMask = aSrc.iInvertedMask;
iMaskOriginPoint = aSrc.iMaskOriginPoint;
iTexture = aSrc.iTexture;
iBitmapTouchCount = aSrc.iBitmapTouchCount;
iMaskTouchCount = aSrc.iMaskTouchCount;
iImageSize = aSrc.iImageSize;
iSubImages.Reset();
for (TInt i=0; i<aSrc.iSubImages.Count();i++)
{
iSubImages.Append(aSrc.iSubImages[i]);
}
}
void CHuiCanvasGraphicImage::Reset()
{
// Reset does not delete owned items !
CHuiCanvasImage::Reset();
iBitmap = NULL;
iMask = NULL;
iSubImages.Reset();
iBitmapHandle = 0;
iMaskHandle = 0;
iInvertedMask = EFalse;
iMaskOriginPoint = TPoint(0,0);
iTexture = NULL;
iBitmapTouchCount = -1;
iMaskTouchCount = -1;
iImageSize = TSize(0,0);
};
CHuiCanvasGraphicImage::~CHuiCanvasGraphicImage()
{
delete iBitmap;
delete iMask;
for (TInt i=0;i<iSubImages.Count();i++)
{
delete iSubImages[i].iBitmap;
delete iSubImages[i].iMask;
}
iSubImages.Close();
}
CHuiCanvasTextImage::CHuiCanvasTextImage() :
iText(NULL),
iFindTextPtr(NULL),
iFontHandle(0),
iRasterizationOffset(TPoint(0,0)),
iTextBoxMaxSize(TSize(KMinTInt,KMinTInt)),
iTextWidth(KMinTInt),
iBaseLineOffset(KMinTInt),
iTextAlign(CGraphicsContext::ELeft),
iMargin(0),
iAngle(0),
iUseColorModulation(EFalse),
iFont(NULL)
{
}
void CHuiCanvasTextImage::CopyAttributes(CHuiCanvasTextImage& aSrc)
{
CHuiCanvasImage::CopyAttributes(aSrc);
iText = aSrc.iText;
iFindTextPtr = aSrc.iFindTextPtr;
iFontHandle = aSrc.iFontHandle;
iRasterizationOffset = aSrc.iRasterizationOffset;
iTextBoxMaxSize = aSrc.iTextBoxMaxSize;
iTextWidth = aSrc.iTextWidth;
iBaseLineOffset = aSrc.iBaseLineOffset;
iTextAlign = aSrc.iTextAlign;
iMargin = aSrc.iMargin;
iAngle = aSrc.iAngle;
iUseColorModulation = aSrc.iUseColorModulation;
iTextParams = aSrc.iTextParams;
iFont = aSrc.iFont;
}
void CHuiCanvasTextImage::Reset()
{
// Reset does not delete owned items !
CHuiCanvasImage::Reset();
iText = NULL;
iFindTextPtr = NULL;
iFontHandle = 0,
iRasterizationOffset = TPoint(0,0);
iTextBoxMaxSize = TSize(KMinTInt,KMinTInt);
iTextWidth = KMinTInt;
iBaseLineOffset = KMinTInt;
iTextAlign = CGraphicsContext::ELeft;
iMargin = 0;
iAngle = 0;
iUseColorModulation = EFalse;
iFont = NULL;
}
CHuiCanvasTextImage::~CHuiCanvasTextImage()
{
delete iText;
iText = NULL;
}
TBool CHuiCanvasTextImage::UseColorModulation() const
{
return iUseColorModulation;
}
NONSHARABLE_CLASS( CHuiCanvasRenderBufferImage ) : public CHuiCanvasImage
{
public:
CHuiCanvasRenderBufferImage();
~CHuiCanvasRenderBufferImage();
virtual void CopyAttributes(CHuiCanvasRenderBufferImage& aSrc);
virtual void Reset();
public:
CHuiCanvasRenderBuffer* iCanvasRenderBuffer;
TAny* iOwner;
};
CHuiCanvasRenderBufferImage::CHuiCanvasRenderBufferImage() :
iCanvasRenderBuffer(NULL),
iOwner(NULL)
{
}
void CHuiCanvasRenderBufferImage::CopyAttributes(CHuiCanvasRenderBufferImage& aSrc)
{
CHuiCanvasImage::CopyAttributes(aSrc);
iCanvasRenderBuffer= aSrc.iCanvasRenderBuffer;
iOwner = aSrc.iOwner;
}
void CHuiCanvasRenderBufferImage::Reset()
{
// Reset does not delete owned items !
CHuiCanvasImage::Reset();
iCanvasRenderBuffer = NULL;
iOwner = NULL;
}
CHuiCanvasRenderBufferImage::~CHuiCanvasRenderBufferImage()
{
delete iCanvasRenderBuffer;
}
NONSHARABLE_CLASS( CHuiCanvasImageRasterizer ) : public CBase
{
public:
CHuiCanvasImageRasterizer();
~CHuiCanvasImageRasterizer();
void ConstructL(TDisplayMode aPreferredMode = ENone);
TBool IsSeparateTextMaskUsed();
static CFbsBitmap* CreateMovedMaskL(CFbsBitmap& aOriginalMask, TPoint aNewTopLeftCorner);
static CFbsBitmap* CreateInvertedMaskL(CFbsBitmap& aOriginalMask);
protected:
CFbsBitmap* iTempMask;
CFbsBitmapDevice* iTempMaskDevice;
CFbsBitGc* iTempMaskGc;
CFbsBitmap* iTempBitmap;
CFbsBitmapDevice* iTempBitmapDevice;
CFbsBitGc* iTempBitmapGc;
};
CHuiCanvasImageRasterizer::CHuiCanvasImageRasterizer()
{
}
CHuiCanvasImageRasterizer::~CHuiCanvasImageRasterizer()
{
delete iTempMaskGc;
delete iTempMaskDevice;
delete iTempMask;
delete iTempBitmapGc;
delete iTempBitmapDevice;
delete iTempBitmap;
}
void CHuiCanvasImageRasterizer::ConstructL(TDisplayMode aPreferredMode)
{
iTempMask = NULL;
iTempMaskDevice = NULL;
iTempMaskGc = NULL;
iTempBitmap = NULL;
iTempBitmapDevice = NULL;
iTempBitmapGc = NULL;
// Select fastest color mode for uploading rasterized texts, we assume that EColor16MA
// should be best for HW accelrated rendereres (requiring least amount of conversions
// needed before uploading to texture memory) and EColor64K is good for
// bitgdi because its backbuffer is in that format.
//
// NOTE: EHuiRendererGles10 and EHuiRendererGles11 temporarely use EColor64K
// beacuse it looks as there are some problems with uploading 16MA
// mode bitmaps into multisegmented textures.
//
TDisplayMode colorMode = EColor16MA;
if (aPreferredMode == EGray256 &&
CHuiStatic::Env().Renderer() == EHuiRendererVg10)
{
colorMode = EGray256;
}
else if (aPreferredMode == EColor64K)
{
colorMode = EColor64K;
}
else if (CHuiStatic::Env().Renderer() == EHuiRendererBitgdi ||
CHuiStatic::Env().Renderer() == EHuiRendererGles10 ||
CHuiStatic::Env().Renderer() == EHuiRendererGles11)
{
colorMode = EColor64K;
}
iTempBitmap = new (ELeave) CFbsBitmap;
iTempBitmap->Create(TSize(4,4), colorMode);
iTempBitmapDevice = CFbsBitmapDevice::NewL(iTempBitmap);
User::LeaveIfError(iTempBitmapDevice->CreateContext(iTempBitmapGc));
if (colorMode != EGray256)
{
iTempMask = new (ELeave) CFbsBitmap;
iTempMask->Create(TSize(4,4), EGray256);
iTempMaskDevice = CFbsBitmapDevice::NewL(iTempMask);
User::LeaveIfError(iTempMaskDevice->CreateContext(iTempMaskGc));
}
}
TBool CHuiCanvasImageRasterizer::IsSeparateTextMaskUsed()
{
if (iTempBitmap &&
iTempBitmap->DisplayMode() != EColor16MA &&
iTempBitmap->DisplayMode() != EColor16MAP &&
iTempBitmap->DisplayMode() != EGray256)
{
return ETrue;
}
else
{
return EFalse;
}
}
CFbsBitmap* CHuiCanvasImageRasterizer::CreateInvertedMaskL(CFbsBitmap& aOriginalMask)
{
TSize size = aOriginalMask.SizeInPixels();
CFbsBitmap* newMask = new(ELeave)CFbsBitmap;
CleanupStack::PushL(newMask);
CFbsBitmap* bitmap = new(ELeave)CFbsBitmap;
CleanupStack::PushL(bitmap);
newMask->Create(size, aOriginalMask.DisplayMode());
bitmap->Create(size, aOriginalMask.DisplayMode());
CFbsDevice* newMaskdevice = NULL;
CFbsBitGc* newMaskGc = NULL;
CFbsDevice* bitmapDevice = NULL;
CFbsBitGc* bitmapGc = NULL;
newMaskdevice = CFbsBitmapDevice::NewL(newMask);
CleanupStack::PushL(newMaskdevice);
User::LeaveIfError(newMaskdevice->CreateContext(newMaskGc));
bitmapDevice = CFbsBitmapDevice::NewL(bitmap);
CleanupStack::PushL(bitmapDevice);
User::LeaveIfError(bitmapDevice->CreateContext(bitmapGc));
bitmapGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
bitmapGc->SetPenStyle(CGraphicsContext::ESolidPen);
bitmapGc->SetPenColor(KRgbBlack);
bitmapGc->SetBrushColor(KRgbBlack);
bitmapGc->DrawRect(TRect(TPoint(0,0), size));
newMaskGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
newMaskGc->SetPenStyle(CGraphicsContext::ESolidPen);
newMaskGc->SetPenColor(KRgbWhite);
newMaskGc->SetBrushColor(KRgbWhite);
newMaskGc->DrawRect(TRect(TPoint(0,0), size));
newMaskGc->BitBltMasked(TPoint(0,0), bitmap, TRect(TPoint(0,0), size), &aOriginalMask, EFalse);
delete bitmapGc;
delete newMaskGc;
CleanupStack::PopAndDestroy(bitmapDevice);
CleanupStack::PopAndDestroy(newMaskdevice);
CleanupStack::PopAndDestroy(bitmap);
CleanupStack::Pop(newMask);
return newMask;
}
CFbsBitmap* CHuiCanvasImageRasterizer::CreateMovedMaskL(CFbsBitmap& aOriginalMask, TPoint aNewTopLeftCorner)
{
TSize size = aOriginalMask.SizeInPixels();
CFbsBitmap* newMask = new(ELeave)CFbsBitmap;
CleanupStack::PushL(newMask);
newMask->Create(size, aOriginalMask.DisplayMode());
CFbsDevice* newMaskdevice = NULL;
CFbsBitGc* newMaskGc = NULL;
newMaskdevice = CFbsBitmapDevice::NewL(newMask);
CleanupStack::PushL(newMaskdevice);
User::LeaveIfError(newMaskdevice->CreateContext(newMaskGc));
newMaskGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
newMaskGc->SetPenStyle(CGraphicsContext::ESolidPen);
newMaskGc->SetPenColor(KRgbWhite);
newMaskGc->SetBrushColor(KRgbWhite);
newMaskGc->DrawRect(TRect(TPoint(0,0), size));
newMaskGc->BitBlt(TPoint(0,0), &aOriginalMask, TRect(aNewTopLeftCorner, size));
newMaskGc->BitBlt(aNewTopLeftCorner, &aOriginalMask, TRect(TPoint(0,0), size));
delete newMaskGc;
CleanupStack::PopAndDestroy(newMaskdevice);
CleanupStack::Pop(newMask);
return newMask;
}
NONSHARABLE_CLASS( CHuiCanvasTextImageRasterizer ) : public CHuiCanvasImageRasterizer
{
public:
CHuiCanvasTextImageRasterizer();
~CHuiCanvasTextImageRasterizer();
void RasterizeL(CHuiCanvasTextImage& aTextImage);
private:
TSize CalculateRasterizedTextSize(const TDesC& aText, CFont* aFont) const;
void InitL(CHuiCanvasTextImage& aTextImage, TSize& aTextsize);
void DrawL(CHuiCanvasTextImage& aTextImage, TSize& aTextsize, TPoint& aRasterizedOffset);
void UploadL(CHuiCanvasTextImage& aTextImage, TPoint& aRasterrizedOffset);
private:
TInt iFontHandle;
CFbsBitGcFont* iFont;
};
CHuiCanvasTextImageRasterizer::CHuiCanvasTextImageRasterizer()
{
}
CHuiCanvasTextImageRasterizer::~CHuiCanvasTextImageRasterizer()
{
delete iFont;
}
TSize CHuiCanvasTextImageRasterizer::CalculateRasterizedTextSize(const TDesC& aText, CFont* aFont) const
{
CFont::TMeasureTextInput input;
input.iFlags = CFont::TMeasureTextInput::EFVisualOrder;
CFont::TMeasureTextOutput output;
TInt textAdvance = aFont->MeasureText( aText, &input, &output );
TRect bounds = output.iBounds;
bounds.iTl.iX = Min(bounds.iTl.iX, 0);
bounds.iBr.iX = Max(bounds.iBr.iX, textAdvance);
TSize textSize = TSize(bounds.Width(), aFont->FontMaxHeight());
const CAknLayoutFont* layoutFont = 0;
if (CCoeEnv::Static())
{
layoutFont = CAknLayoutFont::AsCAknLayoutFontOrNull( aFont );
}
if ( layoutFont )
{
textSize.iHeight = aFont->HeightInPixels();
TInt textPaneHeight = layoutFont->TextPaneHeight();
TInt textPaneTopToBaseline = layoutFont->TextPaneTopToBaseline();
textSize.iHeight += textPaneHeight - textPaneTopToBaseline;
textSize.iHeight += KHuiFontVerticalShiftInPixels;
}
else
{
textSize.iHeight = Max(textSize.iHeight, aFont->HeightInPixels());
textSize.iHeight += 3; // the best approximation - fails on big (>=72) fonts
}
return textSize;
}
void CHuiCanvasTextImageRasterizer::RasterizeL(CHuiCanvasTextImage& aTextImage)
{
TPoint rasterizedOffset(0, 0);
if (aTextImage.iFontHandle && (iFontHandle != aTextImage.iFontHandle))
{
delete iFont;
iFont = NULL;
// the font might have been duplicated earlier in order to
// find out whether it is an outline font or not. As the CHuiCanvasTextImage
// does not own the font pointer by it's member iFont, just copy the pointer
// to the instance of this class..
if (aTextImage.iFont)
{
iFont = aTextImage.iFont;
}
else
{
// Font is cached as pointer because next text may same font and in that case
// we are much faster as there is no need to do font duplication again.
iFont = new (ELeave) CFbsBitGcFont();
iFontHandle = aTextImage.iFontHandle;
TInt fontDuplicateErrorCode = iFont->Duplicate(iFontHandle);
if (fontDuplicateErrorCode)
{
// Invalid handle which would panic inside fbsbitgc
iFontHandle = 0;
delete iFont;
iFont = NULL;
aTextImage.iFont = NULL;
}
else
{
// we need a reference to the font in CHuiCanvasTextImage aswell..
aTextImage.iFont = iFont;
}
}
}
if (iFont)
{
TSize textSize = TSize(0,0);
TInt textAscent = 0;
// If no max size is provided, we need to approximate the needed texture size.
if (aTextImage.iTextBoxMaxSize == TSize(KMinTInt,KMinTInt))
{
textSize = CalculateRasterizedTextSize(*aTextImage.iText, iFont);
// To get most closest values for the font metrics we could try to cast
// font into aknlayoutfont, but that does not seem to work when font is
// duplicated trough handle. This solution is not accurate and results slightly
// too large textures but avoids text clipping.
TReal32 height = textSize.iHeight;
textSize.iHeight = height * 1.2;
textAscent = height * 0.9;
// If rotated 90 degress, then flip width and heigths
if (aTextImage.iAngle == 90 || aTextImage.iAngle == -90)
{
TSize horizontalTextSize = textSize;
textSize.iWidth = horizontalTextSize.iHeight;
textSize.iHeight = horizontalTextSize.iWidth;
}
// Since we modify the actual point where drawing is to happen
// (so that image size gets minimized), we use rasterizedOffset to specify
// offset we have used. That offset needs to be used when image of the text
// is to be drawn because with these coordinates text goes exactly where it ´
// is supposed to go.
if (aTextImage.iAngle == 90)
{
// Rotated clockwise
rasterizedOffset.iX = textSize.iWidth-textAscent;
}
else if (aTextImage.iAngle == -90)
{
// Rotated anti clockwise
rasterizedOffset.iX = textAscent;
// Drawing happens upwards
rasterizedOffset.iY = textSize.iHeight;
}
else
{
// No rotation, just drawn lower
rasterizedOffset.iY = textAscent;
}
}
else
{
// Clipping text box is provided, much easier this way.
textSize = aTextImage.iTextBoxMaxSize;
}
// Setup gc parameters
InitL(aTextImage, textSize);
// Draw the text
DrawL(aTextImage, textSize, rasterizedOffset);
}
// Upload content
UploadL(aTextImage, rasterizedOffset);
}
void CHuiCanvasTextImageRasterizer::InitL(CHuiCanvasTextImage& aTextImage, TSize& aTextsize)
{
TFontUnderline underline = (TFontUnderline)aTextImage.iGcParams.iUnderline;
TFontStrikethrough strikethrough = (TFontStrikethrough)aTextImage.iGcParams.iStrikethrough;
CGraphicsContext::TBrushStyle brushstyle = (CGraphicsContext::TBrushStyle)aTextImage.iGcParams.iBrushStyle;
CGraphicsContext::TPenStyle penstyle = (CGraphicsContext::TPenStyle)aTextImage.iGcParams.iPenStyle;
TInt shadowmode = aTextImage.iGcParams.iShadowMode; // TODO: Is this even needed for texts ?
TBool outlinefont = iFont->FontSpecInTwips().iFontStyle.IsEffectOn(FontEffect::EOutline);
if (IsSeparateTextMaskUsed())
{
iTempMask->Resize(aTextsize);
iTempMaskDevice->Resize(aTextsize);
delete iTempMaskGc;
iTempMaskGc = NULL;
User::LeaveIfError(iTempMaskDevice->CreateContext(iTempMaskGc));
// Mask bitmap, this defines the shape of the text.
iTempMaskGc->SetBrushColor(KRgbBlack);
iTempMaskGc->Clear();
iTempMaskGc->SetPenColor(KRgbWhite);
// We dont call the UseFont() variant with handle param because it is slower
iTempMaskGc->UseFont(iFont);
// Set correct gc settings
iTempMaskGc->SetPenStyle(penstyle);
iTempMaskGc->SetBrushStyle(brushstyle);
iTempMaskGc->SetUnderlineStyle(underline);
iTempMaskGc->SetStrikethroughStyle(strikethrough);
iTempMaskGc->SetShadowMode(shadowmode);
}
iTempBitmap->Resize(aTextsize);
iTempBitmapDevice->Resize(aTextsize);
delete iTempBitmapGc;
iTempBitmapGc = NULL;
User::LeaveIfError(iTempBitmapDevice->CreateContext(iTempBitmapGc));
if (IsSeparateTextMaskUsed())
{
// Just fill with correct color, text shape comes from mask
if (KHuiCanvasTextUseColorModulation)
{
iTempBitmapGc->SetPenColor(KRgbWhite);
}
else
{
iTempBitmapGc->SetPenColor(aTextImage.iGcParams.iPenColor);
}
iTempBitmapGc->SetBrushColor(aTextImage.iGcParams.iBrushColor);
iTempBitmapGc->Clear();
}
else
{
// First clear
TRgb clearColor;
if (iTempBitmap->DisplayMode() == EColor16MA)
{
clearColor = aTextImage.iGcParams.iPenColor;
iTempBitmapGc->SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha);
}
else
{
clearColor = KRgbBlack;
iTempBitmapGc->SetDrawMode(CGraphicsContext::EDrawModePEN);
}
clearColor.SetAlpha(0);
iTempBitmapGc->SetPenColor(clearColor);
iTempBitmapGc->SetBrushColor(clearColor);
iTempBitmapGc->Clear();
// Set correct gc settings
if (!outlinefont && KHuiCanvasTextUseColorModulation)
{
iTempBitmapGc->SetPenColor(KRgbWhite);
}
else
{
iTempBitmapGc->SetPenColor(aTextImage.iGcParams.iPenColor);
iTempBitmapGc->SetBrushColor(aTextImage.iGcParams.iBrushColor);
}
iTempBitmapGc->SetPenStyle(penstyle);
if (!outlinefont)
{
iTempBitmapGc->SetBrushStyle( CGraphicsContext::ENullBrush );
}
else
{
iTempBitmapGc->SetBrushStyle( brushstyle );
}
iTempBitmapGc->SetUnderlineStyle(underline);
iTempBitmapGc->SetStrikethroughStyle(strikethrough);
iTempBitmapGc->SetShadowMode(shadowmode);
}
// We dont call the UseFont() variant with handle param because it is slower
iTempBitmapGc->UseFont(iFont);
// Are we using color modulation, store info to entry
aTextImage.iUseColorModulation = outlinefont ? EFalse : KHuiCanvasTextUseColorModulation;
// Debug, enable this to see texture outlines.
//iTempMaskGc->DrawRect(TRect(TPoint(0,0), textSize));
}
void CHuiCanvasTextImageRasterizer::DrawL(CHuiCanvasTextImage& aTextImage, TSize& aTextsize, TPoint& aRasterizedOffset)
{
#ifdef SYMBIAN_BUILD_GCE
CGraphicsContext::TTextParameters gcTextParams = *(CGraphicsContext::TTextParameters*)& aTextImage.iTextParams;
#endif
// Based on supplied params, select the approprite DrawText variant.
TBool up = (aTextImage.iAngle == -90);
if (aTextImage.iTextBoxMaxSize != TSize(KMinTInt,KMinTInt))
{
if (aTextImage.iBaseLineOffset != KMinTInt)
{
CGraphicsContext::TTextAlign align = (CGraphicsContext::TTextAlign) aTextImage.iTextAlign;
if (aTextImage.iTextWidth != KMinTInt)
{
if (aTextImage.iAngle == 0)
{
if (IsSeparateTextMaskUsed())
{
iTempMaskGc->DrawText(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize), aTextImage.iBaseLineOffset, aTextImage.iTextWidth, align, aTextImage.iMargin);
}
else
{
// No need to draw separate mask, iTempBitmapGc is using EDrawModeWriteAlpha mode
}
iTempBitmapGc->DrawText(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize), aTextImage.iBaseLineOffset, aTextImage.iTextWidth, align, aTextImage.iMargin);
}
else
{
if (IsSeparateTextMaskUsed())
{
iTempMaskGc->DrawTextVertical(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize), aTextImage.iBaseLineOffset, aTextImage.iTextWidth, up, align, aTextImage.iMargin);
}
else
{
// No need to draw separate mask, iTempBitmapGc is using EDrawModeWriteAlpha mode
}
iTempBitmapGc->DrawTextVertical(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize), aTextImage.iBaseLineOffset, aTextImage.iTextWidth, up, align, aTextImage.iMargin);
}
}
else
{
if (aTextImage.iAngle == 0)
{
if (IsSeparateTextMaskUsed())
{
iTempMaskGc->DrawText(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize), aTextImage.iBaseLineOffset, align, aTextImage.iMargin);
}
else
{
// No need to draw separate mask, iTempBitmapGc is using EDrawModeWriteAlpha mode
}
iTempBitmapGc->DrawText(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize), aTextImage.iBaseLineOffset, align, aTextImage.iMargin);
}
else
{
if (IsSeparateTextMaskUsed())
{
iTempMaskGc->DrawTextVertical(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize), aTextImage.iBaseLineOffset, up, align, aTextImage.iMargin);
}
else
{
// No need to draw separate mask, iTempBitmapGc is using EDrawModeWriteAlpha mode
}
iTempBitmapGc->DrawTextVertical(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize), aTextImage.iBaseLineOffset, up, align, aTextImage.iMargin);
}
}
}
else
{
if (aTextImage.iAngle == 0)
{
if (IsSeparateTextMaskUsed())
{
iTempMaskGc->DrawText(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize) );
}
else
{
// No need to draw separate mask, iTempBitmapGc is using EDrawModeWriteAlpha mode
}
iTempBitmapGc->DrawText(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize));
}
else
{
if (IsSeparateTextMaskUsed())
{
iTempMaskGc->DrawTextVertical(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize), up );
}
else
{
// No need to draw separate mask, iTempBitmapGc is using EDrawModeWriteAlpha mode
}
iTempBitmapGc->DrawTextVertical(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
TRect(TPoint(0,0), aTextsize), up);
}
}
}
else
{
if (aTextImage.iAngle == 0)
{
if (IsSeparateTextMaskUsed())
{
iTempMaskGc->DrawText(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
aRasterizedOffset);
}
else
{
// No need to draw separate mask, iTempBitmapGc is using EDrawModeWriteAlpha mode
}
iTempBitmapGc->DrawText(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
aRasterizedOffset);
}
else
{
if (IsSeparateTextMaskUsed())
{
iTempMaskGc->DrawTextVertical(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
aRasterizedOffset, up);
}
else
{
// No need to draw separate mask, iTempBitmapGc is using EDrawModeWriteAlpha mode
}
iTempBitmapGc->DrawTextVertical(*aTextImage.iText,
#ifdef SYMBIAN_BUILD_GCE
&gcTextParams,
#endif
aRasterizedOffset, up);
}
}
}
void CHuiCanvasTextImageRasterizer::UploadL(CHuiCanvasTextImage& aTextImage, TPoint& aRasterizedOffset)
{
// Upload bitmap content...this may be slow depending on used HW acceleration !
TSize colorBitmapSize = iTempBitmap->SizeInPixels();
TSize maskBitmapSize = IsSeparateTextMaskUsed() ? iTempMask->SizeInPixels() : colorBitmapSize;
CFbsBitmap* textColorBitmap = iTempBitmap;
CFbsBitmap* textMaskBitmap = IsSeparateTextMaskUsed() ? iTempMask : NULL;
if (maskBitmapSize.iWidth > 0 &&
maskBitmapSize.iHeight > 0 &&
colorBitmapSize.iWidth > 0 &&
colorBitmapSize.iHeight > 0)
{
// TODO: For BitGdi it might be good to use direct upload, but it would require changes such as
// making new textColorBitmap, textMaskBitmap everytime.
THuiTextureUploadFlags uploadFlags = THuiTextureUploadFlags(EHuiTextureUploadFlagDoNotRetainResolution | EHuiTextureUploadFlagRetainColorDepth);
#ifdef HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
TTime startTime;
startTime.UniversalTime();
#endif
aTextImage.iRasterizationOffset = aRasterizedOffset;
CHuiTexture* texture = aTextImage.iTexture;
texture->UploadL(*textColorBitmap, textMaskBitmap, uploadFlags);
#ifdef HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
TTime endTime;
endTime.UniversalTime();
TInt uploadTimeInMilliSeconds = endTime.MicroSecondsFrom( startTime ).Int64()/1000;
RDebug::Print(_L(">>> CHuiCanvasTextImageRasterizer::UpdateCachedTextL: Upload of %ix%i %S took %i ms"),
texture->Size().iWidth,
texture->Size().iHeight,
aTextImage.iText,
uploadTimeInMilliSeconds);
#endif
}
}
NONSHARABLE_CLASS( CHuiCanvasGraphicImageRasterizer ) : public CHuiCanvasImageRasterizer
{
public:
CHuiCanvasGraphicImageRasterizer();
~CHuiCanvasGraphicImageRasterizer();
void ConstructL(TBool aUseGray256 = EFalse);
void RasterizeL(CHuiCanvasGraphicImage& aImage);
private:
};
CHuiCanvasGraphicImageRasterizer::CHuiCanvasGraphicImageRasterizer()
{
}
CHuiCanvasGraphicImageRasterizer::~CHuiCanvasGraphicImageRasterizer()
{
}
void CHuiCanvasGraphicImageRasterizer::ConstructL(TBool /*aUseGray256*/)
{
}
void CHuiCanvasGraphicImageRasterizer::RasterizeL(CHuiCanvasGraphicImage& aImage)
{
CFbsBitmap* bitmap = NULL;
CFbsBitmap* mask = NULL;
TInt bitmapTouchCount = KHuiInitialBitmapTouchCount;
TInt maskTouchCount = KHuiInitialBitmapTouchCount;
#ifdef SYMBIAN_BUILD_GCE
TBool volatileBitmapOrMask = EFalse;
#else
TBool volatileBitmapOrMask = ETrue;
#endif
TInt bitmapHandle = aImage.iBitmapHandle;
TInt maskHandle = aImage.iMaskHandle;
TBool invertMask = aImage.iInvertedMask;
TPoint maskOriginPoint = aImage.iMaskOriginPoint;
if (!bitmapHandle)
{
return;
}
// Lets see if we have already cached the pointer...
if (aImage.iBitmap)
{
// ...yes lets use it
bitmap = aImage.iBitmap;
}
else
{
// ...no, so we need to duplicate the handle
bitmap = new(ELeave)CFbsBitmap;
bitmap->Duplicate(bitmapHandle);
if (bitmap)
{
if (KHuiCanvasKeepBitmapPointer)
{
aImage.iBitmap = bitmap;
}
else
{
CleanupStack::PushL(bitmap);
}
}
}
#ifdef SYMBIAN_BUILD_GCE
if (bitmap)
{
bitmapTouchCount = bitmap->TouchCount();
volatileBitmapOrMask |= bitmap->IsVolatile();
}
#endif
// Mask is optional
if (maskHandle)
{
// Lets see if we have already cached the pointer...
if (aImage.iMask)
{
// ...yes lets use it
mask = aImage.iMask;
}
else
{
// ...no, so we need to duplicate the handle
mask = new(ELeave)CFbsBitmap;
mask->Duplicate(maskHandle);
if (mask)
{
if (KHuiCanvasKeepBitmapPointer)
{
aImage.iMask = mask;
}
else
{
CleanupStack::PushL(mask);
}
}
}
#ifdef SYMBIAN_BUILD_GCE
if (mask)
{
maskTouchCount = mask->TouchCount();
volatileBitmapOrMask |= mask->IsVolatile();
}
#endif
// If mask is defined not to be inverted,
// we must invert it because toolkit assumes they are always inverted. Huh.
#ifdef __NVG
// Inverting masks is done ONLY for non-extended bitmaps!
if (bitmap->ExtendedBitmapType() == KNullUid) // => Bitmap isn't extended
{
#endif
if (invertMask && mask->DisplayMode() == EGray2)
{
CFbsBitmap* invertedMask = CreateInvertedMaskL(*mask);
// If we are not using stored pointer, delete it
if (mask != aImage.iMask)
{
CleanupStack::PopAndDestroy(mask);
}
mask = invertedMask;
CleanupStack::PushL(mask);
}
// Upload expects mask to begin always at TPoint(0,0), so if mask
// offset is defined we re-create mask with the given new
// offset. Note that it might be a good idea to combine this op
// with mask inversion op, but we assume that these are not really used
// very often, even more rarely together.
if (maskOriginPoint != TPoint(0,0))
{
CFbsBitmap* movedMask = CreateMovedMaskL(*mask, maskOriginPoint);
// If we are not using stored pointer, delete it
if (mask != aImage.iMask)
{
CleanupStack::PopAndDestroy(mask);
}
mask = movedMask;
CleanupStack::PushL(mask);
}
#ifdef __NVG
}
#endif
}
// Bitmap has changed, must be updated ?
TBool touchCountChanged = EFalse;
touchCountChanged |= volatileBitmapOrMask;
touchCountChanged |= (aImage.iBitmapTouchCount != bitmapTouchCount);
touchCountChanged |= (aImage.iMaskTouchCount != maskTouchCount);
TBool serialNumberChanged = (aImage.iSerialNumber != bitmap->SerialNumber());
// Is touch count check enabled ?
TBool touchCountCheckEnabled = CHuiStatic::Env().CanvasTextureCache().IsTouchCountCheckEnabled();
// Has it old content at all ?
TBool hasContent = aImage.iTexture->HasContent();
if (!hasContent || (touchCountCheckEnabled && touchCountChanged) || serialNumberChanged)
{
// Upload bitmap content...this may be slow depending on used HW acceleration !
if (bitmap->SizeInPixels().iWidth > 0 &&
bitmap->SizeInPixels().iHeight > 0 &&
(!mask || (mask->SizeInPixels().iWidth > 0 && mask->SizeInPixels().iHeight > 0)))
{
// EHuiTextureUploadFlagDoNotRetainResolution should be removed when
// texturecoordinates with segmented textures work.
// This flag causes ugly scaling to happen with small textures.
THuiTextureUploadFlags uploadFlags = THuiTextureUploadFlags(EHuiTextureUploadFlagDoNotRetainResolution | EHuiTextureUploadFlagRetainColorDepth);
#ifdef HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
TTime startTime;
startTime.UniversalTime();
#endif
CHuiTexture* texture = aImage.iTexture;
texture->UploadL(*bitmap, mask, uploadFlags);
// Save the bitmap's serial number to the cached item
aImage.iSerialNumber = bitmap->SerialNumber();
#ifdef HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
TTime endTime;
endTime.UniversalTime();
TInt uploadTimeInMilliSeconds = endTime.MicroSecondsFrom( startTime ).Int64()/1000;
if (volatileBitmapOrMask)
{
RDebug::Print(_L(">>> CHuiCanvasGraphicImageRasterizer::UpdateCachedImageL: Reason for uploading is volatileBitmapOrMask"));
}
if (aImage.iBitmapTouchCount != bitmapTouchCount)
{
RDebug::Print(_L(">>> CHuiCanvasGraphicImageRasterizer::UpdateCachedImageL: Reason for uploading is iBitmapTouchCount %i, old was %i "), bitmapTouchCount, aImage.iBitmapTouchCount);
}
if (aImage.iMaskTouchCount != maskTouchCount)
{
RDebug::Print(_L(">>> CHuiCanvasGraphicImageRasterizer::UpdateCachedImageL: Reason for uploading is iMaskTouchCount %i, old was %i "), maskTouchCount, aImage.iMaskTouchCount);
}
if (serialNumberChanged)
{
RDebug::Print(_L(">>> CHuiCanvasGraphicImageRasterizer::UpdateCachedImageL: Reason for uploading is changed bitmap serial number %li, old was %li "), bitmap->SerialNumber(), aImage.iSerialNumber);
}
RDebug::Print(_L(">>> CHuiCanvasGraphicImageRasterizer::UpdateCachedImageL: Upload of %ix%i %i+%i took %i ms"),
texture->Size().iWidth,
texture->Size().iHeight,
aImage.iBitmapHandle,
aImage.iMaskHandle,
uploadTimeInMilliSeconds);
#endif
}
#ifdef SYMBIAN_BUILD_GCE
// Texture upload itself must not modify bitmap or mask touch count.
// Otherwise, it's impossible to detect client data access
// (since there is not necessarily any synchronisation between client & alf).
aImage.iBitmapTouchCount = bitmapTouchCount;
aImage.iMaskTouchCount = maskTouchCount;
#endif
}
else
{
if (touchCountChanged && !touchCountCheckEnabled)
{
#ifdef HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
RDebug::Print(_L(">>> CHuiCanvasGraphicImageRasterizer::UpdateCachedImageL: skipping uploading because touchcount check is disabled"));
#endif
}
}
// If we are not using stored pointer, delete it
if (mask && (mask != aImage.iMask))
{
CleanupStack::PopAndDestroy(mask);
mask = NULL;
}
// If we are not using stored pointer, delete it
if (bitmap && (bitmap != aImage.iBitmap))
{
CleanupStack::PopAndDestroy(bitmap);
bitmap = NULL;
}
}
NONSHARABLE_CLASS( CHuiCanvasCombinedGraphicImageRasterizer ) : public CHuiCanvasImageRasterizer
{
public:
CHuiCanvasCombinedGraphicImageRasterizer();
~CHuiCanvasCombinedGraphicImageRasterizer();
void RasterizeL(CHuiCanvasGraphicImage& aImage);
private:
void InitL(CHuiCanvasGraphicImage& aImage);
void DrawL(CHuiCanvasGraphicImage& aImage);
void UploadL(CHuiCanvasGraphicImage& aImage);
};
CHuiCanvasCombinedGraphicImageRasterizer::CHuiCanvasCombinedGraphicImageRasterizer()
{
}
CHuiCanvasCombinedGraphicImageRasterizer::~CHuiCanvasCombinedGraphicImageRasterizer()
{
}
void CHuiCanvasCombinedGraphicImageRasterizer::RasterizeL(CHuiCanvasGraphicImage& aImage)
{
// First check the bitmaps
TBool bitmapsChanged = EFalse;
for (TInt i=0; i<aImage.iSubImages.Count(); i++)
{
// Lets see if we have already cached the pointer...
if (!aImage.iSubImages[i].iBitmap)
{
// ...no we must duplicate handle
if (aImage.iSubImages[i].iBitmapHandle)
{
aImage.iSubImages[i].iBitmap = new (ELeave) CFbsBitmap;
aImage.iSubImages[i].iBitmap->Duplicate(aImage.iSubImages[i].iBitmapHandle);
}
}
if (!aImage.iSubImages[i].iMask)
{
// Lets see if we have already cached the pointer...
if (aImage.iSubImages[i].iMaskHandle)
{
aImage.iSubImages[i].iMask = new (ELeave) CFbsBitmap;
aImage.iSubImages[i].iMask->Duplicate(aImage.iSubImages[i].iMaskHandle);
}
}
if (aImage.iSubImages[i].iBitmap)
{
bitmapsChanged |= (aImage.iSubImages[i].iBitmapTouchCount != aImage.iSubImages[i].iBitmap->TouchCount());
}
if (aImage.iSubImages[i].iMask)
{
bitmapsChanged |= (aImage.iSubImages[i].iMaskTouchCount != aImage.iSubImages[i].iMask->TouchCount());
}
}
if (bitmapsChanged)
{
InitL(aImage);
DrawL(aImage);
UploadL(aImage);
// Touch counts updated after upload in case upload changes the count ?
for (TInt i=0; i<aImage.iSubImages.Count(); i++)
{
if (aImage.iSubImages[i].iBitmap)
aImage.iSubImages[i].iBitmapTouchCount = aImage.iSubImages[i].iBitmap->TouchCount();
if (aImage.iSubImages[i].iMask)
aImage.iSubImages[i].iMaskTouchCount = aImage.iSubImages[i].iMask->TouchCount();
}
}
if (!KHuiCanvasKeepBitmapPointer)
{
for (TInt i=0; i<aImage.iSubImages.Count(); i++)
{
delete aImage.iSubImages[i].iBitmap;
aImage.iSubImages[i].iBitmap = NULL;
delete aImage.iSubImages[i].iMask;
aImage.iSubImages[i].iMask = NULL;
}
}
}
void CHuiCanvasCombinedGraphicImageRasterizer::InitL(CHuiCanvasGraphicImage& aImage)
{
if (IsSeparateTextMaskUsed())
{
iTempMask->Resize(aImage.iImageSize);
iTempMaskDevice->Resize(aImage.iImageSize);
delete iTempMaskGc;
iTempMaskGc = NULL;
User::LeaveIfError(iTempMaskDevice->CreateContext(iTempMaskGc));
iTempMaskGc->Reset();
iTempMaskGc->SetBrushColor(KRgbBlack);
iTempMaskGc->Clear();
iTempMaskGc->SetPenColor(KRgbWhite);
}
iTempBitmap->Resize(aImage.iImageSize);
iTempBitmapDevice->Resize(aImage.iImageSize);
delete iTempBitmapGc;
iTempBitmapGc = NULL;
User::LeaveIfError(iTempBitmapDevice->CreateContext(iTempBitmapGc));
if (IsSeparateTextMaskUsed())
{
TRgb clearColor(KRgbWhite);
clearColor.SetAlpha(0);
iTempBitmapGc->SetPenColor(clearColor);
iTempBitmapGc->SetBrushColor(clearColor);
iTempBitmapGc->SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha);
iTempBitmapGc->Clear();
iTempBitmapGc->SetPenColor(KRgbWhite);
iTempMaskGc->SetPenColor(clearColor);
iTempMaskGc->SetBrushColor(clearColor);
iTempMaskGc->SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha);
iTempMaskGc->Clear();
iTempMaskGc->SetPenColor(KRgbWhite);
}
else
{
// First clear
TRgb clearColor(KRgbWhite);
clearColor.SetAlpha(0);
iTempBitmapGc->SetPenColor(clearColor);
iTempBitmapGc->SetBrushColor(clearColor);
iTempBitmapGc->SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha);
iTempBitmapGc->Clear();
iTempBitmapGc->SetPenColor(KRgbWhite);
}
}
void CHuiCanvasCombinedGraphicImageRasterizer::DrawL(CHuiCanvasGraphicImage& aImage)
{
for (TInt i=0; i<aImage.iSubImages.Count(); i++)
{
CFbsBitmap* bitmap = aImage.iSubImages[i].iBitmap;
CFbsBitmap* mask = aImage.iSubImages[i].iMask;
TPoint point = aImage.iSubImages[i].iCombinedBitmapPoint;
if (bitmap)
{
if (IsSeparateTextMaskUsed())
{
TRect sourceRect = TRect(TPoint(0,0), bitmap->SizeInPixels());
if (bitmap)
{
iTempBitmapGc->BitBlt(point, bitmap);
}
if (mask)
{
iTempMaskGc->BitBlt(point, mask);
}
}
else
{
TRect sourceRect = TRect(TPoint(0,0), bitmap->SizeInPixels());
if (mask)
{
TBool invertMask = (mask->DisplayMode() == EGray256);
iTempBitmapGc->BitBltMasked(point, bitmap, sourceRect, mask, invertMask);
}
else
{
iTempBitmapGc->BitBlt(point, bitmap);
}
}
}
}
}
void CHuiCanvasCombinedGraphicImageRasterizer::UploadL(CHuiCanvasGraphicImage& aImage)
{
CFbsBitmap* mask = IsSeparateTextMaskUsed() ? iTempMask : NULL;
// Upload bitmap content...this may be slow depending on used HW acceleration !
if (iTempBitmap->SizeInPixels().iWidth > 0 &&
iTempBitmap->SizeInPixels().iHeight > 0 &&
(!mask || (mask->SizeInPixels().iWidth > 0 && mask->SizeInPixels().iHeight)))
{
// EHuiTextureUploadFlagDoNotRetainResolution should be removed when
// texturecoordinates with segmented textures work.
// This flag causes ugly scaling to happen with small textures.
THuiTextureUploadFlags uploadFlags = THuiTextureUploadFlags(EHuiTextureUploadFlagDoNotRetainResolution | EHuiTextureUploadFlagRetainColorDepth);
#ifdef HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
TTime startTime;
startTime.UniversalTime();
#endif
CHuiTexture* texture = aImage.iTexture;
texture->UploadL(*iTempBitmap, mask, uploadFlags);
#ifdef HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
TTime endTime;
endTime.UniversalTime();
TInt uploadTimeInMilliSeconds = endTime.MicroSecondsFrom( startTime ).Int64()/1000;
RDebug::Print(_L(">>> CHuiCanvasCombinedGraphicImageRasterizer::UploadCombinedImageL: Upload of %ix%i %i+%i took %i ms"),
texture->Size().iWidth,
texture->Size().iHeight,
aImage.iBitmapHandle,
aImage.iMaskHandle,
uploadTimeInMilliSeconds);
#endif
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt ImageTimeOrderFunc(const CHuiCanvasGraphicImage& aFirst, const CHuiCanvasGraphicImage& aSecond)
{
if (aFirst.iLastUsed < aSecond.iLastUsed)
{
return -1; // less than
}
else if (aFirst.iLastUsed > aSecond.iLastUsed)
{
return 1; // more than
}
else
{
return 0;
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt TextTimeOrderFunc(const CHuiCanvasTextImage& aFirst, const CHuiCanvasTextImage& aSecond)
{
if (aFirst.iLastUsed < aSecond.iLastUsed)
{
return -1; // less than
}
else if (aFirst.iLastUsed > aSecond.iLastUsed)
{
return 1; // more than
}
else
{
return 0;
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt RenderBufferTimeOrderFunc(const CHuiCanvasRenderBufferImage& aFirst, const CHuiCanvasRenderBufferImage& aSecond)
{
if (aFirst.iLastUsed < aSecond.iLastUsed)
{
return -1; // less than
}
else if (aFirst.iLastUsed > aSecond.iLastUsed)
{
return 1; // more than
}
else
{
return 0;
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt GcImageOrderFunc(const THuiCachedGcParams& /*aFirst*/, const THuiCachedGcParams& /*aSecond*/)
{
return 0; // equal enough, note that we don't check every field
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt GcTextOrderFunc(const THuiCachedGcParams& aFirst, const THuiCachedGcParams& aSecond)
{
if (!KHuiCanvasTextUseColorModulation)
{
if (aFirst.iPenColor.Value() < aSecond.iPenColor.Value())
{
return -1; // less than
}
else if (aFirst.iPenColor.Value() > aSecond.iPenColor.Value())
{
return 1; // more than
}
else
{
// For PC-lint
}
}
if (aFirst.iPenStyle < aSecond.iPenStyle)
{
return -1; // less than
}
else if (aFirst.iPenStyle > aSecond.iPenStyle)
{
return 1; // more than
}
else
{
// For PC-lint
}
if (aFirst.iDrawMode < aSecond.iDrawMode)
{
return -1; // less than
}
else if (aFirst.iDrawMode > aSecond.iDrawMode)
{
return 1; // more than
}
else
{
// For PC-lint
}
if (aFirst.iStrikethrough < aSecond.iStrikethrough)
{
return -1; // less than
}
else if (aFirst.iStrikethrough > aSecond.iStrikethrough)
{
return 1; // more than
}
else
{
// For PC-lint
}
if (aFirst.iUnderline < aSecond.iUnderline)
{
return -1; // less than
}
else if (aFirst.iUnderline > aSecond.iUnderline)
{
return 1; // more than
}
else
{
// For PC-lint
}
return 0; // equal enough, note that we don't check every field
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt ImageOrderFunc(const CHuiCanvasGraphicImage& aFirst, const CHuiCanvasGraphicImage& aSecond)
{
// Bitmap handle
if (aFirst.iBitmapHandle < aSecond.iBitmapHandle)
{
return -1; // less than
}
else if (aFirst.iBitmapHandle > aSecond.iBitmapHandle)
{
return 1; // more than
}
else
{
// For PC-lint
}
// Mask handle
if (aFirst.iMaskHandle < aSecond.iMaskHandle)
{
return -1; // less than
}
else if (aFirst.iMaskHandle > aSecond.iMaskHandle)
{
return 1; // more than
}
else
{
// For PC-lint
}
// Inverted mask
if (aFirst.iInvertedMask < aSecond.iInvertedMask)
{
return -1; // less than
}
else if (aFirst.iInvertedMask > aSecond.iInvertedMask)
{
return 1; // more than
}
else
{
// For PC-lint
}
// Mask origin point X
if (aFirst.iMaskOriginPoint.iX < aSecond.iMaskOriginPoint.iX)
{
return -1; // less than
}
else if (aFirst.iMaskOriginPoint.iX > aSecond.iMaskOriginPoint.iX)
{
return 1; // more than
}
else
{
// For PC-lint
}
// Mask origin point Y
if (aFirst.iMaskOriginPoint.iY < aSecond.iMaskOriginPoint.iY)
{
return -1; // less than
}
else if (aFirst.iMaskOriginPoint.iY > aSecond.iMaskOriginPoint.iY)
{
return 1; // more than
}
else
{
// For PC-lint
}
if (aFirst.iImageSize.iHeight < aSecond.iImageSize.iHeight)
{
return -1; // less than
}
else if (aFirst.iImageSize.iHeight > aSecond.iImageSize.iHeight)
{
return 1; // more than
}
else
{
// For PC-lint
}
if (aFirst.iImageSize.iWidth < aSecond.iImageSize.iWidth)
{
return -1; // less than
}
else if (aFirst.iImageSize.iWidth > aSecond.iImageSize.iWidth)
{
return 1; // more than
}
else
{
// For PC-lint
}
if (aFirst.iSubImages.Count() < aSecond.iSubImages.Count())
{
return -1;
}
else if (aFirst.iSubImages.Count() > aSecond.iSubImages.Count())
{
return 1;
}
else
{
// Same amount, must check array content
if (aFirst.iSubImages.Count() && aSecond.iSubImages.Count())
{
for (TInt i=0; i<aFirst.iSubImages.Count();i++)
{
if (aFirst.iSubImages[i].iBitmapHandle < aSecond.iSubImages[i].iBitmapHandle)
{
return -1;
}
else if (aFirst.iSubImages[i].iBitmapHandle > aSecond.iSubImages[i].iBitmapHandle)
{
return 1;
}
else
{
// For PC-lint
}
if (aFirst.iSubImages[i].iMaskHandle < aSecond.iSubImages[i].iMaskHandle)
{
return -1;
}
else if (aFirst.iSubImages[i].iMaskHandle > aSecond.iSubImages[i].iMaskHandle)
{
return 1;
}
else
{
// For PC-lint
}
if (aFirst.iSubImages[i].iCombinedBitmapPoint.iX < aSecond.iSubImages[i].iCombinedBitmapPoint.iX)
{
return -1;
}
else if (aFirst.iSubImages[i].iCombinedBitmapPoint.iX > aSecond.iSubImages[i].iCombinedBitmapPoint.iX)
{
return 1;
}
else
{
// For PC-lint
}
if (aFirst.iSubImages[i].iCombinedBitmapPoint.iY < aSecond.iSubImages[i].iCombinedBitmapPoint.iY)
{
return -1;
}
else if (aFirst.iSubImages[i].iCombinedBitmapPoint.iY > aSecond.iSubImages[i].iCombinedBitmapPoint.iY)
{
return 1;
}
else
{
// For PC-lint
}
}
}
else
{
// For PC-lint
}
}
return GcImageOrderFunc(aFirst.iGcParams, aSecond.iGcParams);
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt TextOrderFunc(const CHuiCanvasTextImage& aFirst, const CHuiCanvasTextImage& aSecond)
{
// Font handle
if (aFirst.iFontHandle < aSecond.iFontHandle)
{
return -1; // less than
}
else if (aFirst.iFontHandle > aSecond.iFontHandle)
{
return 1; // more than
}
else
{
// For PC-lint
}
// Text width
if (aFirst.iTextWidth < aSecond.iTextWidth)
{
return -1; // less than
}
else if (aFirst.iTextWidth > aSecond.iTextWidth)
{
return 1; // more than
}
else
{
// For PC-lint
}
// iTextBoxMaxSize.iWidth
if (aFirst.iTextBoxMaxSize.iWidth < aSecond.iTextBoxMaxSize.iWidth)
{
return -1; // less than
}
else if (aFirst.iTextBoxMaxSize.iWidth > aSecond.iTextBoxMaxSize.iWidth)
{
return 1; // more than
}
else
{
// For PC-lint
}
// Text content
TInt textcompare = KErrNotFound;
if ( aFirst.iFindTextPtr )
{
textcompare = aFirst.iFindTextPtr->Compare(*aSecond.iText);
}
else
{
textcompare = aFirst.iText->Compare(*aSecond.iText);
}
if (textcompare < 0)
{
return -1;
}
else if (textcompare > 0)
{
return 1;
}
else
{
// For PC-lint
}
// iBaseLineOffset
if (aFirst.iBaseLineOffset < aSecond.iBaseLineOffset)
{
return -1; // less than
}
else if (aFirst.iBaseLineOffset > aSecond.iBaseLineOffset)
{
return 1; // more than
}
else
{
// For PC-lint
}
// iTextAlign
if (aFirst.iTextAlign < aSecond.iTextAlign)
{
return -1; // less than
}
else if (aFirst.iTextAlign > aSecond.iTextAlign)
{
return 1; // more than
}
else
{
// For PC-lint
}
// iMargin
if (aFirst.iMargin < aSecond.iMargin)
{
return -1; // less than
}
else if (aFirst.iMargin > aSecond.iMargin)
{
return 1; // more than
}
else
{
// For PC-lint
}
// iAngle
if (aFirst.iAngle < aSecond.iAngle)
{
return -1; // less than
}
else if (aFirst.iAngle > aSecond.iAngle)
{
return 1; // more than
}
else
{
// For PC-lint
}
// Font handle
if (aFirst.iTextParams.iStart < aSecond.iTextParams.iStart)
{
return -1; // less than
}
else if (aFirst.iTextParams.iStart > aSecond.iTextParams.iStart)
{
return 1; // more than
}
else
{
// For PC-lint
}
// Font handle
if (aFirst.iTextParams.iEnd < aSecond.iTextParams.iEnd)
{
return -1; // less than
}
else if (aFirst.iTextParams.iEnd > aSecond.iTextParams.iEnd)
{
return 1; // more than
}
else
{
// For PC-lint
}
// Font handle
if (aFirst.iTextParams.iFlags < aSecond.iTextParams.iFlags)
{
return -1; // less than
}
else if (aFirst.iTextParams.iFlags > aSecond.iTextParams.iFlags)
{
return 1; // more than
}
else
{
// For PC-lint
}
return GcTextOrderFunc(aFirst.iGcParams, aSecond.iGcParams);
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt RenderBufferOrderFunc(const CHuiCanvasRenderBufferImage& aFirst, const CHuiCanvasRenderBufferImage& aSecond)
{
if (aFirst.iOwner < aSecond.iOwner)
{
return -1; // less than
}
else if (aFirst.iOwner > aSecond.iOwner)
{
return 1; // more than
}
else
{
// For PC-lint
}
return 0; // Equal
}
// ---------------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------------
//
CHuiCanvasTextureCache::CHuiCanvasTextureCache()
{
SetMemoryLevel(EHuiMemoryLevelNormal);
CHuiStatic::Env().AddLowMemoryObserver(this);
}
// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CHuiCanvasTextureCache::~CHuiCanvasTextureCache()
{
CHuiStatic::Env().RemoveLowMemoryObserver(this);
iCachedTexts.ResetAndDestroy();
iCachedImages.ResetAndDestroy();
iRecycledTextures.ResetAndDestroy();
if (iSearchedTextImageEntry)
{
iSearchedTextImageEntry->Reset();
delete iSearchedTextImageEntry;
}
if (iSearchedGraphicImageEntry)
{
iSearchedGraphicImageEntry->Reset();
delete iSearchedGraphicImageEntry;
}
if (iSearchedRenderBufferImageEntry)
{
iSearchedRenderBufferImageEntry->Reset();
delete iSearchedRenderBufferImageEntry;
}
delete iTextImageRasterizer;
delete iOutLineTextImageRasterizer;
delete iGraphicImageRasterizer;
delete iCombinedGraphicImageRasterizer;
delete iCanvasGc;
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
CHuiCanvasTextureCache* CHuiCanvasTextureCache::NewL()
{
CHuiCanvasTextureCache* self = CHuiCanvasTextureCache::NewLC();
CleanupStack::Pop(self);
return self;
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
CHuiCanvasTextureCache* CHuiCanvasTextureCache::NewLC()
{
CHuiCanvasTextureCache* self = new (ELeave) CHuiCanvasTextureCache();
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::ConstructL()
{
iMemoryLevel = EHuiMemoryLevelNormal;
iCanvasGc = CHuiStatic::Renderer().CreateCanvasGcL();
iTextImageRasterizer = new (ELeave) CHuiCanvasTextImageRasterizer;
iTextImageRasterizer->ConstructL(EGray256);
iOutLineTextImageRasterizer = new (ELeave) CHuiCanvasTextImageRasterizer;
iOutLineTextImageRasterizer->ConstructL(EColor16MA);
iGraphicImageRasterizer = new (ELeave) CHuiCanvasGraphicImageRasterizer;
iGraphicImageRasterizer->ConstructL();
iCombinedGraphicImageRasterizer = new (ELeave) CHuiCanvasCombinedGraphicImageRasterizer;
iCombinedGraphicImageRasterizer->ConstructL(EColor64K);
iSearchedTextImageEntry = new (ELeave) CHuiCanvasTextImage;
iSearchedGraphicImageEntry = new (ELeave) CHuiCanvasGraphicImage;
iSearchedRenderBufferImageEntry = new (ELeave) CHuiCanvasRenderBufferImage;
}
// ---------------------------------------------------------------------------
//
//
// ---------------------------------------------------------------------------
//
const CHuiCanvasGraphicImage* CHuiCanvasTextureCache::FindCachedImageL(
const THuiCachedImageParams& aCachedImageParams,
const CHuiCanvasVisual& aUser)
{
TInt cachedEntry = KErrNotFound;
iSearchedGraphicImageEntry->Reset();
iSearchedGraphicImageEntry->iBitmapHandle = aCachedImageParams.iBitmapHandle;
iSearchedGraphicImageEntry->iMaskHandle = aCachedImageParams.iMaskHandle;
iSearchedGraphicImageEntry->iTexture = NULL;
iSearchedGraphicImageEntry->iInvertedMask = aCachedImageParams.iInvertedMask;
iSearchedGraphicImageEntry->iMaskOriginPoint = aCachedImageParams.iMaskOriginPoint;
iSearchedGraphicImageEntry->iBitmapTouchCount = KHuiInitialBitmapTouchCount;
iSearchedGraphicImageEntry->iMaskTouchCount = KHuiInitialBitmapTouchCount;
iSearchedGraphicImageEntry->iGcParams = aCachedImageParams.iGcParams;
iSearchedGraphicImageEntry->iCache = this;
cachedEntry = iCachedImages.FindInOrder(iSearchedGraphicImageEntry, ImageOrderFunc);
if (cachedEntry == KErrNotFound)
{
User::Leave(KErrNotFound);
}
iCachedImages[cachedEntry]->RefreshUser(aUser);
return iCachedImages[cachedEntry];
}
// ---------------------------------------------------------------------------
//
//
// ---------------------------------------------------------------------------
//
const CHuiCanvasGraphicImage* CHuiCanvasTextureCache::CreateCombinedCachedImageL(
const RArray<THuiCachedCombinedImageParams>& aCachedImageParams,
const THuiCachedGcParams& aGcParams,
const TSize& aImageSize,
const CHuiCanvasVisual& aUser)
{
TBool needUpdate = EFalse;
TInt cachedEntry = KErrNotFound;
iSearchedGraphicImageEntry->Reset();
iSearchedGraphicImageEntry->iGcParams = aGcParams;
iSearchedGraphicImageEntry->iImageSize = aImageSize;
iSearchedGraphicImageEntry->iCache = this;
for (TInt i=0; i<aCachedImageParams.Count();i++)
{
THuiCachedBitmap cachedBitmap;
cachedBitmap.iBitmapHandle = aCachedImageParams[i].iBitmapHandle;
cachedBitmap.iMaskHandle = aCachedImageParams[i].iMaskHandle;
cachedBitmap.iCombinedBitmapPoint = aCachedImageParams[i].iCombinedBitmapPoint;
iSearchedGraphicImageEntry->iSubImages.Append(cachedBitmap);
}
cachedEntry = iCachedImages.FindInOrder(iSearchedGraphicImageEntry, ImageOrderFunc);
if (cachedEntry == KErrNotFound)
{
// Create new entry object
CHuiCanvasGraphicImage* newEntry = new (ELeave) CHuiCanvasGraphicImage;
CleanupStack::PushL(newEntry);
newEntry->CopyAttributes(*iSearchedGraphicImageEntry);
// Create new accelerated image
CHuiTexture* newtexture = CreateRecycledTextureL();
newEntry->iTexture = newtexture;
User::LeaveIfError(iCachedImages.InsertInOrder(newEntry, ImageOrderFunc));
cachedEntry = iCachedImages.FindInOrder(newEntry, ImageOrderFunc);
CleanupStack::Pop(newEntry);
needUpdate = ETrue;
}
else
{
// Check if needs to be updated by checking active users list
if (!iCachedImages[cachedEntry]->IsUser(aUser))
{
needUpdate = ETrue;
}
}
if (needUpdate)
{
TRAPD(updateError, iCombinedGraphicImageRasterizer->RasterizeL(*iCachedImages[cachedEntry]));
if (updateError == KErrNoMemory)
{
// Call grim reaper to initiate system level texture memory actions !
HandleOutOfTextureMemory();
User::LeaveIfError(updateError);
}
}
iCachedImages[cachedEntry]->RefreshUser(aUser);
return iCachedImages[cachedEntry];
}
// ---------------------------------------------------------------------------
// Improvement idea for HW accelerated, upload all images of the buffer
// into single texture and use texture coordinates to draw each image.
// ---------------------------------------------------------------------------
//
const CHuiCanvasGraphicImage* CHuiCanvasTextureCache::CreateCachedImageL(
const THuiCachedImageParams& aCachedImageParams,
const CHuiCanvasVisual& aUser)
{
TBool needUpdate = EFalse;
TInt cachedEntry = KErrNotFound;
iSearchedGraphicImageEntry->Reset();
iSearchedGraphicImageEntry->iBitmapHandle = aCachedImageParams.iBitmapHandle;
iSearchedGraphicImageEntry->iMaskHandle = aCachedImageParams.iMaskHandle;
iSearchedGraphicImageEntry->iTexture = NULL;
iSearchedGraphicImageEntry->iInvertedMask = aCachedImageParams.iInvertedMask;
iSearchedGraphicImageEntry->iMaskOriginPoint = aCachedImageParams.iMaskOriginPoint;
iSearchedGraphicImageEntry->iBitmapTouchCount = KHuiInitialBitmapTouchCount;
iSearchedGraphicImageEntry->iMaskTouchCount = KHuiInitialBitmapTouchCount;
iSearchedGraphicImageEntry->iGcParams = aCachedImageParams.iGcParams;
iSearchedGraphicImageEntry->iCache = this;
cachedEntry = iCachedImages.FindInOrder(iSearchedGraphicImageEntry, ImageOrderFunc);
if (cachedEntry == KErrNotFound)
{
// Create new entry object
CHuiCanvasGraphicImage* newEntry = new (ELeave) CHuiCanvasGraphicImage;
CleanupStack::PushL(newEntry);
newEntry->CopyAttributes(*iSearchedGraphicImageEntry);
// Create new accelerated image
CHuiTexture* newtexture = CreateRecycledTextureL();
newEntry->iTexture = newtexture;
User::LeaveIfError(iCachedImages.InsertInOrder(newEntry, ImageOrderFunc));
cachedEntry = iCachedImages.FindInOrder(newEntry, ImageOrderFunc);
CleanupStack::Pop(newEntry);
needUpdate = ETrue;
}
else
{
// Check if needs to be updated by checking active users list
if (!iCachedImages[cachedEntry]->IsUser(aUser))
{
needUpdate = ETrue;
}
}
if (needUpdate)
{
TRAPD(updateError, iGraphicImageRasterizer->RasterizeL(*iCachedImages[cachedEntry]));
if (updateError == KErrNoMemory)
{
// Call grim reaper to initiate system level texture memory actions !
HandleOutOfTextureMemory();
User::LeaveIfError(updateError);
}
}
iCachedImages[cachedEntry]->RefreshUser(aUser);
return iCachedImages[cachedEntry];
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
const CHuiCanvasTextImage* CHuiCanvasTextureCache::CreateCachedTextL(
const THuiCachedTextParams& aCachedTextParams,
const CHuiCanvasVisual& aUser)
{
TBool needUpdate = EFalse;
TInt cachedEntry = KErrNotFound;
iSearchedTextImageEntry->Reset();
iSearchedTextImageEntry->iFindTextPtr= aCachedTextParams.iFindTextPtr; // Not owned, gets be updated if not found
iSearchedTextImageEntry->iTexture = NULL; // Gets updated if not found
iSearchedTextImageEntry->iFontHandle = aCachedTextParams.iFontHandle;
iSearchedTextImageEntry->iRasterizationOffset = TPoint(0,0); // Gets updated when text has been rasterized
Mem::Copy( &iSearchedTextImageEntry->iTextParams, &aCachedTextParams.iTextParams, sizeof(THuiCanvasTextParameters));
iSearchedTextImageEntry->iTextBoxMaxSize = aCachedTextParams.iTextBoxMaxSize;
iSearchedTextImageEntry->iTextWidth = aCachedTextParams.iTextWidth;
iSearchedTextImageEntry->iBaseLineOffset = aCachedTextParams.iBaseLineOffset;
iSearchedTextImageEntry->iTextAlign = aCachedTextParams.iTextAlign;
iSearchedTextImageEntry->iMargin = aCachedTextParams.iMargin;
iSearchedTextImageEntry->iAngle = aCachedTextParams.iAngle;
iSearchedTextImageEntry->iGcParams = aCachedTextParams.iGcParams;
cachedEntry = iCachedTexts.FindInOrder(iSearchedTextImageEntry, TextOrderFunc);
if (cachedEntry == KErrNotFound)
{
// Create new entry object
CHuiCanvasTextImage* newTextEntry = new (ELeave) CHuiCanvasTextImage;
CleanupStack::PushL(newTextEntry);
newTextEntry->CopyAttributes(*iSearchedTextImageEntry);
// Create new accelerated image
CHuiTexture* newtexture = CreateRecycledTextureL();
newTextEntry->iTexture = newtexture;
// Create text which entry will own
HBufC* ownedText = aCachedTextParams.iFindTextPtr->AllocL();
newTextEntry->iText = ownedText;
newTextEntry->iFindTextPtr = NULL;
TInt insertSuccess = iCachedTexts.InsertInOrder(newTextEntry, TextOrderFunc);
__ASSERT_DEBUG( !insertSuccess, USER_INVARIANT() );
User::LeaveIfError( insertSuccess );
cachedEntry = iCachedTexts.FindInOrder(newTextEntry, TextOrderFunc);
CleanupStack::Pop(newTextEntry);
needUpdate = ETrue;
}
else
{
// If it was found, great. No need to update it.
}
if (needUpdate)
{
TBool outlinefont = EFalse;
if (!(*iCachedTexts[cachedEntry]).iFont)
{
CFbsBitGcFont* font = new (ELeave) CFbsBitGcFont();
CleanupStack::PushL(font);
User::LeaveIfError(font->Duplicate((*iCachedTexts[cachedEntry]).iFontHandle));
(*iCachedTexts[cachedEntry]).iFont = font;
CleanupStack::Pop(); // font
}
outlinefont = (*iCachedTexts[cachedEntry]).iFont->FontSpecInTwips().iFontStyle.IsEffectOn(FontEffect::EOutline);
TInt updateError(KErrNone);
if (!outlinefont)
{
TRAP(updateError, iTextImageRasterizer->RasterizeL(*iCachedTexts[cachedEntry]));
}
else
{
TRAP(updateError, iOutLineTextImageRasterizer->RasterizeL(*iCachedTexts[cachedEntry]));
}
if (updateError == KErrNoMemory)
{
// Call grim reaper to initiate system level texture memory actions !
HandleOutOfTextureMemory();
User::LeaveIfError(updateError);
}
}
iCachedTexts[cachedEntry]->RefreshUser(aUser);
return iCachedTexts[cachedEntry];
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::ReleaseAllCachedEntries(const CHuiCanvasVisual& aUser)
{
// Texts
for (TInt i=0; i < iCachedTexts.Count(); i++)
{
TInt index = iCachedTexts[i]->iActiveUsers.FindInAddressOrder(&aUser);
if (index != KErrNotFound)
{
iCachedTexts[i]->iActiveUsers.Remove(index);
iHasReleasedTexts = ETrue;
}
}
// Images
for (TInt i=0; i < iCachedImages.Count(); i++)
{
TInt index = iCachedImages[i]->iActiveUsers.FindInAddressOrder(&aUser);
if (index != KErrNotFound)
{
iCachedImages[i]->iActiveUsers.Remove(index);
iHasReleasedImages = ETrue;
}
}
// Render buffers are _not_ released together with images and text
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::DeleteAllReleasedEntries(TBool aAllowKeepCached)
{
// Texts
if (iHasReleasedTexts)
{
RPointerArray<CHuiCanvasTextImage> unusedEntries;
FindUnusedTextEntries(unusedEntries);
// Remove unused entries if they fit into the cache, leave last used ones in the cache if cache size allows
if (aAllowKeepCached)
{
SelectPreservedUnusedTextEntries(unusedEntries);
}
DeleteUnusedTextEntries(unusedEntries);
unusedEntries.Close();
iHasReleasedTexts = EFalse;
}
// Images
if (iHasReleasedImages)
{
RPointerArray<CHuiCanvasGraphicImage> unusedEntries;
FindUnusedImageEntries(unusedEntries);
// Remove unused entries if they fit into the cache, leave last used ones in the cache if cache size allows
if (aAllowKeepCached)
{
SelectPreservedUnusedImageEntries(unusedEntries);
}
DeleteUnusedImageEntries(unusedEntries);
unusedEntries.Close();
iHasReleasedImages = EFalse;
}
// Render buffers
if (iHasReleasedRenderBuffers)
{
RPointerArray<CHuiCanvasRenderBufferImage> unusedEntries;
FindUnusedRenderBufferEntries(unusedEntries);
// Remove unused entries if they fit into the cache, leave last used ones in the cache if cache size allows
if (aAllowKeepCached)
{
SelectPreservedUnusedRenderBufferEntries(unusedEntries);
}
DeleteUnusedRenderBufferEntries(unusedEntries);
unusedEntries.Close();
iHasReleasedRenderBuffers = EFalse;
}
}
#ifdef HUI_DEBUG_PRINT_CANVAS_TEXTURE_CACHE_EGL
#include "eglext.h"
typedef EGLBoolean (*NOK_resource_profiling)(EGLDisplay, EGLint, EGLint*, EGLint, EGLint*);
void ReportGraphicsMemoryUsage()
{
RDebug::Print(_L("About to ask EGL profiling ext"));
EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
/*
** Search for extName in the extensions string. Use of strstr()
** is not sufficient because extension names can be prefixes of
** other extension names. Could use strtok() but the constant
** string returned by glGetString can be in read-only memory.
*/
/* TBool extensionFound = EFalse;
char *p = (char *) eglQueryString(dpy,EGL_EXTENSIONS);
char *end;
int extNameLen = strlen("EGL_NOK_resource_profiling");
end = p + strlen(p);
while (p < end)
{
int n = strcspn(p, " ");
if ((extNameLen == n) && (strncmp("EGL_NOK_resource_profiling", p, n) == 0))
{
extensionFound = ETrue;
break;
}
p += (n + 1);
}
if (!extensionFound)
{
TRACES("EGL_NOK_resource_profiling not found from EGL_EXTENSIONS");
}*/
// okay, let's fetch the function ptr to our profiling functions
// resource profiling extension function ptr
NOK_resource_profiling eglQueryProfilingData = (NOK_resource_profiling)eglGetProcAddress("eglQueryProfilingDataNOK");
if (!eglQueryProfilingData)
{
RDebug::Print(_L("EGL profiling ext not present"));
return;
}
EGLint data_count;
EGLint* prof_data;
TInt i(0);
/* Find out how much profiling data is available */
eglQueryProfilingData(dpy, EGL_PROF_QUERY_GLOBAL_BIT_NOK | EGL_PROF_QUERY_MEMORY_USAGE_BIT_NOK,
NULL, 0, &data_count);
/* Allocate room for the profiling data */
prof_data = (EGLint*)User::Alloc(data_count * sizeof(EGLint));
if (prof_data == NULL)
{
RDebug::Print(_L("Could not get mem for EGL profiling ext"));
return;
}
/* Retrieve the profiling data */
eglQueryProfilingData(dpy,
EGL_PROF_QUERY_GLOBAL_BIT_NOK |
EGL_PROF_QUERY_MEMORY_USAGE_BIT_NOK,
prof_data,
data_count,
&data_count);
/* Iterate over the returned data */
while (i < data_count)
{
switch (prof_data[i++])
{
case EGL_PROF_USED_MEMORY_NOK:
{
RDebug::Print(_L("EGL Profiling: used mem %d"), prof_data[i++]);
break;
}
case EGL_PROF_TOTAL_MEMORY_NOK:
{
RDebug::Print(_L("EGL Profiling: total mem available: %d"), prof_data[i++]);
break;
}
case EGL_PROF_THREAD_ID_NOK:
{
if (sizeof(EGLNativeThreadIdTypeNOK) == 8)
{
i+=2;
}
else
{
i++;
}
break;
}
case EGL_PROF_THREAD_USED_PRIVATE_MEMORY_NOK:
case EGL_PROF_THREAD_USED_SHARED_MEMORY_NOK:
default:
{
i++;
break;
}
}
}
User::Free(prof_data);
}
#endif
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::AdvanceTime()
{
#ifdef HUI_DEBUG_PRINT_CANVAS_TEXTURE_CACHE
RDebug::Print(_L("-- CHuiCanvasTextureCache::AdvanceTime: begin --"));
if (IsLowMemoryStateEnabled())
{
RDebug::Print(_L("-- CHuiCanvasTextureCache::AdvanceTime: LOW MEMORY STATE ENABLED"));
}
if(iEglProfiling > 10)
{
#ifdef HUI_DEBUG_PRINT_CANVAS_TEXTURE_CACHE_EGL
ReportGraphicsMemoryUsage();
#endif
iEglProfiling = 0;
}
else
{
iEglProfiling++;
}
#endif
// We do not delete all, by leaving something unused in the cache
// we can improve performance.
CalculateGraphicsMemoryUsage();
DeleteAllReleasedEntries(ETrue);
// Reset bitmap sizes to save memory. Commented out as resize appears to
// be relatively slow (0.2 - 0.5 ms). Not resizing here means that it uses
// more RAM between frames.
//iTempMask->Resize(KEmptyBitmapSize);
//iTempBitmap->Resize(KEmptyBitmapSize);
//iFontHandle = 0;
//delete iFont;
//iFont = NULL;
#ifdef HUI_DEBUG_PRINT_CANVAS_TEXTURE_CACHE
RDebug::Print(_L("-- Cached texts: %i --"), iCachedTexts.Count());
RDebug::Print(_L("-- Cached imgs : %i --"), iCachedImages.Count());
TInt totalPixels = 0;
for(TInt i=iCachedTexts.Count() - 1; i >= 0; i--)
{
TSize size = iCachedTexts[i]->iTexture->Size();
RDebug::Print(_L("-- Cached text: %ix%i, users=%i, string=%S --"), size.iWidth, size.iHeight, iCachedTexts[i]->iActiveUsers.Count(), iCachedTexts[i]->iText );
totalPixels += size.iWidth * size.iHeight;
#ifdef HUI_DEBUG_PRINT_CANVAS_TEXTURE_CACHE_EXTENDED
RDebug::Print(_L("-- iFontHandle: %i --"), iCachedTexts[i]->iFontHandle );
RDebug::Print(_L("-- iTextWidth: %i --"), iCachedTexts[i]->iTextWidth );
RDebug::Print(_L("-- iTextBoxMaxSize.iWidth: %i --"), iCachedTexts[i]->iTextBoxMaxSize.iWidth );
RDebug::Print(_L("-- iBaseLineOffset: %i --"), iCachedTexts[i]->iBaseLineOffset );
RDebug::Print(_L("-- iTextAlign: %i --"), iCachedTexts[i]->iTextAlign );
RDebug::Print(_L("-- iMargin: %i --"), iCachedTexts[i]->iMargin );
RDebug::Print(_L("-- iAngle: %i --"), iCachedTexts[i]->iAngle );
RDebug::Print(_L("-- iTextParams.iStart: %i --"), iCachedTexts[i]->iTextParams.iStart );
RDebug::Print(_L("-- iTextParams.iEnd: %i --"), iCachedTexts[i]->iTextParams.iEnd );
RDebug::Print(_L("-- iTextParams.iFlagst: %i --"), iCachedTexts[i]->iTextParams.iFlags );
THuiCachedGcParams p = iCachedTexts[i]->iGcParams;
RDebug::Print(_L("-- iPenColor: %i --"), p.iPenColor );
RDebug::Print(_L("-- iPenStyle: %i --"), p.iPenStyle );
RDebug::Print(_L("-- iDrawMode: %i --"), p.iDrawMode );
RDebug::Print(_L("-- iBrushColor: %i --"), p.iBrushColor );
RDebug::Print(_L("-- iBrushStyle: %i --"), p.iBrushStyle );
RDebug::Print(_L("-- iShadowMode: %i --"), p.iShadowMode );
RDebug::Print(_L("-- iStrikethrough: %i --"), p.iStrikethrough );
RDebug::Print(_L("-- iUnderline: %i --"), p.iUnderline );
RDebug::Print(_L("-- iUserDisplayMode: %i --"), p.iUserDisplayMode );
RDebug::Print(_L("-- iShadowColor: %i --"), p.iShadowColor );
#endif
}
for(TInt i=iCachedImages.Count() - 1; i >= 0; i--)
{
TSize size = iCachedImages[i]->iTexture->Size();
if (iCachedImages[i]->iSubImages.Count())
{
RDebug::Print(_L("-- Cached combined image: %i+%i %ix%i, users=%i --"), iCachedImages[i]->iBitmapHandle, iCachedImages[i]->iMaskHandle, size.iWidth, size.iHeight, iCachedImages[i]->iActiveUsers.Count());
RDebug::Print(_L("-- Blit size: %ix%i --"), iCachedImages[i]->iImageSize.iWidth, iCachedImages[i]->iImageSize.iHeight);
for (TInt j=0; j<iCachedImages[i]->iSubImages.Count();j++)
{
RDebug::Print(_L("-- subimage: %i+%i Point: %i,%i--"),
iCachedImages[i]->iSubImages[j].iBitmapHandle,
iCachedImages[i]->iSubImages[j].iMaskHandle,
iCachedImages[i]->iSubImages[j].iCombinedBitmapPoint.iX,
iCachedImages[i]->iSubImages[j].iCombinedBitmapPoint.iY);
}
}
else
{
RDebug::Print(_L("-- Cached image: %i+%i %ix%i, users=%i --"), iCachedImages[i]->iBitmapHandle,iCachedImages[i]->iMaskHandle, size.iWidth, size.iHeight, iCachedImages[i]->iActiveUsers.Count());
}
totalPixels += size.iWidth * size.iHeight;
}
RDebug::Print(_L("-- Estimated canvas texture mem usage with 32bpp %i KBytes --"), totalPixels*4 / 1024);
TInt unusedCanvasTextures = CalculateUnusedCanvasTextureUsageInKbytes();
RDebug::Print(_L("-- Estimated canvas unused texture mem usage with 32bpp %i KBytes --"), unusedCanvasTextures);
TInt totalUsedTextureMemoryInKBytes = CHuiStatic::Env().TextureManager().EstimatedTextureMemUsage(KHuiTextureEstimatedBpp) / 1024;
RDebug::Print(_L("-- Estimated total texture mem usage with 32bpp %i KBytes --"), totalUsedTextureMemoryInKBytes);
TInt totalPixelsForRenderBuffers = 0;
for(TInt i=iCachedRenderBuffers.Count() - 1; i >= 0; i--)
{
if (iCachedRenderBuffers[i]->iCanvasRenderBuffer)
{
TSize size = iCachedRenderBuffers[i]->iCanvasRenderBuffer->Size();
RDebug::Print(_L("-- Cached render buffer: %ix%i, users=%i --"), size.iWidth, size.iHeight, iCachedRenderBuffers[i]->iActiveUsers.Count());
totalPixelsForRenderBuffers += size.iWidth * size.iHeight;
}
else
{
RDebug::Print(_L("-- Cached render buffer: 0x0, users=%i --"), iCachedRenderBuffers[i]->iActiveUsers.Count());
}
}
RDebug::Print(_L("-- Estimated canvas render buffer mem usage with 32bpp %i KBytes --"), totalPixelsForRenderBuffers*4 / 1024);
TInt totalAllocSize = 0;
User::AllocSize(totalAllocSize);
RDebug::Print(_L("-- User::AllocSize = %i"), totalAllocSize);
RDebug::Print(_L("-- CHuiCanvasTextureCache::AdvanceTime: end --"));
#endif
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::FindUnusedImageEntries(RPointerArray<CHuiCanvasGraphicImage>& aIndexEntries)
{
for(TInt i=iCachedImages.Count() - 1; i >= 0; i--)
{
CHuiCanvasGraphicImage* entry = iCachedImages[i];
if (!entry->iActiveUsers.Count())
{
aIndexEntries.InsertInOrderAllowRepeats(entry, ImageTimeOrderFunc);
}
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::FindUnusedTextEntries(RPointerArray<CHuiCanvasTextImage>& aIndexEntries)
{
for(TInt i=iCachedTexts.Count() - 1; i >= 0; i--)
{
CHuiCanvasTextImage* entry = iCachedTexts[i];
if (!entry->iActiveUsers.Count())
{
aIndexEntries.InsertInOrderAllowRepeats(entry, TextTimeOrderFunc);
}
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::FindUnusedRenderBufferEntries(RPointerArray<CHuiCanvasRenderBufferImage>& aIndexEntries)
{
for(TInt i=iCachedRenderBuffers.Count() - 1; i >= 0; i--)
{
CHuiCanvasRenderBufferImage* entry = iCachedRenderBuffers[i];
if (!entry->iActiveUsers.Count())
{
aIndexEntries.InsertInOrderAllowRepeats(entry, RenderBufferTimeOrderFunc);
}
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::DeleteUnusedImageEntries(RPointerArray<CHuiCanvasGraphicImage>& aIndexEntries)
{
// Remove given entries
for(TInt i=aIndexEntries.Count() - 1; i >= 0; i--)
{
TInt indexEntry = iCachedImages.FindInOrder(aIndexEntries[i], ImageOrderFunc);
if (indexEntry != KErrNotFound)
{
CHuiCanvasGraphicImage* entry = iCachedImages[indexEntry];
iCachedImages.Remove(indexEntry);
delete entry;
}
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::DeleteUnusedTextEntries(RPointerArray<CHuiCanvasTextImage>& aIndexEntries)
{
// Remove given entries
for(TInt i=aIndexEntries.Count() - 1; i >= 0; i--)
{
TInt indexEntry = iCachedTexts.FindInOrder(aIndexEntries[i], TextOrderFunc);
if (indexEntry != KErrNotFound)
{
CHuiCanvasTextImage* entry = iCachedTexts[indexEntry];
iCachedTexts.Remove(indexEntry);
delete entry;
}
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::DeleteUnusedRenderBufferEntries(RPointerArray<CHuiCanvasRenderBufferImage>& aIndexEntries)
{
// Remove given entries
for(TInt i=aIndexEntries.Count() - 1; i >= 0; i--)
{
TInt indexEntry = iCachedRenderBuffers.FindInOrder(aIndexEntries[i], RenderBufferOrderFunc);
if (indexEntry != KErrNotFound)
{
CHuiCanvasRenderBufferImage* entry = iCachedRenderBuffers[indexEntry];
iCachedRenderBuffers.Remove(indexEntry);
delete entry;
}
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::SelectPreservedUnusedImageEntries(RPointerArray<CHuiCanvasGraphicImage>& aIndexEntries)
{
TInt totalUnusedTextureBytes = 0;
// Remove unused entries if they fit into the cache, leave last used ones in the cache if cache size allows
for(TInt i=aIndexEntries.Count() - 1; i >= 0; i--)
{
// Always delete bitmaps from unused entries, we can again duplicate pointers from handles when needed.
// Pointers are kept only for perfromance reasons.
CHuiCanvasGraphicImage* entry = aIndexEntries[i];
delete entry->iBitmap;
entry->iBitmap = NULL;
delete entry->iMask;
entry->iMask = NULL;
for (TInt j=0; j < entry->iSubImages.Count(); j++)
{
delete entry->iSubImages[j].iBitmap;
entry->iSubImages[j].iBitmap = NULL;
delete entry->iSubImages[j].iMask;
entry->iSubImages[j].iMask = NULL;
}
TSize textureSize = entry->iTexture->Size();
TInt textureEstimatedSizeInBytes = textureSize.iWidth * textureSize.iHeight * KHuiCanvasImageEstimatedBpp/8.f;
if (totalUnusedTextureBytes + textureEstimatedSizeInBytes < iUnusedCanvasImageTextureCacheSizeInKBytes*1024)
{
// Fits in the cache, remove from unused list, but keep zero size textures
if (textureEstimatedSizeInBytes != 0)
{
aIndexEntries.Remove(i);
}
}
totalUnusedTextureBytes += textureEstimatedSizeInBytes;
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::SelectPreservedUnusedTextEntries(RPointerArray<CHuiCanvasTextImage>& aIndexEntries)
{
TInt totalUnusedTextureBytes = 0;
// Remove unused entries if they fit into the cache, leave last used ones in the cache if cache size allows
for(TInt i=aIndexEntries.Count() - 1; i >= 0; i--)
{
TSize textureSize = aIndexEntries[i]->iTexture->Size();
TInt textureEstimatedSizeInBytes = textureSize.iWidth * textureSize.iHeight * KHuiCanvasTextEstimatedBpp/8.f;
if (totalUnusedTextureBytes + textureEstimatedSizeInBytes < iUnusedCanvasTextTextureCacheSizeInKBytes*1024)
{
// Fits in the cache, remove from unused list, but keep zero size textures
if (textureEstimatedSizeInBytes != 0)
{
aIndexEntries.Remove(i);
}
}
totalUnusedTextureBytes += textureEstimatedSizeInBytes;
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::SelectPreservedUnusedRenderBufferEntries(RPointerArray<CHuiCanvasRenderBufferImage>& aIndexEntries)
{
TInt totalUnusedRenderBufferBytes = 0;
// Remove unused entries if they fit into the cache, leave last used ones in the cache if cache size allows
for(TInt i=aIndexEntries.Count() - 1; i >= 0; i--)
{
CHuiCanvasRenderBufferImage* entry = aIndexEntries[i];
if (entry->iCanvasRenderBuffer)
{
TSize renderBufferSize = entry->iCanvasRenderBuffer->Size();
TInt renderBufferEstimatedSizeInBytes = renderBufferSize.iWidth * renderBufferSize.iHeight * KHuiCanvasRenderBufferEstimatedBpp/8.f;
if (totalUnusedRenderBufferBytes + renderBufferEstimatedSizeInBytes < iUnusedCanvasRenderBufferCacheSizeInKBytes*1024)
{
aIndexEntries.Remove(i);
}
totalUnusedRenderBufferBytes += renderBufferEstimatedSizeInBytes;
}
else
{
#ifdef HUI_DEBUG_PRINT_CANVAS_TEXTURE_CACHE
RDebug::Print(_L("CHuiCanvasTextureCache::PurgeUnusedRenderBufferEntries: Non existing render buffer, Render does not support canvas render buffers ?"));
#endif
}
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::HandleOutOfTextureMemory()
{
RDebug::Print(_L("CHuiCanvasTextureCache::HandleOutOfTextureMemory: Out of memory happened !"));
DeleteAllReleasedEntries(EFalse);
// TODO: Who you gonna call when texture memory is full and we cannot ourself
// delete anymore textures ?
//
// Grim reaper ?!
//
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::CalculateGraphicsMemoryUsage()
{
// TODO: This should handle NVG textures
if (iMaxTextureMemoryInKBytes)
{
// How much all alfred textures use memory currently, this include alf apps and all canvas textures
TInt totalUsedTextureMemoryInKBytes = CHuiStatic::Env().TextureManager().EstimatedTextureMemUsage(KHuiTextureEstimatedBpp) / 1024;
// How much there are unused textures cached currently
TInt totalUnusedCanvasTextureMemoryUsageInKBytes = CalculateUnusedCanvasTextureUsageInKbytes();
// Calculate how much there is space for unused textures
TInt availableCacheSizeInKBytes = iMaxTextureMemoryInKBytes - (totalUsedTextureMemoryInKBytes - totalUnusedCanvasTextureMemoryUsageInKBytes);
// Divide available space for unused textures between texts and images using defined ratio
if (availableCacheSizeInKBytes > 0)
{
iUnusedCanvasTextTextureCacheSizeInKBytes = availableCacheSizeInKBytes * KHuiCanvasUnusedTextImageCacheRatio;
iUnusedCanvasImageTextureCacheSizeInKBytes = availableCacheSizeInKBytes * (1.f - KHuiCanvasUnusedTextImageCacheRatio);
}
else
{
iUnusedCanvasTextTextureCacheSizeInKBytes = 0;
iUnusedCanvasImageTextureCacheSizeInKBytes = 0;
}
}
else
{
iUnusedCanvasTextTextureCacheSizeInKBytes = 0;
iUnusedCanvasImageTextureCacheSizeInKBytes = 0;
}
if (iMaxRenderBufferMemoryInKBytes)
{
// How much there are render buffers cached currently
TInt totalInKBytes = CalculateTotalCanvasRenderBufferUsageInKbytes();
// How much available for unused
TInt availableCacheSizeInKBytes = iMaxRenderBufferMemoryInKBytes - totalInKBytes;
if (availableCacheSizeInKBytes > 0)
{
iUnusedCanvasRenderBufferCacheSizeInKBytes = iMaxRenderBufferMemoryInKBytes - totalInKBytes;
}
else
{
iUnusedCanvasRenderBufferCacheSizeInKBytes = 0;
}
}
else
{
iUnusedCanvasRenderBufferCacheSizeInKBytes = 0;
}
#ifdef HUI_DEBUG_PRINT_CANVAS_TEXTURE_CACHE
RDebug::Print(_L("-- CHuiCanvasTextureCache::CalculateTextureUsage: iUnusedCanvasTextTextureCacheSizeInKBytes = %i"), iUnusedCanvasTextTextureCacheSizeInKBytes);
RDebug::Print(_L("-- CHuiCanvasTextureCache::CalculateTextureUsage: iUnusedCanvasImageTextureCacheSizeInKBytes = %i"), iUnusedCanvasImageTextureCacheSizeInKBytes);
RDebug::Print(_L("-- CHuiCanvasTextureCache::CalculateTextureUsage: iUnusedCanvasRenderBufferCacheSizeInKBytes = %i"), iUnusedCanvasRenderBufferCacheSizeInKBytes);
#endif
}
void CHuiCanvasTextureCache::EnableTouchCountCheck(TBool aEnable)
{
iTouchCountCheckEnabled = aEnable;
}
TBool CHuiCanvasTextureCache::IsTouchCountCheckEnabled()
{
return iTouchCountCheckEnabled;
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt CHuiCanvasTextureCache::CalculateUnusedCanvasTextureUsageInKbytes()
{
// TODO: This should handle NVG textures
TInt totalUnusedTextureBytes = 0;
// Texts
RPointerArray<CHuiCanvasTextImage> textEntries;
FindUnusedTextEntries(textEntries);
for(TInt i=textEntries.Count() - 1; i >= 0; i--)
{
TSize textureSize = textEntries[i]->iTexture->Size();
totalUnusedTextureBytes += textureSize.iWidth * textureSize.iHeight * KHuiCanvasTextEstimatedBpp/8.f;
}
textEntries.Close();
// Images
RPointerArray<CHuiCanvasGraphicImage> imageEntries;
FindUnusedImageEntries(imageEntries);
for(TInt i=imageEntries.Count() - 1; i >= 0; i--)
{
TSize textureSize = imageEntries[i]->iTexture->Size();
totalUnusedTextureBytes += textureSize.iWidth * textureSize.iHeight * KHuiCanvasImageEstimatedBpp/8.f;
}
imageEntries.Close();
return totalUnusedTextureBytes/1024;
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt CHuiCanvasTextureCache::CalculateUnusedCanvasRenderBufferUsageInKbytes()
{
TInt totalUnusedRenderBufferBytes = 0;
RPointerArray<CHuiCanvasRenderBufferImage> entries;
FindUnusedRenderBufferEntries(entries);
for(TInt i=entries.Count() - 1; i >= 0; i--)
{
if (iCachedRenderBuffers[i]->iCanvasRenderBuffer)
{
TSize renderBufferSize = entries[i]->iCanvasRenderBuffer->Size();
totalUnusedRenderBufferBytes += renderBufferSize.iWidth * renderBufferSize.iHeight * KHuiCanvasRenderBufferEstimatedBpp/8.f;
}
}
entries.Close();
return totalUnusedRenderBufferBytes/1024;
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TInt CHuiCanvasTextureCache::CalculateTotalCanvasRenderBufferUsageInKbytes()
{
TInt totalRenderBufferBytes = 0;
for(TInt i=iCachedRenderBuffers.Count() - 1; i >= 0; i--)
{
if (iCachedRenderBuffers[i]->iCanvasRenderBuffer)
{
TSize renderBufferSize = iCachedRenderBuffers[i]->iCanvasRenderBuffer->Size();
totalRenderBufferBytes += renderBufferSize.iWidth * renderBufferSize.iHeight * KHuiCanvasRenderBufferEstimatedBpp/8.f;
}
}
return totalRenderBufferBytes/1024;
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
CHuiTexture* CHuiCanvasTextureCache::CreateRecycledTextureL()
{
CHuiTexture* texture = NULL;
if (iRecycledTextures.Count())
{
// If there are recycled textures, use one of those
TInt last = iRecycledTextures.Count() - 1;
texture = iRecycledTextures[last];
iRecycledTextures.Remove(last);
}
else
{
// No recycled textures available, create new one
texture = CHuiTexture::NewL();
}
return texture;
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::DeleteRecycledTexture(CHuiTexture* aTexture)
{
aTexture->Reset();
TInt error = KErrNone;
if (iRecycledTextures.Count() < KHuiMaxRecycledTextureCount)
{
error = iRecycledTextures.Append(aTexture);
if (error)
{
delete aTexture;
}
}
else
{
delete aTexture;
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
CHuiCanvasRenderBuffer* CHuiCanvasTextureCache::CreateCachedRenderBufferL(const CHuiCanvasVisual& aUser, TSize aSizeHint)
{
if (IsLowMemoryStateEnabled())
{
// No canvas render buffers created in low memory state
return NULL;
}
TInt cachedEntry = KErrNotFound;
CHuiCanvasRenderBufferImage* newRenderBufferEntry = NULL;
iSearchedRenderBufferImageEntry->Reset();
iSearchedRenderBufferImageEntry->iOwner = (TAny*)&aUser;
cachedEntry = iCachedRenderBuffers.FindInOrder(iSearchedRenderBufferImageEntry, RenderBufferOrderFunc);
if (cachedEntry == KErrNotFound)
{
// Try first getting reused render buffer
newRenderBufferEntry = ReUseReleasedRenderBuffer(aSizeHint);
// Create new entry object if needed
if (!newRenderBufferEntry)
{
newRenderBufferEntry = new (ELeave) CHuiCanvasRenderBufferImage;
}
// Copy attributes (only iOwner attribute)
newRenderBufferEntry->iOwner = iSearchedRenderBufferImageEntry->iOwner;
CleanupStack::PushL(newRenderBufferEntry);
// Create new render buffer
if (!newRenderBufferEntry->iCanvasRenderBuffer)
{
newRenderBufferEntry->iCanvasRenderBuffer = iCanvasGc->CreateRenderBufferL(TSize(0,0));
}
// Insert to cache array
TInt insertSuccess = iCachedRenderBuffers.InsertInOrder(newRenderBufferEntry, RenderBufferOrderFunc);
__ASSERT_DEBUG( !insertSuccess, USER_INVARIANT() );
User::LeaveIfError( insertSuccess );
// We don't know which index it got, so look it up
cachedEntry = iCachedRenderBuffers.FindInOrder(newRenderBufferEntry, RenderBufferOrderFunc);
CleanupStack::Pop(newRenderBufferEntry);
}
else
{
// If it was found, great.
}
iCachedRenderBuffers[cachedEntry]->RefreshUser(aUser);
return iCachedRenderBuffers[cachedEntry]->iCanvasRenderBuffer;
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
CHuiCanvasRenderBuffer* CHuiCanvasTextureCache::FindCachedRenderBuffer(const CHuiCanvasVisual& aUser)
{
TInt cachedEntry = KErrNotFound;
iSearchedRenderBufferImageEntry->Reset();
iSearchedRenderBufferImageEntry->iOwner = (TAny*)&aUser;
cachedEntry = iCachedRenderBuffers.FindInOrder(iSearchedRenderBufferImageEntry, RenderBufferOrderFunc);
if (cachedEntry != KErrNotFound)
{
iCachedRenderBuffers[cachedEntry]->RefreshUser(aUser);
return iCachedRenderBuffers[cachedEntry]->iCanvasRenderBuffer;
}
else
{
return NULL;
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CHuiCanvasTextureCache::ReleaseCachedRenderBuffer(const CHuiCanvasVisual& aUser)
{
TInt cachedEntry = KErrNotFound;
iSearchedRenderBufferImageEntry->Reset();
iSearchedRenderBufferImageEntry->iOwner = (TAny*)&aUser;
cachedEntry = iCachedRenderBuffers.FindInOrder(iSearchedRenderBufferImageEntry, RenderBufferOrderFunc);
if (cachedEntry != KErrNotFound)
{
CHuiCanvasRenderBufferImage* releasedEntry = iCachedRenderBuffers[cachedEntry];
iCachedRenderBuffers.Remove(cachedEntry);
releasedEntry->RemoveUser(aUser);
releasedEntry->iOwner = releasedEntry; // No real owner anymore, se it self as owner
if(!IsLowMemoryStateEnabled())
{
// re-insert released entry (new position in the array order because we clered iOwner)
TInt insertSuccess = iCachedRenderBuffers.InsertInOrder(releasedEntry, RenderBufferOrderFunc);
if (insertSuccess != KErrNone)
{
// oh no re-insert failed, now we must delete the entry to avoid memory leak.
delete releasedEntry;
}
}
else
{
delete releasedEntry;
}
iHasReleasedRenderBuffers = ETrue;
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
CHuiCanvasRenderBufferImage* CHuiCanvasTextureCache::ReUseReleasedRenderBuffer(TSize aSizeHint)
{
if (aSizeHint == TSize(0,0))
{
return NULL;
}
for(TInt i=iCachedRenderBuffers.Count() - 1; i >= 0; i--)
{
// Check if there are any released buffers with have requested size
if (!iCachedRenderBuffers[i]->IsAnyUser() &&
iCachedRenderBuffers[i]->iCanvasRenderBuffer &&
iCachedRenderBuffers[i]->iCanvasRenderBuffer->Size() == aSizeHint)
{
// Remove from array and return instance to caller
CHuiCanvasRenderBufferImage* reusedEntry = iCachedRenderBuffers[i];
iCachedRenderBuffers.Remove(i);
return reusedEntry;
}
}
return NULL;
}
void CHuiCanvasTextureCache::EnableLowMemoryState(TBool aEnable)
{
// deprecated
}
THuiMemoryLevel CHuiCanvasTextureCache::MemoryLevel()
{
return iMemoryLevel;
}
void CHuiCanvasTextureCache::SetMemoryLevel(THuiMemoryLevel aLevel)
{
iMemoryLevel = aLevel;
if (iMemoryLevel <= EHuiMemoryLevelLowest)
{
#ifdef HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
RDebug::Print(_L("-- CHuiCanvasTextureCache::SetMemoryLevel: Enabling lowest memory state"));
#endif
// Set cache sizes to minimum
iMaxTextureMemoryInKBytes = 0;
iMaxRenderBufferMemoryInKBytes = 0;
// Delete images, even if they are in use
for(TInt i=iCachedImages.Count() - 1; i >= 0; i--)
{
CHuiCanvasGraphicImage* entry = iCachedImages[i];
iCachedImages.Remove(i);
delete entry;
}
// Delete texts, even if they are in use
for(TInt i=iCachedTexts.Count() - 1; i >= 0; i--)
{
CHuiCanvasTextImage* entry = iCachedTexts[i];
iCachedTexts.Remove(i);
delete entry;
}
// Delete canvas render buffers, even if they are in use
for(TInt i=iCachedRenderBuffers.Count() - 1; i >= 0; i--)
{
CHuiCanvasRenderBufferImage* entry = iCachedRenderBuffers[i];
iCachedRenderBuffers.Remove(i);
delete entry;
}
}
else if (iMemoryLevel <= EHuiMemoryLevelLow)
{
#ifdef HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
RDebug::Print(_L("-- CHuiCanvasTextureCache::SetMemoryLevel: Enabling low memory state"));
#endif
// Set cache sizes to minimum
iMaxTextureMemoryInKBytes = 0;
iMaxRenderBufferMemoryInKBytes = 0;
// Set flags to make sure we check all entries
iHasReleasedTexts = ETrue;
iHasReleasedImages = ETrue;
iHasReleasedRenderBuffers = ETrue;
// Delete released cached entries
DeleteAllReleasedEntries(EFalse);
// Delete canvas render buffers, even if they are in use
for(TInt i=iCachedRenderBuffers.Count() - 1; i >= 0; i--)
{
CHuiCanvasRenderBufferImage* entry = iCachedRenderBuffers[i];
iCachedRenderBuffers.Remove(i);
delete entry;
}
}
else
{
#ifdef HUI_DEBUG_PRINT_PERFORMANCE_INTERVAL
RDebug::Print(_L("-- CHuiCanvasTextureCache::SetMemoryLevel: Disabling low memory state"));
#endif
// TODO: This could be configurable cenrep or something ?
iMaxTextureMemoryInKBytes = KHuiMaxRecommendedTextureAmountInKBytes;
iMaxRenderBufferMemoryInKBytes = KHuiMaxRecommendedRenderBufferAmountInKBytes;
}
}
TBool CHuiCanvasTextureCache::IsLowMemoryStateEnabled() const
{
if (iMemoryLevel < EHuiMemoryLevelReduced)
{
return ETrue;
}
else
{
return EFalse;
}
}