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