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 reponsible 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