xref: /trunk/main/vcl/source/glyphs/glyphcache.cxx (revision 86e1cf34)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <math.h>
30 
31 #include <gcach_ftyp.hxx>
32 
33 #include <vcl/svapp.hxx>
34 #include <vcl/bitmap.hxx>
35 #include <vcl/salbtype.hxx>
36 
37 #include <outfont.hxx>
38 
39 #ifdef ENABLE_GRAPHITE
40 #include <graphite_features.hxx>
41 #endif
42 
43 #include <rtl/ustring.hxx>		// used only for string=>hashvalue
44 #include <osl/file.hxx>
45 #include <tools/debug.hxx>
46 
47 // =======================================================================
48 // GlyphCache
49 // =======================================================================
50 
51 static GlyphCache* pInstance = NULL;
52 
GlyphCache(GlyphCachePeer & rPeer)53 GlyphCache::GlyphCache( GlyphCachePeer& rPeer )
54 :   mrPeer( rPeer ),
55     mnMaxSize( 1500000 ),
56     mnBytesUsed(sizeof(GlyphCache)),
57     mnLruIndex(0),
58     mnGlyphCount(0),
59     mpCurrentGCFont(NULL),
60     mpFtManager(NULL)
61 {
62     pInstance = this;
63     mpFtManager = new FreetypeManager;
64 }
65 
66 // -----------------------------------------------------------------------
67 
~GlyphCache()68 GlyphCache::~GlyphCache()
69 {
70     InvalidateAllGlyphs();
71     if( mpFtManager )
72         delete mpFtManager;
73 }
74 
75 // -----------------------------------------------------------------------
76 
InvalidateAllGlyphs()77 void GlyphCache::InvalidateAllGlyphs()
78 {
79     // an application about to exit can omit garbage collecting the heap
80     // since it makes things slower and introduces risks if the heap was not perfect
81     // for debugging, for memory grinding or leak checking the env allows to force GC
82     const char* pEnv = getenv( "SAL_FORCE_GC_ON_EXIT" );
83     if( pEnv && (*pEnv != '0') )
84     {
85         // uncache of all glyph shapes and metrics
86         for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
87             delete const_cast<ServerFont*>( it->second );
88         maFontList.clear();
89         mpCurrentGCFont = NULL;
90     }
91 }
92 
93 // -----------------------------------------------------------------------
94 
95 inline
operator ()(const ImplFontSelectData & rFontSelData) const96 size_t GlyphCache::IFSD_Hash::operator()( const ImplFontSelectData& rFontSelData ) const
97 {
98     // TODO: is it worth to improve this hash function?
99     sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
100 #ifdef ENABLE_GRAPHITE
101     if (rFontSelData.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
102         != STRING_NOTFOUND)
103     {
104         rtl::OString aFeatName = rtl::OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
105         nFontId ^= aFeatName.hashCode();
106     }
107 #endif
108     size_t nHash = nFontId << 8;
109     nHash   += rFontSelData.mnHeight;
110     nHash   += rFontSelData.mnOrientation;
111     nHash   += rFontSelData.mbVertical;
112     nHash   += rFontSelData.meItalic;
113     nHash   += rFontSelData.meWeight;
114 #ifdef ENABLE_GRAPHITE
115     nHash   += rFontSelData.meLanguage;
116 #endif
117     return nHash;
118 }
119 
120 // -----------------------------------------------------------------------
121 
operator ()(const ImplFontSelectData & rA,const ImplFontSelectData & rB) const122 bool GlyphCache::IFSD_Equal::operator()( const ImplFontSelectData& rA, const ImplFontSelectData& rB) const
123 {
124     // check font ids
125     sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
126     sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
127     if( nFontIdA != nFontIdB )
128         return false;
129 
130     // compare with the requested metrics
131     if( (rA.mnHeight         != rB.mnHeight)
132     ||  (rA.mnOrientation    != rB.mnOrientation)
133     ||  (rA.mbVertical       != rB.mbVertical)
134     ||  (rA.mbNonAntialiased != rB.mbNonAntialiased) )
135         return false;
136 
137     if( (rA.meItalic != rB.meItalic)
138     ||  (rA.meWeight != rB.meWeight) )
139         return false;
140 
141 	// NOTE: ignoring meFamily deliberately
142 
143     // compare with the requested width, allow default width
144     if( (rA.mnWidth != rB.mnWidth)
145     && ((rA.mnHeight != rB.mnWidth) || (rA.mnWidth != 0)) )
146         return false;
147 #ifdef ENABLE_GRAPHITE
148    if (rA.meLanguage != rB.meLanguage)
149         return false;
150    // check for features
151    if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
152         != STRING_NOTFOUND ||
153         rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
154         != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName)
155         return false;
156 #endif
157     return true;
158 }
159 
160 // -----------------------------------------------------------------------
161 
GetInstance()162 GlyphCache& GlyphCache::GetInstance()
163 {
164 	return *pInstance;
165 }
166 
167 // -----------------------------------------------------------------------
168 
LoadFonts()169 void GlyphCache::LoadFonts()
170 {
171     if( const char* pFontPath = ::getenv( "SAL_FONTPATH_PRIVATE" ) )
172         AddFontPath( String::CreateFromAscii( pFontPath ) );
173     const String& rFontPath = Application::GetFontPath();
174     if( rFontPath.Len() > 0 )
175         AddFontPath( rFontPath );
176 }
177 
178 // -----------------------------------------------------------------------
179 
ClearFontPath()180 void GlyphCache::ClearFontPath()
181 {
182     if( mpFtManager )
183         mpFtManager->ClearFontList();
184 }
185 
186 // -----------------------------------------------------------------------
187 
AddFontPath(const String & rFontPath)188 void GlyphCache::AddFontPath( const String& rFontPath )
189 {
190     if( !mpFtManager )
191         return;
192 
193     for( xub_StrLen nBreaker1 = 0, nBreaker2 = 0; nBreaker2 != STRING_LEN; nBreaker1 = nBreaker2 + 1 )
194     {
195         nBreaker2 = rFontPath.Search( ';', nBreaker1 );
196         if( nBreaker2 == STRING_NOTFOUND )
197             nBreaker2 = STRING_LEN;
198 
199         ::rtl::OUString aUrlName;
200         osl::FileBase::getFileURLFromSystemPath( rFontPath.Copy( nBreaker1, nBreaker2 ), aUrlName );
201         mpFtManager->AddFontDir( aUrlName );
202     }
203 }
204 
205 // -----------------------------------------------------------------------
206 
AddFontFile(const rtl::OString & rNormalizedName,int nFaceNum,sal_IntPtr nFontId,const ImplDevFontAttributes & rDFA,const ExtraKernInfo * pExtraKern)207 void GlyphCache::AddFontFile( const rtl::OString& rNormalizedName, int nFaceNum,
208     sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA, const ExtraKernInfo* pExtraKern )
209 {
210     if( mpFtManager )
211         mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA, pExtraKern );
212 }
213 
214 // -----------------------------------------------------------------------
215 
AnnounceFonts(ImplDevFontList * pList) const216 void GlyphCache::AnnounceFonts( ImplDevFontList* pList ) const
217 {
218     if( mpFtManager )
219         mpFtManager->AnnounceFonts( pList );
220     // VirtDevServerFont::AnnounceFonts( pList );
221 }
222 
223 // -----------------------------------------------------------------------
224 
CacheFont(const ImplFontSelectData & rFontSelData)225 ServerFont* GlyphCache::CacheFont( const ImplFontSelectData& rFontSelData )
226 {
227     // a serverfont request has pFontData
228     if( rFontSelData.mpFontData == NULL )
229         return NULL;
230     // a serverfont request has a fontid > 0
231     sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
232     if( nFontId <= 0 )
233         return NULL;
234 
235     // the FontList's key mpFontData member is reinterpreted as font id
236     ImplFontSelectData aFontSelData = rFontSelData;
237     aFontSelData.mpFontData = reinterpret_cast<ImplFontData*>( nFontId );
238     FontList::iterator it = maFontList.find( aFontSelData );
239     if( it != maFontList.end() )
240     {
241         ServerFont* pFound = it->second;
242         if( pFound )
243             pFound->AddRef();
244         return pFound;
245     }
246 
247     // font not cached yet => create new font item
248     ServerFont* pNew = NULL;
249     if( mpFtManager )
250         pNew = mpFtManager->CreateFont( aFontSelData );
251     // TODO: pNew = VirtDevServerFont::CreateFont( aFontSelData );
252 
253     if( pNew )
254     {
255         maFontList[ aFontSelData ] = pNew;
256         mnBytesUsed += pNew->GetByteCount();
257 
258         // enable garbage collection for new font
259         if( !mpCurrentGCFont )
260         {
261             mpCurrentGCFont = pNew;
262             pNew->mpNextGCFont = pNew;
263             pNew->mpPrevGCFont = pNew;
264         }
265         else
266         {
267             pNew->mpNextGCFont = mpCurrentGCFont;
268             pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
269             pNew->mpPrevGCFont->mpNextGCFont = pNew;
270             mpCurrentGCFont->mpPrevGCFont = pNew;
271         }
272     }
273 
274     return pNew;
275 }
276 
277 // -----------------------------------------------------------------------
278 
UncacheFont(ServerFont & rServerFont)279 void GlyphCache::UncacheFont( ServerFont& rServerFont )
280 {
281     // the interface for rServerFont must be const because a
282     // user who wants to release it only got const ServerFonts.
283     // The caching algorithm needs a non-const object
284     ServerFont* pFont = const_cast<ServerFont*>( &rServerFont );
285     if( (pFont->Release() <= 0)
286     &&  (mnMaxSize <= (mnBytesUsed + mrPeer.GetByteCount())) )
287     {
288         mpCurrentGCFont = pFont;
289         GarbageCollect();
290     }
291 }
292 
293 // -----------------------------------------------------------------------
294 
CalcByteCount() const295 sal_uLong GlyphCache::CalcByteCount() const
296 {
297     sal_uLong nCacheSize = sizeof(*this);
298     for( FontList::const_iterator it = maFontList.begin(); it != maFontList.end(); ++it )
299     {
300         const ServerFont* pSF = it->second;
301         if( pSF )
302             nCacheSize += pSF->GetByteCount();
303     }
304     // TODO: also account something for hashtable management
305     return nCacheSize;
306 }
307 
308 // -----------------------------------------------------------------------
309 
GarbageCollect()310 void GlyphCache::GarbageCollect()
311 {
312     // when current GC font has been destroyed get another one
313     if( !mpCurrentGCFont )
314     {
315         FontList::iterator it = maFontList.begin();
316         if( it != maFontList.end() )
317             mpCurrentGCFont = it->second;
318     }
319 
320     // unless there is no other font to collect
321     if( !mpCurrentGCFont )
322         return;
323 
324     // prepare advance to next font for garbage collection
325     ServerFont* const pServerFont = mpCurrentGCFont;
326     mpCurrentGCFont = pServerFont->mpNextGCFont;
327 
328     if( (pServerFont == mpCurrentGCFont)    // no other fonts
329     ||  (pServerFont->GetRefCount() > 0) )  // font still used
330     {
331         // try to garbage collect at least a few bytes
332         pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 );
333     }
334     else // current GC font is unreferenced
335     {
336         DBG_ASSERT( (pServerFont->GetRefCount() == 0),
337             "GlyphCache::GC detected RefCount underflow" );
338 
339         // free all pServerFont related data
340         pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
341         if( pServerFont == mpCurrentGCFont )
342             mpCurrentGCFont = NULL;
343 	const ImplFontSelectData& rIFSD = pServerFont->GetFontSelData();
344         maFontList.erase( rIFSD );
345         mrPeer.RemovingFont( *pServerFont );
346         mnBytesUsed -= pServerFont->GetByteCount();
347 
348         // remove font from list of garbage collected fonts
349         if( pServerFont->mpPrevGCFont )
350             pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont;
351         if( pServerFont->mpNextGCFont )
352             pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont;
353         if( pServerFont == mpCurrentGCFont )
354             mpCurrentGCFont = NULL;
355 
356         delete pServerFont;
357     }
358 }
359 
360 // -----------------------------------------------------------------------
361 
UsingGlyph(ServerFont &,GlyphData & rGlyphData)362 inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
363 {
364     rGlyphData.SetLruValue( mnLruIndex++ );
365 }
366 
367 // -----------------------------------------------------------------------
368 
AddedGlyph(ServerFont & rServerFont,GlyphData & rGlyphData)369 inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
370 {
371     ++mnGlyphCount;
372     mnBytesUsed += sizeof( rGlyphData );
373     UsingGlyph( rServerFont, rGlyphData );
374     GrowNotify();
375 }
376 
377 // -----------------------------------------------------------------------
378 
GrowNotify()379 void GlyphCache::GrowNotify()
380 {
381     if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize )
382         GarbageCollect();
383 }
384 
385 // -----------------------------------------------------------------------
386 
RemovingGlyph(ServerFont & rSF,GlyphData & rGD,sal_GlyphId aGlyphId)387 inline void GlyphCache::RemovingGlyph( ServerFont& rSF, GlyphData& rGD, sal_GlyphId aGlyphId )
388 {
389     mrPeer.RemovingGlyph( rSF, rGD, aGlyphId );
390     mnBytesUsed -= sizeof( GlyphData );
391     --mnGlyphCount;
392 }
393 
394 // =======================================================================
395 // ServerFont
396 // =======================================================================
397 
ServerFont(const ImplFontSelectData & rFSD)398 ServerFont::ServerFont( const ImplFontSelectData& rFSD )
399 :   maGlyphList( 0),
400     maFontSelData(rFSD),
401     mnExtInfo(0),
402     mnRefCount(1),
403     mnBytesUsed( sizeof(ServerFont) ),
404     mpPrevGCFont( NULL ),
405     mpNextGCFont( NULL ),
406     mnCos( 0x10000),
407     mnSin( 0 ),
408     mnZWJ( 0 ),
409     mnZWNJ( 0 ),
410     mbCollectedZW( false )
411 {
412     // TODO: move update of mpFontEntry into FontEntry class when
413     // it becomes responsible for the ServerFont instantiation
414     ((ImplServerFontEntry*)rFSD.mpFontEntry)->SetServerFont( this );
415 
416     if( rFSD.mnOrientation != 0 )
417     {
418         const double dRad = rFSD.mnOrientation * ( F_2PI / 3600.0 );
419         mnCos = static_cast<long>( 0x10000 * cos( dRad ) + 0.5 );
420         mnSin = static_cast<long>( 0x10000 * sin( dRad ) + 0.5 );
421     }
422 }
423 
424 // -----------------------------------------------------------------------
425 
~ServerFont()426 ServerFont::~ServerFont()
427 {
428     ReleaseFromGarbageCollect();
429 }
430 
431 // -----------------------------------------------------------------------
432 
ReleaseFromGarbageCollect()433 void ServerFont::ReleaseFromGarbageCollect()
434 {
435    // remove from GC list
436     ServerFont* pPrev = mpPrevGCFont;
437     ServerFont* pNext = mpNextGCFont;
438     if( pPrev ) pPrev->mpNextGCFont = pNext;
439     if( pNext ) pNext->mpPrevGCFont = pPrev;
440     mpPrevGCFont = NULL;
441     mpNextGCFont = NULL;
442 }
443 
444 // -----------------------------------------------------------------------
445 
Release() const446 long ServerFont::Release() const
447 {
448     DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
449     return --mnRefCount;
450 }
451 
452 // -----------------------------------------------------------------------
453 
GetGlyphData(sal_GlyphId aGlyphId)454 GlyphData& ServerFont::GetGlyphData( sal_GlyphId aGlyphId )
455 {
456     // usually the GlyphData is cached
457     GlyphList::iterator it = maGlyphList.find( aGlyphId );
458     if( it != maGlyphList.end() ) {
459         GlyphData& rGlyphData = it->second;
460         GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
461         return rGlyphData;
462     }
463 
464     // sometimes not => we need to create and initialize it ourselves
465     GlyphData& rGlyphData = maGlyphList[ aGlyphId ];
466     mnBytesUsed += sizeof( GlyphData );
467     InitGlyphData( aGlyphId, rGlyphData );
468     GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
469     return rGlyphData;
470 }
471 
472 // -----------------------------------------------------------------------
473 
GarbageCollect(long nMinLruIndex)474 void ServerFont::GarbageCollect( long nMinLruIndex )
475 {
476     GlyphList::iterator it_next = maGlyphList.begin();
477     while( it_next != maGlyphList.end() )
478     {
479         GlyphList::iterator it = it_next++;
480         GlyphData& rGD = it->second;
481         if( (nMinLruIndex - rGD.GetLruValue()) > 0 )
482         {
483             OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) );
484             mnBytesUsed -= sizeof( GlyphData );
485             GlyphCache::GetInstance().RemovingGlyph( *this, rGD, it->first );
486             maGlyphList.erase( it );
487             it_next = maGlyphList.begin();
488         }
489     }
490 }
491 
492 // -----------------------------------------------------------------------
493 
TransformPoint(const Point & rPoint) const494 Point ServerFont::TransformPoint( const Point& rPoint ) const
495 {
496     if( mnCos == 0x10000 )
497         return rPoint;
498     // TODO: use 32x32=>64bit intermediate
499     const double dCos = mnCos * (1.0 / 0x10000);
500     const double dSin = mnSin * (1.0 / 0x10000);
501     long nX = (long)(rPoint.X() * dCos + rPoint.Y() * dSin);
502     long nY = (long)(rPoint.Y() * dCos - rPoint.X() * dSin);
503     return Point( nX, nY );
504 }
505 
IsGlyphInvisible(sal_GlyphId aGlyphId)506 bool ServerFont::IsGlyphInvisible( sal_GlyphId aGlyphId )
507 {
508     if (!mbCollectedZW)
509     {
510         mnZWJ = GetGlyphIndex( 0x200D );
511         mnZWNJ = GetGlyphIndex( 0x200C );
512         mbCollectedZW = true;
513     }
514 
515     if( !aGlyphId ) // don't hide the NotDef glyph
516         return false;
517     if( (aGlyphId == mnZWNJ) || (aGlyphId == mnZWJ) )
518         return true;
519 
520     return false;
521 }
522 
523 // =======================================================================
524 
ImplServerFontEntry(ImplFontSelectData & rFSD)525 ImplServerFontEntry::ImplServerFontEntry( ImplFontSelectData& rFSD )
526 :   ImplFontEntry( rFSD )
527 ,   mpServerFont( NULL )
528 ,   mbGotFontOptions( false )
529 ,   mbValidFontOptions( false )
530 {}
531 
532 // -----------------------------------------------------------------------
533 
~ImplServerFontEntry()534 ImplServerFontEntry::~ImplServerFontEntry()
535 {
536     // TODO: remove the ServerFont here instead of in the GlyphCache
537 }
538 
539 // =======================================================================
540 
ExtraKernInfo(sal_IntPtr nFontId)541 ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId )
542 :   mbInitialized( false ),
543     mnFontId( nFontId ),
544     maUnicodeKernPairs( 0 )
545 {}
546 
547 //--------------------------------------------------------------------------
548 
HasKernPairs() const549 bool ExtraKernInfo::HasKernPairs() const
550 {
551     if( !mbInitialized )
552         Initialize();
553     return !maUnicodeKernPairs.empty();
554 }
555 
556 //--------------------------------------------------------------------------
557 
GetUnscaledKernPairs(ImplKernPairData ** ppKernPairs) const558 int ExtraKernInfo::GetUnscaledKernPairs( ImplKernPairData** ppKernPairs ) const
559 {
560     if( !mbInitialized )
561         Initialize();
562 
563     // return early if no kerning available
564     if( maUnicodeKernPairs.empty() )
565         return 0;
566 
567     // allocate kern pair table
568     int nKernCount = maUnicodeKernPairs.size();
569     *ppKernPairs = new ImplKernPairData[ nKernCount ];
570 
571     // fill in unicode kern pairs with the kern value scaled to the font width
572     ImplKernPairData* pKernData = *ppKernPairs;
573     UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.begin();
574     for(; it != maUnicodeKernPairs.end(); ++it )
575         *(pKernData++) = *it;
576 
577     return nKernCount;
578 }
579 
580 //--------------------------------------------------------------------------
581 
GetUnscaledKernValue(sal_Unicode cLeft,sal_Unicode cRight) const582 int ExtraKernInfo::GetUnscaledKernValue( sal_Unicode cLeft, sal_Unicode cRight ) const
583 {
584     if( !mbInitialized )
585         Initialize();
586 
587     if( maUnicodeKernPairs.empty() )
588         return 0;
589 
590     ImplKernPairData aKernPair = { cLeft, cRight, 0 };
591     UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.find( aKernPair );
592     if( it == maUnicodeKernPairs.end() )
593         return 0;
594 
595     int nUnscaledValue = (*it).mnKern;
596     return nUnscaledValue;
597 }
598 
599 // =======================================================================
600 
601