blob: f1fd00a6131499df7b64db39fc4e4a1ba750163f [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* 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.
*/
#define LOG_TAG "Minikin"
#include "minikin/FontFamily.h"
#include <algorithm>
#include <vector>
#include <log/log.h>
#include "minikin/CmapCoverage.h"
#include "minikin/FamilyVariant.h"
#include "minikin/HbUtils.h"
#include "minikin/MinikinFont.h"
#include "FontUtils.h"
#include "Locale.h"
#include "LocaleListCache.h"
#include "MinikinInternal.h"
namespace minikin {
FontFamily::FontFamily(std::vector<std::shared_ptr<Font>>&& fonts)
: FontFamily(FamilyVariant::DEFAULT, std::move(fonts)) {}
FontFamily::FontFamily(FamilyVariant variant, std::vector<std::shared_ptr<Font>>&& fonts)
: FontFamily(kEmptyLocaleListId, variant, std::move(fonts), false /* isCustomFallback */) {}
FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant,
std::vector<std::shared_ptr<Font>>&& fonts, bool isCustomFallback)
: mLocaleListId(localeListId),
mVariant(variant),
mFonts(std::move(fonts)),
mIsColorEmoji(LocaleListCache::getById(localeListId).getEmojiStyle() ==
EmojiStyle::EMOJI),
mIsCustomFallback(isCustomFallback) {
MINIKIN_ASSERT(!mFonts.empty(), "FontFamily must contain at least one font.");
computeCoverage();
}
FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant,
std::vector<std::shared_ptr<Font>>&& fonts,
std::unordered_set<AxisTag>&& supportedAxes, bool isColorEmoji,
bool isCustomFallback, SparseBitSet&& coverage,
std::vector<std::unique_ptr<SparseBitSet>>&& cmapFmt14Coverage)
: mLocaleListId(localeListId),
mVariant(variant),
mFonts(std::move(fonts)),
mSupportedAxes(std::move(supportedAxes)),
mIsColorEmoji(isColorEmoji),
mIsCustomFallback(isCustomFallback),
mCoverage(std::move(coverage)),
mCmapFmt14Coverage(std::move(cmapFmt14Coverage)) {}
// Read fields other than mFonts, mLocaleList.
// static
std::shared_ptr<FontFamily> FontFamily::readFromInternal(BufferReader* reader,
std::vector<std::shared_ptr<Font>>&& fonts,
uint32_t localeListId) {
// FamilyVariant is uint8_t
static_assert(sizeof(FamilyVariant) == 1);
FamilyVariant variant = reader->read<FamilyVariant>();
// AxisTag is uint32_t
static_assert(sizeof(AxisTag) == 4);
const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
std::unordered_set<AxisTag> supportedAxes(axesPtr, axesPtr + axesCount);
bool isColorEmoji = static_cast<bool>(reader->read<uint8_t>());
bool isCustomFallback = static_cast<bool>(reader->read<uint8_t>());
SparseBitSet coverage(reader);
// Read mCmapFmt14Coverage. As it can have null entries, it is stored in the buffer as a sparse
// array (size, non-null entry count, array of (index, entry)).
uint32_t cmapFmt14CoverageSize = reader->read<uint32_t>();
std::vector<std::unique_ptr<SparseBitSet>> cmapFmt14Coverage(cmapFmt14CoverageSize);
uint32_t cmapFmt14CoverageEntryCount = reader->read<uint32_t>();
for (uint32_t i = 0; i < cmapFmt14CoverageEntryCount; i++) {
uint32_t index = reader->read<uint32_t>();
cmapFmt14Coverage[index] = std::make_unique<SparseBitSet>(reader);
}
return std::shared_ptr<FontFamily>(new FontFamily(
localeListId, variant, std::move(fonts), std::move(supportedAxes), isColorEmoji,
isCustomFallback, std::move(coverage), std::move(cmapFmt14Coverage)));
}
// static
uint32_t FontFamily::readLocaleListInternal(BufferReader* reader) {
return LocaleListCache::readFrom(reader);
}
// Write fields other than mFonts.
void FontFamily::writeToInternal(BufferWriter* writer) const {
writer->write<FamilyVariant>(mVariant);
std::vector<AxisTag> axes(mSupportedAxes.begin(), mSupportedAxes.end());
// Sort axes to be deterministic.
std::sort(axes.begin(), axes.end());
writer->writeArray<AxisTag>(axes.data(), axes.size());
writer->write<uint8_t>(mIsColorEmoji);
writer->write<uint8_t>(mIsCustomFallback);
mCoverage.writeTo(writer);
// Write mCmapFmt14Coverage as a sparse array (size, non-null entry count,
// array of (index, entry))
writer->write<uint32_t>(mCmapFmt14Coverage.size());
uint32_t cmapFmt14CoverageEntryCount = 0;
for (const std::unique_ptr<SparseBitSet>& coverage : mCmapFmt14Coverage) {
if (coverage != nullptr) cmapFmt14CoverageEntryCount++;
}
writer->write<uint32_t>(cmapFmt14CoverageEntryCount);
for (size_t i = 0; i < mCmapFmt14Coverage.size(); i++) {
if (mCmapFmt14Coverage[i] != nullptr) {
writer->write<uint32_t>(i);
mCmapFmt14Coverage[i]->writeTo(writer);
}
}
}
void FontFamily::writeLocaleListInternal(BufferWriter* writer) const {
LocaleListCache::writeTo(writer, mLocaleListId);
}
// Compute a matching metric between two styles - 0 is an exact match
static int computeMatch(FontStyle style1, FontStyle style2) {
if (style1 == style2) return 0;
int score = abs(style1.weight() / 100 - style2.weight() / 100);
if (style1.slant() != style2.slant()) {
score += 2;
}
return score;
}
static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
// If desired weight is semibold or darker, and 2 or more grades
// higher than actual (for example, medium 500 -> bold 700), then
// select fake bold.
bool isFakeBold = wanted.weight() >= 600 && (wanted.weight() - actual.weight()) >= 200;
bool isFakeItalic = wanted.slant() == FontStyle::Slant::ITALIC &&
actual.slant() == FontStyle::Slant::UPRIGHT;
return FontFakery(isFakeBold, isFakeItalic);
}
FakedFont FontFamily::getClosestMatch(FontStyle style) const {
int bestIndex = 0;
Font* bestFont = mFonts[bestIndex].get();
int bestMatch = computeMatch(bestFont->style(), style);
for (size_t i = 1; i < mFonts.size(); i++) {
Font* font = mFonts[i].get();
int match = computeMatch(font->style(), style);
if (i == 0 || match < bestMatch) {
bestFont = font;
bestIndex = i;
bestMatch = match;
}
}
return FakedFont{mFonts[bestIndex], computeFakery(style, bestFont->style())};
}
void FontFamily::computeCoverage() {
const std::shared_ptr<Font>& font = getClosestMatch(FontStyle()).font;
HbBlob cmapTable(font->baseFont(), MinikinFont::MakeTag('c', 'm', 'a', 'p'));
if (cmapTable.get() == nullptr) {
ALOGE("Could not get cmap table size!\n");
return;
}
mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage);
for (size_t i = 0; i < mFonts.size(); ++i) {
std::unordered_set<AxisTag> supportedAxes = mFonts[i]->getSupportedAxes();
mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
}
}
bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const {
if (variationSelector == 0) {
return mCoverage.get(codepoint);
}
if (mCmapFmt14Coverage.empty()) {
return false;
}
const uint16_t vsIndex = getVsIndex(variationSelector);
if (vsIndex >= mCmapFmt14Coverage.size()) {
// Even if vsIndex is INVALID_VS_INDEX, we reach here since INVALID_VS_INDEX is defined to
// be at the maximum end of the range.
return false;
}
const std::unique_ptr<SparseBitSet>& bitset = mCmapFmt14Coverage[vsIndex];
if (bitset.get() == nullptr) {
return false;
}
return bitset->get(codepoint);
}
std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
const std::vector<FontVariation>& variations) const {
if (variations.empty() || mSupportedAxes.empty()) {
return nullptr;
}
bool hasSupportedAxis = false;
for (const FontVariation& variation : variations) {
if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
hasSupportedAxis = true;
break;
}
}
if (!hasSupportedAxis) {
// None of variation axes are suppored by this family.
return nullptr;
}
std::vector<std::shared_ptr<Font>> fonts;
for (const auto& font : mFonts) {
bool supportedVariations = false;
std::unordered_set<AxisTag> supportedAxes = font->getSupportedAxes();
if (!supportedAxes.empty()) {
for (const FontVariation& variation : variations) {
if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) {
supportedVariations = true;
break;
}
}
}
std::shared_ptr<MinikinFont> minikinFont;
if (supportedVariations) {
minikinFont = font->typeface()->createFontWithVariation(variations);
}
if (minikinFont == nullptr) {
fonts.push_back(font);
} else {
fonts.push_back(Font::Builder(minikinFont).setStyle(font->style()).build());
}
}
return std::shared_ptr<FontFamily>(
new FontFamily(mLocaleListId, mVariant, std::move(fonts), mIsCustomFallback));
}
} // namespace minikin