1 /*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "Minikin"
18
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <log/log.h>
24 #include <utils/JenkinsHash.h>
25
26 #include <hb-ot.h>
27 #include <hb.h>
28
29 #include <minikin/CmapCoverage.h>
30 #include <minikin/FontFamily.h>
31 #include <minikin/MinikinFont.h>
32 #include "FontLanguage.h"
33 #include "FontLanguageListCache.h"
34 #include "FontUtils.h"
35 #include "HbFontCache.h"
36 #include "MinikinInternal.h"
37
38 using std::vector;
39
40 namespace minikin {
41
FontStyle(int variant,int weight,bool italic)42 FontStyle::FontStyle(int variant, int weight, bool italic)
43 : FontStyle(FontLanguageListCache::kEmptyListId, variant, weight, italic) {}
44
FontStyle(uint32_t languageListId,int variant,int weight,bool italic)45 FontStyle::FontStyle(uint32_t languageListId,
46 int variant,
47 int weight,
48 bool italic)
49 : bits(pack(variant, weight, italic)), mLanguageListId(languageListId) {}
50
hash() const51 android::hash_t FontStyle::hash() const {
52 uint32_t hash = android::JenkinsHashMix(0, bits);
53 hash = android::JenkinsHashMix(hash, mLanguageListId);
54 return android::JenkinsHashWhiten(hash);
55 }
56
57 // static
registerLanguageList(const std::string & languages)58 uint32_t FontStyle::registerLanguageList(const std::string& languages) {
59 std::scoped_lock _l(gMinikinLock);
60 return FontLanguageListCache::getId(languages);
61 }
62
63 // static
pack(int variant,int weight,bool italic)64 uint32_t FontStyle::pack(int variant, int weight, bool italic) {
65 return (weight & kWeightMask) | (italic ? kItalicMask : 0) |
66 (variant << kVariantShift);
67 }
68
Font(const std::shared_ptr<MinikinFont> & typeface,FontStyle style)69 Font::Font(const std::shared_ptr<MinikinFont>& typeface, FontStyle style)
70 : typeface(typeface), style(style) {}
71
Font(std::shared_ptr<MinikinFont> && typeface,FontStyle style)72 Font::Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style)
73 : typeface(typeface), style(style) {}
74
getSupportedAxesLocked() const75 std::unordered_set<AxisTag> Font::getSupportedAxesLocked() const {
76 const uint32_t fvarTag = MinikinFont::MakeTag('f', 'v', 'a', 'r');
77 HbBlob fvarTable(getFontTable(typeface.get(), fvarTag));
78 if (fvarTable.size() == 0) {
79 return std::unordered_set<AxisTag>();
80 }
81
82 std::unordered_set<AxisTag> supportedAxes;
83 analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
84 return supportedAxes;
85 }
86
Font(Font && o)87 Font::Font(Font&& o) {
88 typeface = std::move(o.typeface);
89 style = o.style;
90 o.typeface = nullptr;
91 }
92
Font(const Font & o)93 Font::Font(const Font& o) {
94 typeface = o.typeface;
95 style = o.style;
96 }
97
98 // static
FontFamily(std::vector<Font> && fonts)99 FontFamily::FontFamily(std::vector<Font>&& fonts)
100 : FontFamily(0 /* variant */, std::move(fonts)) {}
101
FontFamily(int variant,std::vector<Font> && fonts)102 FontFamily::FontFamily(int variant, std::vector<Font>&& fonts)
103 : FontFamily(FontLanguageListCache::kEmptyListId,
104 variant,
105 std::move(fonts)) {}
106
FontFamily(uint32_t langId,int variant,std::vector<Font> && fonts)107 FontFamily::FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts)
108 : mLangId(langId),
109 mVariant(variant),
110 mFonts(std::move(fonts)),
111 mHasVSTable(false) {
112 computeCoverage();
113 }
114
analyzeStyle(const std::shared_ptr<MinikinFont> & typeface,int * weight,bool * italic)115 bool FontFamily::analyzeStyle(const std::shared_ptr<MinikinFont>& typeface,
116 int* weight,
117 bool* italic) {
118 std::scoped_lock _l(gMinikinLock);
119 const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
120 HbBlob os2Table(getFontTable(typeface.get(), os2Tag));
121 if (os2Table.get() == nullptr)
122 return false;
123 return ::minikin::analyzeStyle(os2Table.get(), os2Table.size(), weight,
124 italic);
125 }
126
getHwFontFamilyType()127 int FontFamily::getHwFontFamilyType() {
128 return mHwFontFamilyType;
129 }
130
setHwFontFamilyType(int HwFontFamilyType)131 void FontFamily::setHwFontFamilyType(int HwFontFamilyType) {
132 mHwFontFamilyType = HwFontFamilyType;
133 }
134
135 // Compute a matching metric between two styles - 0 is an exact match
computeMatch(FontStyle style1,FontStyle style2)136 static int computeMatch(FontStyle style1, FontStyle style2) {
137 if (style1 == style2)
138 return 0;
139 int score = abs(style1.getWeight() - style2.getWeight());
140 if (style1.getItalic() != style2.getItalic()) {
141 score += 2;
142 }
143 return score;
144 }
145
computeFakery(FontStyle wanted,FontStyle actual)146 static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
147 // If desired weight is semibold or darker, and 2 or more grades
148 // higher than actual (for example, medium 500 -> bold 700), then
149 // select fake bold.
150 int wantedWeight = wanted.getWeight();
151 bool isFakeBold =
152 wantedWeight >= 6 && (wantedWeight - actual.getWeight()) >= 2;
153 bool isFakeItalic = wanted.getItalic() && !actual.getItalic();
154 return FontFakery(isFakeBold, isFakeItalic);
155 }
156
getClosestMatch(FontStyle style) const157 FakedFont FontFamily::getClosestMatch(FontStyle style) const {
158 const Font* bestFont = nullptr;
159 int bestMatch = 0;
160 for (size_t i = 0; i < mFonts.size(); i++) {
161 const Font& font = mFonts[i];
162 int match = computeMatch(font.style, style);
163 if (i == 0 || match < bestMatch) {
164 bestFont = &font;
165 bestMatch = match;
166 }
167 }
168 if (bestFont != nullptr) {
169 return FakedFont{bestFont->typeface.get(),
170 computeFakery(style, bestFont->style)};
171 }
172 return FakedFont{nullptr, FontFakery()};
173 }
174
isColorEmojiFamily() const175 bool FontFamily::isColorEmojiFamily() const {
176 const FontLanguages& languageList = FontLanguageListCache::getById(mLangId);
177 for (size_t i = 0; i < languageList.size(); ++i) {
178 if (languageList[i].getEmojiStyle() == FontLanguage::EMSTYLE_EMOJI) {
179 return true;
180 }
181 }
182 return false;
183 }
184
computeCoverage()185 void FontFamily::computeCoverage() {
186 std::scoped_lock _l(gMinikinLock);
187 const FontStyle defaultStyle;
188 const MinikinFont* typeface = getClosestMatch(defaultStyle).font;
189 const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
190 HbBlob cmapTable(getFontTable(typeface, cmapTag));
191 if (cmapTable.get() == nullptr) {
192 ALOGE("Could not get cmap table size!\n");
193 return;
194 }
195 mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(),
196 &mHasVSTable);
197
198 for (size_t i = 0; i < mFonts.size(); ++i) {
199 std::unordered_set<AxisTag> supportedAxes =
200 mFonts[i].getSupportedAxesLocked();
201 mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
202 }
203 }
204
hasGlyph(uint32_t codepoint,uint32_t variationSelector) const205 bool FontFamily::hasGlyph(uint32_t codepoint,
206 uint32_t variationSelector) const {
207 assertMinikinLocked();
208 if (variationSelector != 0 && !mHasVSTable) {
209 // Early exit if the variation selector is specified but the font doesn't
210 // have a cmap format 14 subtable.
211 return false;
212 }
213
214 const FontStyle defaultStyle;
215 hb_font_t* font = getHbFontLocked(getClosestMatch(defaultStyle).font);
216 uint32_t unusedGlyph;
217 bool result =
218 hb_font_get_glyph(font, codepoint, variationSelector, &unusedGlyph);
219 hb_font_destroy(font);
220 return result;
221 }
222
createFamilyWithVariation(const std::vector<FontVariation> & variations) const223 std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
224 const std::vector<FontVariation>& variations) const {
225 if (variations.empty() || mSupportedAxes.empty()) {
226 return nullptr;
227 }
228
229 bool hasSupportedAxis = false;
230 for (const FontVariation& variation : variations) {
231 if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
232 hasSupportedAxis = true;
233 break;
234 }
235 }
236 if (!hasSupportedAxis) {
237 // None of variation axes are suppored by this family.
238 return nullptr;
239 }
240
241 std::vector<Font> fonts;
242 for (const Font& font : mFonts) {
243 bool supportedVariations = false;
244 std::scoped_lock _l(gMinikinLock);
245 std::unordered_set<AxisTag> supportedAxes = font.getSupportedAxesLocked();
246 if (!supportedAxes.empty()) {
247 for (const FontVariation& variation : variations) {
248 if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) {
249 supportedVariations = true;
250 break;
251 }
252 }
253 }
254 std::shared_ptr<MinikinFont> minikinFont;
255 if (supportedVariations) {
256 minikinFont = font.typeface->createFontWithVariation(variations);
257 }
258 if (minikinFont == nullptr) {
259 minikinFont = font.typeface;
260 }
261 fonts.push_back(Font(std::move(minikinFont), font.style));
262 }
263
264 return std::shared_ptr<FontFamily>(
265 new FontFamily(mLangId, mVariant, std::move(fonts)));
266 }
267
268 } // namespace minikin
269