#include "config.h"
#include "FontCache.h"
#include "Font.h"
#include "FontFallbackList.h"
#include "FontPlatformData.h"
#include "FontSelector.h"
#include "StringHash.h"
#include <wtf/HashMap.h>
static pthread_mutex_t fontDataLock;
static void initFontCacheLockOnce()
{
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&fontDataLock, &mattr);
pthread_mutexattr_destroy(&mattr);
}
static inline void lockFontLock()
{
static pthread_once_t initControl = PTHREAD_ONCE_INIT;
pthread_once(&initControl, initFontCacheLockOnce);
int lockcode = pthread_mutex_lock(&fontDataLock);
#pragma unused (lockcode)
ASSERT_WITH_MESSAGE(lockcode == 0, "fontDataLock lock failed with code:%d", lockcode);
}
static inline void unlockFontLock()
{
int lockcode = pthread_mutex_unlock(&fontDataLock);
#pragma unused (lockcode)
ASSERT_WITH_MESSAGE(lockcode == 0, "fontDataLock unlock failed with code:%d", lockcode);
}
namespace WebCore {
struct FontPlatformDataCacheKey {
FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, bool bold = false, bool italic = false,
bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode)
: m_family(family)
, m_size(size)
, m_bold(bold)
, m_italic(italic)
, m_printerFont(isPrinterFont)
, m_renderingMode(renderingMode)
{
}
bool operator==(const FontPlatformDataCacheKey& other) const
{
return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size &&
m_bold == other.m_bold && m_italic == other.m_italic && m_printerFont == other.m_printerFont &&
m_renderingMode == other.m_renderingMode;
}
AtomicString m_family;
unsigned m_size;
bool m_bold;
bool m_italic;
bool m_printerFont;
FontRenderingMode m_renderingMode;
};
inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
{
unsigned hashCodes[4] = {
CaseFoldingHash::hash(fontKey.m_family),
fontKey.m_size,
static_cast<unsigned>(fontKey.m_bold) << 3 | static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fontKey.m_printerFont) << 1 |
static_cast<unsigned>(fontKey.m_renderingMode)
};
return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), 4 * sizeof(unsigned) / sizeof(UChar));
}
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 bool needsDestruction = false;
static const FontPlatformDataCacheKey& deletedValue()
{
static FontPlatformDataCacheKey key(nullAtom, 0xFFFFFFFFU, false, false);
return key;
}
static const FontPlatformDataCacheKey& emptyValue()
{
static FontPlatformDataCacheKey key(nullAtom, 0, false, false);
return key;
}
};
typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
static FontPlatformDataCache* gFontPlatformDataCache = 0;
static const AtomicString& alternateFamilyName(const AtomicString& familyName)
{
static AtomicString courier("Courier"), courierNew("Courier New");
if (equalIgnoringCase(familyName, courier))
return courierNew;
if (equalIgnoringCase(familyName, courierNew))
return courier;
static AtomicString times("Times"), timesNewRoman("Times New Roman");
if (equalIgnoringCase(familyName, times))
return timesNewRoman;
if (equalIgnoringCase(familyName, timesNewRoman))
return times;
static AtomicString arial("Arial"), helvetica("Helvetica");
if (equalIgnoringCase(familyName, arial))
return helvetica;
if (equalIgnoringCase(familyName, helvetica))
return arial;
static AtomicString helveticaNeue("Helvetica Neue");
if (equalIgnoringCase(familyName, helveticaNeue))
return helvetica;
return emptyAtom;
}
FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
const AtomicString& familyName,
bool checkingAlternateName)
{
lockFontLock();
if (!gFontPlatformDataCache) {
gFontPlatformDataCache = new FontPlatformDataCache;
platformInit();
}
FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.bold(), fontDescription.italic(),
fontDescription.usePrinterFont(), fontDescription.renderingMode());
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) {
const AtomicString& alternateName = alternateFamilyName(familyName);
if (!alternateName.isEmpty())
result = getCachedFontPlatformData(fontDescription, alternateName, true);
if (result)
gFontPlatformDataCache->set(key, new FontPlatformData(*result)); }
unlockFontLock();
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 = false;
static const FontPlatformData& deletedValue()
{
static FontPlatformData key = FontPlatformData::Deleted();
return key;
}
static const FontPlatformData& emptyValue()
{
static FontPlatformData key;
return key;
}
};
typedef HashMap<FontPlatformData, SimpleFontData*, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
static FontDataCache* gFontDataCache = 0;
SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData)
{
if (!platformData)
return 0;
lockFontLock();
if (!gFontDataCache)
gFontDataCache = new FontDataCache;
SimpleFontData* result = gFontDataCache->get(*platformData);
if (!result) {
result = new SimpleFontData(*platformData);
gFontDataCache->set(*platformData, result);
}
unlockFontLock();
return result;
}
const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector)
{
FontPlatformData* 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 = getCachedFontPlatformData(font.fontDescription(), currFamily->family());
}
currFamily = currFamily->next();
}
if (!currFamily)
familyIndex = cAllFamiliesScanned;
if (!result)
result = getSimilarFontPlatformData(font);
if (!result && startIndex == 0) {
if (fontSelector) {
if (FontData* data = fontSelector->getFontData(font.fontDescription(), "-webkit-standard"))
return data;
}
result = getLastResortFallbackFont(font.fontDescription());
}
return getCachedFontData(result);
}
}