blob: 5ab55362f862cca5e66509a1e4de6d669c67ab3a [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
#if defined(STARBOARD)
#include "starboard/file.h"
#else
#include <sys/stat.h>
#endif
#include <cmath>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/memory/singleton.h"
#include "cobalt/base/c_val.h"
#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.h"
#include "SkData.h"
#include "SkGraphics.h"
#include "SkOSFile.h"
#include "SkStream.h"
#include "SkString.h"
#include "SkTArray.h"
#include "SkTSearch.h"
#include "third_party/skia/src/ports/SkFontHost_FreeType_common.h"
///////////////////////////////////////////////////////////////////////////////
namespace {
const int32_t kPeriodicProcessingTimerDelayMs = 5000;
const int32_t kReleaseInactiveSystemTypefaceOpenStreamsDelayMs = 300000;
const int32_t kPeriodicFontCachePurgeDelayMs = 900000;
// NOTE: It is the responsibility of the caller to call Unref() on the SkData.
SkData* NewDataFromFile(const SkString& file_path) {
LOG(INFO) << "Loading font file: " << file_path.c_str();
SkAutoTUnref<SkStream> file_stream(SkStream::NewFromFile(file_path.c_str()));
if (file_stream == NULL) {
LOG(ERROR) << "Failed to open font file: " << file_path.c_str();
return NULL;
}
SkAutoDataUnref data(
SkData::NewFromStream(file_stream, file_stream->getLength()));
if (data == NULL) {
LOG(ERROR) << "Failed to read font file: " << file_path.c_str();
return NULL;
}
return data.detach();
}
int match_score(const SkFontStyle& pattern, const SkFontStyle& candidate) {
int score = 0;
score += std::abs((pattern.width() - candidate.width()) * 100);
score += (pattern.isItalic() == candidate.isItalic()) ? 0 : 1000;
score += std::abs(pattern.weight() - candidate.weight());
return score;
}
} // namespace
///////////////////////////////////////////////////////////////////////////////
class SkTypeface_Cobalt : public SkTypeface_FreeType {
public:
SkTypeface_Cobalt(int index, Style style, bool is_fixed_pitch,
const SkString family_name)
: INHERITED(style, SkTypefaceCache::NewFontID(), is_fixed_pitch),
index_(index),
family_name_(family_name) {}
protected:
virtual void onGetFamilyName(SkString* family_name) const SK_OVERRIDE {
*family_name = family_name_;
}
int index_;
SkString family_name_;
private:
typedef SkTypeface_FreeType INHERITED;
};
class SkTypeface_CobaltStream : public SkTypeface_Cobalt {
public:
SkTypeface_CobaltStream(SkStream* stream, int index, Style style,
bool is_fixed_pitch, const SkString family_name)
: INHERITED(index, style, is_fixed_pitch, family_name),
stream_(SkRef(stream)) {
LOG(INFO) << "Created SkTypeface_CobaltStream: " << family_name.c_str()
<< "(" << style << "); Size: " << stream_->getLength()
<< " bytes";
}
virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
bool* serialize) const SK_OVERRIDE {
SkASSERT(descriptor);
SkASSERT(serialize);
descriptor->setFamilyName(family_name_.c_str());
descriptor->setFontFileName(NULL);
descriptor->setFontIndex(index_);
*serialize = true;
}
virtual SkStream* onOpenStream(int* ttc_index) const SK_OVERRIDE {
*ttc_index = index_;
return stream_->duplicate();
}
private:
typedef SkTypeface_Cobalt INHERITED;
SkAutoTUnref<SkStream> stream_;
};
class SkTypeface_CobaltSystem : public SkTypeface_Cobalt {
public:
SkTypeface_CobaltSystem(const SkFontMgr_Cobalt* manager, SkString path_name,
SkMemoryStream* stream, int index, Style style,
bool is_fixed_pitch, const SkString family_name)
: INHERITED(index, style, is_fixed_pitch, family_name),
font_manager_(manager),
path_name_(path_name),
stream_(SkRef(stream)) {
LOG(INFO) << "Created SkTypeface_CobaltSystem: " << family_name.c_str()
<< "(" << style << "); File: " << path_name.c_str()
<< "; Size: " << stream_->getLength() << " bytes";
SkAutoMutexAcquire scoped_mutex(
font_manager_->GetSystemTypefaceStreamMutex());
font_manager_->AddSystemTypefaceWithActiveOpenStream(this);
}
virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
bool* serialize) const SK_OVERRIDE {
SkASSERT(descriptor);
SkASSERT(serialize);
descriptor->setFamilyName(family_name_.c_str());
descriptor->setFontFileName(path_name_.c_str());
descriptor->setFontIndex(index_);
*serialize = false;
}
virtual SkStream* onOpenStream(int* ttc_index) const SK_OVERRIDE {
*ttc_index = index_;
// Scope the initial mutex lock.
{
// Check for the stream still being open. In this case, the typeface
// stream does not need to be re-loaded. Simply notify the manager of the
// stream activity, as it potentially needs to move the stream from the
// inactive list to the active list.
SkAutoMutexAcquire scoped_mutex(
font_manager_->GetSystemTypefaceStreamMutex());
if (stream_ != NULL) {
font_manager_->NotifySystemTypefaceOfOpenStreamActivity(this);
return stream_->duplicate();
}
}
// This is where the bulk of the time will be spent, so load the font data
// outside of a mutex lock.
SkAutoTUnref<SkData> data(NewDataFromFile(path_name_));
// Re-lock the mutex now that the data has been loaded.
SkAutoMutexAcquire scoped_mutex(
font_manager_->GetSystemTypefaceStreamMutex());
// Verify that the data was not loaded on another thread while it was being
// loaded on this one. If that's the case, then simply use that data.
// Otherwise, update the stream and notify the font manager that the stream
// is open.
if (stream_ == NULL) {
stream_.reset(new SkMemoryStream(data));
LOG(INFO) << "Setting font stream: " << path_name_.c_str()
<< "; Size: " << stream_->getLength() << " bytes";
font_manager_->AddSystemTypefaceWithActiveOpenStream(this);
}
return stream_->duplicate();
}
size_t GetOpenStreamSizeInBytes() const { return stream_->getLength(); }
bool IsOpenStreamInactive() const {
SkAutoTUnref<SkData> data(stream_->copyToData());
// A refcount of 2 indicates that there are no external references to the
// data. The first reference comes from SkMemoryStream and the second comes
// from the call to copyToData().
return data->getRefCnt() == 2;
}
void ReleaseStream() const {
LOG(INFO) << "Releasing font stream: " << path_name_.c_str()
<< "; Size: " << stream_->getLength() << " bytes";
stream_.reset(NULL);
}
private:
typedef SkTypeface_Cobalt INHERITED;
const SkFontMgr_Cobalt* font_manager_;
const SkString path_name_;
// |stream_| is mutable so that it can be modified within onOpenStream().
mutable SkAutoTUnref<SkMemoryStream> stream_;
};
///////////////////////////////////////////////////////////////////////////////
// SkFontStyleSet_Cobalt
//
// This class is used by SkFontMgr_Cobalt to hold SkTypeface_CobaltSystem
// families.
SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt(
const SkFontMgr_Cobalt* const manager, const FontFamily& family,
const char* base_path, SkMutex* const manager_owned_mutex)
: font_manager_(manager),
manager_owned_mutex_(manager_owned_mutex),
is_fallback_font_(family.is_fallback_font),
language_(family.language),
page_ranges_(family.page_ranges),
is_character_map_generated_(false) {
DCHECK(manager_owned_mutex_);
if (family.names.count() == 0) {
return;
}
family_name_ = family.names[0];
for (int i = 0; i < family.fonts.count(); ++i) {
const FontFileInfo& font_file = family.fonts[i];
SkString path_name(SkOSPath::Join(base_path, font_file.file_name.c_str()));
// Sanity check that something exists at this location.
#if defined(STARBOARD)
bool is_font_file_found = SbFileExists(path_name.c_str());
#else
struct stat status;
bool is_font_file_found = (stat(path_name.c_str(), &status) == 0);
#endif // defined(STARBOARD)
if (!is_font_file_found) {
LOG(ERROR) << "Failed to find font file: " << path_name.c_str();
continue;
}
SkFontStyle style(font_file.weight, SkFontStyle::kNormal_Width,
font_file.style == FontFileInfo::kItalic_FontStyle
? SkFontStyle::kItalic_Slant
: SkFontStyle::kUpright_Slant);
std::string full_font_name(font_file.full_font_name.c_str(),
font_file.full_font_name.size());
std::string postcript_name(font_file.postcript_name.c_str(),
font_file.postcript_name.size());
styles_.push_back().reset(SkNEW_ARGS(
SkFontStyleSetEntry_Cobalt,
(path_name, font_file.index, style, full_font_name, postcript_name)));
}
}
int SkFontStyleSet_Cobalt::count() {
SkAutoMutexAcquire scoped_mutex(*manager_owned_mutex_);
return styles_.count();
}
void SkFontStyleSet_Cobalt::getStyle(int index, SkFontStyle* style,
SkString* name) {
// SkFontStyleSet_Cobalt does not support publicly interacting with entries
// via index, as entries can potentially be removed, thereby invalidating the
// indices.
NOTREACHED();
}
SkTypeface* SkFontStyleSet_Cobalt::createTypeface(int index) {
// SkFontStyleSet_Cobalt does not support publicly interacting with entries
// via index, as entries can potentially be removed, thereby invalidating the
// indices.
NOTREACHED();
return NULL;
}
SkTypeface* SkFontStyleSet_Cobalt::matchStyle(const SkFontStyle& pattern) {
SkAutoMutexAcquire scoped_mutex(*manager_owned_mutex_);
return MatchStyleWithoutLocking(pattern);
}
SkTypeface* SkFontStyleSet_Cobalt::MatchStyleWithoutLocking(
const SkFontStyle& pattern) {
while (styles_.count() > 0) {
int style_index = GetClosestStyleIndex(pattern);
SkFontStyleSetEntry_Cobalt* style = styles_[style_index];
if (style->typeface == NULL) {
CreateSystemTypeface(style);
// Check to see if the typeface is still NULL. If this is the case, then
// the style can't be created. Remove it from the array.
if (style->typeface == NULL) {
styles_[style_index].swap(&styles_.back());
styles_.pop_back();
continue;
}
}
return SkRef(style->typeface.get());
}
return NULL;
}
bool SkFontStyleSet_Cobalt::ContainsTypeface(const SkTypeface* typeface) {
for (int i = 0; i < styles_.count(); ++i) {
if (styles_[i]->typeface == typeface) {
return true;
}
}
return false;
}
bool SkFontStyleSet_Cobalt::ContainsCharacter(const SkFontStyle& style,
SkUnichar character) {
// If page ranges exist for this style set, then verify that this character
// falls within the ranges. Otherwise, the style set is treated as having a
// page range containing all characters.
size_t num_ranges = page_ranges_.count();
if (num_ranges > 0) {
int16 page = font_character_map::GetPage(character);
// Verify that the last page range is not less than the page containing the
// character. If it's less, then this character can't be contained
// within the pages. Otherwise, this last range acts as a sentinel for the
// search.
if (page_ranges_[num_ranges - 1].second < page) {
return false;
}
int range_index = 0;
while (page_ranges_[range_index].second < page) {
++range_index;
}
if (page_ranges_[range_index].first > page) {
return false;
}
}
// If this point is reached, then the character is contained within one of
// the font style set's page ranges. Now, verify that the specific character
// is supported by the font style set.
// The character map is lazily generated. Generate it now if it isn't already
// generated.
if (!is_character_map_generated_) {
// Attempt to load the closest font style from the set. If it fails to load,
// it will be removed from the set and, as long as font styles remain in the
// set, the logic will be attempted again.
while (styles_.count() > 0) {
int style_index = GetClosestStyleIndex(style);
SkFontStyleSetEntry_Cobalt* closest_style = styles_[style_index];
// Load the font data from the file and use it to generate the character
// map.
SkAutoTUnref<SkData> font_data(
NewDataFromFile(closest_style->font_file_path));
// The font failed to load. Remove it from the styles and start the loop
// over.
if (font_data == NULL) {
styles_[style_index].swap(&styles_.back());
styles_.pop_back();
continue;
}
GenerateCharacterMapFromData(font_data);
// If the character is contained within the font style set, then go ahead
// and create the typeface with the loaded font data. Otherwise, creating
// the typeface would require an additional load of the file.
bool contains_character = CharacterMapContainsCharacter(character);
if (contains_character) {
CreateSystemTypefaceFromData(closest_style, font_data);
}
return contains_character;
}
// If this point is reached, none of the fonts were successfully loaded.
is_character_map_generated_ = true;
return false;
} else {
return CharacterMapContainsCharacter(character);
}
}
bool SkFontStyleSet_Cobalt::CharacterMapContainsCharacter(SkUnichar character) {
font_character_map::CharacterMap::iterator page_iterator =
character_map_.find(font_character_map::GetPage(character));
return page_iterator != character_map_.end() &&
page_iterator->second.test(
font_character_map::GetPageCharacterIndex(character));
}
void SkFontStyleSet_Cobalt::GenerateCharacterMapFromData(SkData* font_data) {
if (is_character_map_generated_) {
return;
}
FT_Library freetype_lib;
if (FT_Init_FreeType(&freetype_lib) != 0) {
return;
}
FT_Face face;
FT_Error err = FT_New_Memory_Face(freetype_lib, font_data->bytes(),
font_data->size(), 0, &face);
if (!err) {
// map out this font's characters.
FT_UInt glyph_index;
int last_page = -1;
font_character_map::PageCharacters* page_characters = NULL;
SkUnichar code_point = FT_Get_First_Char(face, &glyph_index);
while (glyph_index) {
int page = font_character_map::GetPage(code_point);
if (page != last_page) {
page_characters = &character_map_[page];
last_page = page;
}
page_characters->set(
font_character_map::GetPageCharacterIndex(code_point));
code_point = FT_Get_Next_Char(face, code_point, &glyph_index);
}
// release this font.
FT_Done_Face(face);
}
// shut down FreeType.
FT_Done_FreeType(freetype_lib);
is_character_map_generated_ = true;
}
int SkFontStyleSet_Cobalt::GetClosestStyleIndex(const SkFontStyle& pattern) {
int closest_index = 0;
int min_score = std::numeric_limits<int>::max();
for (int i = 0; i < styles_.count(); ++i) {
int score = match_score(pattern, styles_[i]->font_style);
if (score < min_score) {
closest_index = i;
min_score = score;
}
}
return closest_index;
}
void SkFontStyleSet_Cobalt::CreateSystemTypeface(
SkFontStyleSetEntry_Cobalt* style_entry) {
SkAutoTUnref<SkData> font_data(NewDataFromFile(style_entry->font_file_path));
if (font_data != NULL) {
CreateSystemTypefaceFromData(style_entry, font_data);
}
}
void SkFontStyleSet_Cobalt::CreateSystemTypefaceFromData(
SkFontStyleSetEntry_Cobalt* style_entry, SkData* data) {
DCHECK(!style_entry->typeface);
// Since the font data is available, generate the character map if this is a
// fallback font (which means that it'll need the character map for character
// lookups during fallback).
if (is_fallback_font_) {
GenerateCharacterMapFromData(data);
}
// Pass the SkData into the SkMemoryStream.
SkAutoTUnref<SkMemoryStream> stream(new SkMemoryStream(data));
bool is_fixed_pitch;
SkTypeface::Style style;
SkString name;
if (SkTypeface_FreeType::ScanFont(stream, style_entry->ttc_index, &name,
&style, &is_fixed_pitch)) {
LOG(INFO) << "Scanned font: " << name.c_str() << "(" << style << ")";
style_entry->typeface.reset(SkNEW_ARGS(
SkTypeface_CobaltSystem,
(font_manager_, style_entry->font_file_path, stream,
style_entry->ttc_index, style, is_fixed_pitch, family_name_)));
} else {
LOG(ERROR) << "Failed to scan font: "
<< style_entry->font_file_path.c_str();
}
}
///////////////////////////////////////////////////////////////////////////////
//
// SkFontMgr_Cobalt
//
#ifndef SK_TYPEFACE_COBALT_SYSTEM_OPEN_STREAM_CACHE_LIMIT
#define SK_TYPEFACE_COBALT_SYSTEM_OPEN_STREAM_CACHE_LIMIT (10 * 1024 * 1024)
#endif
namespace {
// Tracking of the cache limit and current cache size. These are stored as
// base::CVal, so that we can easily monitor the system font memory usage.
// We have to put the CVals used by SkFontMgr_Cobalt into their own Singleton,
// because Skia's lazy instance implementation does not guarantee that only one
// SkFontMgr_Cobalt will be created at a time (see skia/src/core/SkLazyPtr.h),
// and we cannot have multiple copies of the same CVal.
class SkFontMgrCVals {
public:
static SkFontMgrCVals* GetInstance() {
return Singleton<SkFontMgrCVals,
DefaultSingletonTraits<SkFontMgrCVals> >::get();
}
const base::CVal<base::cval::SizeInBytes, base::CValPublic>&
system_typeface_open_stream_cache_limit_in_bytes() {
return system_typeface_open_stream_cache_limit_in_bytes_;
}
base::CVal<base::cval::SizeInBytes, base::CValPublic>&
system_typeface_open_stream_cache_size_in_bytes() {
return system_typeface_open_stream_cache_size_in_bytes_;
}
private:
SkFontMgrCVals()
: system_typeface_open_stream_cache_limit_in_bytes_(
"Memory.SystemTypeface.Capacity",
SK_TYPEFACE_COBALT_SYSTEM_OPEN_STREAM_CACHE_LIMIT,
"The capacity, in bytes, of the system fonts. Exceeding this "
"results in *inactive* fonts being released."),
system_typeface_open_stream_cache_size_in_bytes_(
"Memory.SystemTypeface.Size", 0,
"Total number of bytes currently used by the cache.") {}
const base::CVal<base::cval::SizeInBytes, base::CValPublic>
system_typeface_open_stream_cache_limit_in_bytes_;
mutable base::CVal<base::cval::SizeInBytes, base::CValPublic>
system_typeface_open_stream_cache_size_in_bytes_;
friend struct DefaultSingletonTraits<SkFontMgrCVals>;
DISALLOW_COPY_AND_ASSIGN(SkFontMgrCVals);
};
// The timer used to trigger periodic processing of open streams, which
// handles moving open streams from the active to the inactive list and
// releasing inactive open streams. Create it as a lazy instance to ensure it
// gets cleaned up correctly at exit, even though the SkFontMgr_Cobalt may be
// created multiple times and the surviving instance isn't deleted until after
// the thread has been stopped.
base::LazyInstance<scoped_ptr<base::PollerWithThread> >
process_system_typefaces_with_open_streams_poller =
LAZY_INSTANCE_INITIALIZER;
base::LazyInstance<base::Lock> poller_lock = LAZY_INSTANCE_INITIALIZER;
} // namespace
SkFontMgr_Cobalt::SkFontMgr_Cobalt(
const char* directory, const SkTArray<SkString, true>& default_fonts)
: default_family_(NULL),
last_font_cache_purge_time_(base::TimeTicks::Now()) {
// Ensure that both the CValManager and SkFontMgrCVals are initialized. The
// CValManager is created first as it must outlast the SkFontMgrCVals.
base::CValManager::GetInstance();
SkFontMgrCVals::GetInstance();
SkTDArray<FontFamily*> font_families;
SkFontConfigParser::GetFontFamilies(directory, &font_families);
BuildNameToFamilyMap(directory, &font_families);
font_families.deleteAll();
FindDefaultFont(default_fonts);
{
// Create the poller. The lazy instance ensures we only get one copy, and
// that it will be cleaned up at appl exit, but we still need to lock
// so we only construct it once.
base::AutoLock lock(poller_lock.Get());
if (!process_system_typefaces_with_open_streams_poller.Get()) {
process_system_typefaces_with_open_streams_poller.Get().reset(
new base::PollerWithThread(
base::Bind(&SkFontMgr_Cobalt::HandlePeriodicProcessing,
base::Unretained(this)),
base::TimeDelta::FromMilliseconds(
kPeriodicProcessingTimerDelayMs)));
}
}
}
int SkFontMgr_Cobalt::onCountFamilies() const { return family_names_.count(); }
void SkFontMgr_Cobalt::onGetFamilyName(int index, SkString* family_name) const {
if (index < 0 || family_names_.count() <= index) {
family_name->reset();
return;
}
family_name->set(family_names_[index]);
}
SkFontStyleSet_Cobalt* SkFontMgr_Cobalt::onCreateStyleSet(int index) const {
if (index < 0 || family_names_.count() <= index) {
return NULL;
}
NameToFamilyMap::const_iterator family_iterator =
name_to_family_map_.find(family_names_[index].c_str());
if (family_iterator != name_to_family_map_.end()) {
return SkRef(family_iterator->second);
}
return NULL;
}
SkFontStyleSet_Cobalt* SkFontMgr_Cobalt::onMatchFamily(
const char family_name[]) const {
if (!family_name) {
return NULL;
}
SkAutoAsciiToLC tolc(family_name);
NameToFamilyMap::const_iterator family_iterator =
name_to_family_map_.find(tolc.lc());
if (family_iterator != name_to_family_map_.end()) {
return SkRef(family_iterator->second);
}
return NULL;
}
SkTypeface* SkFontMgr_Cobalt::matchFullFontFaceNameHelper(
SkFontStyleSet_Cobalt* style_set,
SkFontStyleSet_Cobalt::SkFontStyleSetEntry_Cobalt* style) const {
DCHECK(style_set != NULL);
if (!style) {
NOTREACHED() << "style should not be NULL.";
return NULL;
}
if (style->typeface == NULL) {
if (!style_set) {
return NULL;
}
style_set->CreateSystemTypeface(style);
}
if (style->typeface != NULL) {
return SkRef(style->typeface.get());
}
return NULL;
}
SkTypeface* SkFontMgr_Cobalt::matchFaceNameOnlyIfFound(
const std::string& font_face_name) const {
// Prioritize looking it up postscript name first since some of our client
// applications prefer this method to specify face names.
SkTypeface* typeface = matchPostScriptName(font_face_name);
if (typeface != NULL) return typeface;
typeface = matchFullFontFaceName(font_face_name);
if (typeface != NULL) return typeface;
return NULL;
}
SkTypeface* SkFontMgr_Cobalt::matchFullFontFaceName(
const std::string& font_face_name) const {
SkAutoMutexAcquire scoped_mutex(style_sets_mutex_);
if (font_face_name.empty()) {
return NULL;
}
FullFontNameToFontFaceInfoMap::const_iterator font_face_iterator =
fullfontname_to_fontface_info_map_.find(font_face_name);
if (font_face_iterator != fullfontname_to_fontface_info_map_.end()) {
return matchFullFontFaceNameHelper(
font_face_iterator->second.style_set_entry_parent,
font_face_iterator->second.style_set_entry);
}
return NULL;
}
SkTypeface* SkFontMgr_Cobalt::matchPostScriptName(
const std::string& font_face_name) const {
if (font_face_name.empty()) {
return NULL;
}
PostScriptToFontFaceInfoMap::const_iterator font_face_iterator =
postscriptname_to_fontface_info_map_.find(font_face_name);
if (font_face_iterator != postscriptname_to_fontface_info_map_.end()) {
return matchFullFontFaceNameHelper(
font_face_iterator->second.style_set_entry_parent,
font_face_iterator->second.style_set_entry);
}
return NULL;
}
SkTypeface* SkFontMgr_Cobalt::onMatchFamilyStyle(
const char family_name[], const SkFontStyle& style) const {
SkTypeface* tf = NULL;
if (family_name) {
SkAutoTUnref<SkFontStyleSet> sset(matchFamily(family_name));
tf = sset->matchStyle(style);
}
if (NULL == tf) {
tf = default_family_->matchStyle(style);
}
return tf;
}
SkTypeface* SkFontMgr_Cobalt::onMatchFaceStyle(const SkTypeface* family_member,
const SkFontStyle& style) const {
// Lock the style sets mutex prior to calling private |SkFontStyleSet_Cobalt|
// functions that expect the mutex to already be locked.
SkAutoMutexAcquire scoped_mutex(style_sets_mutex_);
for (int i = 0; i < font_style_sets_.count(); ++i) {
if (font_style_sets_[i]->ContainsTypeface(family_member)) {
return font_style_sets_[i]->MatchStyleWithoutLocking(style);
}
}
return NULL;
}
#ifdef SK_FM_NEW_MATCH_FAMILY_STYLE_CHARACTER
SkTypeface* SkFontMgr_Cobalt::onMatchFamilyStyleCharacter(
const char family_name[], const SkFontStyle& style, const char* bcp47[],
int bcp47_count, SkUnichar character) const {
#else
SkTypeface* SkFontMgr_Cobalt::onMatchFamilyStyleCharacter(
const char family_name[], const SkFontStyle& style, const char bcp47_val[],
SkUnichar character) const {
const char** bcp47 = &bcp47_val;
int bcp47_count = bcp47_val ? 1 : 0;
#endif
// Lock the style sets mutex prior to calling |FindFamilyStyleCharacter|. It
// expects the mutex to already be locked.
SkAutoMutexAcquire scoped_mutex(style_sets_mutex_);
// Search the fallback families for ones matching the requested language.
// They are given priority over other fallback families in checking for
// character support.
for (int bcp47_index = bcp47_count; bcp47_index-- > 0;) {
SkLanguage language(bcp47[bcp47_index]);
while (!language.GetTag().isEmpty()) {
SkTypeface* matching_typeface =
FindFamilyStyleCharacter(style, language.GetTag(), character);
if (matching_typeface) {
return matching_typeface;
}
language = language.GetParent();
}
}
// Try to find character among all fallback families with no language
// requirement. This will select the first encountered family that contains
// the character.
SkTypeface* matching_typeface =
FindFamilyStyleCharacter(style, SkString(), character);
// If no family was found that supports the character, then just fall back
// to the default family.
return matching_typeface ? matching_typeface
: default_family_->MatchStyleWithoutLocking(style);
}
SkTypeface* SkFontMgr_Cobalt::onCreateFromData(SkData* data,
int ttc_index) const {
SkAutoTUnref<SkStream> stream(new SkMemoryStream(data));
return createFromStream(stream, ttc_index);
}
SkTypeface* SkFontMgr_Cobalt::onCreateFromStream(SkStream* stream,
int ttc_index) const {
bool is_fixed_pitch;
SkTypeface::Style style;
SkString name;
if (!SkTypeface_FreeType::ScanFont(stream, ttc_index, &name, &style,
&is_fixed_pitch)) {
return NULL;
}
return SkNEW_ARGS(SkTypeface_CobaltStream,
(stream, ttc_index, style, is_fixed_pitch, name));
}
SkTypeface* SkFontMgr_Cobalt::onCreateFromFile(const char path[],
int ttc_index) const {
SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
return stream.get() ? createFromStream(stream, ttc_index) : NULL;
}
SkTypeface* SkFontMgr_Cobalt::onLegacyCreateTypeface(
const char family_name[], unsigned style_bits) const {
SkTypeface::Style old_style = (SkTypeface::Style)style_bits;
SkFontStyle style = SkFontStyle(
old_style & SkTypeface::kBold ? SkFontStyle::kBold_Weight
: SkFontStyle::kNormal_Weight,
SkFontStyle::kNormal_Width,
old_style & SkTypeface::kItalic ? SkFontStyle::kItalic_Slant
: SkFontStyle::kUpright_Slant);
return matchFamilyStyle(family_name, style);
}
void SkFontMgr_Cobalt::BuildNameToFamilyMap(const char* base_path,
SkTDArray<FontFamily*>* families) {
for (int i = 0; i < families->count(); i++) {
FontFamily& family = *(*families)[i];
bool named_font = family.names.count() > 0;
if (family.is_fallback_font) {
if (!named_font) {
SkString& fallback_name = family.names.push_back();
fallback_name.printf("%.2x##fallback", i);
}
}
SkAutoTUnref<SkFontStyleSet_Cobalt> new_set(SkNEW_ARGS(
SkFontStyleSet_Cobalt, (this, family, base_path, &style_sets_mutex_)));
// Verify that the set successfully added entries.
if (new_set->styles_.count() == 0) {
continue;
}
for (SkAutoTUnref<SkFontStyleSet_Cobalt::SkFontStyleSetEntry_Cobalt>*
font_style_set_entry = new_set->styles_.begin();
font_style_set_entry != new_set->styles_.end();
++font_style_set_entry) {
if (!font_style_set_entry) {
NOTREACHED() << " Font Style entry is invalid";
continue;
} else if ((*font_style_set_entry).get() == NULL) {
NOTREACHED() << " Font Style entry is NULL";
continue;
}
const std::string& full_font_name =
(*font_style_set_entry)->full_font_name;
DCHECK(!full_font_name.empty());
if (fullfontname_to_fontface_info_map_.find(full_font_name) ==
fullfontname_to_fontface_info_map_.end()) {
DLOG(INFO) << "Adding Full font name [" << full_font_name << "].";
fullfontname_to_fontface_info_map_[full_font_name] =
FullFontNameToFontFaceInfoMap::mapped_type(
font_style_set_entry->get(), new_set.get());
} else {
// Purposely, not overwriting the entry gives priority to the
// earlier entry. This is consistent with how fonts.xml gives
// priority to fonts that are specified earlier in the file.
NOTREACHED() << "Full font name [" << full_font_name
<< "] already registered in BuildNameToFamilyMap.";
}
const std::string& postscript_font_name =
(*font_style_set_entry)->font_postscript_name;
DCHECK(!postscript_font_name.empty());
if (postscriptname_to_fontface_info_map_.find(postscript_font_name) ==
postscriptname_to_fontface_info_map_.end()) {
DLOG(INFO) << "Adding Postscript name [" << postscript_font_name
<< "].";
postscriptname_to_fontface_info_map_[postscript_font_name] =
PostScriptToFontFaceInfoMap::mapped_type(
font_style_set_entry->get(), new_set.get());
} else {
// Purposely, not overwriting the entry gives priority to the
// earlier entry. This is consistent with how fonts.xml gives
// priority to fonts that are specified earlier in the file.
NOTREACHED() << "Adding Postscript name [" << postscript_font_name
<< "] already registered in BuildNameToFamilyMap.";
}
}
font_style_sets_.push_back().reset(SkRef(new_set.get()));
if (named_font) {
for (int j = 0; j < family.names.count(); j++) {
family_names_.push_back(family.names[j]);
name_to_family_map_.insert(
std::make_pair(family.names[j].c_str(), new_set.get()));
}
}
if (family.is_fallback_font) {
fallback_families_.push_back(new_set.get());
}
}
}
void SkFontMgr_Cobalt::FindDefaultFont(
const SkTArray<SkString, true>& default_fonts) {
SkASSERT(!font_style_sets_.empty());
for (size_t i = 0; i < default_fonts.count(); ++i) {
SkAutoTUnref<SkFontStyleSet_Cobalt> set(
onMatchFamily(default_fonts[i].c_str()));
if (NULL == set) {
continue;
}
SkAutoTUnref<SkTypeface> tf(set->MatchStyleWithoutLocking(SkFontStyle()));
if (NULL == tf) {
continue;
}
default_family_ = set.get();
default_typeface_.swap(&tf);
break;
}
if (NULL == default_typeface_) {
default_family_ = font_style_sets_[0].get();
default_typeface_.reset(
default_family_->MatchStyleWithoutLocking(SkFontStyle()));
}
SkASSERT(default_family_);
SkASSERT(default_typeface_);
// Remove the default typeface from the tracked system typefaces with open
// streams. We never want to release it.
for (int i = 0; i < system_typefaces_with_active_open_streams_.count(); ++i) {
if (system_typefaces_with_active_open_streams_[i]->uniqueID() ==
default_typeface_->uniqueID()) {
system_typefaces_with_active_open_streams_.removeShuffle(i);
break;
}
}
}
SkTypeface* SkFontMgr_Cobalt::FindFamilyStyleCharacter(
const SkFontStyle& style, const SkString& language_tag,
SkUnichar character) const {
if (!font_character_map::IsCharacterValid(character)) {
return NULL;
}
for (int i = 0; i < fallback_families_.count(); ++i) {
SkFontStyleSet_Cobalt* family = fallback_families_[i];
// If a language tag was provided and this family doesn't meet the language
// requirement, then just skip over the family.
if (!language_tag.isEmpty() &&
!family->language_.GetTag().startsWith(language_tag.c_str())) {
continue;
}
if (family->ContainsCharacter(style, character)) {
SkTypeface* matching_typeface = family->MatchStyleWithoutLocking(style);
if (matching_typeface) {
return matching_typeface;
}
}
}
return NULL;
}
// NOTE: It is the responsibility of the caller to lock
// |system_typeface_stream_mutex_|
void SkFontMgr_Cobalt::AddSystemTypefaceWithActiveOpenStream(
const SkTypeface_CobaltSystem* system_typeface) const {
SkFontMgrCVals::GetInstance()
->system_typeface_open_stream_cache_size_in_bytes() +=
system_typeface->GetOpenStreamSizeInBytes();
system_typefaces_with_active_open_streams_.push_back(system_typeface);
LOG(INFO) << "Total system typeface memory: "
<< SkFontMgrCVals::GetInstance()
->system_typeface_open_stream_cache_size_in_bytes()
<< " bytes";
}
// NOTE: It is the responsibility of the caller to lock
// |system_typeface_stream_mutex_|
void SkFontMgr_Cobalt::NotifySystemTypefaceOfOpenStreamActivity(
const SkTypeface_CobaltSystem* system_typeface) const {
// Walk through the system_typefaces with inactive open streams, searching for
// the typeface with activity. It potentially is still in the active list and
// won't be found here.
for (int i = 0; i < system_typefaces_with_inactive_open_streams_.count();
++i) {
// We found the typeface in the inactive list. Move it back to the active
// list and break out. The typeface will never be in the list more than
// once, so we don't need to continue searching.
if (system_typefaces_with_inactive_open_streams_[i].typeface ==
system_typeface) {
system_typefaces_with_active_open_streams_.push_back(system_typeface);
system_typefaces_with_inactive_open_streams_.removeShuffle(i);
break;
}
}
}
void SkFontMgr_Cobalt::HandlePeriodicProcessing() {
base::TimeTicks current_time = base::TimeTicks::Now();
ProcessSystemTypefacesWithOpenStreams(current_time);
// If the required delay has elapsed since the last font cache purge, then
// it's time to force another. This is accomplished by setting the limit to
// 1 byte smaller than it's current size, which initiates a partial purge (it
// always purges at least 25% of its contents). After this is done, the cache
// is set back to its previous size.
if ((current_time - last_font_cache_purge_time_).InMilliseconds() >=
kPeriodicFontCachePurgeDelayMs) {
last_font_cache_purge_time_ = current_time;
size_t font_cache_limit = SkGraphics::GetFontCacheLimit();
SkGraphics::SetFontCacheLimit(SkGraphics::GetFontCacheUsed() - 1);
SkGraphics::SetFontCacheLimit(font_cache_limit);
}
}
void SkFontMgr_Cobalt::ProcessSystemTypefacesWithOpenStreams(
const base::TimeTicks& current_time) const {
SkAutoMutexAcquire scoped_mutex(system_typeface_stream_mutex_);
// First, release the inactive open streams that meet the following
// conditions:
// 1. The cache is over its memory limit.
// 2. The stream has been inactive for the required period of time.
// Given that the streams are in temporal order, only the first stream in the
// list ever needs to be checked. If it cannot be released, then all
// subsequent streams are also guaranteed to not be releasable.
while (!system_typefaces_with_inactive_open_streams_.empty()) {
// The cache size is not over the memory limit. No open streams are released
// while the cache is under its limit. Simply break out.
if (SkFontMgrCVals::GetInstance()
->system_typeface_open_stream_cache_size_in_bytes()
.value() < SkFontMgrCVals::GetInstance()
->system_typeface_open_stream_cache_limit_in_bytes()
.value()) {
break;
}
const TimedSystemTypeface& timed_system_typeface =
system_typefaces_with_inactive_open_streams_[0];
// The current stream has not been inactive long enough to be releasable. No
// subsequent streams can meet the threshold either. Simply break out.
if ((current_time - timed_system_typeface.time).InMilliseconds() <
kReleaseInactiveSystemTypefaceOpenStreamsDelayMs) {
break;
}
const SkTypeface_CobaltSystem* system_typeface =
timed_system_typeface.typeface;
DCHECK(system_typeface->IsOpenStreamInactive());
SkFontMgrCVals::GetInstance()
->system_typeface_open_stream_cache_size_in_bytes() -=
system_typeface->GetOpenStreamSizeInBytes();
system_typeface->ReleaseStream();
system_typefaces_with_inactive_open_streams_.removeShuffle(0);
LOG(INFO) << "Total system typeface memory: "
<< SkFontMgrCVals::GetInstance()
->system_typeface_open_stream_cache_size_in_bytes()
<< " bytes";
}
// Now, walk the active open streams. Any that are found to be inactive are
// moved from the active list to the inactive list, with the current time
// included for tracking when it is time to release the inactive stream.
// NOTE: The active streams are processed after the inactive streams so that
// regardless of |kReleaseInactiveSystemTypefaceOpenStreamsDelayMs|, it is
// guaranteed that active streams cannot immediately be released upon being
// moved to the inactive list. They must be retained in the inactive list for
// at least |kProcessSystemTypefacesWithOpenStreamsTimerDelayMs|. If this
// desire changes, then the active open stream processing should be moved
// above the inactive open stream processing.
for (int i = 0; i < system_typefaces_with_active_open_streams_.count(); ++i) {
const SkTypeface_CobaltSystem* system_typeface =
system_typefaces_with_active_open_streams_[i];
if (system_typeface->IsOpenStreamInactive()) {
system_typefaces_with_active_open_streams_.removeShuffle(i--);
system_typefaces_with_inactive_open_streams_.push_back(
TimedSystemTypeface(system_typeface, current_time));
}
}
}
SkMutex& SkFontMgr_Cobalt::GetSystemTypefaceStreamMutex() const {
return system_typeface_stream_mutex_;
}