blob: 746288a5de17095d873cabe7e8246d7a5e67795c [file] [log] [blame]
/*
* Copyright 2017 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 "fuchsia_font_manager.h"
#include <lib/fit/function.h>
#include <trace/event.h>
#include <lib/zx/vmar.h>
#include <unordered_map>
#include "third_party/icu/source/common/unicode/uchar.h"
#include "topaz/runtime/dart/utils/inlines.h"
#include "topaz/runtime/dart/utils/vmo.h"
#include "topaz/runtime/flutter_runner/logging.h"
namespace txt {
namespace {
constexpr char kDefaultFontFamily[] = "Roboto";
void UnmapMemory(const void* buffer, uint64_t size) {
static_assert(sizeof(void*) == sizeof(uint64_t), "pointers aren't 64-bit");
zx::vmar::root_self()->unmap(reinterpret_cast<uintptr_t>(buffer), size);
}
struct ReleaseSkDataContext {
uint64_t buffer_size;
int buffer_id;
fit::function<void()> release_proc;
ReleaseSkDataContext(uint64_t buffer_size, int buffer_id,
fit::function<void()> release_proc)
: buffer_size(buffer_size),
buffer_id(buffer_id),
release_proc(std::move(release_proc)) {}
};
void ReleaseSkData(const void* buffer, void* context) {
auto skdata_context = reinterpret_cast<ReleaseSkDataContext*>(context);
DEBUG_CHECK(skdata_context != nullptr, LOG_TAG, "");
UnmapMemory(buffer, skdata_context->buffer_size);
skdata_context->release_proc();
delete skdata_context;
}
sk_sp<SkData> MakeSkDataFromBuffer(const fuchsia::mem::Buffer& data,
int buffer_id,
fit::function<void()> release_proc) {
bool is_valid;
zx_status_t status = dart_utils::IsSizeValid(data, &is_valid);
if (!is_valid || data.size > std::numeric_limits<size_t>::max()) {
return nullptr;
}
uint64_t size = data.size;
uintptr_t buffer = 0;
status = zx::vmar::root_self()->map(0, data.vmo, 0, size, ZX_VM_PERM_READ,
&buffer);
if (status != ZX_OK)
return nullptr;
auto context =
new ReleaseSkDataContext(size, buffer_id, std::move(release_proc));
return SkData::MakeWithProc(reinterpret_cast<void*>(buffer), size,
ReleaseSkData, context);
}
fuchsia::fonts::Slant SkToFuchsiaSlant(SkFontStyle::Slant slant) {
switch (slant) {
case SkFontStyle::kOblique_Slant:
return fuchsia::fonts::Slant::OBLIQUE;
case SkFontStyle::kItalic_Slant:
return fuchsia::fonts::Slant::ITALIC;
case SkFontStyle::kUpright_Slant:
default:
return fuchsia::fonts::Slant::UPRIGHT;
}
}
SkFontStyle::Slant FuchsiaToSkSlant(fuchsia::fonts::Slant slant) {
switch (slant) {
case fuchsia::fonts::Slant::OBLIQUE:
return SkFontStyle::kOblique_Slant;
case fuchsia::fonts::Slant::ITALIC:
return SkFontStyle::kItalic_Slant;
case fuchsia::fonts::Slant::UPRIGHT:
default:
return SkFontStyle::kUpright_Slant;
}
}
fidl::VectorPtr<std::string> BuildLanguageList(const char* bcp47[],
int bcp47_count) {
DEBUG_CHECK(bcp47 != nullptr || bcp47_count == 0, LOG_TAG, "");
auto languages = fidl::VectorPtr<std::string>::New(0);
for (int i = 0; i < bcp47_count; i++) {
languages.push_back(bcp47[i]);
}
return languages;
}
sk_sp<SkTypeface> CreateTypefaceFromSkData(sk_sp<SkData> data, int font_index) {
return SkFontMgr::RefDefault()->makeFromData(std::move(data), font_index);
}
} // anonymous namespace
class FuchsiaFontManager::TypefaceCache {
public:
TypefaceCache() {}
~TypefaceCache();
// Get an SkTypeface with the given buffer id, font index, and buffer
// data. Creates a new SkTypeface if one does not already exist.
sk_sp<SkTypeface> GetOrCreateTypeface(
int buffer_id, int font_index, const fuchsia::mem::Buffer& buffer) const;
// Callback called when an SkData with the given buffer id is deleted.
void OnSkDataDeleted(int buffer_id) const;
private:
// Used to identify an SkTypeface in the cache.
struct TypefaceId {
int buffer_id;
int font_index;
// Needed by std::unordered_map.
bool operator==(const TypefaceId& other) const {
return (buffer_id == other.buffer_id && font_index == other.font_index);
}
// Used for debugging.
friend std::ostream& operator<<(std::ostream& os, const TypefaceId& id) {
return os << "TypfaceId: [buffer_id: " << id.buffer_id
<< ", font_index: " << id.font_index << "]";
}
};
// Needed by std::unordered_map.
struct TypefaceIdHash {
std::size_t operator()(const TypefaceId& id) const {
return std::hash<int>()(id.buffer_id) ^
(std::hash<int>()(id.font_index) << 1);
}
};
// Try to get an SkData with the given buffer id from the cache. If an
// SkData is not found, create it and add it to the cache.
sk_sp<SkData> GetOrCreateSkData(int buffer_id,
const fuchsia::mem::Buffer& buffer) const;
// Create a new SkTypeface for the given TypefaceId and SkData and add it to
// the cache.
sk_sp<SkTypeface> CreateSkTypeface(TypefaceId id, sk_sp<SkData> buffer) const;
mutable std::unordered_map<TypefaceId, SkTypeface*, TypefaceIdHash>
typeface_cache_;
mutable std::unordered_map<int, std::shared_ptr<BufferHolder>> buffer_cache_;
// Disallow copy and assignment.
TypefaceCache(const TypefaceCache&) = delete;
TypefaceCache& operator=(const TypefaceCache&) = delete;
};
class FuchsiaFontManager::BufferHolder {
public:
BufferHolder(const TypefaceCache* cache, int buffer_id) :
cache_(cache), buffer_id_(buffer_id) {}
~BufferHolder() {}
void SetData(SkData* data) { data_ = data; }
SkData* GetData() const { return data_; }
void OnDataDeleted() {
cache_->OnSkDataDeleted(buffer_id_);
}
private:
const TypefaceCache* cache_;
int buffer_id_;
SkData* data_;
// Disallow copy and assignment.
BufferHolder(const BufferHolder&) = delete;
BufferHolder& operator=(const BufferHolder&) = delete;
};
FuchsiaFontManager::TypefaceCache::~TypefaceCache() {
for (const auto& entry : typeface_cache_) {
entry.second->weak_unref();
}
}
void FuchsiaFontManager::TypefaceCache::OnSkDataDeleted(int buffer_id) const {
bool was_found = buffer_cache_.erase(buffer_id) != 0;
DEBUG_CHECK(was_found, LOG_TAG, "");
}
sk_sp<SkData> FuchsiaFontManager::TypefaceCache::GetOrCreateSkData(
int buffer_id, const fuchsia::mem::Buffer& buffer) const {
auto iter = buffer_cache_.find(buffer_id);
if (iter != buffer_cache_.end()) {
return sk_ref_sp(iter->second->GetData());
}
auto holder = std::make_shared<BufferHolder>(this, buffer_id);
std::weak_ptr<BufferHolder> weak_holder = holder;
auto data = MakeSkDataFromBuffer(buffer, buffer_id, [weak_holder]() {
if (auto holder = weak_holder.lock()) {
holder->OnDataDeleted();
}
});
if (!data) {
return nullptr;
}
holder->SetData(data.get());
buffer_cache_[buffer_id] = std::move(holder);
return data;
}
sk_sp<SkTypeface> FuchsiaFontManager::TypefaceCache::CreateSkTypeface(
TypefaceId id, sk_sp<SkData> buffer) const {
auto result = CreateTypefaceFromSkData(std::move(buffer), id.font_index);
result->weak_ref();
typeface_cache_[id] = result.get();
return result;
}
sk_sp<SkTypeface> FuchsiaFontManager::TypefaceCache::GetOrCreateTypeface(
int buffer_id, int font_index, const fuchsia::mem::Buffer& buffer) const {
auto id = TypefaceId{buffer_id, font_index};
auto iter = typeface_cache_.find(id);
if (iter != typeface_cache_.end()) {
if (iter->second->try_ref()) {
return sk_ref_sp(iter->second);
} else {
iter->second->weak_unref();
typeface_cache_.erase(iter);
}
}
sk_sp<SkData> data = GetOrCreateSkData(buffer_id, buffer);
if (!data) {
return nullptr;
}
return CreateSkTypeface(id, std::move(data));
}
class FuchsiaFontManager::FontStyleSet : public SkFontStyleSet {
public:
FontStyleSet(sk_sp<FuchsiaFontManager> font_manager, std::string family_name,
std::vector<SkFontStyle> styles)
: font_manager_(font_manager),
family_name_(family_name),
styles_(styles) {}
~FontStyleSet() override = default;
int count() override { return styles_.size(); }
void getStyle(int index, SkFontStyle* style, SkString* style_name) override {
DEBUG_CHECK(
index >= 0 && index < static_cast<int>(styles_.size()), LOG_TAG, "");
if (style)
*style = styles_[index];
// We don't have style names. Return an empty name.
if (style_name)
style_name->reset();
}
SkTypeface* createTypeface(int index) override {
DEBUG_CHECK(
index >= 0 && index < static_cast<int>(styles_.size()), LOG_TAG, "");
if (typefaces_.empty())
typefaces_.resize(styles_.size());
if (!typefaces_[index]) {
typefaces_[index] = font_manager_->FetchTypeface(
family_name_.c_str(), styles_[index], /*bcp47=*/nullptr,
/*bcp47_count=*/0, /*character=*/0,
fuchsia::fonts::REQUEST_FLAG_NO_FALLBACK |
fuchsia::fonts::REQUEST_FLAG_EXACT_MATCH);
}
return SkSafeRef(typefaces_[index].get());
}
SkTypeface* matchStyle(const SkFontStyle& pattern) override {
return matchStyleCSS3(pattern);
}
private:
sk_sp<FuchsiaFontManager> font_manager_;
std::string family_name_;
std::vector<SkFontStyle> styles_;
std::vector<sk_sp<SkTypeface>> typefaces_;
// Disallow copy and assignment.
FontStyleSet(const FontStyleSet&) = delete;
FontStyleSet& operator=(const FontStyleSet&) = delete;
};
FuchsiaFontManager::FuchsiaFontManager(fuchsia::fonts::ProviderSyncPtr provider)
: font_provider_(std::move(provider)),
typeface_cache_(new FuchsiaFontManager::TypefaceCache()) {}
FuchsiaFontManager::~FuchsiaFontManager() = default;
int FuchsiaFontManager::onCountFamilies() const {
DEBUG_CHECK(false, LOG_TAG, "");
return 0;
}
void FuchsiaFontManager::onGetFamilyName(int index,
SkString* familyName) const {
DEBUG_CHECK(false, LOG_TAG, "");
}
SkFontStyleSet* FuchsiaFontManager::onCreateStyleSet(int index) const {
DEBUG_CHECK(false, LOG_TAG, "");
return nullptr;
}
SkFontStyleSet* FuchsiaFontManager::onMatchFamily(
const char family_name[]) const {
fuchsia::fonts::FamilyInfoPtr family_info;
int err = font_provider_->GetFamilyInfo(family_name, &family_info);
if (err != ZX_OK) {
#ifndef NDEBUG
FX_LOGF(ERROR, LOG_TAG, "Error fetching family from provider [err=%d]. Did "
"you run Flutter in an environment that has a font manager?", err);
#endif
return nullptr;
}
if (!family_info)
return nullptr;
std::vector<SkFontStyle> styles;
for (auto& style : family_info->styles) {
styles.push_back(
SkFontStyle(style.weight, style.width, FuchsiaToSkSlant(style.slant)));
}
return new FontStyleSet(sk_ref_sp(this), family_info->name,
std::move(styles));
}
SkTypeface* FuchsiaFontManager::onMatchFamilyStyle(
const char familyName[], const SkFontStyle& style) const {
sk_sp<SkTypeface> typeface = FetchTypeface(familyName, style, nullptr, 0, 0);
return typeface.release();
}
SkTypeface* FuchsiaFontManager::onMatchFamilyStyleCharacter(
const char familyName[], const SkFontStyle& style, const char* bcp47[],
int bcp47_count, SkUnichar character) const {
sk_sp<SkTypeface> typeface =
FetchTypeface(kDefaultFontFamily, style, bcp47, bcp47_count, character);
return typeface.release();
}
SkTypeface* FuchsiaFontManager::onMatchFaceStyle(const SkTypeface*,
const SkFontStyle&) const {
DEBUG_CHECK(false, LOG_TAG, "");
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromData(sk_sp<SkData>,
int ttcIndex) const {
DEBUG_CHECK(false, LOG_TAG, "");
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromStreamIndex(
std::unique_ptr<SkStreamAsset>, int ttcIndex) const {
DEBUG_CHECK(false, LOG_TAG, "");
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromStreamArgs(
std::unique_ptr<SkStreamAsset>, const SkFontArguments&) const {
DEBUG_CHECK(false, LOG_TAG, "");
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromFile(const char path[],
int ttcIndex) const {
DEBUG_CHECK(false, LOG_TAG, "");
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onLegacyMakeTypeface(
const char familyName[], SkFontStyle) const {
DEBUG_CHECK(false, LOG_TAG, "");
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::FetchTypeface(
const char family_name[], const SkFontStyle& style, const char* bcp47[],
int bcp47_count, SkUnichar character, uint32_t flags) const {
TRACE_DURATION("flutter", "FuchsiaFontManager::FetchTypeface");
fuchsia::fonts::Request request;
request.family = family_name;
request.weight = style.weight();
request.width = style.width();
request.slant = SkToFuchsiaSlant(style.slant());
request.language = BuildLanguageList(bcp47, bcp47_count);
request.character = character;
request.flags = flags;
fuchsia::fonts::ResponsePtr response;
int err = font_provider_->GetFont(std::move(request), &response);
if (err != ZX_OK) {
#ifndef NDEBUG
FX_LOGF(ERROR, LOG_TAG, "Error fetching font from provider [err=%d]. Did "
"you run Flutter in an environment that has a font manager?", err);
#endif
return nullptr;
}
// The service may return null response if there is no font matching the
// request.
if (!response) {
return nullptr;
}
return typeface_cache_->GetOrCreateTypeface(
response->buffer_id, response->font_index, response->buffer);
}
} // namespace txt