1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
24 #include <gcach_ftyp.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/bitmap.hxx>
28 #include <outfont.hxx>
30 #include <config_graphite.h>
32 #include <graphite_features.hxx>
35 #include <rtl/ustring.hxx> // used only for string=>hashvalue
36 #include <osl/file.hxx>
37 #include <tools/debug.hxx>
39 static GlyphCache
* pInstance
= NULL
;
41 GlyphCache::GlyphCache( GlyphCachePeer
& rPeer
)
44 mnBytesUsed(sizeof(GlyphCache
)),
47 mpCurrentGCFont(NULL
),
51 mpFtManager
= new FreetypeManager
;
55 GlyphCache::~GlyphCache()
57 InvalidateAllGlyphs();
62 void GlyphCache::InvalidateAllGlyphs()
64 for( FontList::iterator it
= maFontList
.begin(), end
= maFontList
.end(); it
!= end
; ++it
)
66 ServerFont
* pServerFont
= it
->second
;
67 mrPeer
.RemovingFont(*pServerFont
);
72 mpCurrentGCFont
= NULL
;
77 size_t GlyphCache::IFSD_Hash::operator()( const FontSelectPattern
& rFontSelData
) const
79 // TODO: is it worth to improve this hash function?
80 sal_IntPtr nFontId
= reinterpret_cast<sal_IntPtr
>( rFontSelData
.mpFontData
);
82 if (rFontSelData
.maTargetName
.indexOf(grutils::GrFeatureParser::FEAT_PREFIX
)
85 OString aFeatName
= OUStringToOString( rFontSelData
.maTargetName
, RTL_TEXTENCODING_UTF8
);
86 nFontId
^= aFeatName
.hashCode();
89 size_t nHash
= nFontId
<< 8;
90 nHash
+= rFontSelData
.mnHeight
;
91 nHash
+= rFontSelData
.mnOrientation
;
92 nHash
+= rFontSelData
.mbVertical
;
93 nHash
+= rFontSelData
.GetSlant();
94 nHash
+= rFontSelData
.GetWeight();
96 nHash
+= rFontSelData
.meLanguage
;
102 bool GlyphCache::IFSD_Equal::operator()( const FontSelectPattern
& rA
, const FontSelectPattern
& rB
) const
105 sal_IntPtr nFontIdA
= reinterpret_cast<sal_IntPtr
>( rA
.mpFontData
);
106 sal_IntPtr nFontIdB
= reinterpret_cast<sal_IntPtr
>( rB
.mpFontData
);
107 if( nFontIdA
!= nFontIdB
)
110 // compare with the requested metrics
111 if( (rA
.mnHeight
!= rB
.mnHeight
)
112 || (rA
.mnOrientation
!= rB
.mnOrientation
)
113 || (rA
.mbVertical
!= rB
.mbVertical
)
114 || (rA
.mbNonAntialiased
!= rB
.mbNonAntialiased
) )
117 if( (rA
.GetSlant() != rB
.GetSlant())
118 || (rA
.GetWeight() != rB
.GetWeight()) )
121 // NOTE: ignoring meFamily deliberately
123 // compare with the requested width, allow default width
124 int nAWidth
= rA
.mnWidth
!= 0 ? rA
.mnWidth
: rA
.mnHeight
;
125 int nBWidth
= rB
.mnWidth
!= 0 ? rB
.mnWidth
: rB
.mnHeight
;
126 if( nAWidth
!= nBWidth
)
130 if (rA
.meLanguage
!= rB
.meLanguage
)
132 // check for features
133 if ((rA
.maTargetName
.indexOf(grutils::GrFeatureParser::FEAT_PREFIX
)
135 rB
.maTargetName
.indexOf(grutils::GrFeatureParser::FEAT_PREFIX
)
136 != -1) && rA
.maTargetName
!= rB
.maTargetName
)
140 if (rA
.mbEmbolden
!= rB
.mbEmbolden
)
143 if (rA
.maItalicMatrix
!= rB
.maItalicMatrix
)
150 GlyphCache
& GlyphCache::GetInstance()
156 void GlyphCache::AddFontFile( const OString
& rNormalizedName
, int nFaceNum
,
157 sal_IntPtr nFontId
, const ImplDevFontAttributes
& rDFA
, const ExtraKernInfo
* pExtraKern
)
160 mpFtManager
->AddFontFile( rNormalizedName
, nFaceNum
, nFontId
, rDFA
, pExtraKern
);
164 void GlyphCache::AnnounceFonts( ImplDevFontList
* pList
) const
167 mpFtManager
->AnnounceFonts( pList
);
170 void GlyphCache::ClearFontCache()
172 InvalidateAllGlyphs();
174 mpFtManager
->ClearFontList();
178 ServerFont
* GlyphCache::CacheFont( const FontSelectPattern
& rFontSelData
)
180 // a serverfont request has pFontData
181 if( rFontSelData
.mpFontData
== NULL
)
183 // a serverfont request has a fontid > 0
184 sal_IntPtr nFontId
= rFontSelData
.mpFontData
->GetFontId();
188 // the FontList's key mpFontData member is reinterpreted as font id
189 FontSelectPattern aFontSelData
= rFontSelData
;
190 aFontSelData
.mpFontData
= reinterpret_cast<PhysicalFontFace
*>( nFontId
);
191 FontList::iterator it
= maFontList
.find( aFontSelData
);
192 if( it
!= maFontList
.end() )
194 ServerFont
* pFound
= it
->second
;
200 // font not cached yet => create new font item
201 ServerFont
* pNew
= NULL
;
203 pNew
= mpFtManager
->CreateFont( aFontSelData
);
207 maFontList
[ aFontSelData
] = pNew
;
208 mnBytesUsed
+= pNew
->GetByteCount();
210 // enable garbage collection for new font
211 if( !mpCurrentGCFont
)
213 mpCurrentGCFont
= pNew
;
214 pNew
->mpNextGCFont
= pNew
;
215 pNew
->mpPrevGCFont
= pNew
;
219 pNew
->mpNextGCFont
= mpCurrentGCFont
;
220 pNew
->mpPrevGCFont
= mpCurrentGCFont
->mpPrevGCFont
;
221 pNew
->mpPrevGCFont
->mpNextGCFont
= pNew
;
222 mpCurrentGCFont
->mpPrevGCFont
= pNew
;
230 void GlyphCache::UncacheFont( ServerFont
& rServerFont
)
232 // the interface for rServerFont must be const because a
233 // user who wants to release it only got const ServerFonts.
234 // The caching algorithm needs a non-const object
235 ServerFont
* pFont
= const_cast<ServerFont
*>( &rServerFont
);
236 if( (pFont
->Release() <= 0)
237 && (mnMaxSize
<= (mnBytesUsed
+ mrPeer
.GetByteCount())) )
239 mpCurrentGCFont
= pFont
;
245 void GlyphCache::GarbageCollect()
247 // when current GC font has been destroyed get another one
248 if( !mpCurrentGCFont
)
250 FontList::iterator it
= maFontList
.begin();
251 if( it
!= maFontList
.end() )
252 mpCurrentGCFont
= it
->second
;
255 // unless there is no other font to collect
256 if( !mpCurrentGCFont
)
259 // prepare advance to next font for garbage collection
260 ServerFont
* const pServerFont
= mpCurrentGCFont
;
261 mpCurrentGCFont
= pServerFont
->mpNextGCFont
;
263 if( (pServerFont
== mpCurrentGCFont
) // no other fonts
264 || (pServerFont
->GetRefCount() > 0) ) // font still used
266 // try to garbage collect at least a few bytes
267 pServerFont
->GarbageCollect( mnLruIndex
- mnGlyphCount
/2 );
269 else // current GC font is unreferenced
271 DBG_ASSERT( (pServerFont
->GetRefCount() == 0),
272 "GlyphCache::GC detected RefCount underflow" );
274 // free all pServerFont related data
275 pServerFont
->GarbageCollect( mnLruIndex
+0x10000000 );
276 if( pServerFont
== mpCurrentGCFont
)
277 mpCurrentGCFont
= NULL
;
278 const FontSelectPattern
& rIFSD
= pServerFont
->GetFontSelData();
279 maFontList
.erase( rIFSD
);
280 mrPeer
.RemovingFont( *pServerFont
);
281 mnBytesUsed
-= pServerFont
->GetByteCount();
283 // remove font from list of garbage collected fonts
284 if( pServerFont
->mpPrevGCFont
)
285 pServerFont
->mpPrevGCFont
->mpNextGCFont
= pServerFont
->mpNextGCFont
;
286 if( pServerFont
->mpNextGCFont
)
287 pServerFont
->mpNextGCFont
->mpPrevGCFont
= pServerFont
->mpPrevGCFont
;
288 if( pServerFont
== mpCurrentGCFont
)
289 mpCurrentGCFont
= NULL
;
296 inline void GlyphCache::UsingGlyph( ServerFont
&, GlyphData
& rGlyphData
)
298 rGlyphData
.SetLruValue( mnLruIndex
++ );
302 inline void GlyphCache::AddedGlyph( ServerFont
& rServerFont
, GlyphData
& rGlyphData
)
305 mnBytesUsed
+= sizeof( rGlyphData
);
306 UsingGlyph( rServerFont
, rGlyphData
);
311 void GlyphCache::GrowNotify()
313 if( (mnBytesUsed
+ mrPeer
.GetByteCount()) > mnMaxSize
)
318 inline void GlyphCache::RemovingGlyph( ServerFont
& rSF
, GlyphData
& rGD
, int nGlyphIndex
)
320 mrPeer
.RemovingGlyph( rSF
, rGD
, nGlyphIndex
);
321 mnBytesUsed
-= sizeof( GlyphData
);
326 void ServerFont::ReleaseFromGarbageCollect()
328 // remove from GC list
329 ServerFont
* pPrev
= mpPrevGCFont
;
330 ServerFont
* pNext
= mpNextGCFont
;
331 if( pPrev
) pPrev
->mpNextGCFont
= pNext
;
332 if( pNext
) pNext
->mpPrevGCFont
= pPrev
;
338 long ServerFont::Release() const
340 DBG_ASSERT( mnRefCount
> 0, "ServerFont: RefCount underflow" );
345 GlyphData
& ServerFont::GetGlyphData( int nGlyphIndex
)
347 // usually the GlyphData is cached
348 GlyphList::iterator it
= maGlyphList
.find( nGlyphIndex
);
349 if( it
!= maGlyphList
.end() ) {
350 GlyphData
& rGlyphData
= it
->second
;
351 GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData
);
355 // sometimes not => we need to create and initialize it ourselves
356 GlyphData
& rGlyphData
= maGlyphList
[ nGlyphIndex
];
357 mnBytesUsed
+= sizeof( GlyphData
);
358 InitGlyphData( nGlyphIndex
, rGlyphData
);
359 GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData
);
364 void ServerFont::GarbageCollect( long nMinLruIndex
)
366 GlyphList::iterator it_next
= maGlyphList
.begin();
367 while( it_next
!= maGlyphList
.end() )
369 GlyphList::iterator it
= it_next
++;
370 GlyphData
& rGD
= it
->second
;
371 if( (nMinLruIndex
- rGD
.GetLruValue()) > 0 )
373 OSL_ASSERT( mnBytesUsed
>= sizeof(GlyphData
) );
374 mnBytesUsed
-= sizeof( GlyphData
);
375 GlyphCache::GetInstance().RemovingGlyph( *this, rGD
, it
->first
);
376 maGlyphList
.erase( it
);
377 it_next
= maGlyphList
.begin();
383 ImplServerFontEntry::ImplServerFontEntry( FontSelectPattern
& rFSD
)
384 : ImplFontEntry( rFSD
)
385 , mpServerFont( NULL
)
386 , mbGotFontOptions( false )
390 void ImplServerFontEntry::SetServerFont(ServerFont
* p
)
392 if (p
== mpServerFont
)
395 mpServerFont
->Release();
398 mpServerFont
->AddRef();
401 ImplServerFontEntry::~ImplServerFontEntry()
403 // TODO: remove the ServerFont here instead of in the GlyphCache
405 mpServerFont
->Release();
409 ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId
)
410 : mbInitialized( false ),
412 maUnicodeKernPairs( 0 )
416 int ExtraKernInfo::GetUnscaledKernPairs( ImplKernPairData
** ppKernPairs
) const
421 // return early if no kerning available
422 if( maUnicodeKernPairs
.empty() )
425 // allocate kern pair table
426 int nKernCount
= maUnicodeKernPairs
.size();
427 *ppKernPairs
= new ImplKernPairData
[ nKernCount
];
429 // fill in unicode kern pairs with the kern value scaled to the font width
430 ImplKernPairData
* pKernData
= *ppKernPairs
;
431 UnicodeKernPairs::const_iterator it
= maUnicodeKernPairs
.begin();
432 for(; it
!= maUnicodeKernPairs
.end(); ++it
)
433 *(pKernData
++) = *it
;
438 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */