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