summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/FontCache.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/graphics/FontCache.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/graphics/FontCache.cpp')
-rw-r--r--Source/WebCore/platform/graphics/FontCache.cpp475
1 files changed, 475 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/FontCache.cpp b/Source/WebCore/platform/graphics/FontCache.cpp
new file mode 100644
index 0000000..149ea79
--- /dev/null
+++ b/Source/WebCore/platform/graphics/FontCache.cpp
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "FontCache.h"
+
+#include "Font.h"
+#include "FontFallbackList.h"
+#include "FontPlatformData.h"
+#include "FontSelector.h"
+#include <wtf/HashMap.h>
+#include <wtf/ListHashSet.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/StringHash.h>
+
+using namespace WTF;
+
+namespace WebCore {
+
+FontCache* fontCache()
+{
+ DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ());
+ return &globalFontCache;
+}
+
+FontCache::FontCache()
+{
+}
+
+struct FontPlatformDataCacheKey : FastAllocBase {
+ FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false,
+ bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode, FontOrientation orientation = Horizontal)
+ : m_size(size)
+ , m_weight(weight)
+ , m_family(family)
+ , m_italic(italic)
+ , m_printerFont(isPrinterFont)
+ , m_renderingMode(renderingMode)
+ , m_orientation(orientation)
+ {
+ }
+
+ FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { }
+ bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); }
+
+ bool operator==(const FontPlatformDataCacheKey& other) const
+ {
+ return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size &&
+ m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont &&
+ m_renderingMode == other.m_renderingMode && m_orientation == other.m_orientation;
+ }
+
+ unsigned m_size;
+ unsigned m_weight;
+ AtomicString m_family;
+ bool m_italic;
+ bool m_printerFont;
+ FontRenderingMode m_renderingMode;
+ FontOrientation m_orientation;
+
+private:
+ static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
+};
+
+inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
+{
+ unsigned hashCodes[4] = {
+ CaseFoldingHash::hash(fontKey.m_family),
+ fontKey.m_size,
+ fontKey.m_weight,
+ 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)
+ };
+ return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes);
+}
+
+struct FontPlatformDataCacheKeyHash {
+ static unsigned hash(const FontPlatformDataCacheKey& font)
+ {
+ return computeHash(font);
+ }
+
+ static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
+ {
+ return a == b;
+ }
+
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataCacheKey> {
+ static const bool emptyValueIsZero = true;
+ static const FontPlatformDataCacheKey& emptyValue()
+ {
+ DEFINE_STATIC_LOCAL(FontPlatformDataCacheKey, key, (nullAtom));
+ return key;
+ }
+ static void constructDeletedValue(FontPlatformDataCacheKey& slot)
+ {
+ new (&slot) FontPlatformDataCacheKey(HashTableDeletedValue);
+ }
+ static bool isDeletedValue(const FontPlatformDataCacheKey& value)
+ {
+ return value.isHashTableDeletedValue();
+ }
+};
+
+typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
+
+static FontPlatformDataCache* gFontPlatformDataCache = 0;
+
+static const AtomicString& alternateFamilyName(const AtomicString& familyName)
+{
+ // Alias Courier <-> Courier New
+ DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier"));
+ DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New"));
+ if (equalIgnoringCase(familyName, courier))
+ return courierNew;
+#if !OS(WINDOWS)
+ // On Windows, Courier New (truetype font) is always present and
+ // Courier is a bitmap font. So, we don't want to map Courier New to
+ // Courier.
+ if (equalIgnoringCase(familyName, courierNew))
+ return courier;
+#endif
+
+ // Alias Times and Times New Roman.
+ DEFINE_STATIC_LOCAL(AtomicString, times, ("Times"));
+ DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman"));
+ if (equalIgnoringCase(familyName, times))
+ return timesNewRoman;
+ if (equalIgnoringCase(familyName, timesNewRoman))
+ return times;
+
+ // Alias Arial and Helvetica
+ DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial"));
+ DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica"));
+ if (equalIgnoringCase(familyName, arial))
+ return helvetica;
+ if (equalIgnoringCase(familyName, helvetica))
+ return arial;
+
+#if OS(WINDOWS)
+ // On Windows, bitmap fonts are blocked altogether so that we have to
+ // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font)
+ DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif"));
+ DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif"));
+ if (equalIgnoringCase(familyName, msSans))
+ return microsoftSans;
+
+ // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no
+ // 'Microsoft Sans Serif-equivalent' for Serif.
+ static AtomicString msSerif("MS Serif");
+ if (equalIgnoringCase(familyName, msSerif))
+ return timesNewRoman;
+#endif
+
+ return emptyAtom;
+}
+
+FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
+ const AtomicString& familyName,
+ bool checkingAlternateName)
+{
+ if (!gFontPlatformDataCache) {
+ gFontPlatformDataCache = new FontPlatformDataCache;
+ platformInit();
+ }
+
+ FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(),
+ fontDescription.usePrinterFont(), fontDescription.renderingMode(), fontDescription.orientation());
+ FontPlatformData* result = 0;
+ bool foundResult;
+ FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key);
+ if (it == gFontPlatformDataCache->end()) {
+ result = createFontPlatformData(fontDescription, familyName);
+ gFontPlatformDataCache->set(key, result);
+ foundResult = result;
+ } else {
+ result = it->second;
+ foundResult = true;
+ }
+
+ if (!foundResult && !checkingAlternateName) {
+ // We were unable to find a font. We have a small set of fonts that we alias to other names,
+ // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name.
+ const AtomicString& alternateName = alternateFamilyName(familyName);
+ if (!alternateName.isEmpty())
+ result = getCachedFontPlatformData(fontDescription, alternateName, true);
+ if (result)
+ gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name.
+ }
+
+ return result;
+}
+
+struct FontDataCacheKeyHash {
+ static unsigned hash(const FontPlatformData& platformData)
+ {
+ return platformData.hash();
+ }
+
+ static bool equal(const FontPlatformData& a, const FontPlatformData& b)
+ {
+ return a == b;
+ }
+
+ static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
+ static const bool emptyValueIsZero = true;
+ static const bool needsDestruction = true;
+ static const FontPlatformData& emptyValue()
+ {
+ DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false));
+ return key;
+ }
+ static void constructDeletedValue(FontPlatformData& slot)
+ {
+ new (&slot) FontPlatformData(HashTableDeletedValue);
+ }
+ static bool isDeletedValue(const FontPlatformData& value)
+ {
+ return value.isHashTableDeletedValue();
+ }
+};
+
+typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
+
+static FontDataCache* gFontDataCache = 0;
+
+const int cMaxInactiveFontData = 120; // Pretty Low Threshold
+const float cTargetInactiveFontData = 100;
+static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0;
+
+SimpleFontData* FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName)
+{
+ FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
+ if (!platformData)
+ return 0;
+
+ return getCachedFontData(platformData);
+}
+
+SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData)
+{
+ if (!platformData)
+ return 0;
+
+ if (!gFontDataCache) {
+ gFontDataCache = new FontDataCache;
+ gInactiveFontData = new ListHashSet<const SimpleFontData*>;
+ }
+
+ FontDataCache::iterator result = gFontDataCache->find(*platformData);
+ if (result == gFontDataCache->end()) {
+ pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), 1);
+ gFontDataCache->set(*platformData, newValue);
+ return newValue.first;
+ }
+ if (!result.get()->second.second++) {
+ ASSERT(gInactiveFontData->contains(result.get()->second.first));
+ gInactiveFontData->remove(result.get()->second.first);
+ }
+
+ return result.get()->second.first;
+}
+
+void FontCache::releaseFontData(const SimpleFontData* fontData)
+{
+ ASSERT(gFontDataCache);
+ ASSERT(!fontData->isCustomFont());
+
+ FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
+ ASSERT(it != gFontDataCache->end());
+
+ if (!--it->second.second) {
+ gInactiveFontData->add(fontData);
+ if (gInactiveFontData->size() > cMaxInactiveFontData)
+ purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
+ }
+}
+
+void FontCache::purgeInactiveFontData(int count)
+{
+ if (!gInactiveFontData)
+ return;
+
+ static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
+ if (isPurging)
+ return;
+
+ isPurging = true;
+
+ Vector<const SimpleFontData*, 20> fontDataToDelete;
+ ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end();
+ ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin();
+ for (int i = 0; i < count && it != end; ++it, ++i) {
+ const SimpleFontData* fontData = *it.get();
+ gFontDataCache->remove(fontData->platformData());
+ fontDataToDelete.append(fontData);
+ }
+
+ if (it == end) {
+ // Removed everything
+ gInactiveFontData->clear();
+ } else {
+ for (int i = 0; i < count; ++i)
+ gInactiveFontData->remove(gInactiveFontData->begin());
+ }
+
+ size_t fontDataToDeleteCount = fontDataToDelete.size();
+ for (size_t i = 0; i < fontDataToDeleteCount; ++i)
+ delete fontDataToDelete[i];
+
+ if (gFontPlatformDataCache) {
+ Vector<FontPlatformDataCacheKey> keysToRemove;
+ keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size());
+ FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end();
+ for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) {
+ if (platformData->second && !gFontDataCache->contains(*platformData->second))
+ keysToRemove.append(platformData->first);
+ }
+
+ size_t keysToRemoveCount = keysToRemove.size();
+ for (size_t i = 0; i < keysToRemoveCount; ++i)
+ delete gFontPlatformDataCache->take(keysToRemove[i]);
+ }
+
+ isPurging = false;
+}
+
+size_t FontCache::fontDataCount()
+{
+ if (gFontDataCache)
+ return gFontDataCache->size();
+ return 0;
+}
+
+size_t FontCache::inactiveFontDataCount()
+{
+ if (gInactiveFontData)
+ return gInactiveFontData->size();
+ return 0;
+}
+
+const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector)
+{
+ SimpleFontData* result = 0;
+
+ int startIndex = familyIndex;
+ const FontFamily* startFamily = &font.fontDescription().family();
+ for (int i = 0; startFamily && i < startIndex; i++)
+ startFamily = startFamily->next();
+ const FontFamily* currFamily = startFamily;
+ while (currFamily && !result) {
+ familyIndex++;
+ if (currFamily->family().length()) {
+ if (fontSelector) {
+ FontData* data = fontSelector->getFontData(font.fontDescription(), currFamily->family());
+ if (data)
+ return data;
+ }
+ result = getCachedFontData(font.fontDescription(), currFamily->family());
+ }
+ currFamily = currFamily->next();
+ }
+
+ if (!currFamily)
+ familyIndex = cAllFamiliesScanned;
+
+ if (!result)
+ // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
+ // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
+ // Geeza Pro font.
+ result = getSimilarFontPlatformData(font);
+
+ if (!result && startIndex == 0) {
+ // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
+ // just use per-character system fallback.
+
+ if (fontSelector) {
+ // Try the user's preferred standard font.
+ if (FontData* data = fontSelector->getFontData(font.fontDescription(), "-webkit-standard"))
+ return data;
+ }
+
+ // Still no result. Hand back our last resort fallback font.
+ result = getLastResortFallbackFont(font.fontDescription());
+ }
+ return result;
+}
+
+static HashSet<FontSelector*>* gClients;
+
+void FontCache::addClient(FontSelector* client)
+{
+ if (!gClients)
+ gClients = new HashSet<FontSelector*>;
+
+ ASSERT(!gClients->contains(client));
+ gClients->add(client);
+}
+
+void FontCache::removeClient(FontSelector* client)
+{
+ ASSERT(gClients);
+ ASSERT(gClients->contains(client));
+
+ gClients->remove(client);
+}
+
+static unsigned gGeneration = 0;
+
+unsigned FontCache::generation()
+{
+ return gGeneration;
+}
+
+void FontCache::invalidate()
+{
+ if (!gClients) {
+ ASSERT(!gFontPlatformDataCache);
+ return;
+ }
+
+ if (gFontPlatformDataCache) {
+ deleteAllValues(*gFontPlatformDataCache);
+ delete gFontPlatformDataCache;
+ gFontPlatformDataCache = new FontPlatformDataCache;
+ }
+
+ gGeneration++;
+
+ Vector<RefPtr<FontSelector> > clients;
+ size_t numClients = gClients->size();
+ clients.reserveInitialCapacity(numClients);
+ HashSet<FontSelector*>::iterator end = gClients->end();
+ for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it)
+ clients.append(*it);
+
+ ASSERT(numClients == clients.size());
+ for (size_t i = 0; i < numClients; ++i)
+ clients[i]->fontCacheInvalidated();
+
+ purgeInactiveFontData();
+}
+
+} // namespace WebCore