Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : :
30 : : #include <stdio.h>
31 : : #include <stdlib.h>
32 : : #include <math.h>
33 : : #include <gcach_ftyp.hxx>
34 : :
35 : : #include <vcl/svapp.hxx>
36 : : #include <vcl/bitmap.hxx>
37 : : #include <outfont.hxx>
38 : :
39 : : #ifdef ENABLE_GRAPHITE
40 : : #include <graphite_features.hxx>
41 : : #endif
42 : :
43 : : #include <rtl/ustring.hxx> // used only for string=>hashvalue
44 : : #include <osl/file.hxx>
45 : : #include <tools/debug.hxx>
46 : :
47 : : // =======================================================================
48 : : // GlyphCache
49 : : // =======================================================================
50 : :
51 : : static GlyphCache* pInstance = NULL;
52 : :
53 : 236 : GlyphCache::GlyphCache( GlyphCachePeer& rPeer )
54 : : : mrPeer( rPeer ),
55 : : mnMaxSize( 1500000 ),
56 : : mnBytesUsed(sizeof(GlyphCache)),
57 : : mnLruIndex(0),
58 : : mnGlyphCount(0),
59 : : mpCurrentGCFont(NULL),
60 [ + - ]: 236 : mpFtManager(NULL)
61 : : {
62 : 236 : pInstance = this;
63 [ + - ][ + - ]: 236 : mpFtManager = new FreetypeManager;
64 : 236 : }
65 : :
66 : : // -----------------------------------------------------------------------
67 : :
68 : 236 : GlyphCache::~GlyphCache()
69 : : {
70 [ + - ]: 236 : InvalidateAllGlyphs();
71 [ + - ][ + - ]: 4755 : for( FontList::iterator it = maFontList.begin(), end = maFontList.end(); it != end; ++it )
[ + + ]
72 : : {
73 [ + - ]: 4519 : ServerFont* pServerFont = it->second;
74 [ + - ]: 4519 : mrPeer.RemovingFont(*pServerFont);
75 [ + - ][ + - ]: 4519 : delete pServerFont;
76 : : }
77 [ + - ]: 236 : if( mpFtManager )
78 [ + - ][ + - ]: 236 : delete mpFtManager;
79 : 236 : }
80 : :
81 : : // -----------------------------------------------------------------------
82 : :
83 : 236 : void GlyphCache::InvalidateAllGlyphs()
84 : : {
85 : : // an application about to exit can omit garbage collecting the heap
86 : : // since it makes things slower and introduces risks if the heap was not perfect
87 : : // for debugging, for memory grinding or leak checking the env allows to force GC
88 : 236 : const char* pEnv = getenv( "SAL_FORCE_GC_ON_EXIT" );
89 [ # # ][ - + ]: 236 : if( pEnv && (*pEnv != '0') )
90 : : {
91 : : // uncache of all glyph shapes and metrics
92 [ # # ][ # # ]: 0 : for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
[ # # ]
93 [ # # ][ # # ]: 0 : delete const_cast<ServerFont*>( it->second );
[ # # ]
94 : 0 : maFontList.clear();
95 : 0 : mpCurrentGCFont = NULL;
96 : : }
97 : 236 : }
98 : :
99 : : // -----------------------------------------------------------------------
100 : :
101 : : inline
102 : 200994 : size_t GlyphCache::IFSD_Hash::operator()( const FontSelectPattern& rFontSelData ) const
103 : : {
104 : : // TODO: is it worth to improve this hash function?
105 : 200994 : sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
106 : : #ifdef ENABLE_GRAPHITE
107 [ - + ]: 200994 : if (rFontSelData.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
108 : : != STRING_NOTFOUND)
109 : : {
110 [ # # ][ # # ]: 0 : rtl::OString aFeatName = rtl::OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
111 : 0 : nFontId ^= aFeatName.hashCode();
112 : : }
113 : : #endif
114 : 200994 : size_t nHash = nFontId << 8;
115 : 200994 : nHash += rFontSelData.mnHeight;
116 : 200994 : nHash += rFontSelData.mnOrientation;
117 : 200994 : nHash += rFontSelData.mbVertical;
118 : 200994 : nHash += rFontSelData.meItalic;
119 : 200994 : nHash += rFontSelData.meWeight;
120 : : #ifdef ENABLE_GRAPHITE
121 : 200994 : nHash += rFontSelData.meLanguage;
122 : : #endif
123 : 200994 : return nHash;
124 : : }
125 : :
126 : : // -----------------------------------------------------------------------
127 : :
128 : 214622 : bool GlyphCache::IFSD_Equal::operator()( const FontSelectPattern& rA, const FontSelectPattern& rB) const
129 : : {
130 : : // check font ids
131 : 214622 : sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
132 : 214622 : sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
133 [ + + ]: 214622 : if( nFontIdA != nFontIdB )
134 : 27 : return false;
135 : :
136 : : // compare with the requested metrics
137 [ + + ][ + - ]: 214595 : if( (rA.mnHeight != rB.mnHeight)
[ + - ][ + + ]
138 : : || (rA.mnOrientation != rB.mnOrientation)
139 : : || (rA.mbVertical != rB.mbVertical)
140 : : || (rA.mbNonAntialiased != rB.mbNonAntialiased) )
141 : 13318 : return false;
142 : :
143 [ + - ][ - + ]: 201277 : if( (rA.meItalic != rB.meItalic)
144 : : || (rA.meWeight != rB.meWeight) )
145 : 0 : return false;
146 : :
147 : : // NOTE: ignoring meFamily deliberately
148 : :
149 : : // compare with the requested width, allow default width
150 [ + + ]: 201277 : int nAWidth = rA.mnWidth != 0 ? rA.mnWidth : rA.mnHeight;
151 [ + + ]: 201277 : int nBWidth = rB.mnWidth != 0 ? rB.mnWidth : rB.mnHeight;
152 [ + + ]: 201277 : if( nAWidth != nBWidth )
153 : 9088 : return false;
154 : :
155 : : #ifdef ENABLE_GRAPHITE
156 [ - + ]: 192189 : if (rA.meLanguage != rB.meLanguage)
157 : 0 : return false;
158 : : // check for features
159 [ + - - + : 384378 : if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
# # ][ - + ]
160 : : != STRING_NOTFOUND ||
161 : 192189 : rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
162 : 0 : != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName)
163 : 0 : return false;
164 : : #endif
165 : :
166 [ - + ]: 192189 : if (rA.mbEmbolden != rB.mbEmbolden)
167 : 0 : return false;
168 : :
169 [ - + ]: 192189 : if (rA.maItalicMatrix != rB.maItalicMatrix)
170 : 0 : return false;
171 : :
172 : 214622 : return true;
173 : : }
174 : :
175 : : // -----------------------------------------------------------------------
176 : :
177 : 68428942 : GlyphCache& GlyphCache::GetInstance()
178 : : {
179 : 68428942 : return *pInstance;
180 : : }
181 : :
182 : : // -----------------------------------------------------------------------
183 : :
184 : 35988 : void GlyphCache::AddFontFile( const rtl::OString& rNormalizedName, int nFaceNum,
185 : : sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA, const ExtraKernInfo* pExtraKern )
186 : : {
187 [ + - ]: 35988 : if( mpFtManager )
188 : 35988 : mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA, pExtraKern );
189 : 35988 : }
190 : :
191 : : // -----------------------------------------------------------------------
192 : :
193 : 245 : void GlyphCache::AnnounceFonts( ImplDevFontList* pList ) const
194 : : {
195 [ + - ]: 245 : if( mpFtManager )
196 : 245 : mpFtManager->AnnounceFonts( pList );
197 : 245 : }
198 : :
199 : : // -----------------------------------------------------------------------
200 : :
201 : 196708 : ServerFont* GlyphCache::CacheFont( const FontSelectPattern& rFontSelData )
202 : : {
203 : : // a serverfont request has pFontData
204 [ - + ]: 196708 : if( rFontSelData.mpFontData == NULL )
205 : 0 : return NULL;
206 : : // a serverfont request has a fontid > 0
207 [ + - ]: 196708 : sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
208 [ - + ]: 196708 : if( nFontId <= 0 )
209 : 0 : return NULL;
210 : :
211 : : // the FontList's key mpFontData member is reinterpreted as font id
212 [ + - ]: 196708 : FontSelectPattern aFontSelData = rFontSelData;
213 : 196708 : aFontSelData.mpFontData = reinterpret_cast<PhysicalFontFace*>( nFontId );
214 [ + - ]: 196708 : FontList::iterator it = maFontList.find( aFontSelData );
215 [ + - ][ + + ]: 196708 : if( it != maFontList.end() )
216 : : {
217 [ + - ]: 192189 : ServerFont* pFound = it->second;
218 [ + - ]: 192189 : if( pFound )
219 : 192189 : pFound->AddRef();
220 : 192189 : return pFound;
221 : : }
222 : :
223 : : // font not cached yet => create new font item
224 : 4519 : ServerFont* pNew = NULL;
225 [ + - ]: 4519 : if( mpFtManager )
226 [ + - ]: 4519 : pNew = mpFtManager->CreateFont( aFontSelData );
227 : :
228 [ + - ]: 4519 : if( pNew )
229 : : {
230 [ + - ]: 4519 : maFontList[ aFontSelData ] = pNew;
231 : 4519 : mnBytesUsed += pNew->GetByteCount();
232 : :
233 : : // enable garbage collection for new font
234 [ + + ]: 4519 : if( !mpCurrentGCFont )
235 : : {
236 : 233 : mpCurrentGCFont = pNew;
237 : 233 : pNew->mpNextGCFont = pNew;
238 : 233 : pNew->mpPrevGCFont = pNew;
239 : : }
240 : : else
241 : : {
242 : 4286 : pNew->mpNextGCFont = mpCurrentGCFont;
243 : 4286 : pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
244 : 4286 : pNew->mpPrevGCFont->mpNextGCFont = pNew;
245 : 4286 : mpCurrentGCFont->mpPrevGCFont = pNew;
246 : : }
247 : : }
248 : :
249 [ + - ]: 196708 : return pNew;
250 : : }
251 : :
252 : : // -----------------------------------------------------------------------
253 : :
254 : 196097 : void GlyphCache::UncacheFont( ServerFont& rServerFont )
255 : : {
256 : : // the interface for rServerFont must be const because a
257 : : // user who wants to release it only got const ServerFonts.
258 : : // The caching algorithm needs a non-const object
259 : 196097 : ServerFont* pFont = const_cast<ServerFont*>( &rServerFont );
260 [ + + - + ]: 311229 : if( (pFont->Release() <= 0)
[ - + ]
261 : 115132 : && (mnMaxSize <= (mnBytesUsed + mrPeer.GetByteCount())) )
262 : : {
263 : 0 : mpCurrentGCFont = pFont;
264 : 0 : GarbageCollect();
265 : : }
266 : 196097 : }
267 : :
268 : : // -----------------------------------------------------------------------
269 : :
270 : 0 : void GlyphCache::GarbageCollect()
271 : : {
272 : : // when current GC font has been destroyed get another one
273 [ # # ]: 0 : if( !mpCurrentGCFont )
274 : : {
275 [ # # ]: 0 : FontList::iterator it = maFontList.begin();
276 [ # # ][ # # ]: 0 : if( it != maFontList.end() )
277 [ # # ]: 0 : mpCurrentGCFont = it->second;
278 : : }
279 : :
280 : : // unless there is no other font to collect
281 [ # # ]: 0 : if( !mpCurrentGCFont )
282 : 0 : return;
283 : :
284 : : // prepare advance to next font for garbage collection
285 : 0 : ServerFont* const pServerFont = mpCurrentGCFont;
286 : 0 : mpCurrentGCFont = pServerFont->mpNextGCFont;
287 : :
288 [ # # # # ]: 0 : if( (pServerFont == mpCurrentGCFont) // no other fonts
[ # # ]
289 : 0 : || (pServerFont->GetRefCount() > 0) ) // font still used
290 : : {
291 : : // try to garbage collect at least a few bytes
292 : 0 : pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 );
293 : : }
294 : : else // current GC font is unreferenced
295 : : {
296 : : DBG_ASSERT( (pServerFont->GetRefCount() == 0),
297 : : "GlyphCache::GC detected RefCount underflow" );
298 : :
299 : : // free all pServerFont related data
300 : 0 : pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
301 [ # # ]: 0 : if( pServerFont == mpCurrentGCFont )
302 : 0 : mpCurrentGCFont = NULL;
303 : 0 : const FontSelectPattern& rIFSD = pServerFont->GetFontSelData();
304 : 0 : maFontList.erase( rIFSD );
305 : 0 : mrPeer.RemovingFont( *pServerFont );
306 : 0 : mnBytesUsed -= pServerFont->GetByteCount();
307 : :
308 : : // remove font from list of garbage collected fonts
309 [ # # ]: 0 : if( pServerFont->mpPrevGCFont )
310 : 0 : pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont;
311 [ # # ]: 0 : if( pServerFont->mpNextGCFont )
312 : 0 : pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont;
313 [ # # ]: 0 : if( pServerFont == mpCurrentGCFont )
314 : 0 : mpCurrentGCFont = NULL;
315 : :
316 [ # # ]: 0 : delete pServerFont;
317 : : }
318 : : }
319 : :
320 : : // -----------------------------------------------------------------------
321 : :
322 : 68428520 : inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
323 : : {
324 : 68428520 : rGlyphData.SetLruValue( mnLruIndex++ );
325 : 68428520 : }
326 : :
327 : : // -----------------------------------------------------------------------
328 : :
329 : 37833 : inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
330 : : {
331 : 37833 : ++mnGlyphCount;
332 : 37833 : mnBytesUsed += sizeof( rGlyphData );
333 : 37833 : UsingGlyph( rServerFont, rGlyphData );
334 : 37833 : GrowNotify();
335 : 37833 : }
336 : :
337 : : // -----------------------------------------------------------------------
338 : :
339 : 37833 : void GlyphCache::GrowNotify()
340 : : {
341 [ - + ]: 37833 : if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize )
342 : 0 : GarbageCollect();
343 : 37833 : }
344 : :
345 : : // -----------------------------------------------------------------------
346 : :
347 : 0 : inline void GlyphCache::RemovingGlyph( ServerFont& rSF, GlyphData& rGD, int nGlyphIndex )
348 : : {
349 : 0 : mrPeer.RemovingGlyph( rSF, rGD, nGlyphIndex );
350 : 0 : mnBytesUsed -= sizeof( GlyphData );
351 : 0 : --mnGlyphCount;
352 : 0 : }
353 : :
354 : : // -----------------------------------------------------------------------
355 : :
356 : 4519 : void ServerFont::ReleaseFromGarbageCollect()
357 : : {
358 : : // remove from GC list
359 : 4519 : ServerFont* pPrev = mpPrevGCFont;
360 : 4519 : ServerFont* pNext = mpNextGCFont;
361 [ + - ]: 4519 : if( pPrev ) pPrev->mpNextGCFont = pNext;
362 [ + - ]: 4519 : if( pNext ) pNext->mpPrevGCFont = pPrev;
363 : 4519 : mpPrevGCFont = NULL;
364 : 4519 : mpNextGCFont = NULL;
365 : 4519 : }
366 : :
367 : : // -----------------------------------------------------------------------
368 : :
369 : 196097 : long ServerFont::Release() const
370 : : {
371 : : DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
372 : 196097 : return --mnRefCount;
373 : : }
374 : :
375 : : // -----------------------------------------------------------------------
376 : :
377 : 68428520 : GlyphData& ServerFont::GetGlyphData( int nGlyphIndex )
378 : : {
379 : : // usually the GlyphData is cached
380 [ + - ]: 68428520 : GlyphList::iterator it = maGlyphList.find( nGlyphIndex );
381 [ + - ][ + + ]: 68428520 : if( it != maGlyphList.end() ) {
382 [ + - ]: 68390687 : GlyphData& rGlyphData = it->second;
383 [ + - ][ + - ]: 68390687 : GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
384 : 68390687 : return rGlyphData;
385 : : }
386 : :
387 : : // sometimes not => we need to create and initialize it ourselves
388 [ + - ]: 37833 : GlyphData& rGlyphData = maGlyphList[ nGlyphIndex ];
389 : 37833 : mnBytesUsed += sizeof( GlyphData );
390 [ + - ]: 37833 : InitGlyphData( nGlyphIndex, rGlyphData );
391 [ + - ][ + - ]: 37833 : GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
392 : 68428520 : return rGlyphData;
393 : : }
394 : :
395 : : // -----------------------------------------------------------------------
396 : :
397 : 0 : void ServerFont::GarbageCollect( long nMinLruIndex )
398 : : {
399 [ # # ]: 0 : GlyphList::iterator it_next = maGlyphList.begin();
400 [ # # ][ # # ]: 0 : while( it_next != maGlyphList.end() )
401 : : {
402 [ # # ]: 0 : GlyphList::iterator it = it_next++;
403 [ # # ]: 0 : GlyphData& rGD = it->second;
404 [ # # ]: 0 : if( (nMinLruIndex - rGD.GetLruValue()) > 0 )
405 : : {
406 : : OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) );
407 : 0 : mnBytesUsed -= sizeof( GlyphData );
408 [ # # ][ # # ]: 0 : GlyphCache::GetInstance().RemovingGlyph( *this, rGD, it->first );
[ # # ]
409 [ # # ]: 0 : maGlyphList.erase( it );
410 [ # # ]: 0 : it_next = maGlyphList.begin();
411 : : }
412 : : }
413 : 0 : }
414 : :
415 : : // =======================================================================
416 : :
417 : 9000 : ImplServerFontEntry::ImplServerFontEntry( FontSelectPattern& rFSD )
418 : : : ImplFontEntry( rFSD )
419 : : , mpServerFont( NULL )
420 [ + - ]: 9000 : , mbGotFontOptions( false )
421 : 9000 : {}
422 : :
423 : : // -----------------------------------------------------------------------
424 : :
425 [ + - ]: 6931 : ImplServerFontEntry::~ImplServerFontEntry()
426 : : {
427 : : // TODO: remove the ServerFont here instead of in the GlyphCache
428 [ - + ]: 13862 : }
429 : :
430 : : // =======================================================================
431 : :
432 : 8575 : ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId )
433 : : : mbInitialized( false ),
434 : : mnFontId( nFontId ),
435 [ + - ]: 8575 : maUnicodeKernPairs( 0 )
436 : 8575 : {}
437 : :
438 : : //--------------------------------------------------------------------------
439 : :
440 : 0 : int ExtraKernInfo::GetUnscaledKernPairs( ImplKernPairData** ppKernPairs ) const
441 : : {
442 [ # # ]: 0 : if( !mbInitialized )
443 [ # # ]: 0 : Initialize();
444 : :
445 : : // return early if no kerning available
446 [ # # ]: 0 : if( maUnicodeKernPairs.empty() )
447 : 0 : return 0;
448 : :
449 : : // allocate kern pair table
450 : 0 : int nKernCount = maUnicodeKernPairs.size();
451 [ # # ]: 0 : *ppKernPairs = new ImplKernPairData[ nKernCount ];
452 : :
453 : : // fill in unicode kern pairs with the kern value scaled to the font width
454 : 0 : ImplKernPairData* pKernData = *ppKernPairs;
455 [ # # ]: 0 : UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.begin();
456 [ # # ][ # # ]: 0 : for(; it != maUnicodeKernPairs.end(); ++it )
457 [ # # ]: 0 : *(pKernData++) = *it;
458 : :
459 : 0 : return nKernCount;
460 : : }
461 : :
462 : : //--------------------------------------------------------------------------
463 : :
464 : 13620 : int ExtraKernInfo::GetUnscaledKernValue( sal_Unicode cLeft, sal_Unicode cRight ) const
465 : : {
466 [ + + ]: 13620 : if( !mbInitialized )
467 [ + - ]: 17 : Initialize();
468 : :
469 [ + + ]: 13620 : if( maUnicodeKernPairs.empty() )
470 : 24 : return 0;
471 : :
472 : 13596 : ImplKernPairData aKernPair = { cLeft, cRight, 0 };
473 [ + - ]: 13596 : UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.find( aKernPair );
474 [ + - ][ + + ]: 13596 : if( it == maUnicodeKernPairs.end() )
475 : 11698 : return 0;
476 : :
477 [ + - ]: 1898 : int nUnscaledValue = (*it).mnKern;
478 : 13620 : return nUnscaledValue;
479 : : }
480 : :
481 : : // =======================================================================
482 : :
483 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|