blob: a93cb4fdaf58fd170e241ca3e8ab73b0dda094ef [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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <log/log.h>
#include <utils/JenkinsHash.h>
#include <hb.h>
#include <hb-ot.h>
#include "FontLanguage.h"
#include "FontLanguageListCache.h"
#include "FontUtils.h"
#include "HbFontCache.h"
#include "MinikinInternal.h"
#include <minikin/CmapCoverage.h>
#include <minikin/MinikinFont.h>
#include <minikin/FontFamily.h>
#include <minikin/MinikinFont.h>
using std::vector;
namespace minikin {
FontStyle::FontStyle(int variant, int weight, bool italic)
: FontStyle(FontLanguageListCache::kEmptyListId, variant, weight, italic) {
}
FontStyle::FontStyle(uint32_t languageListId, int variant, int weight, bool italic)
: bits(pack(variant, weight, italic)), mLanguageListId(languageListId) {
}
android::hash_t FontStyle::hash() const {
uint32_t hash = android::JenkinsHashMix(0, bits);
hash = android::JenkinsHashMix(hash, mLanguageListId);
return android::JenkinsHashWhiten(hash);
}
// static
uint32_t FontStyle::registerLanguageList(const std::string& languages) {
android::AutoMutex _l(gMinikinLock);
return FontLanguageListCache::getId(languages);
}
// static
uint32_t FontStyle::pack(int variant, int weight, bool italic) {
return (weight & kWeightMask) | (italic ? kItalicMask : 0) | (variant << kVariantShift);
}
Font::Font(const std::shared_ptr<MinikinFont>& typeface, FontStyle style)
: typeface(typeface), style(style) {
}
Font::Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style)
: typeface(typeface), style(style) {
}
std::unordered_set<AxisTag> Font::getSupportedAxesLocked() const {
const uint32_t fvarTag = MinikinFont::MakeTag('f', 'v', 'a', 'r');
HbBlob fvarTable(getFontTable(typeface.get(), fvarTag));
if (fvarTable.size() == 0) {
return std::unordered_set<AxisTag>();
}
std::unordered_set<AxisTag> supportedAxes;
analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
return supportedAxes;
}
Font::Font(Font&& o) {
typeface = std::move(o.typeface);
style = o.style;
o.typeface = nullptr;
}
Font::Font(const Font& o) {
typeface = o.typeface;
style = o.style;
}
// static
FontFamily::FontFamily(std::vector<Font>&& fonts) : FontFamily(0 /* variant */, std::move(fonts)) {
}
FontFamily::FontFamily(int variant, std::vector<Font>&& fonts)
: FontFamily(FontLanguageListCache::kEmptyListId, variant, std::move(fonts)) {
}
FontFamily::FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts)
: mLangId(langId), mVariant(variant), mFonts(std::move(fonts)) {
computeCoverage();
}
bool FontFamily::analyzeStyle(const std::shared_ptr<MinikinFont>& typeface, int* weight,
bool* italic) {
android::AutoMutex _l(gMinikinLock);
const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
HbBlob os2Table(getFontTable(typeface.get(), os2Tag));
if (os2Table.get() == nullptr) return false;
return ::minikin::analyzeStyle(os2Table.get(), os2Table.size(), weight, italic);
}
// 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.getWeight() - style2.getWeight());
if (style1.getItalic() != style2.getItalic()) {
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.
int wantedWeight = wanted.getWeight();
bool isFakeBold = wantedWeight >= 6 && (wantedWeight - actual.getWeight()) >= 2;
bool isFakeItalic = wanted.getItalic() && !actual.getItalic();
return FontFakery(isFakeBold, isFakeItalic);
}
FakedFont FontFamily::getClosestMatch(FontStyle style) const {
const Font* bestFont = nullptr;
int bestMatch = 0;
for (size_t i = 0; i < mFonts.size(); i++) {
const Font& font = mFonts[i];
int match = computeMatch(font.style, style);
if (i == 0 || match < bestMatch) {
bestFont = &font;
bestMatch = match;
}
}
if (bestFont != nullptr) {
return FakedFont{ bestFont->typeface.get(), computeFakery(style, bestFont->style) };
}
return FakedFont{ nullptr, FontFakery() };
}
bool FontFamily::isColorEmojiFamily() const {
const FontLanguages& languageList = FontLanguageListCache::getById(mLangId);
for (size_t i = 0; i < languageList.size(); ++i) {
if (languageList[i].getEmojiStyle() == FontLanguage::EMSTYLE_EMOJI) {
return true;
}
}
return false;
}
void FontFamily::computeCoverage() {
android::AutoMutex _l(gMinikinLock);
const FontStyle defaultStyle;
const MinikinFont* typeface = getClosestMatch(defaultStyle).font;
const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
HbBlob cmapTable(getFontTable(typeface, cmapTag));
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].getSupportedAxesLocked();
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<Font> fonts;
for (const Font& font : mFonts) {
bool supportedVariations = false;
android::AutoMutex _l(gMinikinLock);
std::unordered_set<AxisTag> supportedAxes = font.getSupportedAxesLocked();
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) {
minikinFont = font.typeface;
}
fonts.push_back(Font(std::move(minikinFont), font.style));
}
return std::shared_ptr<FontFamily>(new FontFamily(mLangId, mVariant, std::move(fonts)));
}
} // namespace minikin