diff -r 2717213c588a -r 9f66f99ee56f fbs/fontandbitmapserver/sfbs/glyphatlas.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fbs/fontandbitmapserver/sfbs/glyphatlas.cpp Fri Sep 24 16:14:28 2010 +0300 @@ -0,0 +1,1060 @@ +// Copyright (c) 2009-2010 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 "glyphatlas.h" +#include "OstTraceDefinitions.h" +#ifdef OST_TRACE_COMPILER_IN_USE +#include "glyphatlasTraces.h" +#endif + + +extern void Panic(TFbsPanic aPanic); + +static TInt16 Load16(const TUint8* aPtr); +static void DecodeBinaryData(const TSize& aGlyphSize, const TUint8* aEncodedData, TUint8* aByteData); +static void DecodeBinaryDataExLarge(const TSize& aGlyphSize, const TUint8* aEncodedData, TUint8* aByteData); +static void Convert1BppTo8Bpp(TUint32 aSrcData, TUint8*& aDestDataPtr, const TUint8* aDestDataPtrLimit); +static void CopyCharLine(TUint8*& aByteDataPtr, TInt aWidthInBytes, const TUint8* aSrcData, TInt aBitShift, TInt16 aRepeatCount); + +// === CGlyphAtlas Functions === + +/** +Glyph Atlas constructor. +@param aMaxCacheSizeInBytes The maximum amount of specialised graphics memory + that the glyph atlas should use. If this value is KGlyphAtlasNoCacheLimit, + then there is no limit and the atlas will use as much memory as is available + in the system. +*/ +CGlyphAtlas::CGlyphAtlas(TInt aMaxCacheSizeInBytes) + :iLruPageList(_FOFF(CGlyphAtlasPage, iLink)), + iFontEntryArray(32, _FOFF(TFontEntryMap, iFont)), + iMaxCacheSizeInBytes(aMaxCacheSizeInBytes), + iMaxCacheSizeHigh(aMaxCacheSizeInBytes), + iGpuCacheSizeLimitIsMax(ETrue) + { + iMaxCacheSizeLow = ( KGlyphAtlasNoCacheLimit == aMaxCacheSizeInBytes ) + ? KGlyphAtlasLowMemCacheLimitDefault + : ( aMaxCacheSizeInBytes / KGlyphAtlasLowMemCacheLimitDivisor ); + } + +/** +Glyph Atlas destructor. +Frees all the RSgImage handles, frees all the allocated system memory, and +closes the Graphics Resource driver. +*/ +CGlyphAtlas::~CGlyphAtlas() + { + // cycle through all the font entries and destroy them + for (TInt ii = iFontEntryArray.Count()-1; ii >= 0; --ii) + { + DeleteFontEntry(iFontEntryArray[ii].iEntry); + } + iFontEntryArray.Close(); + __ASSERT_DEBUG(iLruPageList.IsEmpty(), Panic(EFbsPanicGlyphAtlasInconsistentState)); + + // there shouldn't be any remaining pages, but if there are, destroy them. + while (!iLruPageList.IsEmpty()) + { + delete iLruPageList.First(); + } + iSgDriver.Close(); + } + +/** +Factory constructor method. Creates a new glyph atlas. + +@param aMaxCacheSizeInBytes The size in bytes, to use as the upper limit + for the size of memory used by the glyph images in the atlas. If this + value is KGlyphAtlasNoCacheLimit, then there is no limit and the atlas + will use as much memory as is available in the system. + +@return A pointer to the newly-constructed atlas + +@leave KErrNoMemory if there was insufficient memory to create the atlas, + or a system wide error code if its RSgDriver failed to open. +*/ +CGlyphAtlas* CGlyphAtlas::NewL(TInt aMaxCacheSizeInBytes) + { + CGlyphAtlas* self = new (ELeave) CGlyphAtlas(aMaxCacheSizeInBytes); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); // self; + return self; + } + +/** +Two-phase constructor. +@leave A system wide error code if RSgDriver failed to open. +*/ +void CGlyphAtlas::ConstructL() + { + User::LeaveIfError(iSgDriver.Open()); + } + +/** +Retrieves a glyph from the atlas. +If the glyph is found, the glyph data passed in is populated. + +@param[in] aFont The font the glyph belongs to. +@param[in] aGlyphCode The glyph code for the glyph being requested. +@param[out] aGlyphImageInfo The glyph image information if this function is successful. +@return KErrNone if the glyph is found, KErrNotFound if not. +*/ +TInt CGlyphAtlas::GetGlyph(const CBitmapFont& aFont, TUint aGlyphCode, TGlyphImageInfo& aGlyphImageInfo) + { + OstTraceExt2( TRACE_NORMAL, CGLYPHATLAS_GETGLYPH, "> f=%x; gc=%04x",(TUint)&aFont, aGlyphCode); + + CGlyphAtlasFontEntry* fontEntry = FindFontEntry(aFont); + if (!fontEntry) + { + OstTrace0( TRACE_NORMAL, CGLYPHATLAS_GETGLYPH_END2, "< KErrNotFound"); + return KErrNotFound; + } + TInt err = fontEntry->GetGlyph(aGlyphCode, aGlyphImageInfo); + + OstTraceExt5( TRACE_NORMAL, CGLYPHATLAS_GETGLYPH_END1, "< id=%08x%08x; w=%u; h=%u; err=%d", + (TUint)I64HIGH(aGlyphImageInfo.iImageId.iId), (TUint)I64LOW(aGlyphImageInfo.iImageId.iId), + (TUint)aGlyphImageInfo.iMetrics.Width(), (TUint)aGlyphImageInfo.iMetrics.Height(), (TInt)err); + + return err; + } + +/** +Adds a glyph to the atlas from a bitmap glyph and retrieves the glyph data. +If there is insufficient memory to create a RSgImage, then the least recently +used pages (and all the glyphs contained within) are removed until there is +enough memory to continue. + +@param[in] aFont The font the glyph belongs to. +@param[in] aArgs The information needed to create a glyph. +@param[out] aGlyphImageInfo Upon return contains all the glyph image information needed to use the + glyph in another process. +@return KErrNone if the glyph was successfully added or other system-wide error. +*/ +TInt CGlyphAtlas::AddGlyph(const CBitmapFont& aFont, const TAddGlyphArgs& aArgs, TGlyphImageInfo& aGlyphImageInfo) + { + OstTraceDefExt5( OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_ADDGLYPH, "> f=%x; bp=%08x; gc=%04x; w=%u; h=%u", + (TUint)&aFont, (TUint)aArgs.iBitmapPointer, aArgs.iGlyphCode, + aArgs.iMetrics->Width(), aArgs.iMetrics->Height()); + + // Find font entry and create if none found + CGlyphAtlasFontEntry* fontEntry = FindFontEntry(aFont); + TBool isNewFont = EFalse; + if (!fontEntry) + { + // Create a new font. + fontEntry = CreateFontEntry(aFont); + if (!fontEntry) + { + OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_ADDGLYPH_END2, "< KErrNoMemory"); + return KErrNoMemory; + } + isNewFont = ETrue; + } + TInt glyphSizeInBytes = 0; + TInt err = fontEntry->AddGlyph(aArgs, aGlyphImageInfo, glyphSizeInBytes); + if (KErrNone != err) + { + if (isNewFont) + { + DeleteFontEntry(fontEntry); + } + OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_ADDGLYPH_END3, "< err=%d", err); + return err; + } + iCacheSizeInBytes += glyphSizeInBytes; + + // If there is a cache limit and it is now exceeded, remove the least + // recently used pages until the cache size is within the upper limit. Do + // not remove the page relating to the glyph which is being added, which is + // now at the head of the LRU array. + if (iMaxCacheSizeInBytes != KGlyphAtlasNoCacheLimit) + { + TBool morePagesToDelete = ETrue; + while ((iCacheSizeInBytes > iMaxCacheSizeInBytes) && morePagesToDelete) + { + morePagesToDelete = DeleteLeastRecentlyUsedPage(EFalse); + } + } + + OstTraceDefExt2(OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_ADDGLYPH_END1, "< id=%08x%08x", + I64HIGH(aGlyphImageInfo.iImageId.iId), I64LOW(aGlyphImageInfo.iImageId.iId)); + + return KErrNone; + } + +/** +Releases all glyphs associated with a particular font when it has been +released by the font system. + +@param aFont The font which is released. + */ +void CGlyphAtlas::FontReleased(const CBitmapFont& aFont) + { + OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_FONTRELEASED, "> f=%x", (TUint)&aFont); + + TInt index = iFontEntryArray.FindInUnsignedKeyOrder(TFontEntryMap(&aFont, NULL)); + if (KErrNotFound == index) + { + OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_FONTRELEASED_END2, "< KErrNotFound"); + return; + } + CGlyphAtlasFontEntry* fontEntry = iFontEntryArray[index].iEntry; + iCacheSizeInBytes -= fontEntry->SizeInBytes(); + delete fontEntry; + iFontEntryArray.Remove(index); + OstTraceDef0(OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_FONTRELEASED_END3, "< KErrNone"); + } + +/** +Searches the array of font entries to find the entry for the given font. +If the font entry is found, the entry is returned. If not a NULL pointer is +returned. + +@param aFont The font to match an entry with. +@return The font entry if a match is found, NULL if not. +*/ +CGlyphAtlasFontEntry* CGlyphAtlas::FindFontEntry(const CBitmapFont& aFont) const + { + TFontEntryMap entryToMatch(&aFont, NULL); + TInt index = iFontEntryArray.FindInUnsignedKeyOrder(entryToMatch); + if (KErrNotFound == index) + { + return NULL; + } + return iFontEntryArray[index].iEntry; + } + +/** +Deletes the given font entry. +The mapping from the font to the font entry is removed. + +@param aFontEntry The entry to delete. +*/ +void CGlyphAtlas::DeleteFontEntry(CGlyphAtlasFontEntry* aFontEntry) + { + __ASSERT_DEBUG(aFontEntry, Panic(EFbsPanicGlyphAtlasInconsistentState)); + TInt index = iFontEntryArray.FindInUnsignedKeyOrder(TFontEntryMap(&aFontEntry->Font(), NULL)); + __ASSERT_DEBUG(KErrNotFound != index, Panic(EFbsPanicGlyphAtlasInconsistentState)); + if (KErrNotFound != index) + { + iFontEntryArray.Remove(index); + } + iCacheSizeInBytes -= aFontEntry->SizeInBytes(); + delete aFontEntry; + } + +/** +Moves the given page to the front (the position of the most recently used page) +of the usage order list. + +@param aPage The most recently used page. +*/ +void CGlyphAtlas::MovePageToFront(CGlyphAtlasPage& aPage) + { + aPage.MoveToFirstInQueue(iLruPageList); + } + +/** +Creates a font entry from the given font and adds a mapping from the font +to the font entry. +If successful, the font entry is returned. +If either the creation of the font entry or the adding the mapping fails, then +a NULL pointer is returned. + +@param aFont The font used to create a font entry from. +@return A new font entry if successful, NULL if not. +*/ +CGlyphAtlasFontEntry* CGlyphAtlas::CreateFontEntry(const CBitmapFont& aFont) + { + CGlyphAtlasFontEntry* fontEntry = new CGlyphAtlasFontEntry(aFont, *this); + if (!fontEntry) + { + return NULL; + } + // Add font entry to font entry array + TFontEntryMap fontEntryMap(&aFont, fontEntry); + TInt err = iFontEntryArray.InsertInUnsignedKeyOrder(fontEntryMap); + __ASSERT_DEBUG(KErrAlreadyExists != err, Panic(EFbsPanicGlyphAtlasInconsistentState)); + if (KErrNone != err) + { + delete fontEntry; + fontEntry = NULL; + } + return fontEntry; + } + +/** +Deletes the least recently used page and removes it from the list of pages +held by the atlas. + +@param aAllowMruPageDeletion ETrue if the most recently used page can be deleted, + EFalse otherwise. +@return ETrue, if there are pages remaining in the atlas after the deletion, EFalse + otherwise. If there is only one page in the atlas and aAllowMruPageDeletion is EFalse, + EFalse is returned. +*/ +TBool CGlyphAtlas::DeleteLeastRecentlyUsedPage(TBool aAllowMruPageDeletion) + { + OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_DELETELEASTRECENTLYUSEDPAGE, "> mru=%d", aAllowMruPageDeletion ); + + CGlyphAtlasPage* lruPage = NULL; + if (!iLruPageList.IsEmpty()) + { + lruPage = iLruPageList.Last(); + if (!aAllowMruPageDeletion && (lruPage == iLruPageList.First())) + { + lruPage = NULL; + } + } + TBool canDeleteMorePages = EFalse; + if (lruPage) + { + iCacheSizeInBytes -= lruPage->SizeInBytes(); + CGlyphAtlasFontEntry& fontEntry = lruPage->FontEntry(); + fontEntry.DeletePage(lruPage); + lruPage = NULL; + if (fontEntry.IsEmpty()) + { + DeleteFontEntry(&fontEntry); + } + canDeleteMorePages = !iLruPageList.IsEmpty(); + } + OstTraceDefExt2(OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_DELETELEASTRECENTLYUSEDPAGE_END, "< more=%u; size=%u", (TUint)canDeleteMorePages, iCacheSizeInBytes); + return canDeleteMorePages; + } + +/** +Utility function that calculates the number of unique fonts associated with the atlas. +@return Number of fonts in the atlas. + */ +TInt CGlyphAtlas::FontCount() const + { + return iFontEntryArray.Count(); + } + +/** +Utility function that calculates the number of glyphs across all fonts stored in +the atlas. +@return Number of glyphs in the atlas. + */ +TInt CGlyphAtlas::GlyphCount() const + { + TInt glyphCount = 0; + for (TInt ii = iFontEntryArray.Count() - 1; ii >= 0; --ii) + { + glyphCount += iFontEntryArray[ii].iEntry->GlyphCount(); + } + return glyphCount; + } + +/** +Utility function that calculates the number of glyphs for a given font in the atlas. +@param The font to return the number of glyphs for. +@return Number of glyphs in the atlas. + */ +TInt CGlyphAtlas::GlyphCount(const CBitmapFont& aFont) const + { + CGlyphAtlasFontEntry* fontEntry = FindFontEntry(aFont); + return (fontEntry) ? fontEntry->GlyphCount() : 0; + } + +void CGlyphAtlas::GetGlyphCacheMetrics( TGlyphCacheMetrics& aGlyphCacheMetrics ) + { + aGlyphCacheMetrics.iMaxCacheSizeInBytes = iMaxCacheSizeInBytes; + aGlyphCacheMetrics.iMaxCacheSizeHigh = iMaxCacheSizeHigh; + aGlyphCacheMetrics.iMaxCacheSizeLow = iMaxCacheSizeLow; + aGlyphCacheMetrics.iCacheSizeInBytes = iCacheSizeInBytes; + aGlyphCacheMetrics.iGpuCacheSizeLimitIsMax = iGpuCacheSizeLimitIsMax; + } + +/** + Function to release the GPU cache. Called in response to the GoomMonitor's + requirement to reduce GPU memory use. + + @param aBytes. The amount of memory the GOoM framework would like us to relinquish. + @param aFlags. The flags conveyed from the GOoM monitor framework. +*/ +void CGlyphAtlas::ReleaseGpuMemory( TInt /*aBytes*/, TInt /*aFlags*/ ) + { + OstTraceDefExt2( OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_RELEASEGPUMEMORY, "> max=%d; size=%d", iMaxCacheSizeInBytes, iCacheSizeInBytes); + + if ( iCacheSizeInBytes > 0 ) + { + while( DeleteLeastRecentlyUsedPage(ETrue) ) + { + // Do nothing + } + } + + // If appropriate, reduce the cache-size limit. + if ( GpuCacheSizeLimitIsMax() ) + { + SwitchGpuCacheSizeLimit(); + } + + OstTraceDefExt2( OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_RELEASEGPUMEMORY_EXIT, "< max=%d; size=%d", iMaxCacheSizeInBytes, iCacheSizeInBytes); + } + +/** + Function to establish GPU memory use. Called in response to the GoomMonitor's + notification that GPU memory may once more be utilised in the usual manner. + + @param aFlags. The flags conveyed from the GOoM monitor framework. + */ +void CGlyphAtlas::InstateGpuMemory( TInt /*aFlags*/ ) + { + OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_INSTATEGPUMEMORY, "> max=%d", iMaxCacheSizeInBytes ); + + // If appropriate, reinstate the full cache-size limit. + if ( !GpuCacheSizeLimitIsMax() ) + { + SwitchGpuCacheSizeLimit(); + } + + OstTraceDef1(OST_TRACE_CATEGORY_DEBUG, TRACE_NORMAL, CGLYPHATLAS_INSTATEGPUMEMORY_EXIT, "< max=%d", iMaxCacheSizeInBytes ); + } + +/** + Utility function to toggle between full and reduced GPU cache-size limits. + + Use in conjunction with TBool GpuCacheSizeLimitIsMax() + */ +void CGlyphAtlas::SwitchGpuCacheSizeLimit() + { + if ( GpuCacheSizeLimitIsMax() ) + { + // The short-circuit operator obviates tautological conditionals. + while ( (iCacheSizeInBytes >= iMaxCacheSizeLow) && DeleteLeastRecentlyUsedPage(ETrue) ) + { + // Do Nothing... + } + + iMaxCacheSizeInBytes = iMaxCacheSizeLow; + iGpuCacheSizeLimitIsMax = EFalse; + } + else + { + iMaxCacheSizeInBytes = iMaxCacheSizeHigh; + iGpuCacheSizeLimitIsMax = ETrue; + } + } + + +/** + Utility function to report whether the GPU cache-size limit is set to the maximum permissible + level, or to its reduced level. + + @return ETrue if the cache-size is set to the maximum permissible limit. + EFalse if it is set to the reduced limit. + */ +TBool CGlyphAtlas::GpuCacheSizeLimitIsMax() const + { + return iGpuCacheSizeLimitIsMax; + } + +// === CGlyphAtlasFontEntry Functions === +/** +Font entry constructor. + +@param aFont The font to which this font entry should be associated. +@param aGlyphAtlas The glyph atlas to which this font entry belongs. +*/ +CGlyphAtlasFontEntry::CGlyphAtlasFontEntry(const CBitmapFont& aFont, CGlyphAtlas& aGlyphAtlas) + :iFont(aFont), + iPageArray(32, _FOFF(TPageMap, iGlyphCode)), + iAtlas(aGlyphAtlas) + { + } + +/** +Font entry destructor. +Destroys the pages owned by the font entry. +*/ +CGlyphAtlasFontEntry::~CGlyphAtlasFontEntry() + { + // cycle through all the font entries and destroy them + for (TInt ii = iPageArray.Count()-1; ii >= 0; --ii) + { + DeletePage(iPageArray[ii].iPage); + } + __ASSERT_DEBUG(iSizeInBytes == 0, Panic(EFbsPanicGlyphAtlasInconsistentState)); + iPageArray.Close(); + } + +/** +Deletes the given page. +The page mapping is removed and the page is deleted. + +@param aPage The page to delete. +*/ +void CGlyphAtlasFontEntry::DeletePage(CGlyphAtlasPage* aPage) + { + __ASSERT_DEBUG(aPage, Panic(EFbsPanicGlyphAtlasInconsistentState)); + __ASSERT_DEBUG(iPageArray.Count() > 0, Panic(EFbsPanicGlyphAtlasInconsistentState)); + + TInt numGlyphsInPage = aPage->GlyphCount(); + + for (TInt ii = 0; ii < numGlyphsInPage; ++ii) + { + TInt index = iPageArray.FindInUnsignedKeyOrder(TPageMap(aPage->GlyphCodeAt(ii), NULL)); + + __ASSERT_DEBUG(KErrNotFound != index, Panic(EFbsPanicGlyphAtlasInconsistentState)); + + if (KErrNotFound != index) + { + iPageArray.Remove(index); + iPageArray.GranularCompress(); + } + } + iSizeInBytes -= aPage->SizeInBytes(); + delete aPage; + + } + +/** +Adds a glyph to the font from a bitmap glyph and gets the glyph image info back. + +@param[in] aArgs The information needed to create a glyph. +@param[out] aGlyphImageInfo Upon return contains all the glyph image information needed to use the + glyph in another process. +@param[out] aSizeInBytes Upon return contains the size of the added glyph's image data in bytes. +@return KErrNone if the glyph was successfully added or other system-wide error. +*/ +TInt CGlyphAtlasFontEntry::AddGlyph(const CGlyphAtlas::TAddGlyphArgs& aArgs, TGlyphImageInfo& aGlyphImageInfo, TInt& aSizeInBytes) + { + CGlyphAtlasPage* newPage = new CGlyphAtlasPage(*this); + if (!newPage) + { + return KErrNoMemory; + } + + TInt err = newPage->AddGlyph(aArgs, aGlyphImageInfo, aSizeInBytes); + if (KErrNone != err) + { + delete newPage; + return err; + } + + err = iPageArray.InsertInUnsignedKeyOrder(TPageMap(aArgs.iGlyphCode, newPage)); + __ASSERT_DEBUG(KErrAlreadyExists != err, Panic(EFbsPanicGlyphAtlasInconsistentState)); + if (KErrNone != err) + { + delete newPage; + return err; + } + iSizeInBytes += aSizeInBytes; + iAtlas.MovePageToFront(*newPage); + return err; + } + +/** +Searches the array of pages to find the page containing the given glyph. +If the page is found, the glyph image info is populated and the page is moved +to the front of the Glyph Atlas' LRU page array. + +@param[in] aGlyphCode The glyph code for the glyph being requested. +@param[out] aGlyphImageInfo Upon return contains all the glyph image information needed to use the + glyph in another process. +@return KErrNone if the glyph was found, KErrNotFound if not. +*/ +TInt CGlyphAtlasFontEntry::GetGlyph(TUint aGlyphCode, TGlyphImageInfo& aGlyphImageInfo) + { + TInt index = iPageArray.FindInUnsignedKeyOrder(TPageMap(aGlyphCode, NULL)); + if (KErrNotFound == index) + { + return KErrNotFound; + } + CGlyphAtlasPage* page = iPageArray[index].iPage; + page->GetGlyph(aGlyphCode, aGlyphImageInfo); + iAtlas.MovePageToFront(*page); + return KErrNone; + } + +/** +Gets the font associated with the font entry. + +@return The font associated with this entry. +*/ +const CBitmapFont& CGlyphAtlasFontEntry::Font() const + { + return iFont; + } + +/** +Gets the amount of memory allocated for all the image data for this font. + +@return The size of the font's image data in bytes. +*/ +TInt CGlyphAtlasFontEntry::SizeInBytes() const + { + return iSizeInBytes; + } + +/** +Tests whether the font entry has any pages. + +@return ETrue if the font entry does not contain any pages, EFalse if it does.. +*/ +TBool CGlyphAtlasFontEntry::IsEmpty() const + { + if (iPageArray.Count() == 0) + { + __ASSERT_DEBUG(iSizeInBytes == 0, Panic(EFbsPanicGlyphAtlasInconsistentState)); + return ETrue; + } + return EFalse; + } + +/** +Gets the glyph atlas the font entry belongs to. + +@return The font entry's glyph atlas. +*/ +CGlyphAtlas& CGlyphAtlasFontEntry::GlyphAtlas() const + { + return iAtlas; + } + +/** +@return The number of glyphs this font entry has. + */ +TInt CGlyphAtlasFontEntry::GlyphCount() const + { + TInt glyphCount = 0; + for (TInt ii = iPageArray.Count() - 1; ii >= 0; --ii) + { + glyphCount += iPageArray[ii].iPage->GlyphCount(); + } + return glyphCount; + } + + +// === CGlyphAtlasPage Functions === + +/** +Page constructor. + +@param aFontEntry The font entry to which the page is associated. +*/ +CGlyphAtlasPage::CGlyphAtlasPage(CGlyphAtlasFontEntry& aFontEntry) + :iFontEntry(aFontEntry) + { + } + +/** +Page destructor. +Releases the RSgImage handles held by the page. +Removes the page from the Glyph Atlas' LRU page array. +*/ +CGlyphAtlasPage::~CGlyphAtlasPage() + { + iLink.Deque(); + iGlyphImage.Close(); + } + +/** +Adds a glyph to the page from a bitmap glyph and gets the glyph image info back. +An RSgImage handle is acquired for the glyph. +If there is not enough specialised graphics memory to create a RSgImage, then the +least recently used pages are deleted until there there is either enough memory +for the creation to be successful or if there are no more pages to delete (in +which case an appropriate out of memory error message is returned). + +@param[in] aArgs The information needed to create a glyph. +@param[out] aGlyphImageInfo Upon return contains all the glyph image information needed to use the + glyph in another process. +@param[out] aSizeInBytes Upon return contains the size of the added glyph's image data in bytes. +@return KErrNone if the glyph was successfully added; + KErrNoMemory if there is not enough system memory available; + KErrNoGraphicsMemory if there is not enough specialised graphics memory available. +*/ +TInt CGlyphAtlasPage::AddGlyph(const CGlyphAtlas::TAddGlyphArgs& aArgs, TGlyphImageInfo& aGlyphImageInfo, TInt& aSizeInBytes) + { + const TSize glyphSize(aArgs.iMetrics->Width(), aArgs.iMetrics->Height()); + // If glyph has zero size (e.g. space), set glyph data and return + if (glyphSize.iWidth == 0 || glyphSize.iHeight == 0) + { + iPosX = 0; + iPosY = 0; + iMetrics = *aArgs.iMetrics; + iGlyphCode = aArgs.iGlyphCode; + iSizeInBytes = 0; + iNumGlyphs++; + aGlyphImageInfo.iImageId = KSgNullDrawableId; + aGlyphImageInfo.iPosX = iPosX; + aGlyphImageInfo.iPosY = iPosY; + aGlyphImageInfo.iMetrics = iMetrics; + return KErrNone; + } + TUint8* buf = NULL; + TSgImageInfo info; + info.iSizeInPixels = glyphSize; + info.iUsage = ESgUsageBitOpenVgImage; + info.iPixelFormat = EUidPixelFormatA_8; + TInt dataStride = 0; + const TInt KDataArraySize = 256; + TUint8 byteDataArray[KDataArraySize]; + TUint8* tempBuf = NULL; + TUint8* byteDataBuf = NULL; + TGlyphBitmapType glyphBitmapType = iFontEntry.Font().GlyphBitmapType(); + + switch (glyphBitmapType) + { + case EMonochromeGlyphBitmap: + // Decompress to 8bpp buffer + dataStride = glyphSize.iWidth; + byteDataBuf = byteDataArray; + // If data too big to fit in byteDataArray, allocate memory on the heap + if (glyphSize.iHeight * glyphSize.iWidth > KDataArraySize) + { + tempBuf = (TUint8*) User::AllocZ(dataStride * glyphSize.iHeight); + if (!tempBuf) + { + return KErrNoMemory; + } + byteDataBuf = tempBuf; + } + else + { + // fill array with zeros. + Mem::FillZ(&byteDataArray, KDataArraySize); + } + + if (glyphSize.iWidth >32) + { + DecodeBinaryDataExLarge(glyphSize, aArgs.iBitmapPointer, byteDataBuf); + } + else + { + DecodeBinaryData(glyphSize, aArgs.iBitmapPointer, byteDataBuf); + } + buf = byteDataBuf; + break; + case EAntiAliasedGlyphBitmap: + buf = const_cast(aArgs.iBitmapPointer); + dataStride = glyphSize.iWidth; + break; + default: + return KErrNotSupported; + } + + TInt err = iGlyphImage.Create(info, buf, dataStride); + + // If RSgImage creation fails due to out of memory, delete the least + // recently used pages to free up memory until either creation succeeds or + // there are no more pages to remove. + TBool morePagesToDelete = ETrue; + while ((KErrNoGraphicsMemory == err || KErrNoMemory == err) && morePagesToDelete) + { + // Delete least used page. Can delete all pages if necessary as this + // page has not been added to the LRU array yet. + morePagesToDelete = iFontEntry.GlyphAtlas().DeleteLeastRecentlyUsedPage(ETrue); + err = iGlyphImage.Create(info, buf, dataStride); + } + + User::Free(tempBuf); + + if (KErrNone != err) + { + return err; + } + aSizeInBytes = glyphSize.iHeight * glyphSize.iWidth; + iGlyphCode = aArgs.iGlyphCode; + iPosX = 0; + iPosY = 0; + iMetrics = *aArgs.iMetrics; + // As the image is stored as one byte per pixel, the size in bytes is + // just the number of pixels. + // TODO: Replace estimating size with call to SgImage/SgDriver to get accurate size. + iSizeInBytes += aSizeInBytes; + iNumGlyphs++; + aGlyphImageInfo.iPosX = iPosX; + aGlyphImageInfo.iPosY = iPosY; + aGlyphImageInfo.iImageId = iGlyphImage.Id(); + aGlyphImageInfo.iMetrics = iMetrics; + return err; + } + +/** +Retrieves the glyph image information for the given glyph code necessary to be +able to use the glyph in another process. + +@param aGlyphCode The glyph code for the glyph being requested +@param aGlyphImageInfo Upon return contains all the glyph image information needed to use the + glyph in another process if the glyph is contained in the page. +*/ +void CGlyphAtlasPage::GetGlyph(TUint aGlyphCode, TGlyphImageInfo& aGlyphImageInfo) const + { + __ASSERT_DEBUG(iGlyphCode == aGlyphCode, Panic(EFbsPanicGlyphAtlasInconsistentState)); + aGlyphImageInfo.iMetrics = iMetrics; + aGlyphImageInfo.iPosX = iPosX; + aGlyphImageInfo.iPosY = iPosY; + aGlyphImageInfo.iImageId = iGlyphImage.Id(); + } + +/** +Gets the amount of memory allocated for the image data for this page. + +@return The size of the page's image data in bytes. +*/ +TInt CGlyphAtlasPage::SizeInBytes() const + { + return iSizeInBytes; + } + +/** +Gets the glyph code at the given index associated with the page. + +@param aIndex The index of the glyph code within the page. +@return The glyph code at the given index. +*/ +TUint CGlyphAtlasPage::GlyphCodeAt(TInt aIndex) const + { + __ASSERT_DEBUG(0 == aIndex, Panic(EFbsPanicGlyphAtlasInconsistentState)); + return iGlyphCode; + } + +/** +Gets the number of glyphs stored in the page. + +@return The number of glyphs in the page. +*/ +TInt CGlyphAtlasPage::GlyphCount() const + { + return iNumGlyphs; + } + +/** +Gets the font entry which owns the page. + +@return The font entry which owns the page. +*/ +CGlyphAtlasFontEntry& CGlyphAtlasPage::FontEntry() const + { + return iFontEntry; + } + + +void CGlyphAtlasPage::MoveToFirstInQueue(TDblQue& aList) + { + if(!aList.IsFirst(this)) + { + iLink.Deque(); + aList.AddFirst(*this); + } + } + +// === Static Utility Functions === + +/** +Combines 2 8-bit unsigned integers into a 16-bit integer. +@param aPtr A pointer to a source buffer of 2 8-bit unsigned integers. +@return The two 8-bit integers combined into a 16-bit integer. +*/ +static TInt16 Load16(const TUint8* aPtr) + { + return TInt16(aPtr[0]+(aPtr[1]<<8)); + } + +/** +Decodes binary data for monochrome glyph bitmap. + +@param aGlyphSize size of glyph in pixels. +@param aEncodedData Pointer to an encoded source buffer. +@param aByteData Pointer to a destination buffer (8 bits per pixel). +*/ +void DecodeBinaryData(const TSize& aGlyphSize, const TUint8* aEncodedData, TUint8* aByteData) + { + const TInt dataHeight = aGlyphSize.iHeight; + const TInt dataWidth = aGlyphSize.iWidth; + TUint32 binaryData = 0; + + // The data is encoded as follows: + // 1 bit for a multiple lines flag (1=yes) + // 4 bits for a repeat count which represents: + // -if the multiple line flag is 0 the number of lines whose data is repeated + // -if the flag is 1, the number of lines which differ from line to line. + // n bits representing the data at 1 bit per pixel, where: + // -if the multiple line flag is 0, n is the width of the glyph. + // -if the flag is 1, n is width of glyph multiplied by the repeat count for this block of data. + // This information presented in continuous packed blocks of: + // [data][reps][multiLineFlag] + TInt bitIndex = 0; + TInt16 repeatCount = 0; + TUint8* byteDataPtr = aByteData; + TUint8* byteDataPtrLimit = NULL; + for (TInt charLine = 0; charLine < dataHeight; charLine += repeatCount) // for lines in the character... + { + // Get first 5 bits of block + repeatCount = Load16(aEncodedData + (bitIndex >> 3)); + repeatCount >>= bitIndex & 7; + // strip out multiple line flag (1st bit) + TInt multiLineFlag = repeatCount & 1; + // Get repeat count (last 4 bits) + repeatCount >>= 1; + repeatCount &= 0xf; + // move bit index to point to first bit of image data + bitIndex += 5; + // end pointer of destination buffer for this block of data to fill + byteDataPtrLimit = aByteData + dataWidth * (charLine + repeatCount); + if (multiLineFlag) + { + while (byteDataPtr < byteDataPtrLimit) + { + // Pointer to beginning of data in source buffer for current scanline + TInt charDataOffsetPtr = TInt(aEncodedData) + (bitIndex >> 3); + // Pointer to beginning of current word. + TUint32* charDataWord = (TUint32*)(charDataOffsetPtr &~ 3); + // Number of bits to shift in current word to get to beginning of scanline + TInt bitShift = bitIndex & 7; + bitShift += (charDataOffsetPtr & 3) << 3; + // Copy scanline data into temporary buffer + binaryData = (*charDataWord++) >> bitShift; + // If data crosses a word boundary, get the rest of the data from next word. + if (bitShift) + { + binaryData |= (*charDataWord << (32-bitShift)); + } + Convert1BppTo8Bpp(binaryData, byteDataPtr, byteDataPtr + dataWidth); + // Move bit index to beginning of next block + bitIndex += dataWidth; + } + } + else + { + TInt charDataOffsetPtr = TInt(aEncodedData) + (bitIndex >> 3); + TUint32* charDataWord = (TUint32*)(charDataOffsetPtr &~ 3); + TInt bitShift = bitIndex & 7; + bitShift += (charDataOffsetPtr & 3) << 3; + binaryData = (*charDataWord++) >> bitShift; + if (bitShift) + { + binaryData |= (*charDataWord << (32-bitShift)); + } + TUint8* startByteDataPtr = byteDataPtr; + Convert1BppTo8Bpp(binaryData, byteDataPtr, byteDataPtr + dataWidth); + + while (byteDataPtr < byteDataPtrLimit) + { + Mem::Copy(byteDataPtr, startByteDataPtr, dataWidth); + byteDataPtr += dataWidth; + } + bitIndex += dataWidth; + } + } + } + +/** +Converts binary data in 1 bit per pixel format to 8 bits per pixel format, where +0 is converted to 0x00 and 1 is converted to 0xFF. + +@param aSrcData Pointer to a 1bpp source buffer. +@param aDestDataPtr Pointer to a 8bpp destination buffer. +@param aDestDataPtrLimit Pointer to the end position in destination buffer to convert to. +*/ +void Convert1BppTo8Bpp(TUint32 aSrcData, TUint8*& aDestDataPtr, const TUint8* aDestDataPtrLimit) + { + for (; aDestDataPtr < aDestDataPtrLimit; ++aDestDataPtr, aSrcData >>= 1) + { + if (aSrcData&1) + { + *aDestDataPtr = 0xFF; + } + } + } + +/** +Decodes binary data for extra large monochrome glyph bitmap. + +@param aGlyphSize Size of glyph in pixels. +@param aEncodedData Pointer to an encoded source buffer. +@param aByteData Pointer to a destination buffer (8 bits per pixel). +*/ +void DecodeBinaryDataExLarge(const TSize& aGlyphSize, const TUint8* aEncodedData, TUint8* aByteData) + { + const TInt dataWidth = aGlyphSize.iWidth; + const TInt dataHeight = aGlyphSize.iHeight; + TInt bitIndex = 0; + TInt16 repeatCount = 0; + + for (TInt charLine = 0; charLine < dataHeight; charLine += repeatCount) // for lines in the character... + { + repeatCount = Load16(aEncodedData + (bitIndex >> 3)); + repeatCount >>= bitIndex & 7; + const TInt multiLineFlag = repeatCount & 1; + repeatCount >>= 1; + repeatCount &= 0xf; + bitIndex += 5; + if (multiLineFlag) + { + for (TInt currentline = 0; currentline < repeatCount; currentline++) + { + CopyCharLine(aByteData, dataWidth, aEncodedData + (bitIndex >> 3), bitIndex & 7, 1); + bitIndex += dataWidth; + } + } + else + { + CopyCharLine(aByteData, dataWidth, aEncodedData + (bitIndex >> 3), bitIndex & 7, repeatCount); + bitIndex += dataWidth; + } + } + } + +/** +Copies glyph image data line(s)(1 bit per pixel) to an 8 bit per pixel +destination buffer. + +@param aByteDataPtr Pointer to a destination buffer (8bpp). +@param aWidthInBytes Stride of the image. +@param aSrcData Pointer to a source buffer (1bpp). +@param aBitShift Number of bits the source data pointer will be shifted. +@param aRepeatCount Number of lines to copy. +*/ +void CopyCharLine(TUint8*& aByteDataPtr, TInt aWidthInBytes, const TUint8* aSrcData, TInt aBitShift, TInt16 aRepeatCount) + { + aBitShift &= 7; + TUint8* ptrLimit = aByteDataPtr + aWidthInBytes; + TUint32* dataWord = (TUint32*)(TInt(aSrcData) &~ 3); + aBitShift += (TInt(aSrcData) - TInt(dataWord)) << 3; + + TUint8* startByteDataPtr = aByteDataPtr; + TUint32 binaryData = 0; + while (aByteDataPtr < ptrLimit) + { + binaryData = *dataWord++; + binaryData >>= aBitShift; + if (aBitShift) + { + binaryData |= (*dataWord << (32-aBitShift)); + } + TUint8* wordLimit = aByteDataPtr + 32; + if (wordLimit > ptrLimit) + { + wordLimit = ptrLimit; + } + Convert1BppTo8Bpp(binaryData, aByteDataPtr, wordLimit); + } + + while (aRepeatCount > 1) + { + Mem::Copy(aByteDataPtr, startByteDataPtr, aWidthInBytes); + aByteDataPtr += aWidthInBytes; + --aRepeatCount; + } + } + +