Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / generic / glyphs / glyphcache.cxx
blobcf75255aff8cfdc05b24deab1fad847438d8b3c7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <math.h>
23 #include <gcach_ftyp.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/bitmap.hxx>
27 #include <outfont.hxx>
29 #include <config_graphite.h>
30 #if ENABLE_GRAPHITE
31 #include <graphite_features.hxx>
32 #endif
34 #include <rtl/ustring.hxx>
35 #include <osl/file.hxx>
36 #include <tools/debug.hxx>
38 static GlyphCache* pInstance = NULL;
40 GlyphCache::GlyphCache( GlyphCachePeer& rPeer )
41 : mrPeer( rPeer ),
42 mnMaxSize( 1500000 ),
43 mnBytesUsed(sizeof(GlyphCache)),
44 mnLruIndex(0),
45 mnGlyphCount(0),
46 mpCurrentGCFont(NULL),
47 mpFtManager(NULL)
49 pInstance = this;
50 mpFtManager = new FreetypeManager;
53 GlyphCache::~GlyphCache()
55 InvalidateAllGlyphs();
56 delete mpFtManager;
59 void GlyphCache::InvalidateAllGlyphs()
61 for( FontList::iterator it = maFontList.begin(), end = maFontList.end(); it != end; ++it )
63 ServerFont* pServerFont = it->second;
64 mrPeer.RemovingFont(*pServerFont);
65 delete pServerFont;
68 maFontList.clear();
69 mpCurrentGCFont = NULL;
72 inline
73 size_t GlyphCache::IFSD_Hash::operator()( const FontSelectPattern& rFontSelData ) const
75 // TODO: is it worth to improve this hash function?
76 sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
77 #if ENABLE_GRAPHITE
78 if (rFontSelData.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
79 != -1)
81 OString aFeatName = OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
82 nFontId ^= aFeatName.hashCode();
84 #endif
85 size_t nHash = nFontId << 8;
86 nHash += rFontSelData.mnHeight;
87 nHash += rFontSelData.mnOrientation;
88 nHash += size_t(rFontSelData.mbVertical);
89 nHash += rFontSelData.GetSlant();
90 nHash += rFontSelData.GetWeight();
91 #if ENABLE_GRAPHITE
92 nHash += rFontSelData.meLanguage;
93 #endif
94 return nHash;
97 bool GlyphCache::IFSD_Equal::operator()( const FontSelectPattern& rA, const FontSelectPattern& rB) const
99 // check font ids
100 sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
101 sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
102 if( nFontIdA != nFontIdB )
103 return false;
105 // compare with the requested metrics
106 if( (rA.mnHeight != rB.mnHeight)
107 || (rA.mnOrientation != rB.mnOrientation)
108 || (rA.mbVertical != rB.mbVertical)
109 || (rA.mbNonAntialiased != rB.mbNonAntialiased) )
110 return false;
112 if( (rA.GetSlant() != rB.GetSlant())
113 || (rA.GetWeight() != rB.GetWeight()) )
114 return false;
116 // NOTE: ignoring meFamily deliberately
118 // compare with the requested width, allow default width
119 int nAWidth = rA.mnWidth != 0 ? rA.mnWidth : rA.mnHeight;
120 int nBWidth = rB.mnWidth != 0 ? rB.mnWidth : rB.mnHeight;
121 if( nAWidth != nBWidth )
122 return false;
124 #if ENABLE_GRAPHITE
125 if (rA.meLanguage != rB.meLanguage)
126 return false;
127 // check for features
128 if ((rA.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
129 != -1 ||
130 rB.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
131 != -1) && rA.maTargetName != rB.maTargetName)
132 return false;
133 #endif
135 if (rA.mbEmbolden != rB.mbEmbolden)
136 return false;
138 if (rA.maItalicMatrix != rB.maItalicMatrix)
139 return false;
141 return true;
144 GlyphCache& GlyphCache::GetInstance()
146 return *pInstance;
149 void GlyphCache::AddFontFile( const OString& rNormalizedName, int nFaceNum,
150 sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA)
152 if( mpFtManager )
153 mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA);
156 void GlyphCache::AnnounceFonts( PhysicalFontCollection* pFontCollection ) const
158 if( mpFtManager )
159 mpFtManager->AnnounceFonts( pFontCollection );
162 void GlyphCache::ClearFontCache()
164 InvalidateAllGlyphs();
165 if (mpFtManager)
166 mpFtManager->ClearFontList();
169 ServerFont* GlyphCache::CacheFont( const FontSelectPattern& rFontSelData )
171 // a serverfont request has pFontData
172 if( rFontSelData.mpFontData == NULL )
173 return NULL;
174 // a serverfont request has a fontid > 0
175 sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
176 if( nFontId <= 0 )
177 return NULL;
179 // the FontList's key mpFontData member is reinterpreted as font id
180 FontSelectPattern aFontSelData = rFontSelData;
181 aFontSelData.mpFontData = reinterpret_cast<PhysicalFontFace*>( nFontId );
182 FontList::iterator it = maFontList.find( aFontSelData );
183 if( it != maFontList.end() )
185 ServerFont* pFound = it->second;
186 if( pFound )
187 pFound->AddRef();
188 return pFound;
191 // font not cached yet => create new font item
192 ServerFont* pNew = NULL;
193 if( mpFtManager )
194 pNew = mpFtManager->CreateFont( aFontSelData );
196 if( pNew )
198 maFontList[ aFontSelData ] = pNew;
199 mnBytesUsed += pNew->GetByteCount();
201 // enable garbage collection for new font
202 if( !mpCurrentGCFont )
204 mpCurrentGCFont = pNew;
205 pNew->mpNextGCFont = pNew;
206 pNew->mpPrevGCFont = pNew;
208 else
210 pNew->mpNextGCFont = mpCurrentGCFont;
211 pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
212 pNew->mpPrevGCFont->mpNextGCFont = pNew;
213 mpCurrentGCFont->mpPrevGCFont = pNew;
217 return pNew;
220 void GlyphCache::UncacheFont( ServerFont& rServerFont )
222 // the interface for rServerFont must be const because a
223 // user who wants to release it only got const ServerFonts.
224 // The caching algorithm needs a non-const object
225 ServerFont* pFont = const_cast<ServerFont*>( &rServerFont );
226 if( (pFont->Release() <= 0) && (mnMaxSize <= mnBytesUsed) )
228 mpCurrentGCFont = pFont;
229 GarbageCollect();
233 void GlyphCache::GarbageCollect()
235 // when current GC font has been destroyed get another one
236 if( !mpCurrentGCFont )
238 FontList::iterator it = maFontList.begin();
239 if( it != maFontList.end() )
240 mpCurrentGCFont = it->second;
243 // unless there is no other font to collect
244 if( !mpCurrentGCFont )
245 return;
247 // prepare advance to next font for garbage collection
248 ServerFont* const pServerFont = mpCurrentGCFont;
249 mpCurrentGCFont = pServerFont->mpNextGCFont;
251 if( (pServerFont == mpCurrentGCFont) // no other fonts
252 || (pServerFont->GetRefCount() > 0) ) // font still used
254 // try to garbage collect at least a few bytes
255 pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 );
257 else // current GC font is unreferenced
259 DBG_ASSERT( (pServerFont->GetRefCount() == 0),
260 "GlyphCache::GC detected RefCount underflow" );
262 // free all pServerFont related data
263 pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
264 if( pServerFont == mpCurrentGCFont )
265 mpCurrentGCFont = NULL;
266 const FontSelectPattern& rIFSD = pServerFont->GetFontSelData();
267 maFontList.erase( rIFSD );
268 mrPeer.RemovingFont( *pServerFont );
269 mnBytesUsed -= pServerFont->GetByteCount();
271 // remove font from list of garbage collected fonts
272 if( pServerFont->mpPrevGCFont )
273 pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont;
274 if( pServerFont->mpNextGCFont )
275 pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont;
276 if( pServerFont == mpCurrentGCFont )
277 mpCurrentGCFont = NULL;
279 delete pServerFont;
283 inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
285 rGlyphData.SetLruValue( mnLruIndex++ );
288 inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
290 ++mnGlyphCount;
291 mnBytesUsed += sizeof( rGlyphData );
292 UsingGlyph( rServerFont, rGlyphData );
293 GrowNotify();
296 void GlyphCache::GrowNotify()
298 if( mnBytesUsed > mnMaxSize )
299 GarbageCollect();
302 inline void GlyphCache::RemovingGlyph( GlyphData& rGD )
304 mrPeer.RemovingGlyph( rGD );
305 mnBytesUsed -= sizeof( GlyphData );
306 --mnGlyphCount;
309 void ServerFont::ReleaseFromGarbageCollect()
311 // remove from GC list
312 ServerFont* pPrev = mpPrevGCFont;
313 ServerFont* pNext = mpNextGCFont;
314 if( pPrev ) pPrev->mpNextGCFont = pNext;
315 if( pNext ) pNext->mpPrevGCFont = pPrev;
316 mpPrevGCFont = NULL;
317 mpNextGCFont = NULL;
320 long ServerFont::Release() const
322 DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
323 return --mnRefCount;
326 GlyphData& ServerFont::GetGlyphData( sal_GlyphId aGlyphId )
328 // usually the GlyphData is cached
329 GlyphList::iterator it = maGlyphList.find( aGlyphId );
330 if( it != maGlyphList.end() ) {
331 GlyphData& rGlyphData = it->second;
332 GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
333 return rGlyphData;
336 // sometimes not => we need to create and initialize it ourselves
337 GlyphData& rGlyphData = maGlyphList[ aGlyphId ];
338 mnBytesUsed += sizeof( GlyphData );
339 InitGlyphData( aGlyphId, rGlyphData );
340 GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
341 return rGlyphData;
344 void ServerFont::GarbageCollect( long nMinLruIndex )
346 GlyphList::iterator it = maGlyphList.begin();
347 while( it != maGlyphList.end() )
349 GlyphData& rGD = it->second;
350 if( (nMinLruIndex - rGD.GetLruValue()) > 0 )
352 OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) );
353 mnBytesUsed -= sizeof( GlyphData );
354 GlyphCache::GetInstance().RemovingGlyph( rGD );
355 it = maGlyphList.erase( it );
357 else
358 ++it;
362 ImplServerFontEntry::ImplServerFontEntry( FontSelectPattern& rFSD )
363 : ImplFontEntry( rFSD )
364 , mpServerFont( NULL )
365 , mbGotFontOptions( false )
368 void ImplServerFontEntry::SetServerFont(ServerFont* p)
370 if (p == mpServerFont)
371 return;
372 if (mpServerFont)
373 mpServerFont->Release();
374 mpServerFont = p;
375 if (mpServerFont)
376 mpServerFont->AddRef();
379 ImplServerFontEntry::~ImplServerFontEntry()
381 // TODO: remove the ServerFont here instead of in the GlyphCache
382 if (mpServerFont)
383 mpServerFont->Release();
385 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */