From d71fe99fe4d54230572223915166bea8efd67d85 Mon Sep 17 00:00:00 2001 From: "reed@google.com" Date: Mon, 25 Feb 2013 20:38:07 +0000 Subject: check-point: skiafy SkFontHost_fontconfig from chrome git-svn-id: http://skia.googlecode.com/svn/trunk@7852 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/ports/SkFontHost_fontconfig.cpp | 438 ++++++++++++++++++++++-------------- 1 file changed, 267 insertions(+), 171 deletions(-) (limited to 'src/ports') diff --git a/src/ports/SkFontHost_fontconfig.cpp b/src/ports/SkFontHost_fontconfig.cpp index 355ea79645..b7927a982c 100644 --- a/src/ports/SkFontHost_fontconfig.cpp +++ b/src/ports/SkFontHost_fontconfig.cpp @@ -5,247 +5,343 @@ * found in the LICENSE file. */ +/* Derived from chromium's skia/ext/SkFontHost_fontconfig.cpp */ + #include #include -#include +#include +#include +#include +#include "SkFontConfigInterface.h" +#include "SkFontDescriptor.h" #include "SkFontHost.h" #include "SkStream.h" -/** An extern from SkFontHost_FreeType. */ -SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name); +SK_DECLARE_STATIC_MUTEX(gFontConfigInterfaceMutex); +static SkFontConfigInterface* gFontConfigInterface; -/** This lock must be held while modifying global_fc_* globals. */ -SK_DECLARE_STATIC_MUTEX(global_fc_map_lock); +SkFontConfigInterface* SkFontConfigInterface::RefGlobal() { + SkAutoMutexAcquire ac(gFontConfigInterfaceMutex); + + return SkSafeRef(gFontConfigInterface); +} -/** Map from file names to file ids. */ -static std::map global_fc_map; -/** Map from file ids to file names. */ -static std::map global_fc_map_inverted; -/** The next file id. */ -static unsigned global_fc_map_next_id = 0; +SkFontConfigInterface* SkFontConfigInterface::SetGlobal(SkFontConfigInterface* fc) { + SkAutoMutexAcquire ac(gFontConfigInterfaceMutex); -/** - * Check to see if the filename has already been assigned a fileid and, if so, use it. - * Otherwise, assign one. Return the resulting fileid. - */ -static unsigned FileIdFromFilename(const char* filename) { - SkAutoMutexAcquire ac(global_fc_map_lock); - - std::map::const_iterator i = global_fc_map.find(filename); - if (i == global_fc_map.end()) { - const unsigned fileid = global_fc_map_next_id++; - global_fc_map[filename] = fileid; - global_fc_map_inverted[fileid] = filename; - return fileid; - } else { - return i->second; + SkRefCnt_SafeAssign(gFontConfigInterface, fc); + return fc; +} + +/////////////////////////////////////////////////////////////////////////////// + +SK_DECLARE_STATIC_MUTEX(global_remote_font_map_lock); +static std::map >* global_remote_fonts; + +// Initialize the map declared above. Note that its corresponding mutex must be +// locked before calling this function. +static void AllocateGlobalRemoteFontsMapOnce() { + if (!global_remote_fonts) { + global_remote_fonts = + new std::map >(); } } -static unsigned FileIdFromUniqueId(unsigned uniqueid) { +static unsigned global_next_remote_font_id; + +// This is the maximum size of the font cache. +static const unsigned kFontCacheMemoryBudget = 2 * 1024 * 1024; // 2MB + +// UniqueIds are encoded as (filefaceid << 8) | style +// For system fonts, filefaceid = (fileid << 4) | face_index. +// For remote fonts, filefaceid = fileid. + +static unsigned UniqueIdToFileFaceId(unsigned uniqueid) +{ return uniqueid >> 8; } -static SkTypeface::Style StyleFromUniqueId(unsigned uniqueid) { +static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid) +{ return static_cast(uniqueid & 0xff); } -static unsigned UniqueIdFromFileIdAndStyle(unsigned fileid, SkTypeface::Style style) { +static unsigned FileFaceIdAndStyleToUniqueId(unsigned filefaceid, + SkTypeface::Style style) +{ SkASSERT((style & 0xff) == style); - return (fileid << 8) | static_cast(style); + return (filefaceid << 8) | static_cast(style); +} + +static const unsigned kRemoteFontMask = 0x00800000u; + +static bool IsRemoteFont(unsigned filefaceid) +{ + return filefaceid & kRemoteFontMask; } class FontConfigTypeface : public SkTypeface { public: - FontConfigTypeface(Style style, uint32_t id) : SkTypeface(style, id) { } -}; + FontConfigTypeface(Style style, uint32_t id) + : SkTypeface(style, id) + { } -/** - * Find a matching font where @type (one of FC_*) is equal to @value. For a - * list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27. - * The variable arguments are a list of triples, just like the first three - * arguments, and must be NULL terminated. - * - * For example, - * FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf", NULL); - */ -static FcPattern* FontMatch(const char* type, FcType vtype, const void* value, ...) { - va_list ap; - va_start(ap, value); - - FcPattern* pattern = FcPatternCreate(); - - for (;;) { - FcValue fcvalue; - fcvalue.type = vtype; - switch (vtype) { - case FcTypeString: - fcvalue.u.s = (FcChar8*) value; - break; - case FcTypeInteger: - fcvalue.u.i = (int)(intptr_t)value; - break; - default: - SkDEBUGFAIL("FontMatch unhandled type"); + virtual ~FontConfigTypeface() + { + const uint32_t id = uniqueID(); + if (IsRemoteFont(UniqueIdToFileFaceId(id))) { + SkAutoMutexAcquire ac(global_remote_font_map_lock); + AllocateGlobalRemoteFontsMapOnce(); + std::map >::iterator iter + = global_remote_fonts->find(id); + if (iter != global_remote_fonts->end()) { + sk_free(iter->second.first); // remove the font on memory. + global_remote_fonts->erase(iter); + } } - FcPatternAdd(pattern, type, fcvalue, FcFalse); - - type = va_arg(ap, const char *); - if (!type) - break; - // FcType is promoted to int when passed through ... - vtype = static_cast(va_arg(ap, int)); - value = va_arg(ap, const void *); - }; - va_end(ap); - - FcConfigSubstitute(NULL, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - - FcResult result; - FcPattern* match = FcFontMatch(NULL, pattern, &result); - FcPatternDestroy(pattern); - - return match; -} + } +}; // static SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, const char familyName[], SkTypeface::Style style) { - const char* resolved_family_name = NULL; - FcPattern* face_match = NULL; - - { - SkAutoMutexAcquire ac(global_fc_map_lock); - if (FcTrue != FcInit()) { - SkASSERT(false && "Could not initialize fontconfig."); - } - } + std::string resolved_family_name; if (familyFace) { - // Here we use the inverted global id map to find the filename from the - // SkTypeface object. Given the filename we can ask fontconfig for the - // familyname of the font. - SkAutoMutexAcquire ac(global_fc_map_lock); - - const unsigned fileid = FileIdFromUniqueId(familyFace->uniqueID()); - std::map::const_iterator i = global_fc_map_inverted.find(fileid); - if (i == global_fc_map_inverted.end()) { + // Given the fileid we can ask fontconfig for the familyname of the + // font. + const unsigned filefaceid = UniqueIdToFileFaceId(familyFace->uniqueID()); + if (!GetFcImpl()->Match(&resolved_family_name, NULL, + true /* filefaceid valid */, filefaceid, "", + NULL, 0, NULL, NULL)) { return NULL; } - - face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(), NULL); - if (!face_match) { - return NULL; - } - - FcChar8* family; - if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) { - FcPatternDestroy(face_match); - return NULL; - } - // At this point, @family is pointing into the @face_match object so we - // cannot release it yet. - - resolved_family_name = reinterpret_cast(family); } else if (familyName) { resolved_family_name = familyName; } - const int bold = (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL; - const int italic = (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN; - - FcPattern* match; - if (resolved_family_name) { - match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name, - FC_WEIGHT, FcTypeInteger, bold, - FC_SLANT, FcTypeInteger, italic, - NULL); - } else { - match = FontMatch(FC_WEIGHT, FcTypeInteger, reinterpret_cast(bold), - FC_SLANT, FcTypeInteger, italic, - NULL); + bool bold = style & SkTypeface::kBold; + bool italic = style & SkTypeface::kItalic; + unsigned filefaceid; + if (!GetFcImpl()->Match(NULL, &filefaceid, + false, -1, /* no filefaceid */ + resolved_family_name, NULL, 0, + &bold, &italic)) { + return NULL; } + const SkTypeface::Style resulting_style = static_cast( + (bold ? SkTypeface::kBold : 0) | + (italic ? SkTypeface::kItalic : 0)); + + const unsigned id = FileFaceIdAndStyleToUniqueId(filefaceid, + resulting_style); + SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (resulting_style, id)); + return typeface; +} - if (face_match) - FcPatternDestroy(face_match); +// static +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) +{ + if (!stream) + return NULL; - if (!match) + const size_t length = stream->read(0, 0); + if (!length) return NULL; + if (length >= 1024 * 1024 * 1024) + return NULL; // don't accept too large fonts (>= 1GB) for safety. - FcChar8* filename; - if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) { - FcPatternDestroy(match); + uint8_t* font = (uint8_t*)sk_malloc_throw(length); + if (stream->read(font, length) != length) { + sk_free(font); return NULL; } - // Now @filename is pointing into @match - const unsigned fileid = FileIdFromFilename(reinterpret_cast(filename)); - const unsigned id = UniqueIdFromFileIdAndStyle(fileid, style); - SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id)); - FcPatternDestroy(match); + SkTypeface::Style style = static_cast(0); + unsigned id = 0; + { + SkAutoMutexAcquire ac(global_remote_font_map_lock); + AllocateGlobalRemoteFontsMapOnce(); + id = FileFaceIdAndStyleToUniqueId( + global_next_remote_font_id | kRemoteFontMask, style); + + if (++global_next_remote_font_id >= kRemoteFontMask) + global_next_remote_font_id = 0; + + if (!global_remote_fonts->insert( + std::make_pair(id, std::make_pair(font, length))).second) { + sk_free(font); + return NULL; + } + } + SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id)); return typeface; } // static -SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { - SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented"); +SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) +{ + SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented"); return NULL; } -// static -SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { - SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented"); - return NULL; +uint32_t SkFontHost::NextLogicalFont(SkFontID curr, SkFontID orig) { + // We don't handle font fallback, WebKit does. + return 0; } -// static -SkStream* SkFontHost::OpenStream(uint32_t id) { - SkAutoMutexAcquire ac(global_fc_map_lock); - const unsigned fileid = FileIdFromUniqueId(id); +/////////////////////////////////////////////////////////////////////////////// - std::map::const_iterator i = global_fc_map_inverted.find(fileid); - if (i == global_fc_map_inverted.end()) { - return NULL; - } +// Serialize, Deserialize need to be compatible across platforms, hence the use +// of SkFontDescriptor. + +void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { + SkFontDescriptor desc(face->style()); + + std::string resolved_family_name; + + const unsigned filefaceid = UniqueIdToFileFaceId(face->uniqueID()); + if (GetFcImpl()->Match(&resolved_family_name, NULL, + true /* filefaceid valid */, filefaceid, "", NULL, 0, NULL, NULL)) + desc.setFamilyName(resolved_family_name.c_str()); + else + desc.setFamilyName("sans-serif"); - return SkNEW_ARGS(SkFILEStream, (i->second.c_str())); + // would also like other names (see SkFontDescriptor.h) + + desc.serialize(stream); + + // by convention, we also write out the actual sfnt data, preceeded by + // a packed-length. For now we skip that, so we just write the zero. + stream->writePackedUInt(0); } -size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) { - SkAutoMutexAcquire ac(global_fc_map_lock); - const unsigned fileid = FileIdFromUniqueId(fontID); +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + SkFontDescriptor desc(stream); + + // by convention, Serialize will have also written the actual sfnt data. + // for now, we just want to skip it. + size_t size = stream->readPackedUInt(); + stream->skip(size); - std::map::const_iterator i = global_fc_map_inverted.find(fileid); - if (i == global_fc_map_inverted.end()) { - return 0; + return SkFontHost::CreateTypeface(NULL, desc.getFamilyName(), + desc.getStyle()); +} + +/////////////////////////////////////////////////////////////////////////////// + +class SkFileDescriptorStream : public SkStream { + public: + SkFileDescriptorStream(int fd) { + memory_ = NULL; + offset_ = 0; + + // this ensures that if we fail in the constructor, we will safely + // ignore all subsequent calls to read() because we will always trim + // the requested size down to 0 + length_ = 0; + + struct stat st; + if (fstat(fd, &st)) + return; + + void* memory = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); + if (memory == MAP_FAILED) + return; + + memory_ = reinterpret_cast(memory); + length_ = st.st_size; } - const std::string& str = i->second; - if (path) { - memcpy(path, str.c_str(), SkMin32(str.size(), length)); + virtual ~SkFileDescriptorStream() { + munmap(const_cast(memory_), length_); } - if (index) { // TODO: check if we're in a TTC - *index = 0; + + virtual bool rewind() OVERRIDE { + offset_ = 0; + return true; } - return str.size(); -} -void SkFontHost::Serialize(const SkTypeface*, SkWStream*) { - SkDEBUGFAIL("SkFontHost::Serialize unimplemented"); -} + // SkStream implementation. + virtual size_t read(void* buffer, size_t size) OVERRIDE { + if (!buffer && !size) { + // This is request for the length of the stream. + return length_; + } -SkTypeface* SkFontHost::Deserialize(SkStream* stream) { - SkDEBUGFAIL("SkFontHost::Deserialize unimplemented"); - return NULL; + size_t remaining = length_ - offset_; + if (size > remaining) + size = remaining; + if (buffer) + memcpy(buffer, memory_ + offset_, size); + + offset_ += size; + return size; + } + + virtual const void* getMemoryBase() OVERRIDE { + return memory_; + } + + private: + const uint8_t* memory_; + size_t offset_, length_; +}; + +/////////////////////////////////////////////////////////////////////////////// + +// static +SkStream* SkFontHost::OpenStream(uint32_t id) +{ + const unsigned filefaceid = UniqueIdToFileFaceId(id); + + if (IsRemoteFont(filefaceid)) { + // remote font + SkAutoMutexAcquire ac(global_remote_font_map_lock); + AllocateGlobalRemoteFontsMapOnce(); + std::map >::const_iterator iter + = global_remote_fonts->find(id); + if (iter == global_remote_fonts->end()) + return NULL; + return SkNEW_ARGS( + SkMemoryStream, (iter->second.first, iter->second.second)); + } + + // system font + const int fd = GetFcImpl()->Open(filefaceid); + if (fd < 0) + return NULL; + + return SkNEW_ARGS(SkFileDescriptorStream, (fd)); } -SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { - // We don't handle font fallback, WebKit does. +// static +size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, + int32_t* index) { + const unsigned filefaceid = UniqueIdToFileFaceId(fontID); + + if (IsRemoteFont(filefaceid)) + return 0; + + if (index) { + *index = filefaceid & 0xfu; + // 1 is a bogus return value. + // We had better change the signature of this function in Skia + // to return bool to indicate success/failure and have another + // out param for fileName length. + if (!path) + return 1; + } + + if (path) + SkASSERT(!"SkFontHost::GetFileName does not support the font path " + "retrieval."); + return 0; } -- cgit v1.2.3