xref: /aosp_15_r20/frameworks/minikin/libs/minikin/Font.cpp (revision 834a2baab5fdfc28e9a428ee87c7ea8f6a06a53d)
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 #include "minikin/Font.h"
18 
19 #include <hb-ot.h>
20 #include <hb.h>
21 #include <log/log.h>
22 
23 #include <vector>
24 
25 #include "FeatureFlags.h"
26 #include "FontUtils.h"
27 #include "LocaleListCache.h"
28 #include "MinikinInternal.h"
29 #include "minikin/Constants.h"
30 #include "minikin/Hasher.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 
hash_type(const VariationSettings & vars)59 inline android::hash_t hash_type(const VariationSettings& vars) {
60     return Hasher().update(vars).hash();
61 }
62 
build()63 std::shared_ptr<Font> Font::Builder::build() {
64     if (mIsWeightSet && mIsSlantSet) {
65         // No need to read OS/2 header of the font file.
66         return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
67                                               prepareFont(mTypeface), mLocaleListId));
68     }
69 
70     HbFontUniquePtr font = prepareFont(mTypeface);
71     FontStyle styleFromFont = analyzeStyle(font);
72     if (!mIsWeightSet) {
73         mWeight = styleFromFont.weight();
74     }
75     if (!mIsSlantSet) {
76         mSlant = styleFromFont.slant();
77     }
78     return std::shared_ptr<Font>(new Font(std::move(mTypeface), FontStyle(mWeight, mSlant),
79                                           std::move(font), mLocaleListId));
80 }
81 
Font(BufferReader * reader)82 Font::Font(BufferReader* reader)
83         : mExternalRefsHolder(nullptr),
84           mExternalRefsBuilder(nullptr),
85           mTypefaceMetadataReader(nullptr) {
86     mStyle = FontStyle(reader);
87     mLocaleListId = LocaleListCache::readFrom(reader);
88     const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
89     if (axesCount > 0) {
90         mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[axesCount]);
91         std::copy(axesPtr, axesPtr + axesCount, mSupportedAxes.get());
92         mSupportedAxesCount = axesCount;
93     } else {
94         mSupportedAxes = nullptr;
95         mSupportedAxesCount = 0;
96     }
97 
98     mTypefaceMetadataReader = *reader;
99     MinikinFontFactory::getInstance().skip(reader);
100 }
101 
Font(const std::shared_ptr<Font> & parent,const VariationSettings & axes)102 Font::Font(const std::shared_ptr<Font>& parent, const VariationSettings& axes)
103         : mExternalRefsHolder(nullptr), mTypefaceMetadataReader(nullptr) {
104     mStyle = parent->style();
105     mLocaleListId = parent->getLocaleListId();
106     mSupportedAxesCount = parent->mSupportedAxesCount;
107     if (mSupportedAxesCount != 0) {
108         uint16_t axesCount = parent->mSupportedAxesCount;
109         AxisTag* axesPtr = parent->mSupportedAxes.get();
110         mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[mSupportedAxesCount]);
111         std::copy(axesPtr, axesPtr + axesCount, mSupportedAxes.get());
112     }
113 
114     if (parent->typefaceMetadataReader().current() == nullptr) {
115         // The parent font is fully initialized. Just create new one.
116         std::shared_ptr<MinikinFont> typeface =
117                 parent->baseTypeface()->createFontWithVariation(axes);
118         HbFontUniquePtr hbFont = prepareFont(typeface);
119         mExternalRefsHolder.exchange(new ExternalRefs(std::move(typeface), std::move(hbFont)));
120     } else {
121         // If not fully initialized, set external ref builder for lazy creation.
122         mExternalRefsBuilder = [=]() {
123             std::shared_ptr<MinikinFont> typeface =
124                     parent->baseTypeface()->createFontWithVariation(axes);
125             HbFontUniquePtr hbFont = prepareFont(typeface);
126             return new ExternalRefs(std::move(typeface), std::move(hbFont));
127         };
128     }
129 }
130 
writeTo(BufferWriter * writer) const131 void Font::writeTo(BufferWriter* writer) const {
132     mStyle.writeTo(writer);
133     LocaleListCache::writeTo(writer, mLocaleListId);
134     writer->writeArray<AxisTag>(mSupportedAxes.get(), mSupportedAxesCount);
135     MinikinFontFactory::getInstance().write(writer, baseTypeface().get());
136 }
137 
Font(Font && o)138 Font::Font(Font&& o) noexcept
139         : mStyle(o.mStyle),
140           mLocaleListId(o.mLocaleListId),
141           mSupportedAxes(std::move(o.mSupportedAxes)),
142           mSupportedAxesCount(o.mSupportedAxesCount),
143           mTypefaceMetadataReader(o.mTypefaceMetadataReader) {
144     mExternalRefsHolder.store(o.mExternalRefsHolder.exchange(nullptr));
145 }
146 
operator =(Font && o)147 Font& Font::operator=(Font&& o) noexcept {
148     resetExternalRefs(o.mExternalRefsHolder.exchange(nullptr));
149     mStyle = o.mStyle;
150     mLocaleListId = o.mLocaleListId;
151     mTypefaceMetadataReader = o.mTypefaceMetadataReader;
152     mSupportedAxesCount = o.mSupportedAxesCount;
153     mSupportedAxes = std::move(o.mSupportedAxes);
154     return *this;
155 }
156 
isAxisSupported(uint32_t tag) const157 bool Font::isAxisSupported(uint32_t tag) const {
158     if (mSupportedAxesCount == 0) {
159         return false;
160     }
161     return std::binary_search(mSupportedAxes.get(), mSupportedAxes.get() + mSupportedAxesCount,
162                               tag);
163 }
164 
~Font()165 Font::~Font() {
166     resetExternalRefs(nullptr);
167 
168     FVarTable* fvarTable = mFVarTableHolder.exchange(nullptr);
169     if (fvarTable != nullptr) {
170         delete fvarTable;
171     }
172 }
173 
resetExternalRefs(ExternalRefs * refs)174 void Font::resetExternalRefs(ExternalRefs* refs) {
175     ExternalRefs* oldRefs = mExternalRefsHolder.exchange(refs);
176     if (oldRefs != nullptr) {
177         delete oldRefs;
178     }
179 }
180 
baseTypeface() const181 const std::shared_ptr<MinikinFont>& Font::baseTypeface() const {
182     return getExternalRefs()->mTypeface;
183 }
184 
baseFont() const185 const HbFontUniquePtr& Font::baseFont() const {
186     return getExternalRefs()->mBaseFont;
187 }
188 
getExternalRefs() const189 const Font::ExternalRefs* Font::getExternalRefs() const {
190     // Thread safety note: getExternalRefs() is thread-safe.
191     // getExternalRefs() returns the first ExternalRefs set to mExternalRefsHolder.
192     // When multiple threads called getExternalRefs() at the same time and
193     // mExternalRefsHolder is not set, multiple ExternalRefs may be created,
194     // but only one ExternalRefs will be set to mExternalRefsHolder and
195     // others will be deleted.
196     Font::ExternalRefs* externalRefs = mExternalRefsHolder.load();
197     if (externalRefs) return externalRefs;
198     // mExternalRefsHolder is null. Try creating an ExternalRefs.
199     Font::ExternalRefs* newExternalRefs;
200     if (mExternalRefsBuilder != nullptr) {
201         newExternalRefs = mExternalRefsBuilder();
202     } else {
203         std::shared_ptr<MinikinFont> typeface =
204                 MinikinFontFactory::getInstance().create(mTypefaceMetadataReader);
205         HbFontUniquePtr font = prepareFont(typeface);
206         newExternalRefs = new Font::ExternalRefs(std::move(typeface), std::move(font));
207     }
208     // Set the new ExternalRefs to mExternalRefsHolder if it is still null.
209     Font::ExternalRefs* expected = nullptr;
210     if (mExternalRefsHolder.compare_exchange_strong(expected, newExternalRefs)) {
211         return newExternalRefs;
212     } else {
213         // Another thread has already created and set an ExternalRefs.
214         // Delete ours and use theirs instead.
215         delete newExternalRefs;
216         // compare_exchange_strong writes the stored value into 'expected'
217         // when comparison fails.
218         return expected;
219     }
220 }
221 
getFVarTable() const222 const FVarTable& Font::getFVarTable() const {
223     FVarTable* fvarTable = mFVarTableHolder.load();
224     if (fvarTable) return *fvarTable;
225 
226     FVarTable* newFvar = new FVarTable();
227     HbBlob fvarBlob(baseFont(), TAG_fvar);
228     if (fvarBlob) {
229         readFVarTable(fvarBlob.get(), fvarBlob.size(), newFvar);
230     }
231     FVarTable* expected = nullptr;
232     if (mFVarTableHolder.compare_exchange_strong(expected, newFvar)) {
233         return *newFvar;
234     } else {
235         delete newFvar;
236         return *expected;
237     }
238 }
239 
240 // static
prepareFont(const std::shared_ptr<MinikinFont> & typeface)241 HbFontUniquePtr Font::prepareFont(const std::shared_ptr<MinikinFont>& typeface) {
242     const char* buf = reinterpret_cast<const char*>(typeface->GetFontData());
243     size_t size = typeface->GetFontSize();
244     uint32_t ttcIndex = typeface->GetFontIndex();
245 
246     HbBlobUniquePtr blob(hb_blob_create(buf, size, HB_MEMORY_MODE_READONLY, nullptr, nullptr));
247     HbFaceUniquePtr face(hb_face_create(blob.get(), ttcIndex));
248     HbFontUniquePtr parent(hb_font_create(face.get()));
249     hb_ot_font_set_funcs(parent.get());
250 
251     uint32_t upem = hb_face_get_upem(face.get());
252     hb_font_set_scale(parent.get(), upem, upem);
253 
254     HbFontUniquePtr font(hb_font_create_sub_font(parent.get()));
255     std::vector<hb_variation_t> variations;
256     variations.reserve(typeface->GetAxes().size());
257     for (const FontVariation& variation : typeface->GetAxes()) {
258         variations.push_back({variation.axisTag, variation.value});
259     }
260     hb_font_set_variations(font.get(), variations.data(), variations.size());
261     return font;
262 }
263 
264 // static
analyzeStyle(const HbFontUniquePtr & font)265 FontStyle Font::analyzeStyle(const HbFontUniquePtr& font) {
266     HbBlob os2Table(font, MakeTag('O', 'S', '/', '2'));
267     if (!os2Table) {
268         return FontStyle();
269     }
270 
271     int weight;
272     bool italic;
273     if (!::minikin::analyzeStyle(os2Table.get(), os2Table.size(), &weight, &italic)) {
274         return FontStyle();
275     }
276     // TODO: Update weight/italic based on fvar value.
277     return FontStyle(static_cast<uint16_t>(weight), static_cast<FontStyle::Slant>(italic));
278 }
279 
calculateSupportedAxes()280 void Font::calculateSupportedAxes() {
281     HbBlob fvarTable(baseFont(), MakeTag('f', 'v', 'a', 'r'));
282     if (!fvarTable) {
283         mSupportedAxesCount = 0;
284         mSupportedAxes = nullptr;
285         return;
286     }
287     std::unordered_set<AxisTag> supportedAxes;
288     analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
289     mSupportedAxesCount = supportedAxes.size();
290     mSupportedAxes = sortedArrayFromSet(supportedAxes);
291 }
292 
getAdjustedFont(int wght,int ital) const293 HbFontUniquePtr Font::getAdjustedFont(int wght, int ital) const {
294     return getExternalRefs()->getAdjustedFont(wght, ital);
295 }
296 
getAdjustedFont(int wght,int ital) const297 HbFontUniquePtr Font::ExternalRefs::getAdjustedFont(int wght, int ital) const {
298     if (wght == -1 && ital == -1) {
299         return HbFontUniquePtr(hb_font_reference(mBaseFont.get()));
300     }
301 
302     const uint16_t key = packKey(wght, ital);
303 
304     std::lock_guard<std::mutex> lock(mMutex);
305     auto it = mVarFontCache.find(key);
306     if (it != mVarFontCache.end()) {
307         return HbFontUniquePtr(hb_font_reference(it->second.get()));
308     }
309 
310     HbFontUniquePtr font(hb_font_create_sub_font(mBaseFont.get()));
311     std::vector<hb_variation_t> variations;
312     variations.reserve(mTypeface->GetAxes().size());
313     for (const FontVariation& variation : mTypeface->GetAxes()) {
314         if (wght != -1 && variation.axisTag == TAG_wght) {
315             continue;  // Add wght axis later
316         } else if (ital != -1 && variation.axisTag == TAG_ital) {
317             continue;  // Add ital axis later
318         } else {
319             variations.push_back({variation.axisTag, variation.value});
320         }
321     }
322     if (wght != -1) {
323         variations.push_back({TAG_wght, static_cast<float>(wght)});
324     }
325     if (ital != -1) {
326         variations.push_back({TAG_ital, static_cast<float>(ital)});
327     }
328     hb_font_set_variations(font.get(), variations.data(), variations.size());
329     mVarFontCache.emplace(key, HbFontUniquePtr(hb_font_reference(font.get())));
330     return font;
331 }
332 
getAdjustedTypeface(int wght,int ital) const333 std::shared_ptr<MinikinFont> Font::getAdjustedTypeface(int wght, int ital) const {
334     return getExternalRefs()->getAdjustedTypeface(wght, ital);
335 }
336 
getAdjustedTypeface(int wght,int ital) const337 const std::shared_ptr<MinikinFont>& Font::ExternalRefs::getAdjustedTypeface(int wght,
338                                                                             int ital) const {
339     if (wght == -1 && ital == -1) {
340         return mTypeface;
341     }
342 
343     const uint16_t key = packKey(wght, ital);
344 
345     std::lock_guard<std::mutex> lock(mMutex);
346 
347     std::map<uint16_t, std::shared_ptr<MinikinFont>>::iterator it = mVarTypefaceCache.find(key);
348     if (it != mVarTypefaceCache.end()) {
349         return it->second;
350     }
351 
352     std::vector<FontVariation> variations;
353     variations.reserve(mTypeface->GetAxes().size());
354     for (const FontVariation& variation : mTypeface->GetAxes()) {
355         if (wght != -1 && variation.axisTag == TAG_wght) {
356             continue;  // Add wght axis later
357         } else if (ital != -1 && variation.axisTag == TAG_ital) {
358             continue;  // Add ital axis later
359         } else {
360             variations.push_back({variation.axisTag, variation.value});
361         }
362     }
363     if (wght != -1) {
364         variations.push_back({TAG_wght, static_cast<float>(wght)});
365     }
366     if (ital != -1) {
367         variations.push_back({TAG_ital, static_cast<float>(ital)});
368     }
369     std::shared_ptr<MinikinFont> newTypeface = mTypeface->createFontWithVariation(variations);
370 
371     auto [result_iterator, _] =
372             mVarTypefaceCache.insert(std::make_pair(key, std::move(newTypeface)));
373 
374     return result_iterator->second;
375 }
376 
getAdjustedFont(const VariationSettings & axes) const377 HbFontUniquePtr Font::getAdjustedFont(const VariationSettings& axes) const {
378     return getExternalRefs()->getAdjustedFont(axes, getFVarTable());
379 }
380 
getAdjustedFont(const VariationSettings & axes,const FVarTable & table) const381 HbFontUniquePtr Font::ExternalRefs::getAdjustedFont(const VariationSettings& axes,
382                                                     const FVarTable& table) const {
383     if (axes.empty()) {
384         return HbFontUniquePtr(hb_font_reference(mBaseFont.get()));
385     }
386 
387     std::lock_guard<std::mutex> lock(mMutex);
388     HbFontUniquePtr* cached = mVarFontCache2.get(axes);
389     if (cached != nullptr) {
390         return HbFontUniquePtr(hb_font_reference(cached->get()));
391     }
392 
393     HbFontUniquePtr font(hb_font_create_sub_font(mBaseFont.get()));
394     std::vector<hb_variation_t> variations;
395     variations.reserve(axes.size());
396     for (const FontVariation& variation : axes) {
397         auto it = table.find(variation.axisTag);
398         if (it == table.end() || it->second.defValue == variation.value) {
399             continue;
400         }
401         variations.push_back({variation.axisTag, variation.value});
402     }
403     hb_font_set_variations(font.get(), variations.data(), variations.size());
404     mVarFontCache2.put(axes, new HbFontUniquePtr(hb_font_reference(font.get())));
405     return font;
406 }
407 
getAdjustedTypeface(const VariationSettings & axes) const408 std::shared_ptr<MinikinFont> Font::getAdjustedTypeface(const VariationSettings& axes) const {
409     return getExternalRefs()->getAdjustedTypeface(axes, getFVarTable());
410 }
411 
getAdjustedTypeface(const VariationSettings & axes,const FVarTable & table) const412 std::shared_ptr<MinikinFont> Font::ExternalRefs::getAdjustedTypeface(const VariationSettings& axes,
413                                                                      const FVarTable& table) const {
414     if (axes.empty()) {
415         return mTypeface;
416     }
417 
418     std::lock_guard<std::mutex> lock(mMutex);
419     const std::shared_ptr<MinikinFont>& cached = mVarTypefaceCache2.get(axes);
420     if (cached != nullptr) {
421         return cached;
422     }
423 
424     std::vector<FontVariation> variations;
425     variations.reserve(axes.size());
426     for (const FontVariation& variation : axes) {
427         auto it = table.find(variation.axisTag);
428         if (it == table.end() || it->second.defValue == variation.value) {
429             continue;
430         }
431         variations.push_back({variation.axisTag, variation.value});
432     }
433     std::shared_ptr<MinikinFont> newTypeface =
434             mTypeface->createFontWithVariation(VariationSettings(variations, false));
435     mVarTypefaceCache2.put(axes, newTypeface);
436     return mVarTypefaceCache2.get(axes);
437 }
438 
hbFont() const439 HbFontUniquePtr FakedFont::hbFont() const {
440     if (features::typeface_redesign_readonly()) {
441         return font->getAdjustedFont(fakery.variationSettings());
442     } else {
443         return font->getAdjustedFont(fakery.wghtAdjustment(), fakery.italAdjustment());
444     }
445 }
446 
typeface() const447 std::shared_ptr<MinikinFont> FakedFont::typeface() const {
448     if (features::typeface_redesign_readonly()) {
449         return font->getAdjustedTypeface(fakery.variationSettings());
450     } else {
451         return font->getAdjustedTypeface(fakery.wghtAdjustment(), fakery.italAdjustment());
452     }
453 }
454 
455 }  // namespace minikin
456