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 0 : GlyphCache::GlyphCache( GlyphCachePeer& rPeer )
41 : : mrPeer( rPeer ),
42 : mnMaxSize( 1500000 ),
43 : mnBytesUsed(sizeof(GlyphCache)),
44 : mnLruIndex(0),
45 : mnGlyphCount(0),
46 : mpCurrentGCFont(NULL),
47 0 : mpFtManager(NULL)
48 : {
49 0 : pInstance = this;
50 0 : mpFtManager = new FreetypeManager;
51 0 : }
52 :
53 0 : GlyphCache::~GlyphCache()
54 : {
55 0 : InvalidateAllGlyphs();
56 0 : delete mpFtManager;
57 0 : }
58 :
59 0 : void GlyphCache::InvalidateAllGlyphs()
60 : {
61 0 : for( FontList::iterator it = maFontList.begin(), end = maFontList.end(); it != end; ++it )
62 : {
63 0 : ServerFont* pServerFont = it->second;
64 0 : mrPeer.RemovingFont(*pServerFont);
65 0 : delete pServerFont;
66 : }
67 :
68 0 : maFontList.clear();
69 0 : mpCurrentGCFont = NULL;
70 0 : }
71 :
72 : inline
73 0 : size_t GlyphCache::IFSD_Hash::operator()( const FontSelectPattern& rFontSelData ) const
74 : {
75 : // TODO: is it worth to improve this hash function?
76 0 : sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
77 : #if ENABLE_GRAPHITE
78 0 : 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 0 : size_t nHash = nFontId << 8;
86 0 : nHash += rFontSelData.mnHeight;
87 0 : nHash += rFontSelData.mnOrientation;
88 0 : nHash += size_t(rFontSelData.mbVertical);
89 0 : nHash += rFontSelData.GetSlant();
90 0 : nHash += rFontSelData.GetWeight();
91 : #if ENABLE_GRAPHITE
92 0 : nHash += rFontSelData.meLanguage;
93 : #endif
94 0 : return nHash;
95 : }
96 :
97 0 : bool GlyphCache::IFSD_Equal::operator()( const FontSelectPattern& rA, const FontSelectPattern& rB) const
98 : {
99 : // check font ids
100 0 : sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
101 0 : sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
102 0 : if( nFontIdA != nFontIdB )
103 0 : return false;
104 :
105 : // compare with the requested metrics
106 0 : if( (rA.mnHeight != rB.mnHeight)
107 0 : || (rA.mnOrientation != rB.mnOrientation)
108 0 : || (rA.mbVertical != rB.mbVertical)
109 0 : || (rA.mbNonAntialiased != rB.mbNonAntialiased) )
110 0 : return false;
111 :
112 0 : if( (rA.GetSlant() != rB.GetSlant())
113 0 : || (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 0 : int nAWidth = rA.mnWidth != 0 ? rA.mnWidth : rA.mnHeight;
120 0 : int nBWidth = rB.mnWidth != 0 ? rB.mnWidth : rB.mnHeight;
121 0 : if( nAWidth != nBWidth )
122 0 : return false;
123 :
124 : #if ENABLE_GRAPHITE
125 0 : if (rA.meLanguage != rB.meLanguage)
126 0 : return false;
127 : // check for features
128 0 : if ((rA.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
129 0 : != -1 ||
130 0 : rB.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
131 0 : != -1) && rA.maTargetName != rB.maTargetName)
132 0 : return false;
133 : #endif
134 :
135 0 : if (rA.mbEmbolden != rB.mbEmbolden)
136 0 : return false;
137 :
138 0 : if (rA.maItalicMatrix != rB.maItalicMatrix)
139 0 : return false;
140 :
141 0 : return true;
142 : }
143 :
144 0 : GlyphCache& GlyphCache::GetInstance()
145 : {
146 0 : return *pInstance;
147 : }
148 :
149 0 : void GlyphCache::AddFontFile( const OString& rNormalizedName, int nFaceNum,
150 : sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA)
151 : {
152 0 : if( mpFtManager )
153 0 : mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA);
154 0 : }
155 :
156 0 : void GlyphCache::AnnounceFonts( PhysicalFontCollection* pFontCollection ) const
157 : {
158 0 : if( mpFtManager )
159 0 : mpFtManager->AnnounceFonts( pFontCollection );
160 0 : }
161 :
162 0 : void GlyphCache::ClearFontCache()
163 : {
164 0 : InvalidateAllGlyphs();
165 0 : if (mpFtManager)
166 0 : mpFtManager->ClearFontList();
167 0 : }
168 :
169 0 : ServerFont* GlyphCache::CacheFont( const FontSelectPattern& rFontSelData )
170 : {
171 : // a serverfont request has pFontData
172 0 : if( rFontSelData.mpFontData == NULL )
173 0 : return NULL;
174 : // a serverfont request has a fontid > 0
175 0 : sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
176 0 : if( nFontId <= 0 )
177 0 : return NULL;
178 :
179 : // the FontList's key mpFontData member is reinterpreted as font id
180 0 : FontSelectPattern aFontSelData = rFontSelData;
181 0 : aFontSelData.mpFontData = reinterpret_cast<PhysicalFontFace*>( nFontId );
182 0 : FontList::iterator it = maFontList.find( aFontSelData );
183 0 : if( it != maFontList.end() )
184 : {
185 0 : ServerFont* pFound = it->second;
186 0 : if( pFound )
187 0 : pFound->AddRef();
188 0 : return pFound;
189 : }
190 :
191 : // font not cached yet => create new font item
192 0 : ServerFont* pNew = NULL;
193 0 : if( mpFtManager )
194 0 : pNew = mpFtManager->CreateFont( aFontSelData );
195 :
196 0 : if( pNew )
197 : {
198 0 : maFontList[ aFontSelData ] = pNew;
199 0 : mnBytesUsed += pNew->GetByteCount();
200 :
201 : // enable garbage collection for new font
202 0 : if( !mpCurrentGCFont )
203 : {
204 0 : mpCurrentGCFont = pNew;
205 0 : pNew->mpNextGCFont = pNew;
206 0 : pNew->mpPrevGCFont = pNew;
207 : }
208 : else
209 : {
210 0 : pNew->mpNextGCFont = mpCurrentGCFont;
211 0 : pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
212 0 : pNew->mpPrevGCFont->mpNextGCFont = pNew;
213 0 : mpCurrentGCFont->mpPrevGCFont = pNew;
214 : }
215 : }
216 :
217 0 : return pNew;
218 : }
219 :
220 0 : 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 0 : ServerFont* pFont = const_cast<ServerFont*>( &rServerFont );
226 0 : if( (pFont->Release() <= 0)
227 0 : && (mnMaxSize <= (mnBytesUsed + mrPeer.GetByteCount())) )
228 : {
229 0 : mpCurrentGCFont = pFont;
230 0 : GarbageCollect();
231 : }
232 0 : }
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 0 : inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
285 : {
286 0 : rGlyphData.SetLruValue( mnLruIndex++ );
287 0 : }
288 :
289 0 : inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
290 : {
291 0 : ++mnGlyphCount;
292 0 : mnBytesUsed += sizeof( rGlyphData );
293 0 : UsingGlyph( rServerFont, rGlyphData );
294 0 : GrowNotify();
295 0 : }
296 :
297 0 : void GlyphCache::GrowNotify()
298 : {
299 0 : if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize )
300 0 : GarbageCollect();
301 0 : }
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 0 : void ServerFont::ReleaseFromGarbageCollect()
311 : {
312 : // remove from GC list
313 0 : ServerFont* pPrev = mpPrevGCFont;
314 0 : ServerFont* pNext = mpNextGCFont;
315 0 : if( pPrev ) pPrev->mpNextGCFont = pNext;
316 0 : if( pNext ) pNext->mpPrevGCFont = pPrev;
317 0 : mpPrevGCFont = NULL;
318 0 : mpNextGCFont = NULL;
319 0 : }
320 :
321 0 : long ServerFont::Release() const
322 : {
323 : DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
324 0 : return --mnRefCount;
325 : }
326 :
327 0 : GlyphData& ServerFont::GetGlyphData( sal_GlyphId aGlyphId )
328 : {
329 : // usually the GlyphData is cached
330 0 : GlyphList::iterator it = maGlyphList.find( aGlyphId );
331 0 : if( it != maGlyphList.end() ) {
332 0 : GlyphData& rGlyphData = it->second;
333 0 : GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
334 0 : return rGlyphData;
335 : }
336 :
337 : // sometimes not => we need to create and initialize it ourselves
338 0 : GlyphData& rGlyphData = maGlyphList[ aGlyphId ];
339 0 : mnBytesUsed += sizeof( GlyphData );
340 0 : InitGlyphData( aGlyphId, rGlyphData );
341 0 : GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
342 0 : 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 0 : ImplServerFontEntry::ImplServerFontEntry( FontSelectPattern& rFSD )
364 : : ImplFontEntry( rFSD )
365 : , mpServerFont( NULL )
366 0 : , mbGotFontOptions( false )
367 0 : {}
368 :
369 0 : void ImplServerFontEntry::SetServerFont(ServerFont* p)
370 : {
371 0 : if (p == mpServerFont)
372 0 : return;
373 0 : if (mpServerFont)
374 0 : mpServerFont->Release();
375 0 : mpServerFont = p;
376 0 : if (mpServerFont)
377 0 : mpServerFont->AddRef();
378 : }
379 :
380 0 : ImplServerFontEntry::~ImplServerFontEntry()
381 : {
382 : // TODO: remove the ServerFont here instead of in the GlyphCache
383 0 : if (mpServerFont)
384 0 : mpServerFont->Release();
385 3 : }
386 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|