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 :
21 : #include <stdio.h>
22 : #include <stdlib.h>
23 : #include <math.h>
24 : #include <gcach_ftyp.hxx>
25 :
26 : #include <vcl/svapp.hxx>
27 : #include <vcl/bitmap.hxx>
28 : #include <outfont.hxx>
29 :
30 : #ifdef ENABLE_GRAPHITE
31 : #include <graphite_features.hxx>
32 : #endif
33 :
34 : #include <rtl/ustring.hxx> // used only for string=>hashvalue
35 : #include <osl/file.hxx>
36 : #include <tools/debug.hxx>
37 :
38 : // =======================================================================
39 : // GlyphCache
40 : // =======================================================================
41 :
42 : static GlyphCache* pInstance = NULL;
43 :
44 74 : GlyphCache::GlyphCache( GlyphCachePeer& rPeer )
45 : : mrPeer( rPeer ),
46 : mnMaxSize( 1500000 ),
47 : mnBytesUsed(sizeof(GlyphCache)),
48 : mnLruIndex(0),
49 : mnGlyphCount(0),
50 : mpCurrentGCFont(NULL),
51 74 : mpFtManager(NULL)
52 : {
53 74 : pInstance = this;
54 74 : mpFtManager = new FreetypeManager;
55 74 : }
56 :
57 : // -----------------------------------------------------------------------
58 :
59 148 : GlyphCache::~GlyphCache()
60 : {
61 74 : InvalidateAllGlyphs();
62 74 : delete mpFtManager;
63 74 : }
64 :
65 : // -----------------------------------------------------------------------
66 :
67 74 : void GlyphCache::InvalidateAllGlyphs()
68 : {
69 2323 : for( FontList::iterator it = maFontList.begin(), end = maFontList.end(); it != end; ++it )
70 : {
71 2249 : ServerFont* pServerFont = it->second;
72 2249 : mrPeer.RemovingFont(*pServerFont);
73 2249 : delete pServerFont;
74 : }
75 :
76 74 : maFontList.clear();
77 74 : mpCurrentGCFont = NULL;
78 74 : }
79 :
80 : // -----------------------------------------------------------------------
81 :
82 : inline
83 44159 : size_t GlyphCache::IFSD_Hash::operator()( const FontSelectPattern& rFontSelData ) const
84 : {
85 : // TODO: is it worth to improve this hash function?
86 44159 : sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
87 : #ifdef ENABLE_GRAPHITE
88 44159 : if (rFontSelData.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
89 : != STRING_NOTFOUND)
90 : {
91 0 : rtl::OString aFeatName = rtl::OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
92 0 : nFontId ^= aFeatName.hashCode();
93 : }
94 : #endif
95 44159 : size_t nHash = nFontId << 8;
96 44159 : nHash += rFontSelData.mnHeight;
97 44159 : nHash += rFontSelData.mnOrientation;
98 44159 : nHash += rFontSelData.mbVertical;
99 44159 : nHash += rFontSelData.meItalic;
100 44159 : nHash += rFontSelData.meWeight;
101 : #ifdef ENABLE_GRAPHITE
102 44159 : nHash += rFontSelData.meLanguage;
103 : #endif
104 44159 : return nHash;
105 : }
106 :
107 : // -----------------------------------------------------------------------
108 :
109 52967 : bool GlyphCache::IFSD_Equal::operator()( const FontSelectPattern& rA, const FontSelectPattern& rB) const
110 : {
111 : // check font ids
112 52967 : sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
113 52967 : sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
114 52967 : if( nFontIdA != nFontIdB )
115 14 : return false;
116 :
117 : // compare with the requested metrics
118 52953 : if( (rA.mnHeight != rB.mnHeight)
119 : || (rA.mnOrientation != rB.mnOrientation)
120 : || (rA.mbVertical != rB.mbVertical)
121 : || (rA.mbNonAntialiased != rB.mbNonAntialiased) )
122 8404 : return false;
123 :
124 44549 : if( (rA.meItalic != rB.meItalic)
125 : || (rA.meWeight != rB.meWeight) )
126 0 : return false;
127 :
128 : // NOTE: ignoring meFamily deliberately
129 :
130 : // compare with the requested width, allow default width
131 44549 : int nAWidth = rA.mnWidth != 0 ? rA.mnWidth : rA.mnHeight;
132 44549 : int nBWidth = rB.mnWidth != 0 ? rB.mnWidth : rB.mnHeight;
133 44549 : if( nAWidth != nBWidth )
134 4816 : return false;
135 :
136 : #ifdef ENABLE_GRAPHITE
137 39733 : if (rA.meLanguage != rB.meLanguage)
138 0 : return false;
139 : // check for features
140 79466 : if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
141 : != STRING_NOTFOUND ||
142 39733 : rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
143 0 : != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName)
144 0 : return false;
145 : #endif
146 :
147 39733 : if (rA.mbEmbolden != rB.mbEmbolden)
148 0 : return false;
149 :
150 39733 : if (rA.maItalicMatrix != rB.maItalicMatrix)
151 0 : return false;
152 :
153 39733 : return true;
154 : }
155 :
156 : // -----------------------------------------------------------------------
157 :
158 1525490 : GlyphCache& GlyphCache::GetInstance()
159 : {
160 1525490 : return *pInstance;
161 : }
162 :
163 : // -----------------------------------------------------------------------
164 :
165 9672 : void GlyphCache::AddFontFile( const rtl::OString& rNormalizedName, int nFaceNum,
166 : sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA, const ExtraKernInfo* pExtraKern )
167 : {
168 9672 : if( mpFtManager )
169 9672 : mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA, pExtraKern );
170 9672 : }
171 :
172 : // -----------------------------------------------------------------------
173 :
174 80 : void GlyphCache::AnnounceFonts( ImplDevFontList* pList ) const
175 : {
176 80 : if( mpFtManager )
177 80 : mpFtManager->AnnounceFonts( pList );
178 80 : }
179 :
180 0 : void GlyphCache::ClearFontCache()
181 : {
182 0 : InvalidateAllGlyphs();
183 0 : if (mpFtManager)
184 0 : mpFtManager->ClearFontList();
185 0 : }
186 :
187 : // -----------------------------------------------------------------------
188 :
189 41982 : ServerFont* GlyphCache::CacheFont( const FontSelectPattern& rFontSelData )
190 : {
191 : // a serverfont request has pFontData
192 41982 : if( rFontSelData.mpFontData == NULL )
193 0 : return NULL;
194 : // a serverfont request has a fontid > 0
195 41982 : sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
196 41982 : if( nFontId <= 0 )
197 0 : return NULL;
198 :
199 : // the FontList's key mpFontData member is reinterpreted as font id
200 41982 : FontSelectPattern aFontSelData = rFontSelData;
201 41982 : aFontSelData.mpFontData = reinterpret_cast<PhysicalFontFace*>( nFontId );
202 41982 : FontList::iterator it = maFontList.find( aFontSelData );
203 41982 : if( it != maFontList.end() )
204 : {
205 39733 : ServerFont* pFound = it->second;
206 39733 : if( pFound )
207 39733 : pFound->AddRef();
208 39733 : return pFound;
209 : }
210 :
211 : // font not cached yet => create new font item
212 2249 : ServerFont* pNew = NULL;
213 2249 : if( mpFtManager )
214 2249 : pNew = mpFtManager->CreateFont( aFontSelData );
215 :
216 2249 : if( pNew )
217 : {
218 2249 : maFontList[ aFontSelData ] = pNew;
219 2249 : mnBytesUsed += pNew->GetByteCount();
220 :
221 : // enable garbage collection for new font
222 2249 : if( !mpCurrentGCFont )
223 : {
224 72 : mpCurrentGCFont = pNew;
225 72 : pNew->mpNextGCFont = pNew;
226 72 : pNew->mpPrevGCFont = pNew;
227 : }
228 : else
229 : {
230 2177 : pNew->mpNextGCFont = mpCurrentGCFont;
231 2177 : pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
232 2177 : pNew->mpPrevGCFont->mpNextGCFont = pNew;
233 2177 : mpCurrentGCFont->mpPrevGCFont = pNew;
234 : }
235 : }
236 :
237 2249 : return pNew;
238 : }
239 :
240 : // -----------------------------------------------------------------------
241 :
242 39298 : void GlyphCache::UncacheFont( ServerFont& rServerFont )
243 : {
244 : // the interface for rServerFont must be const because a
245 : // user who wants to release it only got const ServerFonts.
246 : // The caching algorithm needs a non-const object
247 39298 : ServerFont* pFont = const_cast<ServerFont*>( &rServerFont );
248 70931 : if( (pFont->Release() <= 0)
249 31633 : && (mnMaxSize <= (mnBytesUsed + mrPeer.GetByteCount())) )
250 : {
251 0 : mpCurrentGCFont = pFont;
252 0 : GarbageCollect();
253 : }
254 39298 : }
255 :
256 : // -----------------------------------------------------------------------
257 :
258 0 : void GlyphCache::GarbageCollect()
259 : {
260 : // when current GC font has been destroyed get another one
261 0 : if( !mpCurrentGCFont )
262 : {
263 0 : FontList::iterator it = maFontList.begin();
264 0 : if( it != maFontList.end() )
265 0 : mpCurrentGCFont = it->second;
266 : }
267 :
268 : // unless there is no other font to collect
269 0 : if( !mpCurrentGCFont )
270 0 : return;
271 :
272 : // prepare advance to next font for garbage collection
273 0 : ServerFont* const pServerFont = mpCurrentGCFont;
274 0 : mpCurrentGCFont = pServerFont->mpNextGCFont;
275 :
276 0 : if( (pServerFont == mpCurrentGCFont) // no other fonts
277 0 : || (pServerFont->GetRefCount() > 0) ) // font still used
278 : {
279 : // try to garbage collect at least a few bytes
280 0 : pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 );
281 : }
282 : else // current GC font is unreferenced
283 : {
284 : DBG_ASSERT( (pServerFont->GetRefCount() == 0),
285 : "GlyphCache::GC detected RefCount underflow" );
286 :
287 : // free all pServerFont related data
288 0 : pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
289 0 : if( pServerFont == mpCurrentGCFont )
290 0 : mpCurrentGCFont = NULL;
291 0 : const FontSelectPattern& rIFSD = pServerFont->GetFontSelData();
292 0 : maFontList.erase( rIFSD );
293 0 : mrPeer.RemovingFont( *pServerFont );
294 0 : mnBytesUsed -= pServerFont->GetByteCount();
295 :
296 : // remove font from list of garbage collected fonts
297 0 : if( pServerFont->mpPrevGCFont )
298 0 : pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont;
299 0 : if( pServerFont->mpNextGCFont )
300 0 : pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont;
301 0 : if( pServerFont == mpCurrentGCFont )
302 0 : mpCurrentGCFont = NULL;
303 :
304 0 : delete pServerFont;
305 : }
306 : }
307 :
308 : // -----------------------------------------------------------------------
309 :
310 1525490 : inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
311 : {
312 1525490 : rGlyphData.SetLruValue( mnLruIndex++ );
313 1525490 : }
314 :
315 : // -----------------------------------------------------------------------
316 :
317 17880 : inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
318 : {
319 17880 : ++mnGlyphCount;
320 17880 : mnBytesUsed += sizeof( rGlyphData );
321 17880 : UsingGlyph( rServerFont, rGlyphData );
322 17880 : GrowNotify();
323 17880 : }
324 :
325 : // -----------------------------------------------------------------------
326 :
327 17880 : void GlyphCache::GrowNotify()
328 : {
329 17880 : if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize )
330 0 : GarbageCollect();
331 17880 : }
332 :
333 : // -----------------------------------------------------------------------
334 :
335 0 : inline void GlyphCache::RemovingGlyph( ServerFont& rSF, GlyphData& rGD, int nGlyphIndex )
336 : {
337 0 : mrPeer.RemovingGlyph( rSF, rGD, nGlyphIndex );
338 0 : mnBytesUsed -= sizeof( GlyphData );
339 0 : --mnGlyphCount;
340 0 : }
341 :
342 : // -----------------------------------------------------------------------
343 :
344 2249 : void ServerFont::ReleaseFromGarbageCollect()
345 : {
346 : // remove from GC list
347 2249 : ServerFont* pPrev = mpPrevGCFont;
348 2249 : ServerFont* pNext = mpNextGCFont;
349 2249 : if( pPrev ) pPrev->mpNextGCFont = pNext;
350 2249 : if( pNext ) pNext->mpPrevGCFont = pPrev;
351 2249 : mpPrevGCFont = NULL;
352 2249 : mpNextGCFont = NULL;
353 2249 : }
354 :
355 : // -----------------------------------------------------------------------
356 :
357 39298 : long ServerFont::Release() const
358 : {
359 : DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
360 39298 : return --mnRefCount;
361 : }
362 :
363 : // -----------------------------------------------------------------------
364 :
365 1525490 : GlyphData& ServerFont::GetGlyphData( int nGlyphIndex )
366 : {
367 : // usually the GlyphData is cached
368 1525490 : GlyphList::iterator it = maGlyphList.find( nGlyphIndex );
369 1525490 : if( it != maGlyphList.end() ) {
370 1507610 : GlyphData& rGlyphData = it->second;
371 1507610 : GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
372 1507610 : return rGlyphData;
373 : }
374 :
375 : // sometimes not => we need to create and initialize it ourselves
376 17880 : GlyphData& rGlyphData = maGlyphList[ nGlyphIndex ];
377 17880 : mnBytesUsed += sizeof( GlyphData );
378 17880 : InitGlyphData( nGlyphIndex, rGlyphData );
379 17880 : GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
380 17880 : return rGlyphData;
381 : }
382 :
383 : // -----------------------------------------------------------------------
384 :
385 0 : void ServerFont::GarbageCollect( long nMinLruIndex )
386 : {
387 0 : GlyphList::iterator it_next = maGlyphList.begin();
388 0 : while( it_next != maGlyphList.end() )
389 : {
390 0 : GlyphList::iterator it = it_next++;
391 0 : GlyphData& rGD = it->second;
392 0 : if( (nMinLruIndex - rGD.GetLruValue()) > 0 )
393 : {
394 : OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) );
395 0 : mnBytesUsed -= sizeof( GlyphData );
396 0 : GlyphCache::GetInstance().RemovingGlyph( *this, rGD, it->first );
397 0 : maGlyphList.erase( it );
398 0 : it_next = maGlyphList.begin();
399 : }
400 : }
401 0 : }
402 :
403 : // =======================================================================
404 :
405 4269 : ImplServerFontEntry::ImplServerFontEntry( FontSelectPattern& rFSD )
406 : : ImplFontEntry( rFSD )
407 : , mpServerFont( NULL )
408 4269 : , mbGotFontOptions( false )
409 4269 : {}
410 :
411 : // -----------------------------------------------------------------------
412 :
413 3162 : ImplServerFontEntry::~ImplServerFontEntry()
414 : {
415 : // TODO: remove the ServerFont here instead of in the GlyphCache
416 3162 : }
417 :
418 : // =======================================================================
419 :
420 2800 : ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId )
421 : : mbInitialized( false ),
422 : mnFontId( nFontId ),
423 2800 : maUnicodeKernPairs( 0 )
424 2800 : {}
425 :
426 : //--------------------------------------------------------------------------
427 :
428 0 : int ExtraKernInfo::GetUnscaledKernPairs( ImplKernPairData** ppKernPairs ) const
429 : {
430 0 : if( !mbInitialized )
431 0 : Initialize();
432 :
433 : // return early if no kerning available
434 0 : if( maUnicodeKernPairs.empty() )
435 0 : return 0;
436 :
437 : // allocate kern pair table
438 0 : int nKernCount = maUnicodeKernPairs.size();
439 0 : *ppKernPairs = new ImplKernPairData[ nKernCount ];
440 :
441 : // fill in unicode kern pairs with the kern value scaled to the font width
442 0 : ImplKernPairData* pKernData = *ppKernPairs;
443 0 : UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.begin();
444 0 : for(; it != maUnicodeKernPairs.end(); ++it )
445 0 : *(pKernData++) = *it;
446 :
447 0 : return nKernCount;
448 : }
449 :
450 : // =======================================================================
451 :
452 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|