OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. | |
3 * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions | |
7 * are met: | |
8 * | |
9 * 1. Redistributions of source code must retain the above copyright | |
10 * notice, this list of conditions and the following disclaimer. | |
11 * 2. Redistributions in binary form must reproduce the above copyright | |
12 * notice, this list of conditions and the following disclaimer in the | |
13 * documentation and/or other materials provided with the distribution. | |
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
15 * its contributors may be used to endorse or promote products derived | |
16 * from this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 */ | |
29 | |
30 #include "config.h" | |
31 #include "FontCache.h" | |
32 | |
33 #include "Font.h" | |
34 #include "FontFallbackList.h" | |
35 #include "FontPlatformData.h" | |
36 #include "FontSelector.h" | |
37 #include "StringHash.h" | |
38 #include <wtf/HashMap.h> | |
39 #include <wtf/ListHashSet.h> | |
40 | |
41 using namespace WTF; | |
42 | |
43 namespace WebCore { | |
44 | |
45 struct FontPlatformDataCacheKey { | |
46 FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsign
ed size = 0, unsigned weight = 0, bool italic = false, | |
47 bool isPrinterFont = false, FontRenderingMode rende
ringMode = NormalRenderingMode) | |
48 : m_family(family) | |
49 , m_size(size) | |
50 , m_weight(weight) | |
51 , m_italic(italic) | |
52 , m_printerFont(isPrinterFont) | |
53 , m_renderingMode(renderingMode) | |
54 { | |
55 } | |
56 | |
57 FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDelete
dSize()) { } | |
58 bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize
(); } | |
59 | |
60 bool operator==(const FontPlatformDataCacheKey& other) const | |
61 { | |
62 return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_
size && | |
63 m_weight == other.m_weight && m_italic == other.m_italic && m_pri
nterFont == other.m_printerFont && | |
64 m_renderingMode == other.m_renderingMode; | |
65 } | |
66 | |
67 AtomicString m_family; | |
68 unsigned m_size; | |
69 unsigned m_weight; | |
70 bool m_italic; | |
71 bool m_printerFont; | |
72 FontRenderingMode m_renderingMode; | |
73 | |
74 private: | |
75 static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; } | |
76 }; | |
77 | |
78 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey) | |
79 { | |
80 unsigned hashCodes[4] = { | |
81 CaseFoldingHash::hash(fontKey.m_family), | |
82 fontKey.m_size, | |
83 fontKey.m_weight, | |
84 static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fon
tKey.m_printerFont) << 1 | static_cast<unsigned>(fontKey.m_renderingMode) | |
85 }; | |
86 return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(h
ashCodes) / sizeof(UChar)); | |
87 } | |
88 | |
89 struct FontPlatformDataCacheKeyHash { | |
90 static unsigned hash(const FontPlatformDataCacheKey& font) | |
91 { | |
92 return computeHash(font); | |
93 } | |
94 | |
95 static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataC
acheKey& b) | |
96 { | |
97 return a == b; | |
98 } | |
99 | |
100 static const bool safeToCompareToEmptyOrDeleted = true; | |
101 }; | |
102 | |
103 struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataC
acheKey> { | |
104 static const bool emptyValueIsZero = true; | |
105 static const FontPlatformDataCacheKey& emptyValue() | |
106 { | |
107 static FontPlatformDataCacheKey key(nullAtom); | |
108 return key; | |
109 } | |
110 static void constructDeletedValue(FontPlatformDataCacheKey& slot) | |
111 { | |
112 new (&slot) FontPlatformDataCacheKey(HashTableDeletedValue); | |
113 } | |
114 static bool isDeletedValue(const FontPlatformDataCacheKey& value) | |
115 { | |
116 return value.isHashTableDeletedValue(); | |
117 } | |
118 }; | |
119 | |
120 typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCac
heKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache; | |
121 | |
122 static FontPlatformDataCache* gFontPlatformDataCache = 0; | |
123 | |
124 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fo
ntDescription, | |
125 const AtomicString& famil
yName, | |
126 bool checkingAlternateNam
e) | |
127 { | |
128 if (!gFontPlatformDataCache) { | |
129 gFontPlatformDataCache = new FontPlatformDataCache; | |
130 platformInit(); | |
131 } | |
132 | |
133 FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize()
, fontDescription.weight(), fontDescription.italic(), | |
134 fontDescription.usePrinterFont(), fontDescripti
on.renderingMode()); | |
135 FontPlatformData* result = 0; | |
136 bool foundResult; | |
137 FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key); | |
138 if (it == gFontPlatformDataCache->end()) { | |
139 result = createFontPlatformData(fontDescription, familyName); | |
140 gFontPlatformDataCache->set(key, result); | |
141 foundResult = result; | |
142 } else { | |
143 result = it->second; | |
144 foundResult = true; | |
145 } | |
146 | |
147 if (!foundResult && !checkingAlternateName) { | |
148 // We were unable to find a font. We have a small set of fonts that we
alias to other names, | |
149 // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the
font under the aliased name. | |
150 const AtomicString& alternateName = alternateFamilyName(familyName); | |
151 if (!alternateName.isEmpty()) | |
152 result = getCachedFontPlatformData(fontDescription, alternateName, t
rue); | |
153 if (result) | |
154 gFontPlatformDataCache->set(key, new FontPlatformData(*result)); //
Cache the result under the old name. | |
155 } | |
156 | |
157 return result; | |
158 } | |
159 | |
160 struct FontDataCacheKeyHash { | |
161 static unsigned hash(const FontPlatformData& platformData) | |
162 { | |
163 return platformData.hash(); | |
164 } | |
165 | |
166 static bool equal(const FontPlatformData& a, const FontPlatformData& b) | |
167 { | |
168 return a == b; | |
169 } | |
170 | |
171 static const bool safeToCompareToEmptyOrDeleted = true; | |
172 }; | |
173 | |
174 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> { | |
175 static const bool emptyValueIsZero = true; | |
176 static const bool needsDestruction = true; | |
177 static const FontPlatformData& emptyValue() | |
178 { | |
179 static FontPlatformData key; | |
180 return key; | |
181 } | |
182 static void constructDeletedValue(FontPlatformData& slot) | |
183 { | |
184 new (&slot) FontPlatformData(HashTableDeletedValue); | |
185 } | |
186 static bool isDeletedValue(const FontPlatformData& value) | |
187 { | |
188 return value.isHashTableDeletedValue(); | |
189 } | |
190 }; | |
191 | |
192 typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCache
KeyHash, FontDataCacheKeyTraits> FontDataCache; | |
193 | |
194 static FontDataCache* gFontDataCache = 0; | |
195 | |
196 const int cMaxInactiveFontData = 120; // Pretty Low Threshold | |
197 const float cTargetInactiveFontData = 100; | |
198 static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0; | |
199 | |
200 SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformDat
a) | |
201 { | |
202 if (!platformData) | |
203 return 0; | |
204 | |
205 if (!gFontDataCache) { | |
206 gFontDataCache = new FontDataCache; | |
207 gInactiveFontData = new ListHashSet<const SimpleFontData*>; | |
208 } | |
209 | |
210 FontDataCache::iterator result = gFontDataCache->find(*platformData); | |
211 if (result == gFontDataCache->end()) { | |
212 pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformDat
a), 1); | |
213 gFontDataCache->set(*platformData, newValue); | |
214 return newValue.first; | |
215 } | |
216 if (!result.get()->second.second++) { | |
217 ASSERT(gInactiveFontData->contains(result.get()->second.first)); | |
218 gInactiveFontData->remove(result.get()->second.first); | |
219 } | |
220 | |
221 return result.get()->second.first; | |
222 } | |
223 | |
224 void FontCache::releaseFontData(const SimpleFontData* fontData) | |
225 { | |
226 ASSERT(gFontDataCache); | |
227 ASSERT(!fontData->isCustomFont()); | |
228 | |
229 FontDataCache::iterator it = gFontDataCache->find(fontData->platformData()); | |
230 ASSERT(it != gFontDataCache->end()); | |
231 | |
232 if (!--it->second.second) { | |
233 gInactiveFontData->add(fontData); | |
234 if (gInactiveFontData->size() > cMaxInactiveFontData) | |
235 purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFon
tData); | |
236 } | |
237 } | |
238 | |
239 void FontCache::purgeInactiveFontData(int count) | |
240 { | |
241 if (!gInactiveFontData) | |
242 return; | |
243 | |
244 static bool isPurging; // Guard against reentry when e.g. a deleted FontDat
a releases its small caps FontData. | |
245 if (isPurging) | |
246 return; | |
247 | |
248 isPurging = true; | |
249 | |
250 ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end(); | |
251 ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin()
; | |
252 for (int i = 0; i < count && it != end; ++it, ++i) { | |
253 const SimpleFontData* fontData = *it.get(); | |
254 gFontDataCache->remove(fontData->platformData()); | |
255 delete fontData; | |
256 } | |
257 | |
258 if (it == end) { | |
259 // Removed everything | |
260 gInactiveFontData->clear(); | |
261 } else { | |
262 for (int i = 0; i < count; ++i) | |
263 gInactiveFontData->remove(gInactiveFontData->begin()); | |
264 } | |
265 | |
266 Vector<FontPlatformDataCacheKey> keysToRemove; | |
267 keysToRemove.reserveCapacity(gFontPlatformDataCache->size()); | |
268 FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->en
d(); | |
269 for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->
begin(); platformData != platformDataEnd; ++platformData) { | |
270 if (platformData->second && !gFontDataCache->contains(*platformData->sec
ond)) | |
271 keysToRemove.append(platformData->first); | |
272 } | |
273 | |
274 size_t keysToRemoveCount = keysToRemove.size(); | |
275 for (size_t i = 0; i < keysToRemoveCount; ++i) | |
276 delete gFontPlatformDataCache->take(keysToRemove[i]); | |
277 | |
278 isPurging = false; | |
279 } | |
280 | |
281 size_t FontCache::fontDataCount() | |
282 { | |
283 if (gFontDataCache) | |
284 return gFontDataCache->size(); | |
285 return 0; | |
286 } | |
287 | |
288 size_t FontCache::inactiveFontDataCount() | |
289 { | |
290 if (gInactiveFontData) | |
291 return gInactiveFontData->size(); | |
292 return 0; | |
293 } | |
294 | |
295 const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontS
elector* fontSelector) | |
296 { | |
297 FontPlatformData* result = 0; | |
298 | |
299 int startIndex = familyIndex; | |
300 const FontFamily* startFamily = &font.fontDescription().family(); | |
301 for (int i = 0; startFamily && i < startIndex; i++) | |
302 startFamily = startFamily->next(); | |
303 const FontFamily* currFamily = startFamily; | |
304 while (currFamily && !result) { | |
305 familyIndex++; | |
306 if (currFamily->family().length()) { | |
307 if (fontSelector) { | |
308 FontData* data = fontSelector->getFontData(font.fontDescription(
), currFamily->family()); | |
309 if (data) | |
310 return data; | |
311 } | |
312 result = getCachedFontPlatformData(font.fontDescription(), currFamil
y->family()); | |
313 } | |
314 currFamily = currFamily->next(); | |
315 } | |
316 | |
317 if (!currFamily) | |
318 familyIndex = cAllFamiliesScanned; | |
319 | |
320 if (!result) | |
321 // We didn't find a font. Try to find a similar font using our own speci
fic knowledge about our platform. | |
322 // For example on OS X, we know to map any families containing the words
Arabic, Pashto, or Urdu to the | |
323 // Geeza Pro font. | |
324 result = getSimilarFontPlatformData(font); | |
325 | |
326 if (!result && startIndex == 0) { | |
327 // If it's the primary font that we couldn't find, we try the following.
In all other cases, we will | |
328 // just use per-character system fallback. | |
329 | |
330 if (fontSelector) { | |
331 // Try the user's preferred standard font. | |
332 if (FontData* data = fontSelector->getFontData(font.fontDescription(
), "-webkit-standard")) | |
333 return data; | |
334 } | |
335 | |
336 // Still no result. Hand back our last resort fallback font. | |
337 result = getLastResortFallbackFont(font.fontDescription()); | |
338 } | |
339 | |
340 // Now that we have a result, we need to go from FontPlatformData -> FontDat
a. | |
341 return getCachedFontData(result); | |
342 } | |
343 | |
344 static HashSet<FontSelector*>* gClients; | |
345 | |
346 void FontCache::addClient(FontSelector* client) | |
347 { | |
348 if (!gClients) | |
349 gClients = new HashSet<FontSelector*>; | |
350 | |
351 ASSERT(!gClients->contains(client)); | |
352 gClients->add(client); | |
353 } | |
354 | |
355 void FontCache::removeClient(FontSelector* client) | |
356 { | |
357 ASSERT(gClients); | |
358 ASSERT(gClients->contains(client)); | |
359 | |
360 gClients->remove(client); | |
361 } | |
362 | |
363 static unsigned gGeneration = 0; | |
364 | |
365 unsigned FontCache::generation() | |
366 { | |
367 return gGeneration; | |
368 } | |
369 | |
370 void FontCache::invalidate() | |
371 { | |
372 if (!gClients) { | |
373 ASSERT(!gFontPlatformDataCache); | |
374 return; | |
375 } | |
376 | |
377 if (gFontPlatformDataCache) { | |
378 deleteAllValues(*gFontPlatformDataCache); | |
379 delete gFontPlatformDataCache; | |
380 gFontPlatformDataCache = new FontPlatformDataCache; | |
381 } | |
382 | |
383 gGeneration++; | |
384 | |
385 Vector<RefPtr<FontSelector> > clients; | |
386 size_t numClients = gClients->size(); | |
387 clients.reserveCapacity(numClients); | |
388 HashSet<FontSelector*>::iterator end = gClients->end(); | |
389 for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++i
t) | |
390 clients.append(*it); | |
391 | |
392 ASSERT(numClients == clients.size()); | |
393 for (size_t i = 0; i < numClients; ++i) | |
394 clients[i]->fontCacheInvalidated(); | |
395 | |
396 purgeInactiveFontData(); | |
397 } | |
398 | |
399 } // namespace WebCore | |
OLD | NEW |