merge the formfield patch from ooo-build
[ooovba.git] / vcl / source / glyphs / glyphcache.cxx
blob693ed88436115144c86d0c9b8331826536dd09eb
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
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.
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).
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.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <math.h>
34 #include <vcl/salbtype.hxx>
35 #include <gcach_ftyp.hxx>
37 #include <vcl/svapp.hxx>
38 #include <vcl/bitmap.hxx>
39 #include <vcl/outfont.hxx>
41 #ifdef ENABLE_GRAPHITE
42 #include <vcl/graphite_features.hxx>
43 #endif
45 #include <rtl/ustring.hxx> // used only for string=>hashvalue
46 #include <osl/file.hxx>
47 #include <tools/debug.hxx>
49 // =======================================================================
50 // GlyphCache
51 // =======================================================================
53 static GlyphCache* pInstance = NULL;
55 GlyphCache::GlyphCache( GlyphCachePeer& rPeer )
56 : mrPeer( rPeer ),
57 mnMaxSize( 1500000 ),
58 mnBytesUsed(sizeof(GlyphCache)),
59 mnLruIndex(0),
60 mnGlyphCount(0),
61 mpCurrentGCFont(NULL),
62 mpFtManager(NULL)
64 pInstance = this;
65 mpFtManager = new FreetypeManager;
68 // -----------------------------------------------------------------------
70 GlyphCache::~GlyphCache()
72 // TODO:
73 // for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
74 // delete const_cast<ServerFont*>( it->second );
75 if( mpFtManager )
76 delete mpFtManager;
80 // -----------------------------------------------------------------------
82 #ifndef IRIX
83 inline
84 #endif
85 size_t GlyphCache::IFSD_Hash::operator()( const ImplFontSelectData& rFontSelData ) const
87 // TODO: is it worth to improve this hash function?
88 sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
89 #ifdef ENABLE_GRAPHITE
90 if (rFontSelData.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
91 != STRING_NOTFOUND)
93 rtl::OString aFeatName = rtl::OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
94 nFontId ^= aFeatName.hashCode();
96 #endif
97 size_t nHash = nFontId << 8;
98 nHash += rFontSelData.mnHeight;
99 nHash += rFontSelData.mnOrientation;
100 nHash += rFontSelData.mbVertical;
101 nHash += rFontSelData.meItalic;
102 nHash += rFontSelData.meWeight;
103 #ifdef ENABLE_GRAPHITE
104 nHash += rFontSelData.meLanguage;
105 #endif
106 return nHash;
109 // -----------------------------------------------------------------------
111 bool GlyphCache::IFSD_Equal::operator()( const ImplFontSelectData& rA, const ImplFontSelectData& rB) const
113 // check font ids
114 sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
115 sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
116 if( nFontIdA != nFontIdB )
117 return false;
119 // compare with the requested metrics
120 if( (rA.mnHeight != rB.mnHeight)
121 || (rA.mnOrientation != rB.mnOrientation)
122 || (rA.mbVertical != rB.mbVertical)
123 || (rA.mbNonAntialiased != rB.mbNonAntialiased) )
124 return false;
126 if( (rA.meItalic != rB.meItalic)
127 || (rA.meWeight != rB.meWeight) )
128 return false;
130 // NOTE: ignoring meFamily deliberately
132 // compare with the requested width, allow default width
133 if( (rA.mnWidth != rB.mnWidth)
134 && ((rA.mnHeight != rB.mnWidth) || (rA.mnWidth != 0)) )
135 return false;
136 #ifdef ENABLE_GRAPHITE
137 if (rA.meLanguage != rB.meLanguage)
138 return false;
139 // check for features
140 if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
141 != STRING_NOTFOUND ||
142 rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
143 != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName)
144 return false;
145 #endif
146 return true;
149 // -----------------------------------------------------------------------
151 GlyphCache& GlyphCache::GetInstance()
153 return *pInstance;
156 // -----------------------------------------------------------------------
158 void GlyphCache::LoadFonts()
160 if( const char* pFontPath = ::getenv( "SAL_FONTPATH_PRIVATE" ) )
161 AddFontPath( String::CreateFromAscii( pFontPath ) );
162 const String& rFontPath = Application::GetFontPath();
163 if( rFontPath.Len() > 0 )
164 AddFontPath( rFontPath );
167 // -----------------------------------------------------------------------
169 void GlyphCache::ClearFontPath()
171 if( mpFtManager )
172 mpFtManager->ClearFontList();
175 // -----------------------------------------------------------------------
177 void GlyphCache::AddFontPath( const String& rFontPath )
179 if( !mpFtManager )
180 return;
182 for( xub_StrLen nBreaker1 = 0, nBreaker2 = 0; nBreaker2 != STRING_LEN; nBreaker1 = nBreaker2 + 1 )
184 nBreaker2 = rFontPath.Search( ';', nBreaker1 );
185 if( nBreaker2 == STRING_NOTFOUND )
186 nBreaker2 = STRING_LEN;
188 ::rtl::OUString aUrlName;
189 osl::FileBase::getFileURLFromSystemPath( rFontPath.Copy( nBreaker1, nBreaker2 ), aUrlName );
190 mpFtManager->AddFontDir( aUrlName );
194 // -----------------------------------------------------------------------
196 void GlyphCache::AddFontFile( const rtl::OString& rNormalizedName, int nFaceNum,
197 sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA, const ExtraKernInfo* pExtraKern )
199 if( mpFtManager )
200 mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA, pExtraKern );
203 // -----------------------------------------------------------------------
205 void GlyphCache::AnnounceFonts( ImplDevFontList* pList ) const
207 if( mpFtManager )
208 mpFtManager->AnnounceFonts( pList );
209 // VirtDevServerFont::AnnounceFonts( pList );
212 // -----------------------------------------------------------------------
214 ServerFont* GlyphCache::CacheFont( const ImplFontSelectData& rFontSelData )
216 // a serverfont request has pFontData
217 if( rFontSelData.mpFontData == NULL )
218 return NULL;
219 // a serverfont request has a fontid > 0
220 sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
221 if( nFontId <= 0 )
222 return NULL;
224 // the FontList's key mpFontData member is reinterpreted as font id
225 ImplFontSelectData aFontSelData = rFontSelData;
226 aFontSelData.mpFontData = reinterpret_cast<ImplFontData*>( nFontId );
227 FontList::iterator it = maFontList.find( aFontSelData );
228 if( it != maFontList.end() )
230 ServerFont* pFound = it->second;
231 if( pFound )
232 pFound->AddRef();
233 return pFound;
236 // font not cached yet => create new font item
237 ServerFont* pNew = NULL;
238 if( mpFtManager )
239 pNew = mpFtManager->CreateFont( aFontSelData );
240 // TODO: pNew = VirtDevServerFont::CreateFont( aFontSelData );
242 if( pNew )
244 maFontList[ aFontSelData ] = pNew;
245 mnBytesUsed += pNew->GetByteCount();
247 // enable garbage collection for new font
248 if( !mpCurrentGCFont )
250 mpCurrentGCFont = pNew;
251 pNew->mpNextGCFont = pNew;
252 pNew->mpPrevGCFont = pNew;
254 else
256 pNew->mpNextGCFont = mpCurrentGCFont;
257 pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
258 pNew->mpPrevGCFont->mpNextGCFont = pNew;
259 mpCurrentGCFont->mpPrevGCFont = pNew;
263 return pNew;
266 // -----------------------------------------------------------------------
268 void GlyphCache::UncacheFont( ServerFont& rServerFont )
270 // the interface for rServerFont must be const because a
271 // user who wants to release it only got const ServerFonts.
272 // The caching algorithm needs a non-const object
273 ServerFont* pFont = const_cast<ServerFont*>( &rServerFont );
274 if( (pFont->Release() <= 0)
275 && (mnMaxSize <= (mnBytesUsed + mrPeer.GetByteCount())) )
277 mpCurrentGCFont = pFont;
278 GarbageCollect();
282 // -----------------------------------------------------------------------
284 ULONG GlyphCache::CalcByteCount() const
286 ULONG nCacheSize = sizeof(*this);
287 for( FontList::const_iterator it = maFontList.begin(); it != maFontList.end(); ++it )
289 const ServerFont* pSF = it->second;
290 if( pSF )
291 nCacheSize += pSF->GetByteCount();
293 // TODO: also account something for hashtable management
294 return nCacheSize;
297 // -----------------------------------------------------------------------
299 void GlyphCache::GarbageCollect()
301 // when current GC font has been destroyed get another one
302 if( !mpCurrentGCFont )
304 FontList::iterator it = maFontList.begin();
305 if( it != maFontList.end() )
306 mpCurrentGCFont = it->second;
309 // unless there is no other font to collect
310 if( !mpCurrentGCFont )
311 return;
313 // prepare advance to next font for garbage collection
314 ServerFont* const pServerFont = mpCurrentGCFont;
315 mpCurrentGCFont = pServerFont->mpNextGCFont;
317 if( (pServerFont == mpCurrentGCFont) // no other fonts
318 || (pServerFont->GetRefCount() > 0) ) // font still used
320 // try to garbage collect at least a few bytes
321 pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 );
323 else // current GC font is unreferenced
325 DBG_ASSERT( (pServerFont->GetRefCount() == 0),
326 "GlyphCache::GC detected RefCount underflow" );
328 // free all pServerFont related data
329 pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
330 if( pServerFont == mpCurrentGCFont )
331 mpCurrentGCFont = NULL;
332 const ImplFontSelectData& rIFSD = pServerFont->GetFontSelData();
333 maFontList.erase( rIFSD );
334 mrPeer.RemovingFont( *pServerFont );
335 mnBytesUsed -= pServerFont->GetByteCount();
337 // remove font from list of garbage collected fonts
338 if( pServerFont->mpPrevGCFont )
339 pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont;
340 if( pServerFont->mpNextGCFont )
341 pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont;
342 if( pServerFont == mpCurrentGCFont )
343 mpCurrentGCFont = NULL;
345 delete pServerFont;
349 // -----------------------------------------------------------------------
351 inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
353 rGlyphData.SetLruValue( mnLruIndex++ );
356 // -----------------------------------------------------------------------
358 inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
360 ++mnGlyphCount;
361 mnBytesUsed += sizeof( rGlyphData );
362 UsingGlyph( rServerFont, rGlyphData );
363 GrowNotify();
366 // -----------------------------------------------------------------------
368 void GlyphCache::GrowNotify()
370 if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize )
371 GarbageCollect();
374 // -----------------------------------------------------------------------
376 inline void GlyphCache::RemovingGlyph( ServerFont& rSF, GlyphData& rGD, int nGlyphIndex )
378 mrPeer.RemovingGlyph( rSF, rGD, nGlyphIndex );
379 mnBytesUsed -= sizeof( GlyphData );
380 --mnGlyphCount;
383 // =======================================================================
384 // ServerFont
385 // =======================================================================
387 ServerFont::ServerFont( const ImplFontSelectData& rFSD )
388 : maGlyphList( 0),
389 maFontSelData(rFSD),
390 mnExtInfo(0),
391 mnRefCount(1),
392 mnBytesUsed( sizeof(ServerFont) ),
393 mpPrevGCFont( NULL ),
394 mpNextGCFont( NULL ),
395 mnCos( 0x10000),
396 mnSin( 0 ),
397 mnZWJ( 0 ),
398 mnZWNJ( 0 ),
399 mbCollectedZW( false )
401 // TODO: move update of mpFontEntry into FontEntry class when
402 // it becomes reponsible for the ServerFont instantiation
403 ((ImplServerFontEntry*)rFSD.mpFontEntry)->SetServerFont( this );
405 if( rFSD.mnOrientation != 0 )
407 const double dRad = rFSD.mnOrientation * ( F_2PI / 3600.0 );
408 mnCos = static_cast<long>( 0x10000 * cos( dRad ) + 0.5 );
409 mnSin = static_cast<long>( 0x10000 * sin( dRad ) + 0.5 );
413 // -----------------------------------------------------------------------
415 ServerFont::~ServerFont()
417 ReleaseFromGarbageCollect();
420 // -----------------------------------------------------------------------
422 void ServerFont::ReleaseFromGarbageCollect()
424 // remove from GC list
425 ServerFont* pPrev = mpPrevGCFont;
426 ServerFont* pNext = mpNextGCFont;
427 if( pPrev ) pPrev->mpNextGCFont = pNext;
428 if( pNext ) pNext->mpPrevGCFont = pPrev;
429 mpPrevGCFont = NULL;
430 mpNextGCFont = NULL;
433 // -----------------------------------------------------------------------
435 long ServerFont::Release() const
437 DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
438 return --mnRefCount;
441 // -----------------------------------------------------------------------
443 GlyphData& ServerFont::GetGlyphData( int nGlyphIndex )
445 // usually the GlyphData is cached
446 GlyphList::iterator it = maGlyphList.find( nGlyphIndex );
447 if( it != maGlyphList.end() ) {
448 GlyphData& rGlyphData = it->second;
449 GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
450 return rGlyphData;
453 // sometimes not => we need to create and initialize it ourselves
454 GlyphData& rGlyphData = maGlyphList[ nGlyphIndex ];
455 mnBytesUsed += sizeof( GlyphData );
456 InitGlyphData( nGlyphIndex, rGlyphData );
457 GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
458 return rGlyphData;
461 // -----------------------------------------------------------------------
463 void ServerFont::GarbageCollect( long nMinLruIndex )
465 GlyphList::iterator it_next = maGlyphList.begin();
466 while( it_next != maGlyphList.end() )
468 GlyphList::iterator it = it_next++;
469 GlyphData& rGD = it->second;
470 if( (nMinLruIndex - rGD.GetLruValue()) > 0 )
472 OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) );
473 mnBytesUsed -= sizeof( GlyphData );
474 GlyphCache::GetInstance().RemovingGlyph( *this, rGD, it->first );
475 maGlyphList.erase( it );
476 it_next = maGlyphList.begin();
481 // -----------------------------------------------------------------------
483 Point ServerFont::TransformPoint( const Point& rPoint ) const
485 if( mnCos == 0x10000 )
486 return rPoint;
487 // TODO: use 32x32=>64bit intermediate
488 const double dCos = mnCos * (1.0 / 0x10000);
489 const double dSin = mnSin * (1.0 / 0x10000);
490 long nX = (long)(rPoint.X() * dCos + rPoint.Y() * dSin);
491 long nY = (long)(rPoint.Y() * dCos - rPoint.X() * dSin);
492 return Point( nX, nY );
495 bool ServerFont::IsGlyphInvisible( int nGlyphIndex )
497 if (!mbCollectedZW)
499 mnZWJ = GetGlyphIndex( 0x200D );
500 mnZWNJ = GetGlyphIndex( 0x200C );
501 mbCollectedZW = true;
504 if( !nGlyphIndex ) // don't hide the NotDef glyph
505 return false;
506 if( (nGlyphIndex == mnZWNJ) || (nGlyphIndex == mnZWJ) )
507 return true;
509 return false;
512 // =======================================================================
514 ImplServerFontEntry::ImplServerFontEntry( ImplFontSelectData& rFSD )
515 : ImplFontEntry( rFSD ),
516 mpServerFont( NULL )
519 // -----------------------------------------------------------------------
521 ImplServerFontEntry::~ImplServerFontEntry()
523 // TODO: remove the ServerFont here instead of in the GlyphCache
526 // =======================================================================
528 ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId )
529 : mbInitialized( false ),
530 mnFontId( nFontId ),
531 maUnicodeKernPairs( 0 )
534 //--------------------------------------------------------------------------
536 bool ExtraKernInfo::HasKernPairs() const
538 if( !mbInitialized )
539 Initialize();
540 return !maUnicodeKernPairs.empty();
543 //--------------------------------------------------------------------------
545 int ExtraKernInfo::GetUnscaledKernPairs( ImplKernPairData** ppKernPairs ) const
547 if( !mbInitialized )
548 Initialize();
550 // return early if no kerning available
551 if( maUnicodeKernPairs.empty() )
552 return 0;
554 // allocate kern pair table
555 int nKernCount = maUnicodeKernPairs.size();
556 *ppKernPairs = new ImplKernPairData[ nKernCount ];
558 // fill in unicode kern pairs with the kern value scaled to the font width
559 ImplKernPairData* pKernData = *ppKernPairs;
560 UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.begin();
561 for(; it != maUnicodeKernPairs.end(); ++it )
562 *(pKernData++) = *it;
564 return nKernCount;
567 //--------------------------------------------------------------------------
569 int ExtraKernInfo::GetUnscaledKernValue( sal_Unicode cLeft, sal_Unicode cRight ) const
571 if( !mbInitialized )
572 Initialize();
574 if( maUnicodeKernPairs.empty() )
575 return 0;
577 ImplKernPairData aKernPair = { cLeft, cRight, 0 };
578 UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.find( aKernPair );
579 if( it == maUnicodeKernPairs.end() )
580 return 0;
582 int nUnscaledValue = (*it).mnKern;
583 return nUnscaledValue;
586 // =======================================================================