1 /*
2 * Copyright (C) 2021 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 "minikin/Font.h"
20
21 #include <hb-ot.h>
22 #include <hb.h>
23 #include <log/log.h>
24
25 #include <vector>
26
27 #include "FontUtils.h"
28 #include "LocaleListCache.h"
29 #include "MinikinInternal.h"
30 #include "minikin/Constants.h"
31 #include "minikin/HbUtils.h"
32 #include "minikin/MinikinFont.h"
33 #include "minikin/MinikinFontFactory.h"
34
35 namespace minikin {
36
37 namespace {
38
39 // |-------|-------|
40 // X : (1 bit) 1 if weight variation is available, otherwise 0.
41 // Y : (1 bit) 1 if italic variation is available, otherwise 0.
42 // I : (1 bit) 1 for italic, 0 for upright
43 // WWWWWWWWWW : (10 bits) unsigned 10 bits integer for weight value.
packKey(int wght,int ital)44 inline uint16_t packKey(int wght, int ital) {
45 uint16_t res = 0;
46 if (wght != -1) {
47 res |= 1u;
48 res |= static_cast<uint16_t>(wght) << 3;
49 }
50 if (ital != -1) {
51 res |= 1u << 1;
52 res |= (ital == 1) ? 1 << 2 : 0;
53 }
54 return res;
55 }
56
57 } // namespace
58
build()59 std::shared_ptr<Font> Font::Builder::build() {
60 if (mIsWeightSet && mIsSlantSet) {
61 // No need to read OS/2 header of the font file.
62 return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
63 prepareFont(mTypeface), mLocaleListId));
64 }
65
66 HbFontUniquePtr font = prepareFont(mTypeface);
67 FontStyle styleFromFont = analyzeStyle(font);
68 if (!mIsWeightSet) {
69 mWeight = styleFromFont.weight();
70 }
71 if (!mIsSlantSet) {
72 mSlant = styleFromFont.slant();
73 }
74 return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
75 std::move(font), mLocaleListId));
76 }
77
Font(BufferReader * reader)78 Font::Font(BufferReader* reader)
79 : mExternalRefsHolder(nullptr),
80 mExternalRefsBuilder(nullptr),
81 mTypefaceMetadataReader(nullptr) {
82 mStyle = FontStyle(reader);
83 mLocaleListId = LocaleListCache::readFrom(reader);
84 const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
85 if (axesCount > 0) {
86 mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[axesCount]);
87 std::copy(axesPtr, axesPtr + axesCount, mSupportedAxes.get());
88 mSupportedAxesCount = axesCount;
89 } else {
90 mSupportedAxes = nullptr;
91 mSupportedAxesCount = 0;
92 }
93
94 mTypefaceMetadataReader = *reader;
95 MinikinFontFactory::getInstance().skip(reader);
96 }
97
Font(const std::shared_ptr<Font> & parent,const std::vector<FontVariation> & axes)98 Font::Font(const std::shared_ptr<Font>& parent, const std::vector<FontVariation>& axes)
99 : mExternalRefsHolder(nullptr), mTypefaceMetadataReader(nullptr) {
100 mStyle = parent->style();
101 mLocaleListId = parent->getLocaleListId();
102 mSupportedAxesCount = parent->mSupportedAxesCount;
103 if (mSupportedAxesCount != 0) {
104 uint16_t axesCount = parent->mSupportedAxesCount;
105 AxisTag* axesPtr = parent->mSupportedAxes.get();
106 mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[mSupportedAxesCount]);
107 std::copy(axesPtr, axesPtr + axesCount, mSupportedAxes.get());
108 }
109
110 if (parent->typefaceMetadataReader().current() == nullptr) {
111 // The parent font is fully initialized. Just create new one.
112 std::shared_ptr<MinikinFont> typeface =
113 parent->baseTypeface()->createFontWithVariation(axes);
114 HbFontUniquePtr hbFont = prepareFont(typeface);
115 mExternalRefsHolder.exchange(new ExternalRefs(std::move(typeface), std::move(hbFont)));
116 } else {
117 // If not fully initialized, set external ref builder for lazy creation.
118 mExternalRefsBuilder = [=]() {
119 std::shared_ptr<MinikinFont> typeface =
120 parent->baseTypeface()->createFontWithVariation(axes);
121 HbFontUniquePtr hbFont = prepareFont(typeface);
122 return new ExternalRefs(std::move(typeface), std::move(hbFont));
123 };
124 }
125 }
126
writeTo(BufferWriter * writer) const127 void Font::writeTo(BufferWriter* writer) const {
128 mStyle.writeTo(writer);
129 LocaleListCache::writeTo(writer, mLocaleListId);
130 writer->writeArray<AxisTag>(mSupportedAxes.get(), mSupportedAxesCount);
131 MinikinFontFactory::getInstance().write(writer, baseTypeface().get());
132 }
133
Font(Font && o)134 Font::Font(Font&& o) noexcept
135 : mStyle(o.mStyle),
136 mLocaleListId(o.mLocaleListId),
137 mSupportedAxes(std::move(o.mSupportedAxes)),
138 mSupportedAxesCount(o.mSupportedAxesCount),
139 mTypefaceMetadataReader(o.mTypefaceMetadataReader) {
140 mExternalRefsHolder.store(o.mExternalRefsHolder.exchange(nullptr));
141 }
142
operator =(Font && o)143 Font& Font::operator=(Font&& o) noexcept {
144 resetExternalRefs(o.mExternalRefsHolder.exchange(nullptr));
145 mStyle = o.mStyle;
146 mLocaleListId = o.mLocaleListId;
147 mTypefaceMetadataReader = o.mTypefaceMetadataReader;
148 mSupportedAxesCount = o.mSupportedAxesCount;
149 mSupportedAxes = std::move(o.mSupportedAxes);
150 return *this;
151 }
152
isAxisSupported(uint32_t tag) const153 bool Font::isAxisSupported(uint32_t tag) const {
154 if (mSupportedAxesCount == 0) {
155 return false;
156 }
157 return std::binary_search(mSupportedAxes.get(), mSupportedAxes.get() + mSupportedAxesCount,
158 tag);
159 }
160
~Font()161 Font::~Font() {
162 resetExternalRefs(nullptr);
163 }
164
resetExternalRefs(ExternalRefs * refs)165 void Font::resetExternalRefs(ExternalRefs* refs) {
166 ExternalRefs* oldRefs = mExternalRefsHolder.exchange(refs);
167 if (oldRefs != nullptr) {
168 delete oldRefs;
169 }
170 }
171
baseTypeface() const172 const std::shared_ptr<MinikinFont>& Font::baseTypeface() const {
173 return getExternalRefs()->mTypeface;
174 }
175
baseFont() const176 const HbFontUniquePtr& Font::baseFont() const {
177 return getExternalRefs()->mBaseFont;
178 }
179
getExternalRefs() const180 const Font::ExternalRefs* Font::getExternalRefs() const {
181 // Thread safety note: getExternalRefs() is thread-safe.
182 // getExternalRefs() returns the first ExternalRefs set to mExternalRefsHolder.
183 // When multiple threads called getExternalRefs() at the same time and
184 // mExternalRefsHolder is not set, multiple ExternalRefs may be created,
185 // but only one ExternalRefs will be set to mExternalRefsHolder and
186 // others will be deleted.
187 Font::ExternalRefs* externalRefs = mExternalRefsHolder.load();
188 if (externalRefs) return externalRefs;
189 // mExternalRefsHolder is null. Try creating an ExternalRefs.
190 Font::ExternalRefs* newExternalRefs;
191 if (mExternalRefsBuilder != nullptr) {
192 newExternalRefs = mExternalRefsBuilder();
193 } else {
194 std::shared_ptr<MinikinFont> typeface =
195 MinikinFontFactory::getInstance().create(mTypefaceMetadataReader);
196 HbFontUniquePtr font = prepareFont(typeface);
197 newExternalRefs = new Font::ExternalRefs(std::move(typeface), std::move(font));
198 }
199 // Set the new ExternalRefs to mExternalRefsHolder if it is still null.
200 Font::ExternalRefs* expected = nullptr;
201 if (mExternalRefsHolder.compare_exchange_strong(expected, newExternalRefs)) {
202 return newExternalRefs;
203 } else {
204 // Another thread has already created and set an ExternalRefs.
205 // Delete ours and use theirs instead.
206 delete newExternalRefs;
207 // compare_exchange_strong writes the stored value into 'expected'
208 // when comparison fails.
209 return expected;
210 }
211 }
212
213 // static
prepareFont(const std::shared_ptr<MinikinFont> & typeface)214 HbFontUniquePtr Font::prepareFont(const std::shared_ptr<MinikinFont>& typeface) {
215 const char* buf = reinterpret_cast<const char*>(typeface->GetFontData());
216 size_t size = typeface->GetFontSize();
217 uint32_t ttcIndex = typeface->GetFontIndex();
218
219 HbBlobUniquePtr blob(hb_blob_create(buf, size, HB_MEMORY_MODE_READONLY, nullptr, nullptr));
220 HbFaceUniquePtr face(hb_face_create(blob.get(), ttcIndex));
221 HbFontUniquePtr parent(hb_font_create(face.get()));
222 hb_ot_font_set_funcs(parent.get());
223
224 uint32_t upem = hb_face_get_upem(face.get());
225 hb_font_set_scale(parent.get(), upem, upem);
226
227 HbFontUniquePtr font(hb_font_create_sub_font(parent.get()));
228 std::vector<hb_variation_t> variations;
229 variations.reserve(typeface->GetAxes().size());
230 for (const FontVariation& variation : typeface->GetAxes()) {
231 variations.push_back({variation.axisTag, variation.value});
232 }
233 hb_font_set_variations(font.get(), variations.data(), variations.size());
234 return font;
235 }
236
237 // static
analyzeStyle(const HbFontUniquePtr & font)238 FontStyle Font::analyzeStyle(const HbFontUniquePtr& font) {
239 HbBlob os2Table(font, MakeTag('O', 'S', '/', '2'));
240 if (!os2Table) {
241 return FontStyle();
242 }
243
244 int weight;
245 bool italic;
246 if (!::minikin::analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
247 return FontStyle();
248 }
249 // TODO: Update weight/italic based on fvar value.
250 return FontStyle(static_cast<uint16_t>(weight), static_cast<FontStyle::Slant>(italic));
251 }
252
calculateSupportedAxes()253 void Font::calculateSupportedAxes() {
254 HbBlob fvarTable(baseFont(), MakeTag('f', 'v', 'a', 'r'));
255 if (!fvarTable) {
256 mSupportedAxesCount = 0;
257 mSupportedAxes = nullptr;
258 return;
259 }
260 std::unordered_set<AxisTag> supportedAxes;
261 analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
262 mSupportedAxesCount = supportedAxes.size();
263 mSupportedAxes = sortedArrayFromSet(supportedAxes);
264 }
265
getAdjustedFont(int wght,int ital) const266 HbFontUniquePtr Font::getAdjustedFont(int wght, int ital) const {
267 return getExternalRefs()->getAdjustedFont(wght, ital);
268 }
269
getAdjustedFont(int wght,int ital) const270 HbFontUniquePtr Font::ExternalRefs::getAdjustedFont(int wght, int ital) const {
271 if (wght == -1 && ital == -1) {
272 return HbFontUniquePtr(hb_font_reference(mBaseFont.get()));
273 }
274
275 const uint16_t key = packKey(wght, ital);
276
277 std::lock_guard<std::mutex> lock(mMutex);
278 auto it = mVarFontCache.find(key);
279 if (it != mVarFontCache.end()) {
280 return HbFontUniquePtr(hb_font_reference(it->second.get()));
281 }
282
283 HbFontUniquePtr font(hb_font_create_sub_font(mBaseFont.get()));
284 std::vector<hb_variation_t> variations;
285 variations.reserve(mTypeface->GetAxes().size());
286 for (const FontVariation& variation : mTypeface->GetAxes()) {
287 if (wght != -1 && variation.axisTag == TAG_wght) {
288 continue; // Add wght axis later
289 } else if (ital != -1 && variation.axisTag == TAG_ital) {
290 continue; // Add ital axis later
291 } else {
292 variations.push_back({variation.axisTag, variation.value});
293 }
294 }
295 if (wght != -1) {
296 variations.push_back({TAG_wght, static_cast<float>(wght)});
297 }
298 if (ital != -1) {
299 variations.push_back({TAG_ital, static_cast<float>(ital)});
300 }
301 hb_font_set_variations(font.get(), variations.data(), variations.size());
302 mVarFontCache.emplace(key, HbFontUniquePtr(hb_font_reference(font.get())));
303 return font;
304 }
305
getAdjustedTypeface(int wght,int ital) const306 const std::shared_ptr<MinikinFont>& Font::getAdjustedTypeface(int wght, int ital) const {
307 return getExternalRefs()->getAdjustedTypeface(wght, ital);
308 }
309
getAdjustedTypeface(int wght,int ital) const310 const std::shared_ptr<MinikinFont>& Font::ExternalRefs::getAdjustedTypeface(int wght,
311 int ital) const {
312 if (wght == -1 && ital == -1) {
313 return mTypeface;
314 }
315
316 const uint16_t key = packKey(wght, ital);
317
318 std::lock_guard<std::mutex> lock(mMutex);
319
320 std::map<uint16_t, std::shared_ptr<MinikinFont>>::iterator it = mVarTypefaceCache.find(key);
321 if (it != mVarTypefaceCache.end()) {
322 return it->second;
323 }
324
325 std::vector<FontVariation> variations;
326 variations.reserve(mTypeface->GetAxes().size());
327 for (const FontVariation& variation : mTypeface->GetAxes()) {
328 if (wght != -1 && variation.axisTag == TAG_wght) {
329 continue; // Add wght axis later
330 } else if (ital != -1 && variation.axisTag == TAG_ital) {
331 continue; // Add ital axis later
332 } else {
333 variations.push_back({variation.axisTag, variation.value});
334 }
335 }
336 if (wght != -1) {
337 variations.push_back({TAG_wght, static_cast<float>(wght)});
338 }
339 if (ital != -1) {
340 variations.push_back({TAG_ital, static_cast<float>(ital)});
341 }
342 std::shared_ptr<MinikinFont> newTypeface = mTypeface->createFontWithVariation(variations);
343
344 auto [result_iterator, _] =
345 mVarTypefaceCache.insert(std::make_pair(key, std::move(newTypeface)));
346
347 return result_iterator->second;
348 }
349
350 } // namespace minikin
351