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 .
23 #include <gcach_ftyp.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/bitmap.hxx>
27 #include <outfont.hxx>
29 #include <config_graphite.h>
31 #include <graphite_features.hxx>
34 #include <rtl/ustring.hxx>
35 #include <osl/file.hxx>
36 #include <tools/debug.hxx>
38 static GlyphCache
* pInstance
= NULL
;
40 GlyphCache::GlyphCache( GlyphCachePeer
& rPeer
)
43 mnBytesUsed(sizeof(GlyphCache
)),
46 mpCurrentGCFont(NULL
),
50 mpFtManager
= new FreetypeManager
;
53 GlyphCache::~GlyphCache()
55 InvalidateAllGlyphs();
59 void GlyphCache::InvalidateAllGlyphs()
61 for( FontList::iterator it
= maFontList
.begin(), end
= maFontList
.end(); it
!= end
; ++it
)
63 ServerFont
* pServerFont
= it
->second
;
64 mrPeer
.RemovingFont(*pServerFont
);
69 mpCurrentGCFont
= NULL
;
73 size_t GlyphCache::IFSD_Hash::operator()( const FontSelectPattern
& rFontSelData
) const
75 // TODO: is it worth to improve this hash function?
76 sal_IntPtr nFontId
= reinterpret_cast<sal_IntPtr
>( rFontSelData
.mpFontData
);
78 if (rFontSelData
.maTargetName
.indexOf(grutils::GrFeatureParser::FEAT_PREFIX
)
81 OString aFeatName
= OUStringToOString( rFontSelData
.maTargetName
, RTL_TEXTENCODING_UTF8
);
82 nFontId
^= aFeatName
.hashCode();
85 size_t nHash
= nFontId
<< 8;
86 nHash
+= rFontSelData
.mnHeight
;
87 nHash
+= rFontSelData
.mnOrientation
;
88 nHash
+= size_t(rFontSelData
.mbVertical
);
89 nHash
+= rFontSelData
.GetSlant();
90 nHash
+= rFontSelData
.GetWeight();
92 nHash
+= rFontSelData
.meLanguage
;
97 bool GlyphCache::IFSD_Equal::operator()( const FontSelectPattern
& rA
, const FontSelectPattern
& rB
) const
100 sal_IntPtr nFontIdA
= reinterpret_cast<sal_IntPtr
>( rA
.mpFontData
);
101 sal_IntPtr nFontIdB
= reinterpret_cast<sal_IntPtr
>( rB
.mpFontData
);
102 if( nFontIdA
!= nFontIdB
)
105 // compare with the requested metrics
106 if( (rA
.mnHeight
!= rB
.mnHeight
)
107 || (rA
.mnOrientation
!= rB
.mnOrientation
)
108 || (rA
.mbVertical
!= rB
.mbVertical
)
109 || (rA
.mbNonAntialiased
!= rB
.mbNonAntialiased
) )
112 if( (rA
.GetSlant() != rB
.GetSlant())
113 || (rA
.GetWeight() != rB
.GetWeight()) )
116 // NOTE: ignoring meFamily deliberately
118 // compare with the requested width, allow default width
119 int nAWidth
= rA
.mnWidth
!= 0 ? rA
.mnWidth
: rA
.mnHeight
;
120 int nBWidth
= rB
.mnWidth
!= 0 ? rB
.mnWidth
: rB
.mnHeight
;
121 if( nAWidth
!= nBWidth
)
125 if (rA
.meLanguage
!= rB
.meLanguage
)
127 // check for features
128 if ((rA
.maTargetName
.indexOf(grutils::GrFeatureParser::FEAT_PREFIX
)
130 rB
.maTargetName
.indexOf(grutils::GrFeatureParser::FEAT_PREFIX
)
131 != -1) && rA
.maTargetName
!= rB
.maTargetName
)
135 if (rA
.mbEmbolden
!= rB
.mbEmbolden
)
138 if (rA
.maItalicMatrix
!= rB
.maItalicMatrix
)
144 GlyphCache
& GlyphCache::GetInstance()
149 void GlyphCache::AddFontFile( const OString
& rNormalizedName
, int nFaceNum
,
150 sal_IntPtr nFontId
, const ImplDevFontAttributes
& rDFA
)
153 mpFtManager
->AddFontFile( rNormalizedName
, nFaceNum
, nFontId
, rDFA
);
156 void GlyphCache::AnnounceFonts( PhysicalFontCollection
* pFontCollection
) const
159 mpFtManager
->AnnounceFonts( pFontCollection
);
162 void GlyphCache::ClearFontCache()
164 InvalidateAllGlyphs();
166 mpFtManager
->ClearFontList();
169 ServerFont
* GlyphCache::CacheFont( const FontSelectPattern
& rFontSelData
)
171 // a serverfont request has pFontData
172 if( rFontSelData
.mpFontData
== NULL
)
174 // a serverfont request has a fontid > 0
175 sal_IntPtr nFontId
= rFontSelData
.mpFontData
->GetFontId();
179 // the FontList's key mpFontData member is reinterpreted as font id
180 FontSelectPattern aFontSelData
= rFontSelData
;
181 aFontSelData
.mpFontData
= reinterpret_cast<PhysicalFontFace
*>( nFontId
);
182 FontList::iterator it
= maFontList
.find( aFontSelData
);
183 if( it
!= maFontList
.end() )
185 ServerFont
* pFound
= it
->second
;
191 // font not cached yet => create new font item
192 ServerFont
* pNew
= NULL
;
194 pNew
= mpFtManager
->CreateFont( aFontSelData
);
198 maFontList
[ aFontSelData
] = pNew
;
199 mnBytesUsed
+= pNew
->GetByteCount();
201 // enable garbage collection for new font
202 if( !mpCurrentGCFont
)
204 mpCurrentGCFont
= pNew
;
205 pNew
->mpNextGCFont
= pNew
;
206 pNew
->mpPrevGCFont
= pNew
;
210 pNew
->mpNextGCFont
= mpCurrentGCFont
;
211 pNew
->mpPrevGCFont
= mpCurrentGCFont
->mpPrevGCFont
;
212 pNew
->mpPrevGCFont
->mpNextGCFont
= pNew
;
213 mpCurrentGCFont
->mpPrevGCFont
= pNew
;
220 void GlyphCache::UncacheFont( ServerFont
& rServerFont
)
222 // the interface for rServerFont must be const because a
223 // user who wants to release it only got const ServerFonts.
224 // The caching algorithm needs a non-const object
225 ServerFont
* pFont
= const_cast<ServerFont
*>( &rServerFont
);
226 if( (pFont
->Release() <= 0) && (mnMaxSize
<= mnBytesUsed
) )
228 mpCurrentGCFont
= pFont
;
233 void GlyphCache::GarbageCollect()
235 // when current GC font has been destroyed get another one
236 if( !mpCurrentGCFont
)
238 FontList::iterator it
= maFontList
.begin();
239 if( it
!= maFontList
.end() )
240 mpCurrentGCFont
= it
->second
;
243 // unless there is no other font to collect
244 if( !mpCurrentGCFont
)
247 // prepare advance to next font for garbage collection
248 ServerFont
* const pServerFont
= mpCurrentGCFont
;
249 mpCurrentGCFont
= pServerFont
->mpNextGCFont
;
251 if( (pServerFont
== mpCurrentGCFont
) // no other fonts
252 || (pServerFont
->GetRefCount() > 0) ) // font still used
254 // try to garbage collect at least a few bytes
255 pServerFont
->GarbageCollect( mnLruIndex
- mnGlyphCount
/2 );
257 else // current GC font is unreferenced
259 DBG_ASSERT( (pServerFont
->GetRefCount() == 0),
260 "GlyphCache::GC detected RefCount underflow" );
262 // free all pServerFont related data
263 pServerFont
->GarbageCollect( mnLruIndex
+0x10000000 );
264 if( pServerFont
== mpCurrentGCFont
)
265 mpCurrentGCFont
= NULL
;
266 const FontSelectPattern
& rIFSD
= pServerFont
->GetFontSelData();
267 maFontList
.erase( rIFSD
);
268 mrPeer
.RemovingFont( *pServerFont
);
269 mnBytesUsed
-= pServerFont
->GetByteCount();
271 // remove font from list of garbage collected fonts
272 if( pServerFont
->mpPrevGCFont
)
273 pServerFont
->mpPrevGCFont
->mpNextGCFont
= pServerFont
->mpNextGCFont
;
274 if( pServerFont
->mpNextGCFont
)
275 pServerFont
->mpNextGCFont
->mpPrevGCFont
= pServerFont
->mpPrevGCFont
;
276 if( pServerFont
== mpCurrentGCFont
)
277 mpCurrentGCFont
= NULL
;
283 inline void GlyphCache::UsingGlyph( ServerFont
&, GlyphData
& rGlyphData
)
285 rGlyphData
.SetLruValue( mnLruIndex
++ );
288 inline void GlyphCache::AddedGlyph( ServerFont
& rServerFont
, GlyphData
& rGlyphData
)
291 mnBytesUsed
+= sizeof( rGlyphData
);
292 UsingGlyph( rServerFont
, rGlyphData
);
296 void GlyphCache::GrowNotify()
298 if( mnBytesUsed
> mnMaxSize
)
302 inline void GlyphCache::RemovingGlyph( GlyphData
& rGD
)
304 mrPeer
.RemovingGlyph( rGD
);
305 mnBytesUsed
-= sizeof( GlyphData
);
309 void ServerFont::ReleaseFromGarbageCollect()
311 // remove from GC list
312 ServerFont
* pPrev
= mpPrevGCFont
;
313 ServerFont
* pNext
= mpNextGCFont
;
314 if( pPrev
) pPrev
->mpNextGCFont
= pNext
;
315 if( pNext
) pNext
->mpPrevGCFont
= pPrev
;
320 long ServerFont::Release() const
322 DBG_ASSERT( mnRefCount
> 0, "ServerFont: RefCount underflow" );
326 GlyphData
& ServerFont::GetGlyphData( sal_GlyphId aGlyphId
)
328 // usually the GlyphData is cached
329 GlyphList::iterator it
= maGlyphList
.find( aGlyphId
);
330 if( it
!= maGlyphList
.end() ) {
331 GlyphData
& rGlyphData
= it
->second
;
332 GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData
);
336 // sometimes not => we need to create and initialize it ourselves
337 GlyphData
& rGlyphData
= maGlyphList
[ aGlyphId
];
338 mnBytesUsed
+= sizeof( GlyphData
);
339 InitGlyphData( aGlyphId
, rGlyphData
);
340 GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData
);
344 void ServerFont::GarbageCollect( long nMinLruIndex
)
346 GlyphList::iterator it
= maGlyphList
.begin();
347 while( it
!= maGlyphList
.end() )
349 GlyphData
& rGD
= it
->second
;
350 if( (nMinLruIndex
- rGD
.GetLruValue()) > 0 )
352 OSL_ASSERT( mnBytesUsed
>= sizeof(GlyphData
) );
353 mnBytesUsed
-= sizeof( GlyphData
);
354 GlyphCache::GetInstance().RemovingGlyph( rGD
);
355 it
= maGlyphList
.erase( it
);
362 ImplServerFontEntry::ImplServerFontEntry( FontSelectPattern
& rFSD
)
363 : ImplFontEntry( rFSD
)
364 , mpServerFont( NULL
)
365 , mbGotFontOptions( false )
368 void ImplServerFontEntry::SetServerFont(ServerFont
* p
)
370 if (p
== mpServerFont
)
373 mpServerFont
->Release();
376 mpServerFont
->AddRef();
379 ImplServerFontEntry::~ImplServerFontEntry()
381 // TODO: remove the ServerFont here instead of in the GlyphCache
383 mpServerFont
->Release();
385 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */