FontCache.cpp revision 81bc750723a18f21cd17d1b173cd2a4dda9cea6e
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 <wtf/HashMap.h> 38#include <wtf/ListHashSet.h> 39#include <wtf/StdLibExtras.h> 40#include <wtf/text/StringHash.h> 41 42using namespace WTF; 43 44namespace WebCore { 45 46FontCache* fontCache() 47{ 48 DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ()); 49 return &globalFontCache; 50} 51 52FontCache::FontCache() 53{ 54} 55 56struct FontPlatformDataCacheKey { 57 WTF_MAKE_FAST_ALLOCATED; 58public: 59 FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false, 60 bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode, FontOrientation orientation = Horizontal, FontWidthVariant widthVariant = RegularWidth) 61 : m_size(size) 62 , m_weight(weight) 63 , m_family(family) 64 , m_italic(italic) 65 , m_printerFont(isPrinterFont) 66 , m_renderingMode(renderingMode) 67 , m_orientation(orientation) 68 , m_widthVariant(widthVariant) 69 { 70 } 71 72 FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { } 73 bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); } 74 75 bool operator==(const FontPlatformDataCacheKey& other) const 76 { 77 return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size && 78 m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont && 79 m_renderingMode == other.m_renderingMode && m_orientation == other.m_orientation && m_widthVariant == other.m_widthVariant; 80 } 81 82 unsigned m_size; 83 unsigned m_weight; 84 AtomicString m_family; 85 bool m_italic; 86 bool m_printerFont; 87 FontRenderingMode m_renderingMode; 88 FontOrientation m_orientation; 89 FontWidthVariant m_widthVariant; 90 91private: 92 static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; } 93}; 94 95inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey) 96{ 97 unsigned hashCodes[5] = { 98 CaseFoldingHash::hash(fontKey.m_family), 99 fontKey.m_size, 100 fontKey.m_weight, 101 fontKey.m_widthVariant, 102 static_cast<unsigned>(fontKey.m_orientation) << 3 | static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fontKey.m_printerFont) << 1 | static_cast<unsigned>(fontKey.m_renderingMode) 103 }; 104 return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes); 105} 106 107struct FontPlatformDataCacheKeyHash { 108 static unsigned hash(const FontPlatformDataCacheKey& font) 109 { 110 return computeHash(font); 111 } 112 113 static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b) 114 { 115 return a == b; 116 } 117 118 static const bool safeToCompareToEmptyOrDeleted = true; 119}; 120 121struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataCacheKey> { 122 static const bool emptyValueIsZero = true; 123 static const FontPlatformDataCacheKey& emptyValue() 124 { 125 DEFINE_STATIC_LOCAL(FontPlatformDataCacheKey, key, (nullAtom)); 126 return key; 127 } 128 static void constructDeletedValue(FontPlatformDataCacheKey& slot) 129 { 130 new (&slot) FontPlatformDataCacheKey(HashTableDeletedValue); 131 } 132 static bool isDeletedValue(const FontPlatformDataCacheKey& value) 133 { 134 return value.isHashTableDeletedValue(); 135 } 136}; 137 138typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache; 139 140static FontPlatformDataCache* gFontPlatformDataCache = 0; 141 142static const AtomicString& alternateFamilyName(const AtomicString& familyName) 143{ 144 // Alias Courier <-> Courier New 145 DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier")); 146 DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New")); 147 if (equalIgnoringCase(familyName, courier)) 148 return courierNew; 149#if !OS(WINDOWS) 150 // On Windows, Courier New (truetype font) is always present and 151 // Courier is a bitmap font. So, we don't want to map Courier New to 152 // Courier. 153 if (equalIgnoringCase(familyName, courierNew)) 154 return courier; 155#endif 156 157 // Alias Times and Times New Roman. 158 DEFINE_STATIC_LOCAL(AtomicString, times, ("Times")); 159 DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman")); 160 if (equalIgnoringCase(familyName, times)) 161 return timesNewRoman; 162 if (equalIgnoringCase(familyName, timesNewRoman)) 163 return times; 164 165 // Alias Arial and Helvetica 166 DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial")); 167 DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica")); 168 if (equalIgnoringCase(familyName, arial)) 169 return helvetica; 170 if (equalIgnoringCase(familyName, helvetica)) 171 return arial; 172 173#if OS(WINDOWS) 174 // On Windows, bitmap fonts are blocked altogether so that we have to 175 // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font) 176 DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif")); 177 DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif")); 178 if (equalIgnoringCase(familyName, msSans)) 179 return microsoftSans; 180 181 // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no 182 // 'Microsoft Sans Serif-equivalent' for Serif. 183 static AtomicString msSerif("MS Serif"); 184 if (equalIgnoringCase(familyName, msSerif)) 185 return timesNewRoman; 186#endif 187 188 return emptyAtom; 189} 190 191FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription, 192 const AtomicString& familyName, 193 bool checkingAlternateName) 194{ 195 if (!gFontPlatformDataCache) { 196 gFontPlatformDataCache = new FontPlatformDataCache; 197 platformInit(); 198 } 199 200 FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(), 201 fontDescription.usePrinterFont(), fontDescription.renderingMode(), fontDescription.orientation(), fontDescription.widthVariant()); 202 FontPlatformData* result = 0; 203 bool foundResult; 204 FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key); 205 if (it == gFontPlatformDataCache->end()) { 206 result = createFontPlatformData(fontDescription, familyName); 207 gFontPlatformDataCache->set(key, result); 208 foundResult = result; 209 } else { 210 result = it->second; 211 foundResult = true; 212 } 213 214 if (!foundResult && !checkingAlternateName) { 215 // We were unable to find a font. We have a small set of fonts that we alias to other names, 216 // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name. 217 const AtomicString& alternateName = alternateFamilyName(familyName); 218 if (!alternateName.isEmpty()) 219 result = getCachedFontPlatformData(fontDescription, alternateName, true); 220 if (result) 221 gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name. 222 } 223 224 return result; 225} 226 227struct FontDataCacheKeyHash { 228 static unsigned hash(const FontPlatformData& platformData) 229 { 230 return platformData.hash(); 231 } 232 233 static bool equal(const FontPlatformData& a, const FontPlatformData& b) 234 { 235 return a == b; 236 } 237 238 static const bool safeToCompareToEmptyOrDeleted = true; 239}; 240 241struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> { 242 static const bool emptyValueIsZero = true; 243 static const bool needsDestruction = true; 244 static const FontPlatformData& emptyValue() 245 { 246 DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false)); 247 return key; 248 } 249 static void constructDeletedValue(FontPlatformData& slot) 250 { 251 new (&slot) FontPlatformData(HashTableDeletedValue); 252 } 253 static bool isDeletedValue(const FontPlatformData& value) 254 { 255 return value.isHashTableDeletedValue(); 256 } 257}; 258 259typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache; 260 261static FontDataCache* gFontDataCache = 0; 262 263const int cMaxInactiveFontData = 120; // Pretty Low Threshold 264const int cTargetInactiveFontData = 100; 265static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0; 266 267SimpleFontData* FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName) 268{ 269 FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName); 270 if (!platformData) 271 return 0; 272 273 return getCachedFontData(platformData); 274} 275 276SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData) 277{ 278 if (!platformData) 279 return 0; 280 281 if (!gFontDataCache) { 282 gFontDataCache = new FontDataCache; 283 gInactiveFontData = new ListHashSet<const SimpleFontData*>; 284 } 285 286 FontDataCache::iterator result = gFontDataCache->find(*platformData); 287 if (result == gFontDataCache->end()) { 288 pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), 1); 289 gFontDataCache->set(*platformData, newValue); 290 return newValue.first; 291 } 292 if (!result.get()->second.second++) { 293 ASSERT(gInactiveFontData->contains(result.get()->second.first)); 294 gInactiveFontData->remove(result.get()->second.first); 295 } 296 297 return result.get()->second.first; 298} 299 300void FontCache::releaseFontData(const SimpleFontData* fontData) 301{ 302 ASSERT(gFontDataCache); 303 ASSERT(!fontData->isCustomFont()); 304 305 FontDataCache::iterator it = gFontDataCache->find(fontData->platformData()); 306 ASSERT(it != gFontDataCache->end()); 307 308 if (!--it->second.second) { 309 gInactiveFontData->add(fontData); 310 if (gInactiveFontData->size() > cMaxInactiveFontData) 311 purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData); 312 } 313} 314 315void FontCache::purgeInactiveFontData(int count) 316{ 317 if (!gInactiveFontData) 318 return; 319 320 static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData. 321 if (isPurging) 322 return; 323 324 isPurging = true; 325 326 Vector<const SimpleFontData*, 20> fontDataToDelete; 327 ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end(); 328 ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin(); 329 for (int i = 0; i < count && it != end; ++it, ++i) { 330 const SimpleFontData* fontData = *it.get(); 331 gFontDataCache->remove(fontData->platformData()); 332 fontDataToDelete.append(fontData); 333 } 334 335 if (it == end) { 336 // Removed everything 337 gInactiveFontData->clear(); 338 } else { 339 for (int i = 0; i < count; ++i) 340 gInactiveFontData->remove(gInactiveFontData->begin()); 341 } 342 343 size_t fontDataToDeleteCount = fontDataToDelete.size(); 344 for (size_t i = 0; i < fontDataToDeleteCount; ++i) 345 delete fontDataToDelete[i]; 346 347 if (gFontPlatformDataCache) { 348 Vector<FontPlatformDataCacheKey> keysToRemove; 349 keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); 350 FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); 351 for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { 352 if (platformData->second && !gFontDataCache->contains(*platformData->second)) 353 keysToRemove.append(platformData->first); 354 } 355 356 size_t keysToRemoveCount = keysToRemove.size(); 357 for (size_t i = 0; i < keysToRemoveCount; ++i) 358 delete gFontPlatformDataCache->take(keysToRemove[i]); 359 } 360 361 isPurging = false; 362} 363 364size_t FontCache::fontDataCount() 365{ 366 if (gFontDataCache) 367 return gFontDataCache->size(); 368 return 0; 369} 370 371size_t FontCache::inactiveFontDataCount() 372{ 373 if (gInactiveFontData) 374 return gInactiveFontData->size(); 375 return 0; 376} 377 378const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector) 379{ 380 SimpleFontData* result = 0; 381 382 int startIndex = familyIndex; 383 const FontFamily* startFamily = &font.fontDescription().family(); 384 for (int i = 0; startFamily && i < startIndex; i++) 385 startFamily = startFamily->next(); 386 const FontFamily* currFamily = startFamily; 387 while (currFamily && !result) { 388 familyIndex++; 389 if (currFamily->family().length()) { 390 if (fontSelector) { 391 FontData* data = fontSelector->getFontData(font.fontDescription(), currFamily->family()); 392 if (data) 393 return data; 394 } 395 result = getCachedFontData(font.fontDescription(), currFamily->family()); 396 } 397 currFamily = currFamily->next(); 398 } 399 400 if (!currFamily) 401 familyIndex = cAllFamiliesScanned; 402 403 if (!result) 404 // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform. 405 // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the 406 // Geeza Pro font. 407 result = getSimilarFontPlatformData(font); 408 409 if (!result && startIndex == 0) { 410 // If it's the primary font that we couldn't find, we try the following. In all other cases, we will 411 // just use per-character system fallback. 412 413 if (fontSelector) { 414 // Try the user's preferred standard font. 415 if (FontData* data = fontSelector->getFontData(font.fontDescription(), "-webkit-standard")) 416 return data; 417 } 418 419 // Still no result. Hand back our last resort fallback font. 420 result = getLastResortFallbackFont(font.fontDescription()); 421 } 422 return result; 423} 424 425static HashSet<FontSelector*>* gClients; 426 427void FontCache::addClient(FontSelector* client) 428{ 429 if (!gClients) 430 gClients = new HashSet<FontSelector*>; 431 432 ASSERT(!gClients->contains(client)); 433 gClients->add(client); 434} 435 436void FontCache::removeClient(FontSelector* client) 437{ 438 ASSERT(gClients); 439 ASSERT(gClients->contains(client)); 440 441 gClients->remove(client); 442} 443 444static unsigned gGeneration = 0; 445 446unsigned FontCache::generation() 447{ 448 return gGeneration; 449} 450 451void FontCache::invalidate() 452{ 453 if (!gClients) { 454 ASSERT(!gFontPlatformDataCache); 455 return; 456 } 457 458 if (gFontPlatformDataCache) { 459 deleteAllValues(*gFontPlatformDataCache); 460 delete gFontPlatformDataCache; 461 gFontPlatformDataCache = new FontPlatformDataCache; 462 } 463 464 gGeneration++; 465 466 Vector<RefPtr<FontSelector> > clients; 467 size_t numClients = gClients->size(); 468 clients.reserveInitialCapacity(numClients); 469 HashSet<FontSelector*>::iterator end = gClients->end(); 470 for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it) 471 clients.append(*it); 472 473 ASSERT(numClients == clients.size()); 474 for (size_t i = 0; i < numClients; ++i) 475 clients[i]->fontCacheInvalidated(); 476 477 purgeInactiveFontData(); 478} 479 480} // namespace WebCore 481