blob: 80fc69f5031f5b63911f9da19f3f4d72c4d027dc [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 <zx/vmar.h>
#include <unordered_map>
#include "lib/fsl/vmo/sized_vmo.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/weak_ptr.h"
#include "third_party/icu/source/common/unicode/uchar.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;
std::function<void()> release_proc;
ReleaseSkDataContext(uint64_t buffer_size, int buffer_id,
const std::function<void()>& release_proc)
: buffer_size(buffer_size),
buffer_id(buffer_id),
release_proc(release_proc) {}
};
void ReleaseSkData(const void* buffer, void* context) {
auto skdata_context = reinterpret_cast<ReleaseSkDataContext*>(context);
FXL_DCHECK(skdata_context);
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,
std::function<void()> release_proc) {
if (!fsl::SizedVmo::IsSizeValid(data.vmo, data.size) ||
data.size > std::numeric_limits<size_t>::max()) {
return nullptr;
}
uint64_t size = data.size;
uintptr_t buffer = 0;
zx_status_t 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, 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) {
FXL_DCHECK(bcp47 != nullptr || bcp47_count == 0);
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() : weak_factory_(this) {}
~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;
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);
}
};
// Callback called when an SkData with the given buffer id is deleted.
void OnSkDataDeleted(int buffer_id) const;
// 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, SkData*> buffer_cache_;
// Must be last.
mutable fxl::WeakPtrFactory<TypefaceCache> weak_factory_;
FXL_DISALLOW_COPY_AND_ASSIGN(TypefaceCache);
};
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;
FXL_DCHECK(was_found);
}
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);
}
auto weak_this = weak_factory_.GetWeakPtr();
auto data = MakeSkDataFromBuffer(buffer, buffer_id, [weak_this, buffer_id]() {
if (weak_this) {
weak_this->OnSkDataDeleted(buffer_id);
}
});
if (!data) {
return nullptr;
}
buffer_cache_[buffer_id] = data.get();
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 {
FXL_DCHECK(index >= 0 && index < static_cast<int>(styles_.size()));
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 {
FXL_DCHECK(index >= 0 && index < static_cast<int>(styles_.size()));
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_;
FXL_DISALLOW_COPY_AND_ASSIGN(FontStyleSet);
};
FuchsiaFontManager::FuchsiaFontManager(fuchsia::fonts::ProviderSyncPtr provider)
: font_provider_(std::move(provider)),
typeface_cache_(new FuchsiaFontManager::TypefaceCache()) {}
FuchsiaFontManager::~FuchsiaFontManager() = default;
int FuchsiaFontManager::onCountFamilies() const {
FXL_DCHECK(false);
return 0;
}
void FuchsiaFontManager::onGetFamilyName(int index,
SkString* familyName) const {
FXL_DCHECK(false);
}
SkFontStyleSet* FuchsiaFontManager::onCreateStyleSet(int index) const {
FXL_DCHECK(false);
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) {
FXL_DLOG(ERROR) << "Error fetching family from provider [err=" << err
<< "]. Did you run Flutter in an environment that"
<< " has a font manager? ";
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 {
FXL_DCHECK(false);
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromData(sk_sp<SkData>,
int ttcIndex) const {
FXL_DCHECK(false);
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromStreamIndex(
std::unique_ptr<SkStreamAsset>, int ttcIndex) const {
FXL_DCHECK(false);
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromStreamArgs(
std::unique_ptr<SkStreamAsset>, const SkFontArguments&) const {
FXL_DCHECK(false);
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromFile(const char path[],
int ttcIndex) const {
FXL_DCHECK(false);
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onLegacyMakeTypeface(
const char familyName[], SkFontStyle) const {
FXL_DCHECK(false);
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 {
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) {
FXL_DLOG(ERROR) << "Error fetching font from provider [err=" << err
<< "]. Did you run Flutter in an environment that"
<< " has a font manager? ";
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