LCOV - code coverage report
Current view: top level - xenolith/font - XLFontController.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 239 412 58.0 %
Date: 2024-05-12 00:16:13 Functions: 30 48 62.5 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       3             : 
       4             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  of this software and associated documentation files (the "Software"), to deal
       6             :  in the Software without restriction, including without limitation the rights
       7             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  copies of the Software, and to permit persons to whom the Software is
       9             :  furnished to do so, subject to the following conditions:
      10             : 
      11             :  The above copyright notice and this permission notice shall be included in
      12             :  all copies or substantial portions of the Software.
      13             : 
      14             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             :  THE SOFTWARE.
      21             :  **/
      22             : 
      23             : #include "XLFontController.h"
      24             : 
      25             : #include "XLTemporaryResource.h"
      26             : #include "XLTexture.h"
      27             : #include "XLFontExtension.h"
      28             : 
      29             : namespace STAPPLER_VERSIONIZED stappler::xenolith::font {
      30             : 
      31             : XL_DECLARE_EVENT_CLASS(FontController, onLoaded)
      32             : XL_DECLARE_EVENT_CLASS(FontController, onFontSourceUpdated)
      33             : 
      34             : struct FontController::Builder::Data {
      35             :         String name;
      36             :         Rc<FontController> target;
      37             :         Map<String, FontSource> dataQueries;
      38             :         Map<String, FamilyQuery> familyQueries;
      39             :         Map<String, String> aliases;
      40             : };
      41             : 
      42          48 : FontController::Builder::~Builder() {
      43          48 :         if (_data) {
      44          24 :                 delete _data;
      45          24 :                 _data = nullptr;
      46             :         }
      47          48 : }
      48             : 
      49          24 : FontController::Builder::Builder(StringView name) {
      50          24 :         _data = new Data();
      51          24 :         _data->name = name.str<Interface>();
      52          24 : }
      53             : 
      54           0 : FontController::Builder::Builder(FontController *target) {
      55           0 :         _data = new Data();
      56           0 :         _data->target = target;
      57           0 : }
      58             : 
      59          24 : FontController::Builder::Builder(Builder &&other) {
      60          24 :         _data = other._data;
      61          24 :         other._data = nullptr;
      62          24 : }
      63             : 
      64           0 : FontController::Builder &FontController::Builder::operator=(Builder &&other) {
      65           0 :         if (_data) {
      66           0 :                 delete _data;
      67           0 :                 _data = nullptr;
      68             :         }
      69             : 
      70           0 :         _data = other._data;
      71           0 :         other._data = nullptr;
      72           0 :         return *this;
      73             : }
      74             : 
      75          24 : StringView FontController::Builder::getName() const {
      76          24 :         return _data->name;
      77             : }
      78          72 : FontController *FontController::Builder::getTarget() const {
      79          72 :         return _data->target ? _data->target.get() : nullptr;
      80             : }
      81             : 
      82          24 : const FontController::FontSource * FontController::Builder::addFontSource(StringView name, BytesView data) {
      83          24 :         auto it = _data->dataQueries.find(name);
      84          24 :         if (it == _data->dataQueries.end()) {
      85          24 :                 it = _data->dataQueries.emplace(name.str<Interface>(), FontSource()).first;
      86          24 :                 it->second.fontExternalData = data;
      87          24 :                 it->second.preconfiguredParams = false;
      88          24 :                 return &it->second;
      89             :         }
      90             : 
      91           0 :         log::warn("FontController", "Duplicate font source: ", name);
      92           0 :         return nullptr;
      93             : }
      94             : 
      95           0 : const FontController::FontSource * FontController::Builder::addFontSource(StringView name, Bytes && data) {
      96           0 :         auto it = _data->dataQueries.find(name);
      97           0 :         if (it == _data->dataQueries.end()) {
      98           0 :                 it = _data->dataQueries.emplace(name.str<Interface>(), FontSource()).first;
      99           0 :                 it->second.fontMemoryData = move(data);
     100           0 :                 it->second.preconfiguredParams = false;
     101           0 :                 return &it->second;
     102             :         }
     103             : 
     104           0 :         log::warn("FontController", "Duplicate font source: ", name);
     105           0 :         return nullptr;
     106             : }
     107             : 
     108           0 : const FontController::FontSource * FontController::Builder::addFontSource(StringView name, FilePath data) {
     109           0 :         auto it = _data->dataQueries.find(name);
     110           0 :         if (it == _data->dataQueries.end()) {
     111           0 :                 it = _data->dataQueries.emplace(name.str<Interface>(), FontSource()).first;
     112           0 :                 it->second.fontFilePath = data.get().str<Interface>();
     113           0 :                 it->second.preconfiguredParams = false;
     114           0 :                 return &it->second;
     115             :         }
     116             : 
     117           0 :         log::warn("FontController", "Duplicate font source: ", name);
     118           0 :         return nullptr;
     119             : }
     120             : 
     121           0 : const FontController::FontSource * FontController::Builder::addFontSource(StringView name, Function<Bytes()> &&cb) {
     122           0 :         auto it = _data->dataQueries.find(name);
     123           0 :         if (it == _data->dataQueries.end()) {
     124           0 :                 it = _data->dataQueries.emplace(name.str<Interface>(), FontSource()).first;
     125           0 :                 it->second.fontCallback = move(cb);
     126           0 :                 it->second.preconfiguredParams = false;
     127           0 :                 return &it->second;
     128             :         }
     129             : 
     130           0 :         log::warn("FontController", "Duplicate font source: ", name);
     131           0 :         return nullptr;
     132             : }
     133             : 
     134           0 : const FontController::FontSource * FontController::Builder::addFontSource(StringView name, BytesView data, FontLayoutParameters params) {
     135           0 :         auto it = _data->dataQueries.find(name);
     136           0 :         if (it == _data->dataQueries.end()) {
     137           0 :                 it = _data->dataQueries.emplace(name.str<Interface>(), FontSource()).first;
     138           0 :                 it->second.fontExternalData = data;
     139           0 :                 it->second.params = params;
     140           0 :                 it->second.preconfiguredParams = true;
     141           0 :                 return &it->second;
     142             :         }
     143             : 
     144           0 :         log::warn("FontController", "Duplicate font source: ", name);
     145           0 :         return nullptr;
     146             : }
     147             : 
     148           0 : const FontController::FontSource * FontController::Builder::addFontSource(StringView name, Bytes && data, FontLayoutParameters params) {
     149           0 :         auto it = _data->dataQueries.find(name);
     150           0 :         if (it == _data->dataQueries.end()) {
     151           0 :                 it = _data->dataQueries.emplace(name.str<Interface>(), FontSource()).first;
     152           0 :                 it->second.fontMemoryData = move(data);
     153           0 :                 it->second.params = params;
     154           0 :                 it->second.preconfiguredParams = true;
     155           0 :                 return &it->second;
     156             :         }
     157             : 
     158           0 :         log::warn("FontController", "Duplicate font source: ", name);
     159           0 :         return nullptr;
     160             : }
     161             : 
     162           0 : const FontController::FontSource * FontController::Builder::addFontSource(StringView name, FilePath data, FontLayoutParameters params) {
     163           0 :         auto it = _data->dataQueries.find(name);
     164           0 :         if (it == _data->dataQueries.end()) {
     165           0 :                 it = _data->dataQueries.emplace(name.str<Interface>(), FontSource()).first;
     166           0 :                 it->second.fontFilePath = data.get().str<Interface>();
     167           0 :                 it->second.params = params;
     168           0 :                 it->second.preconfiguredParams = true;
     169           0 :                 return &it->second;
     170             :         }
     171             : 
     172           0 :         log::warn("FontController", "Duplicate font source: ", name);
     173           0 :         return nullptr;
     174             : }
     175             : 
     176          72 : const FontController::FontSource * FontController::Builder::addFontSource(StringView name, Function<Bytes()> &&cb, FontLayoutParameters params) {
     177          72 :         auto it = _data->dataQueries.find(name);
     178          72 :         if (it == _data->dataQueries.end()) {
     179          72 :                 it = _data->dataQueries.emplace(name.str<Interface>(), FontSource()).first;
     180          72 :                 it->second.fontCallback = move(cb);
     181          72 :                 it->second.params = params;
     182          72 :                 it->second.preconfiguredParams = true;
     183          72 :                 return &it->second;
     184             :         }
     185             : 
     186           0 :         log::warn("FontController", "Duplicate font source: ", name);
     187           0 :         return nullptr;
     188             : }
     189             : 
     190           0 : const FontController::FontSource *FontController::Builder::getFontSource(StringView name) const {
     191           0 :         auto it = _data->dataQueries.find(name);
     192           0 :         if (it != _data->dataQueries.end()) {
     193           0 :                 return &it->second;
     194             :         }
     195           0 :         return nullptr;
     196             : }
     197             : 
     198          96 : const FontController::FamilyQuery * FontController::Builder::addFontFaceQuery(StringView family, const FontSource *source, bool front) {
     199          96 :         XL_ASSERT(source, "Source should not be nullptr");
     200             : 
     201          96 :         auto it = _data->familyQueries.find(family);
     202          96 :         if (it == _data->familyQueries.end()) {
     203          48 :                 it = _data->familyQueries.emplace(family.str<Interface>(), FamilyQuery{family.str<Interface>()}).first;
     204             :         }
     205             : 
     206          96 :         addSources(&it->second, Vector<const FontSource *>{source}, front);
     207          96 :         return &it->second;
     208             : }
     209             : 
     210           0 : const FontController::FamilyQuery * FontController::Builder::addFontFaceQuery(StringView family, Vector<const FontSource *> &&sources, bool front) {
     211           0 :         auto it = _data->familyQueries.find(family);
     212           0 :         if (it == _data->familyQueries.end()) {
     213           0 :                 it = _data->familyQueries.emplace(family.str<Interface>(), FamilyQuery{family.str<Interface>()}).first;
     214             :         }
     215             : 
     216           0 :         addSources(&it->second, move(sources), front);
     217           0 :         return &it->second;
     218             : }
     219             : 
     220          48 : bool FontController::Builder::addAlias(StringView newAlias, StringView familyName) {
     221          48 :         auto iit = _data->aliases.find(familyName);
     222          48 :         if (iit != _data->aliases.end()) {
     223           0 :                 _data->aliases.insert_or_assign(newAlias.str<Interface>(), iit->second);
     224           0 :                 return true;
     225             :         } else {
     226             :                 // check if family defined
     227          96 :                 for (auto &it : _data->familyQueries) {
     228          96 :                         if (it.second.family == familyName) {
     229          48 :                                 _data->aliases.insert_or_assign(newAlias.str<Interface>(), it.second.family);
     230          48 :                                 return true;
     231             :                         }
     232             :                 }
     233           0 :                 return false;
     234             :         }
     235             : }
     236             : 
     237           0 : Vector<const FontController::FamilyQuery *> FontController::Builder::getFontFamily(StringView family) const {
     238           0 :         Vector<const FontController::FamilyQuery *> families;
     239           0 :         for (auto &it : _data->familyQueries) {
     240           0 :                 if (it.second.family == family) {
     241           0 :                         families.emplace_back(&it.second);
     242             :                 }
     243             :         }
     244           0 :         return families;
     245           0 : }
     246             : 
     247          48 : Map<String, FontController::FontSource> &FontController::Builder::getDataQueries() {
     248          48 :         return _data->dataQueries;
     249             : }
     250             : 
     251          24 : Map<String, FontController::FamilyQuery> &FontController::Builder::getFamilyQueries() {
     252          24 :         return _data->familyQueries;
     253             : }
     254             : 
     255          24 : Map<String, String> &FontController::Builder::getAliases() {
     256          24 :         return _data->aliases;
     257             : }
     258             : 
     259          96 : void FontController::Builder::addSources(FamilyQuery *query, Vector<const FontSource *> &&sources, bool front) {
     260          96 :         if (query->sources.empty() || !front) {
     261          96 :                 query->sources.reserve(query->sources.size() + sources.size());
     262         192 :                 for (auto &iit : sources) {
     263          96 :                         XL_ASSERT(iit, "Source should not be nullptr");
     264          96 :                         if (std::find(query->sources.begin(), query->sources.end(), iit) == query->sources.end()) {
     265          96 :                                 query->sources.emplace_back(iit);
     266             :                         }
     267             :                 }
     268             :         } else {
     269           0 :                 auto iit = query->sources.begin();
     270           0 :                 while (iit != query->sources.end()) {
     271           0 :                         if (std::find(sources.begin(), sources.end(), *iit) != sources.end()) {
     272           0 :                                 iit = query->sources.erase(iit);
     273             :                         } else {
     274           0 :                                 ++ iit;
     275             :                         }
     276             :                 }
     277             : 
     278           0 :                 query->sources.reserve(query->sources.size() + sources.size());
     279             : 
     280           0 :                 auto insertIt = query->sources.begin();
     281           0 :                 for (auto &source : sources) {
     282           0 :                         XL_ASSERT(source, "Source should not be nullptr");
     283           0 :                         if (std::find(query->sources.begin(), query->sources.end(), source) == query->sources.end()) {
     284           0 :                                 query->sources.emplace(insertIt, source);
     285             :                         }
     286             :                 }
     287             :         }
     288          96 :         query->addInFront = front;
     289          96 : }
     290             : 
     291          48 : FontController::~FontController() {
     292          24 :         invalidate(nullptr);
     293          48 : }
     294             : 
     295          24 : bool FontController::init(const Rc<FontExtension> &ext) {
     296          24 :         _ext = ext;
     297          24 :         return true;
     298             : }
     299             : 
     300           0 : void FontController::extend(const Callback<bool(FontController::Builder &)> &cb) {
     301           0 :         Builder builder(this);
     302           0 :         if (cb(builder)) {
     303           0 :                 _ext->acquireController(move(builder));
     304             :         }
     305           0 : }
     306             : 
     307           0 : void FontController::initialize(Application *) {
     308             : 
     309           0 : }
     310             : 
     311          48 : void FontController::invalidate(Application *) {
     312          48 :         if (_image) {
     313             :                 // image need to be finalized to remove cycled refs
     314          24 :                 _image->finalize();
     315          24 :                 _image = nullptr;
     316             :         }
     317          48 : }
     318             : 
     319           0 : void FontController::addFont(StringView family, Rc<FontFaceData> &&data, bool front) {
     320           0 :         std::unique_lock lock(_layoutSharedMutex);
     321           0 :         auto familyIt = _families.find(family);
     322           0 :         if (familyIt == _families.end()) {
     323           0 :                 familyIt = _families.emplace(family.str<Interface>(), FamilySpec()).first;
     324           0 :                 _familiesNames.emplace_back(familyIt->first);
     325             :         }
     326             : 
     327           0 :         if (!familyIt->second.data.empty() && front) {
     328           0 :                 familyIt->second.data.emplace(familyIt->second.data.begin(), move(data));
     329             :         } else {
     330           0 :                 familyIt->second.data.emplace_back(move(data));
     331             :         }
     332             : 
     333           0 :         _dirty = true;
     334           0 :         lock.unlock();
     335           0 : }
     336             : 
     337          48 : void FontController::addFont(StringView family, Vector<Rc<FontFaceData>> &&data, bool front) {
     338          48 :         std::unique_lock lock(_layoutSharedMutex);
     339          48 :         auto familyIt = _families.find(family);
     340          48 :         if (familyIt == _families.end()) {
     341          48 :                 familyIt = _families.emplace(family.str<Interface>(), FamilySpec()).first;
     342          48 :                 _familiesNames.emplace_back(familyIt->first);
     343             :         }
     344             : 
     345          48 :         if (familyIt->second.data.empty()) {
     346          48 :                 familyIt->second.data = move(data);
     347             :         } else {
     348           0 :                 if (front) {
     349           0 :                         for (auto &it : data) {
     350           0 :                                 familyIt->second.data.emplace(familyIt->second.data.begin(), move(it));
     351             :                         }
     352             :                 } else {
     353           0 :                         for (auto &it : data) {
     354           0 :                                 familyIt->second.data.emplace_back(move(it));
     355             :                         }
     356             :                 }
     357             :         }
     358             : 
     359          48 :         _dirty = true;
     360          48 :         lock.unlock();
     361          48 : }
     362             : 
     363           0 : bool FontController::addAlias(StringView newAlias, StringView familyName) {
     364           0 :         std::unique_lock lock(_layoutSharedMutex);
     365           0 :         if (_aliases.find(newAlias) != _aliases.end()) {
     366           0 :                 return false;
     367             :         }
     368             : 
     369           0 :         auto iit = _aliases.find(familyName);
     370           0 :         if (iit != _aliases.end()) {
     371           0 :                 _aliases.emplace(newAlias.str<Interface>(), iit->second);
     372           0 :                 return true;
     373             :         } else {
     374           0 :                 auto f_it = _families.find(familyName);
     375           0 :                 if (f_it != _families.end()) {
     376           0 :                         _aliases.emplace(newAlias.str<Interface>(), familyName.str<Interface>());
     377           0 :                         return true;
     378             :                 }
     379           0 :                 return false;
     380             :         }
     381           0 : }
     382             : 
     383       10559 : Rc<FontFaceSet> FontController::getLayout(FontParameters style) {
     384       10559 :         Rc<FontFaceSet> ret;
     385             : 
     386       10559 :         FontFaceSet *face = nullptr;
     387             : 
     388       10559 :         style.fontSize = style.fontSize * style.density;
     389             : 
     390             :         // check if layout already loaded
     391       10559 :         std::shared_lock sharedLock(_layoutSharedMutex);
     392       10559 :         if (!_loaded) {
     393          24 :                 return nullptr;
     394             :         }
     395             : 
     396       10535 :         if (style.fontFamily.empty()) {
     397           0 :                 style.fontFamily = StringView(_defaultFontFamily);
     398             :         }
     399             : 
     400       10535 :         auto a_it = _aliases.find(style.fontFamily);
     401       10535 :         if (a_it != _aliases.end()) {
     402        3244 :                 style.fontFamily = a_it->second;
     403             :         }
     404             : 
     405       10535 :         auto familyIt = _families.find(style.fontFamily);
     406       10535 :         if (familyIt == _families.end()) {
     407           0 :                 return nullptr;
     408             :         }
     409             : 
     410             :         // search for exact match
     411       10535 :         auto cfgName = FontFaceSet::constructName(style.fontFamily, style);
     412       10535 :         auto it = _layouts.find(cfgName);
     413       10535 :         if (it != _layouts.end()) {
     414        9751 :                 face = it->second.get();
     415             :         }
     416             : 
     417       10535 :         FontSpecializationVector spec;
     418       10535 :         if (!face) {
     419             :                 // find best possible config
     420         784 :                 spec = findSpecialization(familyIt->second, style, nullptr);
     421         784 :                 cfgName = FontFaceSet::constructName(style.fontFamily, spec);
     422         784 :                 auto layoutIt = _layouts.find(cfgName);
     423         784 :                 if (layoutIt != _layouts.end()) {
     424         114 :                         face = layoutIt->second.get();
     425             :                 }
     426             :         }
     427             : 
     428       10535 :         if (face) {
     429        9865 :                 face->touch(_clock, style.persistent);
     430        9865 :                 return face;
     431             :         }
     432             : 
     433             :         // we need to create new layout
     434         670 :         sharedLock.unlock();
     435         670 :         std::unique_lock uniqueLock(_layoutSharedMutex);
     436             : 
     437         670 :         Vector<Rc<FontFaceData>> data;
     438             : 
     439             :         // update best match (if fonts was updated)
     440         670 :         spec = findSpecialization(familyIt->second, style, &data);
     441         670 :         cfgName = FontFaceSet::constructName(style.fontFamily, spec);
     442             : 
     443             :         // check if somebody already created layout for us in another thread
     444         670 :         it = _layouts.find(cfgName);
     445         670 :         if (it != _layouts.end()) {
     446           0 :                 it->second->touch(_clock, style.persistent);
     447           0 :                 return it->second.get();
     448             :         }
     449             : 
     450             :         // create layout
     451         670 :         ret = Rc<FontFaceSet>::create(move(cfgName), style.fontFamily, move(spec), move(data), _ext->getLibrary());
     452         670 :         _layouts.emplace(ret->getName(), ret);
     453         670 :         ret->touch(_clock, style.persistent);
     454         670 :         return ret;
     455       10559 : }
     456             : 
     457           0 : Rc<FontFaceSet> FontController::getLayoutForString(const FontParameters &f, const CharVector &str) {
     458           0 :         if (auto l = getLayout(f)) {
     459           0 :                 Vector<char32_t> failed;
     460           0 :                 if (f.persistent) {
     461           0 :                         l->addString(str, failed);
     462             :                 } else {
     463           0 :                         l->addString(str, failed);
     464             :                 }
     465           0 :                 return l;
     466           0 :         }
     467           0 :         return nullptr;
     468             : }
     469             : 
     470        7844 : Rc<core::DependencyEvent> FontController::addTextureChars(const Rc<FontFaceSet> &l, SpanView<CharLayoutData> chars) {
     471        7844 :         if (l->addTextureChars(chars)) {
     472        1148 :                 if (!_dependency) {
     473        2400 :                         _dependency = Rc<core::DependencyEvent>::alloc(core::DependencyEvent::QueueSet{
     474         600 :                                 _ext->getQueue()
     475        1800 :                         });
     476             :                 }
     477        1148 :                 _dirty = true;
     478        1148 :                 return _dependency;
     479             :         }
     480        6696 :         return nullptr;
     481             : }
     482             : 
     483           0 : uint32_t FontController::getFamilyIndex(StringView name) const {
     484           0 :         std::shared_lock lock(_layoutSharedMutex);
     485           0 :         auto it = std::find(_familiesNames.begin(), _familiesNames.end(), name);
     486           0 :         if (it != _familiesNames.end()) {
     487           0 :                 return it - _familiesNames.begin();
     488             :         }
     489           0 :         return maxOf<uint32_t>();
     490           0 : }
     491             : 
     492         130 : StringView FontController::getFamilyName(uint32_t idx) const {
     493         130 :         std::shared_lock lock(_layoutSharedMutex);
     494         130 :         if (idx < _familiesNames.size()) {
     495           0 :                 return _familiesNames[idx];
     496             :         }
     497         130 :         return StringView();
     498         130 : }
     499             : 
     500        2357 : void FontController::update(Application *, const UpdateTime &clock) {
     501        2357 :         _clock = clock.global;
     502        2357 :         removeUnusedLayouts();
     503        2357 :         if (_dirty && _loaded) {
     504         636 :                 Vector<FontUpdateRequest> objects;
     505         636 :                 std::shared_lock lock(_layoutSharedMutex);
     506        3838 :                 for (auto &it : _layouts) {
     507        9606 :                         for (auto &iit : it.second->getFaces()) {
     508        6404 :                                 if (!iit) {
     509        1177 :                                         continue;
     510             :                                 }
     511             : 
     512        5227 :                                 auto lb = std::lower_bound(objects.begin(), objects.end(), iit,
     513        9837 :                                                 [] (const FontUpdateRequest &l, FontFaceObject *r) {
     514        9837 :                                         return l.object.get() < r;
     515             :                                 });
     516        5227 :                                 if (lb == objects.end()) {
     517        1542 :                                         auto req = iit->getRequiredChars();
     518        1542 :                                         if (!req.empty()) {
     519        1179 :                                                 objects.emplace_back(FontUpdateRequest{iit, move(req), it.second->isPersistent()});
     520             :                                         }
     521        5227 :                                 } else if (lb != objects.end() && lb->object != iit) {
     522        3685 :                                         auto req = iit->getRequiredChars();
     523        3685 :                                         if (!req.empty()) {
     524        2023 :                                                 objects.emplace(lb, FontUpdateRequest{iit, move(req), it.second->isPersistent()});
     525             :                                         }
     526        3685 :                                 }
     527             :                         }
     528             :                 }
     529         636 :                 if (!objects.empty()) {
     530         612 :                         _ext->updateImage(_image, move(objects), move(_dependency));
     531         612 :                         _dependency = nullptr;
     532             :                 }
     533         636 :                 _dirty = false;
     534         636 :         }
     535        2357 : }
     536             : 
     537          24 : void FontController::setImage(Rc<core::DynamicImage> &&image) {
     538          24 :         _image = move(image);
     539          24 :         _texture = Rc<Texture>::create(_image);
     540          24 : }
     541             : 
     542          24 : void FontController::setLoaded(bool value) {
     543          24 :         if (_loaded != value) {
     544          24 :                 _loaded = value;
     545          24 :                 onLoaded(this);
     546          24 :                 UpdateTime t;
     547          24 :                 t.global = platform::clock(core::ClockType::Monotonic);
     548          24 :                 update(nullptr, t);
     549             :         }
     550          24 : }
     551             : 
     552           0 : void FontController::sendFontUpdatedEvent() {
     553           0 :         onFontSourceUpdated(this);
     554           0 : }
     555             : 
     556          24 : void FontController::setAliases(Map<String, String> &&aliases) {
     557          24 :         if (_aliases.empty()) {
     558          24 :                 _aliases = move(aliases);
     559             :         } else {
     560           0 :                 for (auto &it : aliases) {
     561           0 :                         _aliases.insert_or_assign(it.first, it.second);
     562             :                 }
     563             :         }
     564          24 : }
     565             : 
     566        1454 : FontSpecializationVector FontController::findSpecialization(const FamilySpec &family, const FontParameters &params, Vector<Rc<FontFaceData>> *dataList) {
     567        2908 :         auto getFontFaceScore = [] (const FontLayoutParameters &required, const FontLayoutParameters &existed) -> uint32_t {
     568        2908 :                 uint32_t ret = 0;
     569             :                 // if no match - prefer normal variants
     570        2908 :                 if (existed.fontStyle == FontStyle::Normal) { ret += 50; }
     571        2908 :                 if (existed.fontWeight == FontWeight::Normal) { ret += 50; }
     572        2908 :                 if (existed.fontStretch == FontStretch::Normal) { ret += 50; }
     573             : 
     574        2908 :                 if ((required.fontStyle == FontStyle::Italic && existed.fontStyle == FontStyle::Italic)
     575        2908 :                                 || (required.fontStyle == FontStyle::Normal && existed.fontStyle == FontStyle::Normal)) {
     576        1408 :                         ret += 100000;
     577        1500 :                 } else if (existed.fontStyle == FontStyle::Italic) {
     578          24 :                         if (required.fontStyle != FontStyle::Normal) {
     579           0 :                                 ret += ((360 << 6) - std::abs(int(required.fontStyle.get()) - int(FontStyle::Oblique.get()))) / 2;
     580             :                         }
     581        1476 :                 } else if (required.fontStyle == FontStyle::Italic) {
     582           0 :                         if (existed.fontStyle != FontStyle::Normal) {
     583           0 :                                 ret += ((360 << 6) - std::abs(int(FontStyle::Oblique.get()) - int(existed.fontStyle.get()))) / 2;
     584             :                         }
     585             :                 } else {
     586        1476 :                         ret += (360 << 6) - std::abs(int(required.fontStyle.get()) - int(existed.fontStyle.get()));
     587             :                 }
     588             : 
     589        2908 :                 if (existed.fontStyle == required.fontStyle && (existed.fontStyle == FontStyle::Oblique || existed.fontStyle  == FontStyle::Italic)) {
     590             : 
     591        5768 :                 } else if ((existed.fontStyle == FontStyle::Oblique || existed.fontStyle == FontStyle::Italic)
     592        5768 :                                 && (required.fontStyle == FontStyle::Oblique || required.fontStyle == FontStyle::Italic)) {
     593           0 :                         ret += 75000; // Oblique-Italic replacement
     594        2884 :                 } else if (existed.fontStyle == required.fontStyle && existed.fontStyle == FontStyle::Normal) {
     595        1408 :                         ret += 50000;
     596             :                 }
     597             : 
     598        2908 :                 if (existed.fontGrade == required.fontGrade) {
     599        2152 :                         ret += (400 - std::abs(int(required.fontGrade.get()) - int(existed.fontGrade.get()))) * 50;
     600             :                 }
     601             : 
     602        2908 :                 ret += (1000 - std::abs(int(required.fontWeight.get()) - int(existed.fontWeight.get()))) * 100;
     603        2908 :                 ret += ((250 << 1) - std::abs(int(required.fontStretch.get()) - int(existed.fontStretch.get()))) * 100;
     604        2908 :                 return ret;
     605             :         };
     606             : 
     607        1454 :         uint32_t score = 0;
     608        1454 :         FontSpecializationVector ret;
     609             : 
     610        1454 :         Vector<Pair<FontFaceData *, uint32_t>> scores;
     611             : 
     612        1454 :         uint32_t offset = family.data.size();
     613        4362 :         for (auto &it : family.data) {
     614        2908 :                 auto spec = it->getSpecialization(params);
     615        2908 :                 auto specScore = getFontFaceScore(params, spec) + offset;
     616        2908 :                 if (dataList) {
     617        1340 :                         scores.emplace_back(pair(it.get(), specScore));
     618             :                 }
     619        2908 :                 if (specScore >= score) {
     620        1454 :                         score = specScore;
     621        1454 :                         ret = spec;
     622             :                 }
     623        2908 :                 -- offset;
     624             :         }
     625             : 
     626        1454 :         if (dataList) {
     627         670 :                 std::sort(scores.begin(), scores.end(), [] (const Pair<FontFaceData *, uint32_t> &l, const Pair<FontFaceData *, uint32_t> &r) {
     628        1340 :                         if (l.second == r.second) {
     629           0 :                                 return l.first < r.first;
     630             :                         } else {
     631        1340 :                                 return l.second > r.second;
     632             :                         }
     633             :                 });
     634             : 
     635         670 :                 dataList->reserve(scores.size());
     636        2010 :                 for (auto &it : scores) {
     637        1340 :                         dataList->emplace_back(it.first);
     638             :                 }
     639             :         }
     640             : 
     641        1454 :         return ret;
     642        1454 : }
     643             : 
     644        2357 : void FontController::removeUnusedLayouts() {
     645        2357 :         std::unique_lock lock(_layoutSharedMutex);
     646        2357 :         auto it = _layouts.begin();
     647       15267 :         while (it != _layouts.end()) {
     648       12910 :                 if (it->second->isPersistent()) {
     649        2297 :                         ++ it;
     650        2297 :                         continue;
     651             :                 }
     652             : 
     653       10613 :                 if (/*_clock - it->second->getAccessTime() > _unusedInterval.toMicros() &&*/ it->second->getReferenceCount() == 1) {
     654         563 :                         auto c = it->second->getTexturesCount();
     655         563 :                         it = _layouts.erase(it);
     656         563 :                         if (c > 0) {
     657         428 :                                 _dirty = true;
     658             :                         }
     659             :                 } else {
     660       10050 :                         ++ it;
     661             :                 }
     662             :         }
     663        2357 : }
     664             : 
     665             : }

Generated by: LCOV version 1.14