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