diff options
Diffstat (limited to 'vcl/unx/generic/glyphs/glyphcache.cxx')
-rw-r--r-- | vcl/unx/generic/glyphs/glyphcache.cxx | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/vcl/unx/generic/glyphs/glyphcache.cxx b/vcl/unx/generic/glyphs/glyphcache.cxx new file mode 100644 index 000000000000..33f06df2faab --- /dev/null +++ b/vcl/unx/generic/glyphs/glyphcache.cxx @@ -0,0 +1,379 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <stdlib.h> +#include <math.h> +#include "unx/freetype_glyphcache.hxx" + +#include <vcl/svapp.hxx> +#include <vcl/bitmap.hxx> +#include <fontinstance.hxx> +#include <fontattributes.hxx> + +#include <config_graphite.h> +#if ENABLE_GRAPHITE +#include <graphite_features.hxx> +#endif + +#include <rtl/ustring.hxx> +#include <osl/file.hxx> +#include <tools/debug.hxx> + +static GlyphCache* pInstance = nullptr; + +GlyphCache::GlyphCache() +: mnMaxSize( 1500000 ), + mnBytesUsed(sizeof(GlyphCache)), + mnLruIndex(0), + mnGlyphCount(0), + mpCurrentGCFont(nullptr), + mpFtManager(nullptr) +{ + pInstance = this; + mpFtManager = new FreetypeManager; +} + +GlyphCache::~GlyphCache() +{ + InvalidateAllGlyphs(); + delete mpFtManager; +} + +void GlyphCache::InvalidateAllGlyphs() +{ + for( FontList::iterator it = maFontList.begin(), end = maFontList.end(); it != end; ++it ) + { + ServerFont* pServerFont = it->second; + // free all pServerFont related data + pServerFont->GarbageCollect( mnLruIndex+0x10000000 ); + delete pServerFont; + } + + maFontList.clear(); + mpCurrentGCFont = nullptr; +} + +inline +size_t GlyphCache::IFSD_Hash::operator()( const FontSelectPattern& rFontSelData ) const +{ + // TODO: is it worth to improve this hash function? + sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData ); +#if ENABLE_GRAPHITE + if (rFontSelData.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + != -1) + { + OString aFeatName = OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); + nFontId ^= aFeatName.hashCode(); + } +#endif + size_t nHash = nFontId << 8; + nHash += rFontSelData.mnHeight; + nHash += rFontSelData.mnOrientation; + nHash += size_t(rFontSelData.mbVertical); + nHash += rFontSelData.GetSlantType(); + nHash += rFontSelData.GetWeight(); +#if ENABLE_GRAPHITE + nHash += rFontSelData.meLanguage; +#endif + return nHash; +} + +bool GlyphCache::IFSD_Equal::operator()( const FontSelectPattern& rA, const FontSelectPattern& 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.GetSlantType() != rB.GetSlantType()) + || (rA.GetWeight() != rB.GetWeight()) ) + return false; + + // NOTE: ignoring meFamily deliberately + + // compare with the requested width, allow default width + int nAWidth = rA.mnWidth != 0 ? rA.mnWidth : rA.mnHeight; + int nBWidth = rB.mnWidth != 0 ? rB.mnWidth : rB.mnHeight; + if( nAWidth != nBWidth ) + return false; + +#if ENABLE_GRAPHITE + if (rA.meLanguage != rB.meLanguage) + return false; + // check for features + if ((rA.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + != -1 || + rB.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + != -1) && rA.maTargetName != rB.maTargetName) + return false; +#endif + + if (rA.mbEmbolden != rB.mbEmbolden) + return false; + + if (rA.maItalicMatrix != rB.maItalicMatrix) + return false; + + return true; +} + +GlyphCache& GlyphCache::GetInstance() +{ + return *pInstance; +} + +void GlyphCache::AddFontFile( const OString& rNormalizedName, int nFaceNum, + sal_IntPtr nFontId, const FontAttributes& rDFA) +{ + if( mpFtManager ) + mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA); +} + +void GlyphCache::AnnounceFonts( PhysicalFontCollection* pFontCollection ) const +{ + if( mpFtManager ) + mpFtManager->AnnounceFonts( pFontCollection ); +} + +void GlyphCache::ClearFontCache() +{ + InvalidateAllGlyphs(); + if (mpFtManager) + mpFtManager->ClearFontList(); +} + +ServerFont* GlyphCache::CacheFont( const FontSelectPattern& rFontSelData ) +{ + // a serverfont request has pFontData + if( rFontSelData.mpFontData == nullptr ) + return nullptr; + // a serverfont request has a fontid > 0 + sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId(); + if( nFontId <= 0 ) + return nullptr; + + // the FontList's key mpFontData member is reinterpreted as font id + FontSelectPattern aFontSelData = rFontSelData; + aFontSelData.mpFontData = reinterpret_cast<PhysicalFontFace*>( 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 = nullptr; + if( mpFtManager ) + pNew = mpFtManager->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 ) +{ + if( (rServerFont.Release() <= 0) && (mnMaxSize <= mnBytesUsed) ) + { + mpCurrentGCFont = &rServerFont; + GarbageCollect(); + } +} + +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 = nullptr; + const FontSelectPattern& rIFSD = pServerFont->GetFontSelData(); + maFontList.erase( rIFSD ); + 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 = nullptr; + + 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 > mnMaxSize ) + GarbageCollect(); +} + +inline void GlyphCache::RemovingGlyph() +{ + mnBytesUsed -= sizeof( GlyphData ); + --mnGlyphCount; +} + +void ServerFont::ReleaseFromGarbageCollect() +{ + // remove from GC list + ServerFont* pPrev = mpPrevGCFont; + ServerFont* pNext = mpNextGCFont; + if( pPrev ) pPrev->mpNextGCFont = pNext; + if( pNext ) pNext->mpPrevGCFont = pPrev; + mpPrevGCFont = nullptr; + mpNextGCFont = nullptr; +} + +long ServerFont::Release() const +{ + DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" ); + return --mnRefCount; +} + +GlyphData& ServerFont::GetGlyphData( sal_GlyphId aGlyphId ) +{ + // usually the GlyphData is cached + GlyphList::iterator it = maGlyphList.find( aGlyphId ); + 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[ aGlyphId ]; + mnBytesUsed += sizeof( GlyphData ); + InitGlyphData( aGlyphId, rGlyphData ); + GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData ); + return rGlyphData; +} + +void ServerFont::GarbageCollect( long nMinLruIndex ) +{ + GlyphList::iterator it = maGlyphList.begin(); + while( it != maGlyphList.end() ) + { + GlyphData& rGD = it->second; + if( (nMinLruIndex - rGD.GetLruValue()) > 0 ) + { + OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) ); + mnBytesUsed -= sizeof( GlyphData ); + GlyphCache::GetInstance().RemovingGlyph(); + it = maGlyphList.erase( it ); + } + else + ++it; + } +} + +ServerFontInstance::ServerFontInstance( FontSelectPattern& rFSD ) +: LogicalFontInstance( rFSD ) +, mpServerFont( nullptr ) +, mbGotFontOptions( false ) +{} + +void ServerFontInstance::SetServerFont(ServerFont* p) +{ + if (p == mpServerFont) + return; + if (mpServerFont) + mpServerFont->Release(); + mpServerFont = p; + if (mpServerFont) + mpServerFont->AddRef(); +} + +ServerFontInstance::~ServerFontInstance() +{ + // TODO: remove the ServerFont here instead of in the GlyphCache + if (mpServerFont) + mpServerFont->Release(); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |