summaryrefslogtreecommitdiff
path: root/vcl/source/glyphs/glyphcache.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/glyphs/glyphcache.cxx')
-rw-r--r--vcl/source/glyphs/glyphcache.cxx594
1 files changed, 594 insertions, 0 deletions
diff --git a/vcl/source/glyphs/glyphcache.cxx b/vcl/source/glyphs/glyphcache.cxx
new file mode 100644
index 000000000000..ea0f18896b7a
--- /dev/null
+++ b/vcl/source/glyphs/glyphcache.cxx
@@ -0,0 +1,594 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <vcl/salbtype.hxx>
+#include <gcach_ftyp.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/outfont.hxx>
+
+#ifdef ENABLE_GRAPHITE
+#include <vcl/graphite_features.hxx>
+#endif
+
+#include <rtl/ustring.hxx> // used only for string=>hashvalue
+#include <osl/file.hxx>
+#include <tools/debug.hxx>
+
+// =======================================================================
+// GlyphCache
+// =======================================================================
+
+static GlyphCache* pInstance = NULL;
+
+GlyphCache::GlyphCache( GlyphCachePeer& rPeer )
+: mrPeer( rPeer ),
+ mnMaxSize( 1500000 ),
+ mnBytesUsed(sizeof(GlyphCache)),
+ mnLruIndex(0),
+ mnGlyphCount(0),
+ mpCurrentGCFont(NULL),
+ mpFtManager(NULL)
+{
+ pInstance = this;
+ mpFtManager = new FreetypeManager;
+}
+
+// -----------------------------------------------------------------------
+
+GlyphCache::~GlyphCache()
+{
+ InvalidateAllGlyphs();
+ if( mpFtManager )
+ delete mpFtManager;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::InvalidateAllGlyphs()
+{
+#if 0 // TODO: implement uncaching of all glyph shapes and metrics
+ for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
+ delete const_cast<ServerFont*>( it->second );
+ maFontList.clear();
+ mpCurrentGCFont = NULL;
+#endif
+}
+
+// -----------------------------------------------------------------------
+
+inline
+size_t GlyphCache::IFSD_Hash::operator()( const ImplFontSelectData& rFontSelData ) const
+{
+ // TODO: is it worth to improve this hash function?
+ sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
+#ifdef ENABLE_GRAPHITE
+ if (rFontSelData.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
+ != STRING_NOTFOUND)
+ {
+ rtl::OString aFeatName = rtl::OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
+ nFontId ^= aFeatName.hashCode();
+ }
+#endif
+ size_t nHash = nFontId << 8;
+ nHash += rFontSelData.mnHeight;
+ nHash += rFontSelData.mnOrientation;
+ nHash += rFontSelData.mbVertical;
+ nHash += rFontSelData.meItalic;
+ nHash += rFontSelData.meWeight;
+#ifdef ENABLE_GRAPHITE
+ nHash += rFontSelData.meLanguage;
+#endif
+ return nHash;
+}
+
+// -----------------------------------------------------------------------
+
+bool GlyphCache::IFSD_Equal::operator()( const ImplFontSelectData& rA, const ImplFontSelectData& rB) const
+{
+ // check font ids
+ sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
+ sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
+ if( nFontIdA != nFontIdB )
+ return false;
+
+ // compare with the requested metrics
+ if( (rA.mnHeight != rB.mnHeight)
+ || (rA.mnOrientation != rB.mnOrientation)
+ || (rA.mbVertical != rB.mbVertical)
+ || (rA.mbNonAntialiased != rB.mbNonAntialiased) )
+ return false;
+
+ if( (rA.meItalic != rB.meItalic)
+ || (rA.meWeight != rB.meWeight) )
+ return false;
+
+ // NOTE: ignoring meFamily deliberately
+
+ // compare with the requested width, allow default width
+ if( (rA.mnWidth != rB.mnWidth)
+ && ((rA.mnHeight != rB.mnWidth) || (rA.mnWidth != 0)) )
+ return false;
+#ifdef ENABLE_GRAPHITE
+ if (rA.meLanguage != rB.meLanguage)
+ return false;
+ // check for features
+ if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
+ != STRING_NOTFOUND ||
+ rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
+ != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName)
+ return false;
+#endif
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+GlyphCache& GlyphCache::GetInstance()
+{
+ return *pInstance;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::LoadFonts()
+{
+ if( const char* pFontPath = ::getenv( "SAL_FONTPATH_PRIVATE" ) )
+ AddFontPath( String::CreateFromAscii( pFontPath ) );
+ const String& rFontPath = Application::GetFontPath();
+ if( rFontPath.Len() > 0 )
+ AddFontPath( rFontPath );
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::ClearFontPath()
+{
+ if( mpFtManager )
+ mpFtManager->ClearFontList();
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::AddFontPath( const String& rFontPath )
+{
+ if( !mpFtManager )
+ return;
+
+ for( xub_StrLen nBreaker1 = 0, nBreaker2 = 0; nBreaker2 != STRING_LEN; nBreaker1 = nBreaker2 + 1 )
+ {
+ nBreaker2 = rFontPath.Search( ';', nBreaker1 );
+ if( nBreaker2 == STRING_NOTFOUND )
+ nBreaker2 = STRING_LEN;
+
+ ::rtl::OUString aUrlName;
+ osl::FileBase::getFileURLFromSystemPath( rFontPath.Copy( nBreaker1, nBreaker2 ), aUrlName );
+ mpFtManager->AddFontDir( aUrlName );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::AddFontFile( const rtl::OString& rNormalizedName, int nFaceNum,
+ sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA, const ExtraKernInfo* pExtraKern )
+{
+ if( mpFtManager )
+ mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA, pExtraKern );
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::AnnounceFonts( ImplDevFontList* pList ) const
+{
+ if( mpFtManager )
+ mpFtManager->AnnounceFonts( pList );
+ // VirtDevServerFont::AnnounceFonts( pList );
+}
+
+// -----------------------------------------------------------------------
+
+ServerFont* GlyphCache::CacheFont( const ImplFontSelectData& rFontSelData )
+{
+ // a serverfont request has pFontData
+ if( rFontSelData.mpFontData == NULL )
+ return NULL;
+ // a serverfont request has a fontid > 0
+ sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
+ if( nFontId <= 0 )
+ return NULL;
+
+ // the FontList's key mpFontData member is reinterpreted as font id
+ ImplFontSelectData aFontSelData = rFontSelData;
+ aFontSelData.mpFontData = reinterpret_cast<ImplFontData*>( nFontId );
+ FontList::iterator it = maFontList.find( aFontSelData );
+ if( it != maFontList.end() )
+ {
+ ServerFont* pFound = it->second;
+ if( pFound )
+ pFound->AddRef();
+ return pFound;
+ }
+
+ // font not cached yet => create new font item
+ ServerFont* pNew = NULL;
+ if( mpFtManager )
+ pNew = mpFtManager->CreateFont( aFontSelData );
+ // TODO: pNew = VirtDevServerFont::CreateFont( aFontSelData );
+
+ if( pNew )
+ {
+ maFontList[ aFontSelData ] = pNew;
+ mnBytesUsed += pNew->GetByteCount();
+
+ // enable garbage collection for new font
+ if( !mpCurrentGCFont )
+ {
+ mpCurrentGCFont = pNew;
+ pNew->mpNextGCFont = pNew;
+ pNew->mpPrevGCFont = pNew;
+ }
+ else
+ {
+ pNew->mpNextGCFont = mpCurrentGCFont;
+ pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
+ pNew->mpPrevGCFont->mpNextGCFont = pNew;
+ mpCurrentGCFont->mpPrevGCFont = pNew;
+ }
+ }
+
+ return pNew;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::UncacheFont( ServerFont& rServerFont )
+{
+ // the interface for rServerFont must be const because a
+ // user who wants to release it only got const ServerFonts.
+ // The caching algorithm needs a non-const object
+ ServerFont* pFont = const_cast<ServerFont*>( &rServerFont );
+ if( (pFont->Release() <= 0)
+ && (mnMaxSize <= (mnBytesUsed + mrPeer.GetByteCount())) )
+ {
+ mpCurrentGCFont = pFont;
+ GarbageCollect();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+ULONG GlyphCache::CalcByteCount() const
+{
+ ULONG nCacheSize = sizeof(*this);
+ for( FontList::const_iterator it = maFontList.begin(); it != maFontList.end(); ++it )
+ {
+ const ServerFont* pSF = it->second;
+ if( pSF )
+ nCacheSize += pSF->GetByteCount();
+ }
+ // TODO: also account something for hashtable management
+ return nCacheSize;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::GarbageCollect()
+{
+ // when current GC font has been destroyed get another one
+ if( !mpCurrentGCFont )
+ {
+ FontList::iterator it = maFontList.begin();
+ if( it != maFontList.end() )
+ mpCurrentGCFont = it->second;
+ }
+
+ // unless there is no other font to collect
+ if( !mpCurrentGCFont )
+ return;
+
+ // prepare advance to next font for garbage collection
+ ServerFont* const pServerFont = mpCurrentGCFont;
+ mpCurrentGCFont = pServerFont->mpNextGCFont;
+
+ if( (pServerFont == mpCurrentGCFont) // no other fonts
+ || (pServerFont->GetRefCount() > 0) ) // font still used
+ {
+ // try to garbage collect at least a few bytes
+ pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 );
+ }
+ else // current GC font is unreferenced
+ {
+ DBG_ASSERT( (pServerFont->GetRefCount() == 0),
+ "GlyphCache::GC detected RefCount underflow" );
+
+ // free all pServerFont related data
+ pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
+ if( pServerFont == mpCurrentGCFont )
+ mpCurrentGCFont = NULL;
+ const ImplFontSelectData& rIFSD = pServerFont->GetFontSelData();
+ maFontList.erase( rIFSD );
+ mrPeer.RemovingFont( *pServerFont );
+ mnBytesUsed -= pServerFont->GetByteCount();
+
+ // remove font from list of garbage collected fonts
+ if( pServerFont->mpPrevGCFont )
+ pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont;
+ if( pServerFont->mpNextGCFont )
+ pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont;
+ if( pServerFont == mpCurrentGCFont )
+ mpCurrentGCFont = NULL;
+
+ delete pServerFont;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
+{
+ rGlyphData.SetLruValue( mnLruIndex++ );
+}
+
+// -----------------------------------------------------------------------
+
+inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
+{
+ ++mnGlyphCount;
+ mnBytesUsed += sizeof( rGlyphData );
+ UsingGlyph( rServerFont, rGlyphData );
+ GrowNotify();
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::GrowNotify()
+{
+ if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize )
+ GarbageCollect();
+}
+
+// -----------------------------------------------------------------------
+
+inline void GlyphCache::RemovingGlyph( ServerFont& rSF, GlyphData& rGD, int nGlyphIndex )
+{
+ mrPeer.RemovingGlyph( rSF, rGD, nGlyphIndex );
+ mnBytesUsed -= sizeof( GlyphData );
+ --mnGlyphCount;
+}
+
+// =======================================================================
+// ServerFont
+// =======================================================================
+
+ServerFont::ServerFont( const ImplFontSelectData& rFSD )
+: maGlyphList( 0),
+ maFontSelData(rFSD),
+ mnExtInfo(0),
+ mnRefCount(1),
+ mnBytesUsed( sizeof(ServerFont) ),
+ mpPrevGCFont( NULL ),
+ mpNextGCFont( NULL ),
+ mnCos( 0x10000),
+ mnSin( 0 ),
+ mnZWJ( 0 ),
+ mnZWNJ( 0 ),
+ mbCollectedZW( false )
+{
+ // TODO: move update of mpFontEntry into FontEntry class when
+ // it becomes reponsible for the ServerFont instantiation
+ ((ImplServerFontEntry*)rFSD.mpFontEntry)->SetServerFont( this );
+
+ if( rFSD.mnOrientation != 0 )
+ {
+ const double dRad = rFSD.mnOrientation * ( F_2PI / 3600.0 );
+ mnCos = static_cast<long>( 0x10000 * cos( dRad ) + 0.5 );
+ mnSin = static_cast<long>( 0x10000 * sin( dRad ) + 0.5 );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+ServerFont::~ServerFont()
+{
+ ReleaseFromGarbageCollect();
+}
+
+// -----------------------------------------------------------------------
+
+void ServerFont::ReleaseFromGarbageCollect()
+{
+ // remove from GC list
+ ServerFont* pPrev = mpPrevGCFont;
+ ServerFont* pNext = mpNextGCFont;
+ if( pPrev ) pPrev->mpNextGCFont = pNext;
+ if( pNext ) pNext->mpPrevGCFont = pPrev;
+ mpPrevGCFont = NULL;
+ mpNextGCFont = NULL;
+}
+
+// -----------------------------------------------------------------------
+
+long ServerFont::Release() const
+{
+ DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
+ return --mnRefCount;
+}
+
+// -----------------------------------------------------------------------
+
+GlyphData& ServerFont::GetGlyphData( int nGlyphIndex )
+{
+ // usually the GlyphData is cached
+ GlyphList::iterator it = maGlyphList.find( nGlyphIndex );
+ if( it != maGlyphList.end() ) {
+ GlyphData& rGlyphData = it->second;
+ GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
+ return rGlyphData;
+ }
+
+ // sometimes not => we need to create and initialize it ourselves
+ GlyphData& rGlyphData = maGlyphList[ nGlyphIndex ];
+ mnBytesUsed += sizeof( GlyphData );
+ InitGlyphData( nGlyphIndex, rGlyphData );
+ GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
+ return rGlyphData;
+}
+
+// -----------------------------------------------------------------------
+
+void ServerFont::GarbageCollect( long nMinLruIndex )
+{
+ GlyphList::iterator it_next = maGlyphList.begin();
+ while( it_next != maGlyphList.end() )
+ {
+ GlyphList::iterator it = it_next++;
+ GlyphData& rGD = it->second;
+ if( (nMinLruIndex - rGD.GetLruValue()) > 0 )
+ {
+ OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) );
+ mnBytesUsed -= sizeof( GlyphData );
+ GlyphCache::GetInstance().RemovingGlyph( *this, rGD, it->first );
+ maGlyphList.erase( it );
+ it_next = maGlyphList.begin();
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+Point ServerFont::TransformPoint( const Point& rPoint ) const
+{
+ if( mnCos == 0x10000 )
+ return rPoint;
+ // TODO: use 32x32=>64bit intermediate
+ const double dCos = mnCos * (1.0 / 0x10000);
+ const double dSin = mnSin * (1.0 / 0x10000);
+ long nX = (long)(rPoint.X() * dCos + rPoint.Y() * dSin);
+ long nY = (long)(rPoint.Y() * dCos - rPoint.X() * dSin);
+ return Point( nX, nY );
+}
+
+bool ServerFont::IsGlyphInvisible( int nGlyphIndex )
+{
+ if (!mbCollectedZW)
+ {
+ mnZWJ = GetGlyphIndex( 0x200D );
+ mnZWNJ = GetGlyphIndex( 0x200C );
+ mbCollectedZW = true;
+ }
+
+ if( !nGlyphIndex ) // don't hide the NotDef glyph
+ return false;
+ if( (nGlyphIndex == mnZWNJ) || (nGlyphIndex == mnZWJ) )
+ return true;
+
+ return false;
+}
+
+// =======================================================================
+
+ImplServerFontEntry::ImplServerFontEntry( ImplFontSelectData& rFSD )
+: ImplFontEntry( rFSD ),
+ mpServerFont( NULL )
+{}
+
+// -----------------------------------------------------------------------
+
+ImplServerFontEntry::~ImplServerFontEntry()
+{
+ // TODO: remove the ServerFont here instead of in the GlyphCache
+}
+
+// =======================================================================
+
+ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId )
+: mbInitialized( false ),
+ mnFontId( nFontId ),
+ maUnicodeKernPairs( 0 )
+{}
+
+//--------------------------------------------------------------------------
+
+bool ExtraKernInfo::HasKernPairs() const
+{
+ if( !mbInitialized )
+ Initialize();
+ return !maUnicodeKernPairs.empty();
+}
+
+//--------------------------------------------------------------------------
+
+int ExtraKernInfo::GetUnscaledKernPairs( ImplKernPairData** ppKernPairs ) const
+{
+ if( !mbInitialized )
+ Initialize();
+
+ // return early if no kerning available
+ if( maUnicodeKernPairs.empty() )
+ return 0;
+
+ // allocate kern pair table
+ int nKernCount = maUnicodeKernPairs.size();
+ *ppKernPairs = new ImplKernPairData[ nKernCount ];
+
+ // fill in unicode kern pairs with the kern value scaled to the font width
+ ImplKernPairData* pKernData = *ppKernPairs;
+ UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.begin();
+ for(; it != maUnicodeKernPairs.end(); ++it )
+ *(pKernData++) = *it;
+
+ return nKernCount;
+}
+
+//--------------------------------------------------------------------------
+
+int ExtraKernInfo::GetUnscaledKernValue( sal_Unicode cLeft, sal_Unicode cRight ) const
+{
+ if( !mbInitialized )
+ Initialize();
+
+ if( maUnicodeKernPairs.empty() )
+ return 0;
+
+ ImplKernPairData aKernPair = { cLeft, cRight, 0 };
+ UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.find( aKernPair );
+ if( it == maUnicodeKernPairs.end() )
+ return 0;
+
+ int nUnscaledValue = (*it).mnKern;
+ return nUnscaledValue;
+}
+
+// =======================================================================
+