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