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 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 68 GlyphCache::~GlyphCache() 69 { 70 InvalidateAllGlyphs(); 71 if( mpFtManager ) 72 delete mpFtManager; 73 } 74 75 // ----------------------------------------------------------------------- 76 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 96 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 122 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 162 GlyphCache& GlyphCache::GetInstance() 163 { 164 return *pInstance; 165 } 166 167 // ----------------------------------------------------------------------- 168 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 180 void GlyphCache::ClearFontPath() 181 { 182 if( mpFtManager ) 183 mpFtManager->ClearFontList(); 184 } 185 186 // ----------------------------------------------------------------------- 187 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 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 216 void GlyphCache::AnnounceFonts( ImplDevFontList* pList ) const 217 { 218 if( mpFtManager ) 219 mpFtManager->AnnounceFonts( pList ); 220 // VirtDevServerFont::AnnounceFonts( pList ); 221 } 222 223 // ----------------------------------------------------------------------- 224 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 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 295 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 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 362 inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData ) 363 { 364 rGlyphData.SetLruValue( mnLruIndex++ ); 365 } 366 367 // ----------------------------------------------------------------------- 368 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 379 void GlyphCache::GrowNotify() 380 { 381 if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize ) 382 GarbageCollect(); 383 } 384 385 // ----------------------------------------------------------------------- 386 387 inline void GlyphCache::RemovingGlyph( ServerFont& rSF, GlyphData& rGD, int nGlyphIndex ) 388 { 389 mrPeer.RemovingGlyph( rSF, rGD, nGlyphIndex ); 390 mnBytesUsed -= sizeof( GlyphData ); 391 --mnGlyphCount; 392 } 393 394 // ======================================================================= 395 // ServerFont 396 // ======================================================================= 397 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 426 ServerFont::~ServerFont() 427 { 428 ReleaseFromGarbageCollect(); 429 } 430 431 // ----------------------------------------------------------------------- 432 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 446 long ServerFont::Release() const 447 { 448 DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" ); 449 return --mnRefCount; 450 } 451 452 // ----------------------------------------------------------------------- 453 454 GlyphData& ServerFont::GetGlyphData( int nGlyphIndex ) 455 { 456 // usually the GlyphData is cached 457 GlyphList::iterator it = maGlyphList.find( nGlyphIndex ); 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[ nGlyphIndex ]; 466 mnBytesUsed += sizeof( GlyphData ); 467 InitGlyphData( nGlyphIndex, rGlyphData ); 468 GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData ); 469 return rGlyphData; 470 } 471 472 // ----------------------------------------------------------------------- 473 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 494 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 506 bool ServerFont::IsGlyphInvisible( int nGlyphIndex ) 507 { 508 if (!mbCollectedZW) 509 { 510 mnZWJ = GetGlyphIndex( 0x200D ); 511 mnZWNJ = GetGlyphIndex( 0x200C ); 512 mbCollectedZW = true; 513 } 514 515 if( !nGlyphIndex ) // don't hide the NotDef glyph 516 return false; 517 if( (nGlyphIndex == mnZWNJ) || (nGlyphIndex == mnZWJ) ) 518 return true; 519 520 return false; 521 } 522 523 // ======================================================================= 524 525 ImplServerFontEntry::ImplServerFontEntry( ImplFontSelectData& rFSD ) 526 : ImplFontEntry( rFSD ) 527 , mpServerFont( NULL ) 528 , mbGotFontOptions( false ) 529 , mbValidFontOptions( false ) 530 {} 531 532 // ----------------------------------------------------------------------- 533 534 ImplServerFontEntry::~ImplServerFontEntry() 535 { 536 // TODO: remove the ServerFont here instead of in the GlyphCache 537 } 538 539 // ======================================================================= 540 541 ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId ) 542 : mbInitialized( false ), 543 mnFontId( nFontId ), 544 maUnicodeKernPairs( 0 ) 545 {} 546 547 //-------------------------------------------------------------------------- 548 549 bool ExtraKernInfo::HasKernPairs() const 550 { 551 if( !mbInitialized ) 552 Initialize(); 553 return !maUnicodeKernPairs.empty(); 554 } 555 556 //-------------------------------------------------------------------------- 557 558 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 582 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