Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <stdio.h>
21 : #include <stdlib.h>
22 : #include <math.h>
23 : #include <gcach_ftyp.hxx>
24 :
25 : #include <vcl/svapp.hxx>
26 : #include <vcl/bitmap.hxx>
27 : #include <outfont.hxx>
28 :
29 : #include <config_graphite.h>
30 : #if ENABLE_GRAPHITE
31 : #include <graphite_features.hxx>
32 : #endif
33 :
34 : #include <rtl/ustring.hxx>
35 : #include <osl/file.hxx>
36 : #include <tools/debug.hxx>
37 :
38 : static GlyphCache* pInstance = NULL;
39 :
40 304 : GlyphCache::GlyphCache( GlyphCachePeer& rPeer )
41 : : mrPeer( rPeer ),
42 : mnMaxSize( 1500000 ),
43 : mnBytesUsed(sizeof(GlyphCache)),
44 : mnLruIndex(0),
45 : mnGlyphCount(0),
46 : mpCurrentGCFont(NULL),
47 304 : mpFtManager(NULL)
48 : {
49 304 : pInstance = this;
50 304 : mpFtManager = new FreetypeManager;
51 304 : }
52 :
53 608 : GlyphCache::~GlyphCache()
54 : {
55 304 : InvalidateAllGlyphs();
56 304 : delete mpFtManager;
57 304 : }
58 :
59 304 : void GlyphCache::InvalidateAllGlyphs()
60 : {
61 9917 : for( FontList::iterator it = maFontList.begin(), end = maFontList.end(); it != end; ++it )
62 : {
63 9613 : ServerFont* pServerFont = it->second;
64 9613 : mrPeer.RemovingFont(*pServerFont);
65 9613 : delete pServerFont;
66 : }
67 :
68 304 : maFontList.clear();
69 304 : mpCurrentGCFont = NULL;
70 304 : }
71 :
72 : inline
73 301142 : size_t GlyphCache::IFSD_Hash::operator()( const FontSelectPattern& rFontSelData ) const
74 : {
75 : // TODO: is it worth to improve this hash function?
76 301142 : sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
77 : #if ENABLE_GRAPHITE
78 301142 : if (rFontSelData.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
79 : != -1)
80 : {
81 0 : OString aFeatName = OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
82 0 : nFontId ^= aFeatName.hashCode();
83 : }
84 : #endif
85 301142 : size_t nHash = nFontId << 8;
86 301142 : nHash += rFontSelData.mnHeight;
87 301142 : nHash += rFontSelData.mnOrientation;
88 301142 : nHash += size_t(rFontSelData.mbVertical);
89 301142 : nHash += rFontSelData.GetSlant();
90 301142 : nHash += rFontSelData.GetWeight();
91 : #if ENABLE_GRAPHITE
92 301142 : nHash += rFontSelData.meLanguage;
93 : #endif
94 301142 : return nHash;
95 : }
96 :
97 298263 : bool GlyphCache::IFSD_Equal::operator()( const FontSelectPattern& rA, const FontSelectPattern& rB) const
98 : {
99 : // check font ids
100 298263 : sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
101 298263 : sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
102 298263 : if( nFontIdA != nFontIdB )
103 122 : return false;
104 :
105 : // compare with the requested metrics
106 298141 : if( (rA.mnHeight != rB.mnHeight)
107 297743 : || (rA.mnOrientation != rB.mnOrientation)
108 297743 : || (rA.mbVertical != rB.mbVertical)
109 297743 : || (rA.mbNonAntialiased != rB.mbNonAntialiased) )
110 9102 : return false;
111 :
112 578078 : if( (rA.GetSlant() != rB.GetSlant())
113 289039 : || (rA.GetWeight() != rB.GetWeight()) )
114 0 : return false;
115 :
116 : // NOTE: ignoring meFamily deliberately
117 :
118 : // compare with the requested width, allow default width
119 289039 : int nAWidth = rA.mnWidth != 0 ? rA.mnWidth : rA.mnHeight;
120 289039 : int nBWidth = rB.mnWidth != 0 ? rB.mnWidth : rB.mnHeight;
121 289039 : if( nAWidth != nBWidth )
122 7123 : return false;
123 :
124 : #if ENABLE_GRAPHITE
125 281916 : if (rA.meLanguage != rB.meLanguage)
126 0 : return false;
127 : // check for features
128 563832 : if ((rA.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
129 281916 : != -1 ||
130 281916 : rB.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
131 281916 : != -1) && rA.maTargetName != rB.maTargetName)
132 0 : return false;
133 : #endif
134 :
135 281916 : if (rA.mbEmbolden != rB.mbEmbolden)
136 0 : return false;
137 :
138 281916 : if (rA.maItalicMatrix != rB.maItalicMatrix)
139 0 : return false;
140 :
141 281916 : return true;
142 : }
143 :
144 45889415 : GlyphCache& GlyphCache::GetInstance()
145 : {
146 45889415 : return *pInstance;
147 : }
148 :
149 47424 : void GlyphCache::AddFontFile( const OString& rNormalizedName, int nFaceNum,
150 : sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA)
151 : {
152 47424 : if( mpFtManager )
153 47424 : mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA);
154 47424 : }
155 :
156 312 : void GlyphCache::AnnounceFonts( PhysicalFontCollection* pFontCollection ) const
157 : {
158 312 : if( mpFtManager )
159 312 : mpFtManager->AnnounceFonts( pFontCollection );
160 312 : }
161 :
162 0 : void GlyphCache::ClearFontCache()
163 : {
164 0 : InvalidateAllGlyphs();
165 0 : if (mpFtManager)
166 0 : mpFtManager->ClearFontList();
167 0 : }
168 :
169 291529 : ServerFont* GlyphCache::CacheFont( const FontSelectPattern& rFontSelData )
170 : {
171 : // a serverfont request has pFontData
172 291529 : if( rFontSelData.mpFontData == NULL )
173 0 : return NULL;
174 : // a serverfont request has a fontid > 0
175 291529 : sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
176 291529 : if( nFontId <= 0 )
177 0 : return NULL;
178 :
179 : // the FontList's key mpFontData member is reinterpreted as font id
180 291529 : FontSelectPattern aFontSelData = rFontSelData;
181 291529 : aFontSelData.mpFontData = reinterpret_cast<PhysicalFontFace*>( nFontId );
182 291529 : FontList::iterator it = maFontList.find( aFontSelData );
183 291529 : if( it != maFontList.end() )
184 : {
185 281916 : ServerFont* pFound = it->second;
186 281916 : if( pFound )
187 281916 : pFound->AddRef();
188 281916 : return pFound;
189 : }
190 :
191 : // font not cached yet => create new font item
192 9613 : ServerFont* pNew = NULL;
193 9613 : if( mpFtManager )
194 9613 : pNew = mpFtManager->CreateFont( aFontSelData );
195 :
196 9613 : if( pNew )
197 : {
198 9613 : maFontList[ aFontSelData ] = pNew;
199 9613 : mnBytesUsed += pNew->GetByteCount();
200 :
201 : // enable garbage collection for new font
202 9613 : if( !mpCurrentGCFont )
203 : {
204 304 : mpCurrentGCFont = pNew;
205 304 : pNew->mpNextGCFont = pNew;
206 304 : pNew->mpPrevGCFont = pNew;
207 : }
208 : else
209 : {
210 9309 : pNew->mpNextGCFont = mpCurrentGCFont;
211 9309 : pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
212 9309 : pNew->mpPrevGCFont->mpNextGCFont = pNew;
213 9309 : mpCurrentGCFont->mpPrevGCFont = pNew;
214 : }
215 : }
216 :
217 9613 : return pNew;
218 : }
219 :
220 291386 : void GlyphCache::UncacheFont( ServerFont& rServerFont )
221 : {
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 291386 : ServerFont* pFont = const_cast<ServerFont*>( &rServerFont );
226 582772 : if( (pFont->Release() <= 0)
227 291386 : && (mnMaxSize <= (mnBytesUsed + mrPeer.GetByteCount())) )
228 : {
229 0 : mpCurrentGCFont = pFont;
230 0 : GarbageCollect();
231 : }
232 291386 : }
233 :
234 0 : void GlyphCache::GarbageCollect()
235 : {
236 : // when current GC font has been destroyed get another one
237 0 : if( !mpCurrentGCFont )
238 : {
239 0 : FontList::iterator it = maFontList.begin();
240 0 : if( it != maFontList.end() )
241 0 : mpCurrentGCFont = it->second;
242 : }
243 :
244 : // unless there is no other font to collect
245 0 : if( !mpCurrentGCFont )
246 0 : return;
247 :
248 : // prepare advance to next font for garbage collection
249 0 : ServerFont* const pServerFont = mpCurrentGCFont;
250 0 : mpCurrentGCFont = pServerFont->mpNextGCFont;
251 :
252 0 : if( (pServerFont == mpCurrentGCFont) // no other fonts
253 0 : || (pServerFont->GetRefCount() > 0) ) // font still used
254 : {
255 : // try to garbage collect at least a few bytes
256 0 : pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 );
257 : }
258 : else // current GC font is unreferenced
259 : {
260 : DBG_ASSERT( (pServerFont->GetRefCount() == 0),
261 : "GlyphCache::GC detected RefCount underflow" );
262 :
263 : // free all pServerFont related data
264 0 : pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
265 0 : if( pServerFont == mpCurrentGCFont )
266 0 : mpCurrentGCFont = NULL;
267 0 : const FontSelectPattern& rIFSD = pServerFont->GetFontSelData();
268 0 : maFontList.erase( rIFSD );
269 0 : mrPeer.RemovingFont( *pServerFont );
270 0 : mnBytesUsed -= pServerFont->GetByteCount();
271 :
272 : // remove font from list of garbage collected fonts
273 0 : if( pServerFont->mpPrevGCFont )
274 0 : pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont;
275 0 : if( pServerFont->mpNextGCFont )
276 0 : pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont;
277 0 : if( pServerFont == mpCurrentGCFont )
278 0 : mpCurrentGCFont = NULL;
279 :
280 0 : delete pServerFont;
281 : }
282 : }
283 :
284 45887239 : inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
285 : {
286 45887239 : rGlyphData.SetLruValue( mnLruIndex++ );
287 45887239 : }
288 :
289 109973 : inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
290 : {
291 109973 : ++mnGlyphCount;
292 109973 : mnBytesUsed += sizeof( rGlyphData );
293 109973 : UsingGlyph( rServerFont, rGlyphData );
294 109973 : GrowNotify();
295 109973 : }
296 :
297 109973 : void GlyphCache::GrowNotify()
298 : {
299 109973 : if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize )
300 0 : GarbageCollect();
301 109973 : }
302 :
303 0 : inline void GlyphCache::RemovingGlyph( GlyphData& rGD )
304 : {
305 0 : mrPeer.RemovingGlyph( rGD );
306 0 : mnBytesUsed -= sizeof( GlyphData );
307 0 : --mnGlyphCount;
308 0 : }
309 :
310 9613 : void ServerFont::ReleaseFromGarbageCollect()
311 : {
312 : // remove from GC list
313 9613 : ServerFont* pPrev = mpPrevGCFont;
314 9613 : ServerFont* pNext = mpNextGCFont;
315 9613 : if( pPrev ) pPrev->mpNextGCFont = pNext;
316 9613 : if( pNext ) pNext->mpPrevGCFont = pPrev;
317 9613 : mpPrevGCFont = NULL;
318 9613 : mpNextGCFont = NULL;
319 9613 : }
320 :
321 300539 : long ServerFont::Release() const
322 : {
323 : DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
324 300539 : return --mnRefCount;
325 : }
326 :
327 45887239 : GlyphData& ServerFont::GetGlyphData( sal_GlyphId aGlyphId )
328 : {
329 : // usually the GlyphData is cached
330 45887239 : GlyphList::iterator it = maGlyphList.find( aGlyphId );
331 45887239 : if( it != maGlyphList.end() ) {
332 45777266 : GlyphData& rGlyphData = it->second;
333 45777266 : GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
334 45777266 : return rGlyphData;
335 : }
336 :
337 : // sometimes not => we need to create and initialize it ourselves
338 109973 : GlyphData& rGlyphData = maGlyphList[ aGlyphId ];
339 109973 : mnBytesUsed += sizeof( GlyphData );
340 109973 : InitGlyphData( aGlyphId, rGlyphData );
341 109973 : GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
342 109973 : return rGlyphData;
343 : }
344 :
345 0 : void ServerFont::GarbageCollect( long nMinLruIndex )
346 : {
347 0 : GlyphList::iterator it = maGlyphList.begin();
348 0 : while( it != maGlyphList.end() )
349 : {
350 0 : GlyphData& rGD = it->second;
351 0 : if( (nMinLruIndex - rGD.GetLruValue()) > 0 )
352 : {
353 : OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) );
354 0 : mnBytesUsed -= sizeof( GlyphData );
355 0 : GlyphCache::GetInstance().RemovingGlyph( rGD );
356 0 : it = maGlyphList.erase( it );
357 : }
358 : else
359 0 : ++it;
360 : }
361 0 : }
362 :
363 23884 : ImplServerFontEntry::ImplServerFontEntry( FontSelectPattern& rFSD )
364 : : ImplFontEntry( rFSD )
365 : , mpServerFont( NULL )
366 23884 : , mbGotFontOptions( false )
367 23884 : {}
368 :
369 9613 : void ImplServerFontEntry::SetServerFont(ServerFont* p)
370 : {
371 9613 : if (p == mpServerFont)
372 9613 : return;
373 9613 : if (mpServerFont)
374 0 : mpServerFont->Release();
375 9613 : mpServerFont = p;
376 9613 : if (mpServerFont)
377 9613 : mpServerFont->AddRef();
378 : }
379 :
380 69837 : ImplServerFontEntry::~ImplServerFontEntry()
381 : {
382 : // TODO: remove the ServerFont here instead of in the GlyphCache
383 23279 : if (mpServerFont)
384 9153 : mpServerFont->Release();
385 47791 : }
386 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|