LCOV - code coverage report
Current view: top level - xenolith/font - XLFontExtension.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 143 183 78.1 %
Date: 2024-05-12 00:16:13 Functions: 27 29 93.1 %

          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 "XLFontExtension.h"
      24             : #include "XLCoreLoop.h"
      25             : #include "XLCoreQueue.h"
      26             : #include "XLCoreImageStorage.h"
      27             : #include "XLCoreFrameRequest.h"
      28             : #include "XLCoreFrameQueue.h"
      29             : #include "XLApplication.h"
      30             : #include "ft2build.h"
      31             : #include FT_FREETYPE_H
      32             : #include FT_GLYPH_H
      33             : #include FT_MULTIPLE_MASTERS_H
      34             : #include FT_SFNT_NAMES_H
      35             : #include FT_ADVANCES_H
      36             : 
      37             : #include "SPFontFace.h"
      38             : 
      39             : namespace STAPPLER_VERSIONIZED stappler::xenolith::font {
      40             : 
      41          48 : FontExtension::~FontExtension() {
      42          24 :         _queue = nullptr;
      43          48 : }
      44             : 
      45          24 : bool FontExtension::init(Rc<Application> &&loop, Rc<core::Queue> &&q) {
      46          24 :         _mainLoop = move(loop);
      47          24 :         _glLoop = _mainLoop->getGlLoop();
      48          24 :         _queue = move(q);
      49          24 :         _library = Rc<FontLibrary>::alloc();
      50          24 :         if (_queue->isCompiled()) {
      51           0 :                 onActivated();
      52             :         } else {
      53          24 :                 auto linkId = retain();
      54          24 :                 _glLoop->compileQueue(_queue, [this, linkId] (bool success) {
      55          24 :                         if (success) {
      56          24 :                                 _mainLoop->performOnMainThread([this] {
      57          24 :                                         onActivated();
      58          24 :                                 }, this);
      59             :                         }
      60          24 :                         release(linkId);
      61          24 :                 });
      62             :         }
      63          24 :         return true;
      64             : }
      65             : 
      66           0 : void FontExtension::initialize(Application *) {
      67             : 
      68           0 : }
      69             : 
      70          24 : void FontExtension::invalidate(Application *) {
      71          24 :         _library->invalidate();
      72             : 
      73          24 :         _queue = nullptr;
      74          24 :         _mainLoop = nullptr;
      75          24 :         _glLoop = nullptr;
      76          24 : }
      77             : 
      78        2333 : void FontExtension::update(Application *, const UpdateTime &clock) {
      79        2333 :         _library->update();
      80        2333 : }
      81             : 
      82          71 : static Bytes openResourceFont(FontLibrary::DefaultFontName name) {
      83          71 :         auto d = FontLibrary::getFont(name);
      84         144 :         return data::decompress<memory::StandartInterface>(d.data(), d.size());
      85             : }
      86             : 
      87          96 : static String getResourceFontName(FontLibrary::DefaultFontName name) {
      88         192 :         return toString("resource:", FontLibrary::getFontName(name));
      89             : }
      90             : 
      91          72 : static const FontController::FontSource * makeResourceFontQuery(FontController::Builder &builder, FontLibrary::DefaultFontName name,
      92             :                 FontLayoutParameters params = FontLayoutParameters()) {
      93         142 :         return builder.addFontSource( getResourceFontName(name), [name] { return openResourceFont(name); }, params);
      94             : }
      95             : 
      96          24 : FontController::Builder FontExtension::makeDefaultControllerBuilder(StringView key) {
      97          24 :         FontController::Builder ret(key);
      98             : 
      99          24 :         auto res_RobotoFlex = makeResourceFontQuery(ret, DefaultFontName::RobotoFlex_VariableFont);
     100          24 :         auto res_RobotoMono_Variable = makeResourceFontQuery(ret, DefaultFontName::RobotoMono_VariableFont);
     101          24 :         auto res_RobotoMono_Italic_Variable = makeResourceFontQuery(ret, DefaultFontName::RobotoMono_Italic_VariableFont, FontLayoutParameters{
     102             :                 FontStyle::Italic, FontWeight::Normal, FontStretch::Normal});
     103          24 :         auto res_DejaVuSans = ret.addFontSource( getResourceFontName(DefaultFontName::DejaVuSans), FontLibrary::getFont(DefaultFontName::DejaVuSans));
     104             : 
     105          24 :         ret.addFontFaceQuery("sans", res_RobotoFlex);
     106          24 :         ret.addFontFaceQuery("sans", res_DejaVuSans);
     107          24 :         ret.addFontFaceQuery("monospace", res_RobotoMono_Variable);
     108          24 :         ret.addFontFaceQuery("monospace", res_RobotoMono_Italic_Variable);
     109             : 
     110          24 :         ret.addAlias("default", "sans");
     111          24 :         ret.addAlias("system", "sans");
     112             : 
     113          24 :         return ret;
     114           0 : }
     115             : 
     116          24 : Rc<FontController> FontExtension::acquireController(FontController::Builder &&b) {
     117             :         struct ControllerBuilder : Ref {
     118             :                 FontController::Builder builder;
     119             :                 Rc<FontController> controller;
     120             :                 Rc<core::DynamicImage> dynamicImage;
     121             : 
     122             :                 bool invalid = false;
     123             :                 std::atomic<size_t> pendingData = 0;
     124             :                 FontExtension *ext = nullptr;
     125             : 
     126          24 :                 ControllerBuilder(FontController::Builder &&b)
     127          24 :                 : builder(move(b)) {
     128          24 :                         if (auto c = builder.getTarget()) {
     129           0 :                                 controller = c;
     130             :                         }
     131          24 :                 }
     132             : 
     133           0 :                 void invalidate() {
     134           0 :                         controller = nullptr;
     135           0 :                 }
     136             : 
     137          24 :                 void loadData() {
     138          24 :                         if (invalid) {
     139           0 :                                 invalidate();
     140           0 :                                 return;
     141             :                         }
     142             : 
     143          24 :                         ext->getMainLoop()->performOnMainThread([this] () {
     144          72 :                                 for (const Pair<const String, FontController::FamilyQuery> &it : builder.getFamilyQueries()) {
     145          48 :                                         Vector<Rc<FontFaceData>> d; d.reserve(it.second.sources.size());
     146         144 :                                         for (auto &iit : it.second.sources) {
     147          96 :                                                 d.emplace_back(move(iit->data));
     148             :                                         }
     149          48 :                                         controller->addFont(it.second.family, move(d), it.second.addInFront);
     150          48 :                                 }
     151             : 
     152          24 :                                 if (builder.getTarget()) {
     153           0 :                                         for (auto &it : builder.getAliases()) {
     154           0 :                                                 controller->addAlias(it.first, it.second);
     155             :                                         }
     156           0 :                                         controller->sendFontUpdatedEvent();
     157             :                                 } else {
     158          24 :                                         controller->setAliases(Map<String, String>(builder.getAliases()));
     159          24 :                                         controller->setLoaded(true);
     160             :                                 }
     161          24 :                                 controller = nullptr;
     162          24 :                         }, this);
     163             :                 }
     164             : 
     165          96 :                 void onDataLoaded(bool success) {
     166          96 :                         auto v = pendingData.fetch_sub(1);
     167          96 :                         if (!success) {
     168           0 :                                 invalid = true;
     169           0 :                                 if (v == 1) {
     170           0 :                                         invalidate();
     171             :                                 }
     172          96 :                         } else if (v == 1) {
     173           2 :                                 loadData();
     174             :                         }
     175          96 :                 }
     176             : 
     177          24 :                 void onImageLoaded(Rc<core::DynamicImage> &&image) {
     178          24 :                         auto v = pendingData.fetch_sub(1);
     179          24 :                         if (image) {
     180          24 :                                 controller->setImage(move(image));
     181          24 :                                 if (v == 1) {
     182          22 :                                         loadData();
     183             :                                 }
     184             :                         } else {
     185           0 :                                 invalid = true;
     186           0 :                                 if (v == 1) {
     187           0 :                                         invalidate();
     188             :                                 }
     189             :                         }
     190          24 :                 }
     191             :         };
     192             : 
     193          24 :         bool hasController = (b.getTarget() != nullptr);
     194          24 :         auto builder = Rc<ControllerBuilder>::alloc(move(b));
     195          24 :         builder->ext = this;
     196          24 :         if (!hasController) {
     197          24 :                 builder->controller = Rc<FontController>::create(this);
     198          24 :                 builder->pendingData = builder->builder.getDataQueries().size() + 1;
     199             :         } else {
     200           0 :                 builder->pendingData = builder->builder.getDataQueries().size();
     201             :         }
     202             : 
     203         120 :         for (auto &it : builder->builder.getDataQueries()) {
     204          96 :                 _mainLoop->perform([this, name = it.first, sourcePtr = &it.second, builder] (const thread::Task &) mutable -> bool {
     205         187 :                         sourcePtr->data = _library->openFontData(name, sourcePtr->params, sourcePtr->preconfiguredParams, [&] () -> FontLibrary::FontData {
     206          96 :                                 if (sourcePtr->fontCallback) {
     207          72 :                                         return FontLibrary::FontData(move(sourcePtr->fontCallback));
     208          24 :                                 } else if (!sourcePtr->fontExternalData.empty()) {
     209          24 :                                         return FontLibrary::FontData(sourcePtr->fontExternalData, true);
     210           0 :                                 } else if (!sourcePtr->fontMemoryData.empty()) {
     211           0 :                                         return FontLibrary::FontData(move(sourcePtr->fontMemoryData));
     212           0 :                                 } else if (!sourcePtr->fontFilePath.empty()) {
     213           0 :                                         auto d = filesystem::readIntoMemory<Interface>(sourcePtr->fontFilePath);
     214           0 :                                         if (!d.empty()) {
     215           0 :                                                 return FontLibrary::FontData(move(d));
     216             :                                         }
     217           0 :                                 }
     218           0 :                                 return FontLibrary::FontData(BytesView(), false);
     219          96 :                         });
     220          96 :                         if (sourcePtr->data) {
     221          96 :                                 builder->onDataLoaded(true);
     222             :                         } else {
     223           0 :                                 builder->onDataLoaded(false);
     224             :                         }
     225          96 :                         return true;
     226             :                 });
     227             :         }
     228             : 
     229          24 :         if (!hasController) {
     230          48 :                 builder->dynamicImage = Rc<core::DynamicImage>::create([name = builder->builder.getName()] (core::DynamicImage::Builder &builder) {
     231          72 :                         builder.setImage(name,
     232          48 :                                 core::ImageInfo(
     233           0 :                                                 Extent2(2, 2),
     234           0 :                                                 core::ImageUsage::Sampled | core::ImageUsage::TransferSrc,
     235           0 :                                                 core::PassType::Graphics,
     236          48 :                                                 core::ImageFormat::R8_UNORM
     237          24 :                                 ), [] (uint8_t *ptr, uint64_t, const core::ImageData::DataCallback &cb) {
     238          24 :                                         if (ptr) {
     239           0 :                                                 ptr[0] = 0;
     240           0 :                                                 ptr[1] = 255;
     241           0 :                                                 ptr[2] = 255;
     242           0 :                                                 ptr[3] = 0;
     243             :                                         } else {
     244          24 :                                                 Bytes bytes; bytes.reserve(4);
     245          24 :                                                 bytes.emplace_back(0);
     246          24 :                                                 bytes.emplace_back(255);
     247          24 :                                                 bytes.emplace_back(255);
     248          24 :                                                 bytes.emplace_back(0);
     249          24 :                                                 cb(bytes);
     250          24 :                                         }
     251          48 :                                 }, nullptr);
     252          24 :                         return true;
     253          24 :                 });
     254             : 
     255          24 :                 _glLoop->compileImage(builder->dynamicImage, [this, builder] (bool success) {
     256          24 :                         _mainLoop->performOnMainThread([success, builder] {
     257          24 :                                 if (success) {
     258          24 :                                         builder->onImageLoaded(move(builder->dynamicImage));
     259             :                                 } else {
     260           0 :                                         builder->onImageLoaded(nullptr);
     261             :                                 }
     262          24 :                         }, this);
     263          24 :                 });
     264             :         }
     265             : 
     266          48 :         return builder->controller;
     267          24 : }
     268             : 
     269         612 : void FontExtension::updateImage(const Rc<core::DynamicImage> &image, Vector<font::FontUpdateRequest> &&data,
     270             :                 Rc<core::DependencyEvent> &&dep) {
     271         612 :         if (!_active) {
     272           0 :                 _pendingImageQueries.emplace_back(ImageQuery{image, move(data), move(dep)});
     273           0 :                 return;
     274             :         }
     275             : 
     276         612 :         auto input = Rc<RenderFontInput>::alloc();
     277         612 :         input->queue = _mainLoop->getQueue();
     278         612 :         input->image = image;
     279         612 :         input->ext = this;
     280         612 :         input->requests = move(data);
     281             :         /*input->output = [] (const gl::ImageInfo &info, BytesView data) {
     282             :                 Bitmap bmp(data, info.extent.width, info.extent.height, bitmap::PixelFormat::A8);
     283             :                 bmp.save(bitmap::FileFormat::Png, toString(Time::now().toMicros(), ".png"));
     284             :         };*/
     285             : 
     286         612 :         auto req = Rc<core::FrameRequest>::create(_queue);
     287         612 :         if (dep) {
     288         600 :                 req->addSignalDependency(move(dep));
     289             :         }
     290             : 
     291         612 :         for (auto &it : _queue->getInputAttachments()) {
     292         612 :                 req->addInput(it, move(input));
     293         612 :                 break;
     294             :         }
     295             : 
     296         612 :         _glLoop->runRenderQueue(move(req), 0, [app = Rc<Application>(_mainLoop)] (bool success) {
     297         612 :                 app->wakeup();
     298         612 :         });
     299         612 : }
     300             : 
     301          24 : void FontExtension::onActivated() {
     302          24 :         _active = true;
     303             : 
     304          24 :         for (auto &it : _pendingImageQueries) {
     305           0 :                 updateImage(it.image, move(it.chars), move(it.dependency));
     306             :         }
     307             : 
     308          24 :         _pendingImageQueries.clear();
     309          24 : }
     310             : 
     311             : }

Generated by: LCOV version 1.14