diff -r 000000000000 -r 15bf7259bb7c uiacceltk/hitchcock/coretoolkit/src/HuiRasterizedTextMesh.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uiacceltk/hitchcock/coretoolkit/src/HuiRasterizedTextMesh.cpp Tue Feb 02 07:56:43 2010 +0200 @@ -0,0 +1,758 @@ +/* +* Copyright (c) 2006-2007 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 CHuiRasterizedTextMesh. CHuiRasterizedTextMesh +* stores a rasterized bitmap version of a text string. +* +*/ + + +#include +#include "HuiRasterizedTextMesh.h" +#include "HuiRenderPlugin.h" +#include "uiacceltk/HuiEnv.h" +#include "uiacceltk/HuiSkin.h" +#include "uiacceltk/HuiStatic.h" +#include "uiacceltk/HuiFont.h" +#include "uiacceltk/HuiTexture.h" +#include "uiacceltk/HuiGc.h" +#include "uiacceltk/HuiUtil.h" +#include "uiacceltk/HuiPanic.h" + +#include "uiacceltk/huitextstylemanager.h" // @todo remove when text drawing is moved to THuiFont +#include "uiacceltk/huitextstyle.h" // @todo remove when text drawing is moved to THuiFont +#include "uiacceltk/huidropshadow.h" +#include "uiacceltk/HuiTextureProcessor.h" + +#include +#include +#include + + +/** Granularity of line wrapping array. */ +const TInt KLineArrayGranularity = 4; + +enum THuiRasterizeLevel + { + ERasterizeNone = 0x0, + ERasterizeText = 0x1, + ERasterizePictographs = 0x2, + ERasterizeAll = 0xFFFFFFFF + }; + +EXPORT_C CHuiRasterizedTextMesh* CHuiRasterizedTextMesh::NewL() + { + CHuiRasterizedTextMesh* self = CHuiRasterizedTextMesh::NewLC(); + CleanupStack::Pop(self); + return self; + } + + +CHuiRasterizedTextMesh* CHuiRasterizedTextMesh::NewLC() + { + CHuiRasterizedTextMesh* self = new (ELeave) CHuiRasterizedTextMesh(); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + + +CHuiRasterizedTextMesh::CHuiRasterizedTextMesh() + { + } + +CHuiRasterizedTextMesh::~CHuiRasterizedTextMesh() + { + ResetLines(); + iLines.Close(); + ResetPictographLines(); + iPictographLines.Close(); + delete iPictographBitmap; + } + + +void CHuiRasterizedTextMesh::Reset() + { + if (!iUsingPreRasterizedMesh) + { + CHuiTextMesh::Reset(); + ResetLines(); + ResetPictographLines(); + } + } + +void CHuiRasterizedTextMesh::ResetLines() + { + HUI_DEBUG(_L("CHuiRasterizedTextMesh::ResetLines() - Deleting textures for all rasterized lines.")); + + for(TInt i = 0; i < iLines.Count(); ++i) + { + if (!iUsingPreRasterizedMesh) + { + delete iLines[i].iTexture; + } + iLines[i].iTexture = NULL; + } + + iLines.Reset(); + } + +void CHuiRasterizedTextMesh::ResetPictographLines() + { + HUI_DEBUG(_L("CHuiRasterizedTextMesh::ResetPictographLines() - Deleting textures for all rasterized lines.")); + + for(TInt i = 0; i < iPictographLines.Count(); ++i) + { + delete iPictographLines[i].iTexture; iPictographLines[i].iTexture = NULL; + } + + iPictographLines.Reset(); + } + + +TBool CHuiRasterizedTextMesh::IsMaxLineCountReached() const + { + return iLines.Count() >= MaxLineCount(); + } + + +TBool CHuiRasterizedTextMesh::RasterizeLineL(const TDesC& aTextLine, SRasterizedLine & aLineOut) + { + if(iUsingPreRasterizedMesh) + { + return ETrue; + } + // Retrieve the used text style. + THuiTextStyle* textStyle = CHuiStatic::Env().TextStyleManager().TextStyle(iTextStyleId); + + // Calculate line extents and assign it to texture size. + TSize textureSize = textStyle->LineExtentsL(aTextLine); + + if(textureSize.iWidth == 0) + { + // This is an empty string. We will not rasterize it. + // Just add a gap. + aLineOut.iTexture = NULL; + aLineOut.iGap = textureSize.iHeight; // @todo: refacture/rename iGap? iGap is used as a size of an empty line? + + HUI_DEBUG1(_L("CHuiRasterizedTextMesh::RasterizeLineL() - Added line gap: %i"), + aLineOut.iGap); + + return !IsMaxLineCountReached(); + } + + // Create a texture for storing the text into. + if (aLineOut.iTexture == NULL) + { + HUI_DEBUG1(_L("CHuiRasterizedTextMesh::RasterizeLineL() - Registering self (0x%x) as a texture content observer."), this); + aLineOut.iTexture = CHuiTexture::NewL(); + // Register one content observer for the first texture that + // is able to restore all lines in a single run + if (iLines.Count()==1) + { + aLineOut.iTexture->iContentObservers.AppendL(*this); + } + aLineOut.iGap = 0; + } + + // set a name for the texture + aLineOut.iTexture->SetImageFileNameL(aTextLine); + + // Rasterize string using the defined text style. + textStyle->RasterizeLineL(aTextLine, *aLineOut.iTexture); + + if ( RasterizedShadow() && aLineOut.iTexture ) + { + const TInt requestedBlurredSize = HUI_ROUND_FLOAT_TO_INT( 2*iVisual->DropShadowHandler()->iRadius.Now() ); + aLineOut.iTexture->CreateShadowTextureL( requestedBlurredSize, EHuiTextureShadowStyleRasterizedText ); + } + + + return !IsMaxLineCountReached(); + } + +TBool CHuiRasterizedTextMesh::RasterizePictographLineL(const TDesC& aTextLine, CFont* aFont, SRasterizedLine & aLineOut) + { + if(iUsingPreRasterizedMesh) + { + return EFalse; + } + + // Retrieve the used text style. + THuiTextStyle* textStyle = CHuiStatic::Env().TextStyleManager().TextStyle(iTextStyleId); + + // Calculate line extents and assign it to texture size. + TSize textureSize = textStyle->LineExtentsL(aTextLine); + + if(textureSize.iWidth == 0 || !iPictographInterface || !iPictographInterface->Interface()->ContainsPictographs(aTextLine)) + { + // This is an empty string or it does not contain pictographs. We will not rasterize it. + // Just add a gap. + aLineOut.iTexture = NULL; + aLineOut.iGap = textureSize.iHeight; + return !IsMaxLineCountReached(); + } + + // store the actual size to be assigned as the textures logical size + TSize actualsize(textureSize); + + if (aLineOut.iTexture == NULL) + { + // Create a texture for storing the pictographs into. + aLineOut.iTexture = CHuiTexture::NewL(); + HUI_DEBUG1(_L("CHuiRasterizedTextMesh::RasterizePictographLineL() - Registering self (0x%x) as a texture content observer."), this); + // Register one content observer for the first texture that + // is able to restore all lines in a single run + if (iLines.Count()==1) + { + aLineOut.iTexture->iContentObservers.AppendL(*this); + } + aLineOut.iGap = 0; + } + + // set a name for the texture + // @todo is this needed, what names to use + aLineOut.iTexture->SetImageFileNameL(_L("Pictographs")); + + TSize maxTextureSize = aLineOut.iTexture->MaxTextureSize(); + textureSize.iWidth = Min(textureSize.iWidth, maxTextureSize.iWidth); + textureSize.iHeight = Min(textureSize.iHeight, maxTextureSize.iHeight); + + if((textureSize.iWidth == 0) || (textureSize.iHeight == 0)) + { + // Cannot draw into this tiny texture, so leave. + HUI_DEBUG2(_L("CHuiRasterizedTextMesh::RasterizePictographLineL() - texture size was too small to draw into (%i, %i)."), textureSize.iWidth, textureSize.iHeight); + User::Leave(KErrAbort); + } + + User::LeaveIfError( iPictographBitmap->Resize(textureSize) ); + + CFbsBitmapDevice* device = CFbsBitmapDevice::NewL(iPictographBitmap); + CleanupStack::PushL(device); + + CFbsBitGc* gc = 0; + User::LeaveIfError( device->CreateContext(gc) ); + CleanupStack::PushL(gc); + + // Prepare the bitmap for drawing...set drawmode because of EColor16MA mode... + gc->SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha); + + TRgb color = KRgbWhite; + color.SetAlpha(0x00); + gc->SetBrushColor(color); + gc->Clear(); + gc->UseFont(aFont); + + // Draw pictorgraphs + iPictographInterface->Interface()->DrawPictographsInText( + *gc, + *aFont, + aTextLine, TPoint(0, aFont->FontMaxAscent())); + + CleanupStack::PopAndDestroy(gc); + CleanupStack::PopAndDestroy(device); + + aLineOut.iTexture->UploadL(*iPictographBitmap, NULL, EHuiTextureUploadFlagRetainResolution); + aLineOut.iTexture->SetSize(actualsize); + return !IsMaxLineCountReached(); + } + +void CHuiRasterizedTextMesh::BuildL(TBool aRasterize) + { + if (aRasterize) + { + DoBuildL(ERasterizeAll); + } + else + { + DoBuildL(ERasterizeNone); + } + } + +void CHuiRasterizedTextMesh::DoBuildL(TInt aRasterizeFlags) + { + if(iUsingPreRasterizedMesh) + { + return; + } + + TSize extents(0, 0); + + HUI_DEBUG(_L("CHuiRasterizedTextMesh::BuildL() - Updating rasterized text.")); + + // This is never NULL during BuildL(). + const TDesC& text = *Text(); + + // Retrieve the text style used when rasterizing this text mesh. + THuiTextStyle* textStyle = CHuiStatic::Env().TextStyleManager().TextStyle(iTextStyleId); + + // Retrieve the CFont object used when rasterizing this text mesh. + CFont* font = textStyle->Font().NearestFontL(iTextMeshScale); + + // Maximum width of a text line in pixels. + TInt maxWidth = MaxLineWidth(); + + TInt startIndex = 0; + TInt index = 0; + TInt lineCount = 0; + + CArrayFixFlat* linePtrs = new (ELeave) CArrayFixFlat(KLineArrayGranularity); + CleanupStack::PushL(linePtrs); + + while(startIndex < text.Length()) + { + /// @todo What is the Symbian way to determine line break chars? +#define HUI_IS_LINE_BREAK(aChar) (aChar == '\n') + + // Find the next logical line. + while(index < text.Length() && !HUI_IS_LINE_BREAK(text[index])) + { + index++; + } + + TPtrC logicalLine = text.Mid(startIndex, index - startIndex); + ++index; // Skip the line break. + startIndex = index; + + switch(LineMode()) + { + case ELineModeTruncate: + { + ++lineCount; // there's always one line created per logical line + HBufC* buf = logicalLine.AllocLC(); + TPtr ptr = buf->Des(); + // truncate line + CHuiStatic::ConvertToVisualAndClipL(ptr, *font, maxWidth, maxWidth); + // create the line entry if not already existing + + if (aRasterizeFlags != ERasterizeNone) + { + if (iLines.Count() < lineCount) + { + SRasterizedLine line; + line.iTexture = NULL; + line.iGap = 0; + iLines.AppendL(line); + + if (iPictographInterface) + { + SRasterizedLine pictographline; + pictographline.iTexture = NULL; + pictographline.iGap = 0; + iPictographLines.AppendL(pictographline); + } + } + + TInt currentLine = lineCount-1; + if (aRasterizeFlags & ERasterizeText) + { + // rasterize a single line (updates texture in iLines[0].iTexture) + RasterizeLineL(ptr, iLines[currentLine]); + } + + if (aRasterizeFlags & ERasterizePictographs && iPictographInterface) + { + // Rasterize pictographs if needed + RasterizePictographLineL(ptr, font, iPictographLines[currentLine]); + } + + // Get extents from the texture we just created + CHuiTexture* tex = iLines[currentLine].iTexture; + extents.iHeight += iLines[currentLine].iGap; + if(tex) + { + extents.iWidth = Max(extents.iWidth, tex->Size().iWidth); + extents.iHeight += tex->Size().iHeight; + } + } + else + { + // Don't rasterise or create textures, just get the extents of this text. + TSize lineExtents = textStyle->LineExtentsL(ptr); + extents.iWidth = Max(extents.iWidth, lineExtents.iWidth); + extents.iHeight += lineExtents.iHeight; + } + + + CleanupStack::PopAndDestroy(buf); + break; + } + + case ELineModeWrap: + { + // wrap lines to array + HBufC* buf = CHuiStatic::ConvertToVisualAndWrapToArrayL( + logicalLine, maxWidth, *font, *linePtrs); + CleanupStack::PushL(buf); + + // one line may create several wrapped lines + lineCount += linePtrs->Count(); + + if (aRasterizeFlags != ERasterizeNone) + { + + // create new entries.. + while (iLines.Count() < lineCount) + { + SRasterizedLine line; + line.iTexture = NULL; + line.iGap = 0; + iLines.AppendL(line); + + if (iPictographInterface) + { + SRasterizedLine pictographline; + pictographline.iTexture = NULL; + pictographline.iGap = 0; + iPictographLines.AppendL(pictographline); + } + } + + // Do rasterisation if we want to render to texture. + for(TInt i = 0; i < linePtrs->Count(); ++i) + { + TInt currentLine = (lineCount - linePtrs->Count()) + i; + + if (aRasterizeFlags & ERasterizeText) + { + // rasterize a single line (updates texture in iLines[i].iTexture) + RasterizeLineL(linePtrs->At(i), iLines[currentLine]); + } + + if (aRasterizeFlags & ERasterizePictographs && iPictographInterface) + { + // Rasterize pictographs if needed + RasterizePictographLineL(linePtrs->At(i), font, iPictographLines[currentLine]); + } + // Get extents from the texture we just created + CHuiTexture* tex = iLines[i].iTexture; + extents.iHeight += iLines[i].iGap; + + if(tex) + { + extents.iWidth = Max(extents.iWidth, tex->Size().iWidth); + extents.iHeight += tex->Size().iHeight; + } + + TBool moreAvailable = (currentLine + 1 < MaxLineCount()); + if (!moreAvailable) + { + // Maximum number of lines reached. + break; + } + } + } + else + { + // Don't rasterise or create textures, just get the extents of this text. + for(TInt i = 0; i < linePtrs->Count(); ++i) + { + TSize lineExtents = textStyle->LineExtentsL(linePtrs->At(i)); + extents.iWidth = Max(extents.iWidth, lineExtents.iWidth); + extents.iHeight += lineExtents.iHeight; + } + } + + linePtrs->Reset(); + CleanupStack::PopAndDestroy(buf); + break; + } + + default: + break; + } + + // If we have reached the maximum number of lines, stop building. + if(IsMaxLineCountReached()) + { + break; + } + } + + HUI_DEBUG(_L("CHuiRasterizedTextMesh::BuildL() - Finished rasterizing text.")); + + CleanupStack::PopAndDestroy(linePtrs); linePtrs = 0; + + if (iPictographBitmap) + { + iPictographBitmap->Resize(TSize(0, 0)); + } + + HUI_DEBUG(_L("CHuiRasterizedTextMesh::BuildL() - Updating text extents..")); + // The extents of the mesh depend on how many lines there are. + SetExtents(extents); + + HUI_DEBUG(_L("CHuiRasterizedTextMesh::BuildL() - Done!")); + + } + +void CHuiRasterizedTextMesh::ExpandRectWithShadow(TRect& aRect) const + { + if ( iVisual && iLines.Count() ) + { + CHuiDropShadow* shadowHandler = iVisual->DropShadowHandler(); + if ( shadowHandler && + shadowHandler->IsShadowVisible() && + iLines[0].iTexture ) + { + const TInt requestedBlurredSize = HUI_ROUND_FLOAT_TO_INT( 2*shadowHandler->iRadius.Now() ); + THuiTextureHandle shadow; + // take the first line as an example + TBool haveShadowTexture = iLines[0].iTexture->GetShadowTexture( shadow,requestedBlurredSize ); + + if ( haveShadowTexture ) + { + const TRect shadowRect = shadowHandler->ShadowDrawingTRect( + aRect.iTl, + aRect.Size(), + shadow.Size(), + *iVisual ); + + aRect.BoundingRect( shadowRect ); + } + } + } + } + + +void CHuiRasterizedTextMesh::Draw(CHuiGc& aGc, TReal32 aShadowOpacity) const __SOFTFP + { + THuiAlignHorizontal oldHorizAlign = aGc.AlignHorizontal(); + THuiAlignVertical oldVertAlign = aGc.AlignVertical(); + + // Because we are using DrawImage, which respects Gc alignments, and the + // context has already set up the appropriate alignment offset, we must + // disable the alignment temporarily. + aGc.SetAlign(EHuiAlignHLeft, EHuiAlignVTop); + + + // The actual text. + DrawLines(aGc, THuiRealPoint(0.f, 0.f), oldHorizAlign, aShadowOpacity); + + if(!iUsingPreRasterizedMesh) + { + // Pictographs, if needed. + DrawPictographLines(aGc, THuiRealPoint(0.f, 0.f), oldHorizAlign); + } + + aGc.SetAlign(oldHorizAlign, oldVertAlign); + } + + +void CHuiRasterizedTextMesh::DrawLines(CHuiGc& aGc, const THuiRealPoint& aOffset, + THuiAlignHorizontal aLineAlignment, TReal32 aShadowOpacity) const + { + TInt y = 0; + + // Draw the built lines using THuiImages. + for(TInt i = 0; i < iLines.Count(); ++i) + { + const SRasterizedLine& line = iLines[i]; + if(line.iTexture) + { + + THuiImage textImage(*line.iTexture); + THuiRealPoint linePos(0.f, TReal32(y)); + THuiRealSize lineSize = line.iTexture->Size(); + + // Do a downward scaling for line texture from TV resolution to LCD resolution. + if(iTextMeshScale != 1) + { + lineSize.iHeight = lineSize.iHeight/iTextMeshScale; + lineSize.iWidth = lineSize.iWidth/iTextMeshScale; + } + + // Choose the line-specific alignment. + switch(aLineAlignment) + { + case EHuiAlignHRight: + linePos.iX = Extents().iWidth - lineSize.iWidth; + break; + + case EHuiAlignHCenter: + linePos.iX = (Extents().iWidth - lineSize.iWidth) / 2; + break; + + default: + break; + } + + + // Is there a shadow? + if ( RasterizedShadow() ) + { + const TInt requestedBlurredSize = HUI_ROUND_FLOAT_TO_INT( 2*iVisual->DropShadowHandler()->iRadius.Now() ); + THuiTextureHandle shadow; + TBool haveShadowTexture = line.iTexture->GetShadowTexture(shadow,requestedBlurredSize ); + + if ( haveShadowTexture ) + { + THuiImage shadowImage(shadow); + const THuiRealRect shadowDrawingRect = iVisual->DropShadowHandler()->ShadowDrawingRealRect( + linePos, + lineSize, + shadow.Size(), + *iVisual ); + + const TRgb oldColor = aGc.PenColorAlpha(); + aGc.SetPenColor(iVisual->DropShadowHandler()->Color()); + aGc.SetPenAlpha(HUI_ROUND_FLOAT_TO_INT(aShadowOpacity * 255.0f)); + + const THuiQuality oldQuality = aGc.Quality(); + aGc.SetQuality(EHuiQualityFast); + + aGc.DrawImage(shadowImage, shadowDrawingRect.iTl + aOffset, shadowDrawingRect.Size()); + + aGc.SetPenColorAlpha(oldColor); + aGc.SetQuality(oldQuality); + } + } + + aGc.DrawImage(textImage, linePos + aOffset, lineSize); + + // Move one line downwards. + y += TInt(lineSize.iHeight) + line.iGap; + } + + // Move extra gap downwards. + y += line.iGap; + + // Add line spacing. + y += iLineSpacing; + } + } + +void CHuiRasterizedTextMesh::DrawPictographLines(CHuiGc& aGc, const THuiRealPoint& aOffset, + THuiAlignHorizontal aLineAlignment) const + { + if (!iPictographInterface || iUsingPreRasterizedMesh) + { + return; + } + + + TInt y = 0; + + // Draw the built lines using THuiImages. + for(TInt i = 0; i < iPictographLines.Count(); ++i) + { + const SRasterizedLine& line = iPictographLines[i]; + if(line.iTexture) + { + + THuiImage textImage(*line.iTexture); + THuiRealPoint linePos(0.f, TReal32(y)); + THuiRealSize lineSize = line.iTexture->Size(); + + // Choose the line-specific alignment. + switch(aLineAlignment) + { + case EHuiAlignHRight: + linePos.iX = Extents().iWidth - lineSize.iWidth; + break; + + case EHuiAlignHCenter: + linePos.iX = (Extents().iWidth - lineSize.iWidth) / 2; + break; + + default: + break; + } + + aGc.SetPenColor(KRgbWhite); + aGc.DrawImage(textImage, linePos + aOffset, lineSize); + + // Move one line downwards. + y += TInt(lineSize.iHeight) + line.iGap; + } + + // Move extra gap downwards. + y += line.iGap; + + // Add line spacing. + y += iLineSpacing; + } + } + + +void CHuiRasterizedTextMesh::TextureContentUploaded(CHuiTexture& /*aTexture*/) + { + } + + +void CHuiRasterizedTextMesh::TextureContentReleased(CHuiTexture& /*aTexture*/) + { + } + + +void CHuiRasterizedTextMesh::RestoreTextureContentL(CHuiTexture& /*aTexture*/) + { + // We only get one of these, so let's rebuild the text mesh. + HUI_DEBUG(_L("CHuiRasterizedTextMesh::RestoreTextureContentL() - Rebuilding text.")); + + // We want to render the mesh so pass true. + BuildL(ETrue); + } +void CHuiRasterizedTextMesh::InitPictographsL(CAknPictographInterface* aInterface) + { + if(!iUsingPreRasterizedMesh) + { + iPictographInterface = aInterface; + delete iPictographBitmap; + iPictographBitmap = NULL; + iPictographBitmap = new (ELeave) CFbsBitmap(); + User::LeaveIfError( iPictographBitmap->Create(TSize(0, 0), EColor16MA) ); + } + } + +void CHuiRasterizedTextMesh::BuildPictographsL() + { + if(!iUsingPreRasterizedMesh) + { + DoBuildL(ERasterizePictographs); + } + } + +void CHuiRasterizedTextMesh::UpdateMeshL(const TDesC8& aBuffer) + { + iUsingPreRasterizedMesh = ETrue; + ResetLines(); + RDesReadStream stream(aBuffer); + TInt count = stream.ReadInt32L(); + for (TInt i=count-1;i>=0;i--) + { + // lines are in reverse order + SRasterizedLine line; + line.iTexture = dynamic_cast((MHuiTexture*)stream.ReadInt32L()); //scary + line.iGap = stream.ReadInt32L(); + iLines.InsertL(line, 0); + } + TSize extents; + extents.iWidth = stream.ReadInt32L(); + extents.iHeight = stream.ReadInt32L(); + SetExtents(extents); + stream.Close(); + + if (RasterizedShadow()) // update shadow + { + for (TInt i = iLines.Count()-1; i >=0; i-- ) + { + if (iLines[i].iTexture) + { + const TInt requestedBlurredSize = HUI_ROUND_FLOAT_TO_INT( 2*iVisual->DropShadowHandler()->iRadius.Now() ); + iLines[i].iTexture->CreateShadowTextureL( requestedBlurredSize, EHuiTextureShadowStyleRasterizedText ); + } + } + } + } +