LCOV - code coverage report
Current view: top level - src/rendering/fonts - font_planner.cc (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 280 305 91.8 %
Date: 2022-06-15 20:16:21 Functions: 24 29 82.8 %
Branches: 277 426 65.0 %

           Branch data     Line data    Source code
       1                 :            : #ifdef RUN_TESTS
       2                 :            : # include <gtest/gtest.h>
       3                 :            : #endif
       4                 :            : /** @file rendering/fonts/font_planner.cc
       5                 :            :  * @brief Defines an interface that converts unicode characters into pixels,
       6                 :            :  * through scanning available font files for a specified range in unicode at specified size.
       7                 :            :  */
       8                 :            : 
       9                 :            : #include <map>
      10                 :            : #include <regex>
      11                 :            : #include <fstream>
      12                 :            : #include <cassert>
      13                 :            : #include <thread>
      14                 :            : #include <chrono>
      15                 :            : #include <unordered_map>
      16                 :            : #include <memory>
      17                 :            : 
      18                 :            : #include "make_similarities.hh"
      19                 :            : #include "font_planner.hh"
      20                 :            : #include "read_fonts.hh"
      21                 :            : #include "endian.hh"
      22                 :            : #include "share.hh"
      23                 :            : 
      24                 :            : namespace
      25                 :            : {
      26                 :            :     unsigned unload_check_interval = 10;
      27                 :            :     unsigned unload_interval       = 60;
      28                 :            : 
      29                 :            :     static constexpr auto Ropt = std::regex_constants::ECMAScript | std::regex_constants::optimize;
      30                 :            : 
      31                 :            :     std::map<std::pair<std::string,std::pair<unsigned,unsigned>>,
      32                 :            :              std::pair<std::chrono::time_point<std::chrono::system_clock>,
      33                 :            :                        std::shared_ptr<GlyphList>>
      34                 :            :             > loaded_fonts;
      35                 :            :     std::mutex fonts_lock;
      36                 :            : 
      37                 :          9 :     const FontsInfo& GetFontsInfo()
      38                 :            :     {
      39   [ +  +  +  -  :          9 :         static const FontsInfo data = ReadFontsList();
                   +  - ]
      40                 :          9 :         return data;
      41                 :            :     }
      42                 :            : 
      43                 :     137210 :     std::shared_ptr<GlyphList> LoadFont(const std::string& name, unsigned width, unsigned height,
      44                 :            :                                         std::string_view guessed_encoding)
      45                 :            :     {
      46                 :     137210 :         std::pair key(name, std::pair(width,height));
      47                 :     137215 :         auto i = loaded_fonts.lower_bound(key);
      48   [ +  +  +  + ]:     137207 :         if(i == loaded_fonts.end() || i->first != key)
      49                 :            :         {
      50         [ +  - ]:         49 :             std::unique_lock<std::mutex> lk(fonts_lock);
      51                 :         49 :             i = loaded_fonts.lower_bound(key);
      52   [ +  +  +  + ]:         49 :             if(i == loaded_fonts.end() || i->first != key)
      53                 :            :             {
      54                 :            :                 //lk.unlock();
      55   [ +  -  +  -  :         45 :                 auto font = Read_Font(name, width, height, true, guessed_encoding);
                   -  - ]
      56                 :            :                 //lk.lock();
      57   [ +  -  -  +  :         45 :                 i = loaded_fonts.emplace_hint(i, key, std::pair(std::chrono::system_clock::now(),
          -  +  -  -  -  
                      - ]
      58         [ +  - ]:         45 :                                                                 std::make_shared<GlyphList>( std::move(font) )));
      59                 :         45 :             }
      60                 :         49 :         }
      61         [ +  - ]:     137207 :         i->second.first = std::chrono::system_clock::now();
      62         [ +  - ]:     274425 :         return i->second.second;
      63                 :     137216 :     }
      64                 :            : 
      65                 :          2 :     std::vector<std::regex> LoadPreferences()
      66                 :            :     {
      67         [ +  - ]:          2 :         std::vector<std::regex> result;
      68   [ +  -  +  -  :          8 :         auto [location, status] = FindShareFile(std::filesystem::path("fonts") / "preferences.txt");
          +  -  +  -  -  
                      + ]
      69         [ -  + ]:          2 :         if(std::filesystem::exists(status))
      70                 :            :         {
      71         [ +  - ]:          2 :             std::ifstream f{ location };
      72   [ +  -  +  + ]:         52 :             for(std::string line; std::getline(f,line), f; )
      73   [ +  +  +  + ]:         50 :                 if(!line.empty() && line[0] != '#')
      74         [ +  - ]:         38 :                     result.emplace_back(line, Ropt);
      75                 :          2 :         }
      76                 :          2 :         return result;
      77                 :          2 :     }
      78                 :            : 
      79                 :        698 :     int FontNameScore(const std::string& name)
      80                 :            :     {
      81   [ +  +  +  +  :        698 :         static const std::vector<std::regex> patterns = LoadPreferences();
                   +  - ]
      82                 :        698 :         int score = static_cast<int>( patterns.size() );
      83         [ +  + ]:       7128 :         for(auto& p: patterns)
      84                 :            :         {
      85         [ +  + ]:       7058 :             if(std::regex_match(name, p)) break;
      86                 :       6430 :             --score;
      87                 :            :         }
      88                 :        696 :         return score;
      89                 :            :     }
      90                 :     137909 :     bool IsBoldFont(const std::string& name, unsigned width)
      91                 :            :     {
      92   [ +  +  +  +  :     137909 :         static std::regex negative("Terminus[0-9]|mona[67]x|10x20cn|gb16st|unifont|[456]x[0-9]+\\.bdf", Ropt);
                   +  - ]
      93   [ +  +  +  +  :     137909 :         static std::regex affirmative("TerminusBold|VGA|lat[1-9]-[01]|iso[01][0-9]\\.f|[0-9]\\.(?:inc|asm)", Ropt);
                   +  - ]
      94         [ +  + ]:     137909 :         if(std::regex_search(name, negative)) return false;
      95         [ +  + ]:     108159 :         if(std::regex_search(name, affirmative)) return true;
      96                 :      31216 :         return width >= 7;
      97                 :            :     }
      98                 :            : 
      99                 :    1203220 :     unsigned long ScaleFont(unsigned long bitmap, unsigned oldwidth, unsigned newwidth)
     100                 :            :     {
     101         [ +  + ]:    1203220 :         if(oldwidth == newwidth) return bitmap;
     102         [ +  + ]:    1060945 :         if(newwidth == oldwidth*2)
     103                 :            :         {
     104                 :            :             // interleave bits in the word by itself
     105                 :        360 :             bitmap = (bitmap | (bitmap << 16)) & ((~0ull)/65537); // 0000FFFF0000FFFF
     106                 :        360 :             bitmap = (bitmap | (bitmap <<  8)) & ((~0ull)/257);   // 00FF00FF00FF00FF
     107                 :        360 :             bitmap = (bitmap | (bitmap <<  4)) & ((~0ull)/17);    // 0F0F0F0F0F0F0F0F (00001111)
     108                 :        360 :             bitmap = (bitmap | (bitmap <<  2)) & ((~0ull)/5);     // 3333333333333333 (00110011)
     109                 :        360 :             bitmap = (bitmap | (bitmap <<  1)) & ((~0ull)/3);     // 5555555555555555 (01010101)
     110                 :        360 :             bitmap = bitmap | (bitmap << 1);
     111                 :        360 :             return bitmap;
     112                 :            :         }
     113                 :            :         unsigned long result = 0;
     114                 :            :         unsigned carry = 0, index = 0, bit = 0;
     115         [ +  + ]:   11390335 :         for(unsigned n=0; n<oldwidth; ++n)
     116                 :            :         {
     117                 :   10329750 :             bit |= (bitmap >> n)&1;
     118                 :   10329750 :             carry += newwidth;
     119         [ +  + ]:   10329750 :             if(carry >= oldwidth)
     120                 :            :             {
     121                 :    8092112 :                 do {
     122                 :    8092112 :                     result |= bit << index++;
     123                 :    8092112 :                     carry -= oldwidth;
     124         [ +  + ]:    8092112 :                 } while(carry >= oldwidth);
     125                 :            :                 bit = 0;
     126                 :            :             }
     127                 :            :         }
     128         [ -  + ]:    1060585 :         if(bit) result |= 1u << (newwidth-1);
     129                 :            :         return result;
     130                 :            :     }
     131                 :            : } /* namespace */
     132                 :            : 
     133                 :          1 : static auto BuildSieves(char32_t granularity)
     134                 :            : {
     135   [ +  -  +  -  :          1 :     static const auto similarities = ParseSimilarities();
                   +  - ]
     136                 :            : 
     137                 :          1 :     using keytype = std::remove_const_t<FontsInfo::key_type>;
     138         [ +  - ]:          1 :     std::unordered_map<char32_t, std::vector<std::shared_ptr<keytype>>> options;
     139         [ +  - ]:          1 :     auto& info = GetFontsInfo();
     140   [ +  -  +  + ]:        101 :     for(const auto& [key,data]: info)
     141                 :            :     {
     142         [ +  - ]:        100 :         auto k = std::make_shared<keytype>(key);
     143         [ +  + ]:       6882 :         for(char32_t base=0; base<data.second.size(); base+=granularity)
     144                 :            :         {
     145                 :            :             bool ok = false;
     146         [ +  + ]:    6168339 :             for(char32_t ch=0; ch<granularity; ++ch)
     147                 :            :             {
     148   [ +  +  +  + ]:    6162443 :                 if(data.second.size() > base+ch && data.second[base+ch])
     149                 :        886 :                     { ok = true; break; }
     150                 :            : 
     151                 :            :                 // Find viable approximations for this symbol
     152                 :    6161557 :                 auto choices = std::equal_range(similarities.begin(), similarities.end(), std::pair(base+ch,0),
     153                 :  130850901 :                     [](const auto& pair1, const auto& pair2)
     154                 :            :                     {
     155   [ +  +  +  +  :   88227152 :                         return pair1.first < pair2.first;
             +  +  +  + ]
     156                 :            :                     });
     157                 :            :                 // Check if the font implements one of those
     158         [ +  + ]:    6398557 :                 for(auto i = choices.first; i != choices.second; ++i)
     159   [ +  +  +  + ]:     352499 :                     if(data.second.size() > i->second && data.second[i->second])
     160                 :            :                         { ok = true; break; }
     161                 :            :             }
     162         [ +  + ]:       6782 :             if(ok)
     163   [ +  -  +  - ]:       2150 :                 options[base].push_back(k);
     164                 :            :         }
     165                 :        100 :     }
     166                 :          1 :     return options;
     167                 :          0 : }
     168                 :          7 : static const auto& GetSieves(char32_t granularity)
     169                 :            : {
     170   [ +  +  +  -  :          7 :     static const auto sieves = BuildSieves(granularity);
                   +  - ]
     171                 :          7 :     return sieves;
     172                 :            : }
     173                 :            : 
     174                 :          8 : static const auto& GetSimilarities()
     175                 :            : {
     176   [ +  +  +  -  :          8 :     static const auto similarities = ParseSimilarities();
                   +  - ]
     177                 :          8 :     return similarities;
     178                 :            : }
     179                 :            : 
     180                 :      11401 : void FontPlan::Create(unsigned width, unsigned height, char32_t firstch, char32_t numch)
     181                 :            : {
     182         [ +  + ]:      11401 :     std::tuple cur(width,height,firstch,numch);
     183         [ +  + ]:      11401 :     if(cur == prev) return;
     184                 :            : 
     185                 :          7 :     ready = false;
     186                 :          7 :     prev = cur;
     187                 :            : 
     188                 :          7 :     const auto& similarities = GetSimilarities();
     189                 :          7 :     const auto& info = GetFontsInfo();
     190                 :          7 :     const auto& sieves = GetSieves(numch);
     191                 :            : 
     192                 :     137213 :     std::thread updater([=,this,&sieves,&info,&similarities]
     193                 :            :     {
     194                 :          7 :         std::unique_lock<std::mutex> lk(working);
     195                 :            : 
     196                 :          7 :         static constexpr char32_t ilseq = 0xFFFD;
     197                 :            : 
     198                 :          7 :         auto old_loaded_fonts   = std::move(loaded_fonts);   loaded_fonts.clear();
     199                 :          7 :         auto old_font_filenames = std::move(font_filenames); font_filenames.clear();
     200                 :            : 
     201         [ +  + ]:          7 :         resized_bitmaps.clear();
     202   [ +  -  +  - ]:     133499 :         resized_bitmaps.reserve(numch * height * ((width+7)/8));
     203         [ +  - ]:          7 :         bitmap_pointers.resize(numch);
     204         [ +  - ]:          7 :         bold_list.resize(numch);
     205                 :            : 
     206                 :          7 :         std::vector<std::pair<int, std::remove_const_t<FontsInfo::key_type>>> fonts;
     207         [ +  - ]:          7 :         if(auto i = sieves.find(firstch); i != sieves.end())
     208         [ +  + ]:        705 :             for(const auto& keyptr: i->second)
     209                 :            :             {
     210         [ +  - ]:        698 :                 const auto& key = *keyptr;
     211         [ +  - ]:        698 :                 int name_score = FontNameScore(key.first);
     212                 :        696 :                 int wdiff = std::abs(int(width  - key.second.first))*2;
     213                 :        696 :                 int hdiff = std::abs(int(height - key.second.second))*2;
     214   [ +  +  +  + ]:        696 :                 if(width == key.second.first*2 || width*2 == key.second.first) wdiff /= 2;
     215   [ +  +  +  + ]:        696 :                 if(height == key.second.second*2 || height*2 == key.second.second) hdiff /= 2;
     216   [ +  -  +  + ]:        696 :                 int bdiff = IsBoldFont(key.first, key.second.first) ? 0 : 1;
     217                 :        699 :                 int diff = wdiff*wdiff + hdiff*hdiff + bdiff*bdiff;
     218                 :        699 :                 int score = diff*64 - name_score;
     219         [ +  - ]:        699 :                 fonts.emplace_back(score, key);
     220                 :            :             }
     221                 :            :         // If there were no candidates, check for something that implements ilseq
     222                 :          7 :         int ilseq_penalty = 18000000;
     223         [ -  + ]:          7 :         if(fonts.empty())
     224                 :            :         {
     225                 :          0 :             ilseq_penalty = 0;
     226   [ #  #  #  # ]:          0 :             for(const auto& [key,data]: info)
     227                 :            :             {
     228         [ #  # ]:          0 :                 int name_score = FontNameScore(key.first);
     229                 :          0 :                 int wdiff = std::abs(int(width  - key.second.first))*2;
     230                 :          0 :                 int hdiff = std::abs(int(height - key.second.second))*2;
     231   [ #  #  #  # ]:          0 :                 if(width == key.second.first*2 || width*2 == key.second.first) wdiff /= 2;
     232   [ #  #  #  # ]:          0 :                 if(height == key.second.second*2 || height*2 == key.second.second) hdiff /= 2;
     233   [ #  #  #  # ]:          0 :                 int bdiff = IsBoldFont(key.first, key.second.first) ? 0 : 1;
     234                 :          0 :                 int diff = wdiff*wdiff + hdiff*hdiff + bdiff*bdiff;
     235                 :          0 :                 int score = diff*64 - name_score;
     236         [ #  # ]:          0 :                 fonts.emplace_back(score, key);
     237                 :            :             }
     238                 :            :         }
     239                 :          7 :         std::sort(fonts.begin(), fonts.end());
     240                 :          7 :         if(false)
     241                 :            :         {
     242                 :            :             for(const auto& [basescore,key]: fonts)
     243                 :            :                 std::fprintf(stderr, "For target %ux%u, source %ux%u %s has score %d\n",
     244                 :            :                     width,height,  key.second.first,key.second.second, key.first.c_str(), -basescore);
     245                 :            :         }
     246                 :            : 
     247                 :          7 :         std::vector<std::tuple<char32_t, std::size_t, unsigned,unsigned>> resize_pointers;
     248         [ +  + ]:     137220 :         for(char32_t ch = firstch; ch < firstch + numch; ++ch)
     249                 :            :         {
     250                 :            : #ifdef RUN_TESTS
     251   [ +  +  +  - ]:     137213 :             if(ch % 0x100 == 0) fprintf(stderr, "\r%.2g %%...", (ch-firstch) * 100. / numch);
     252                 :            : #endif
     253                 :            :             // List all possible approximations for this symbol */
     254                 :     137213 :             auto choices = std::equal_range(similarities.begin(), similarities.end(), std::pair(ch,0),
     255                 :    3015232 :                 [](const auto& pair1, const auto& pair2)
     256                 :            :                 {
     257   [ +  +  +  +  :    2015739 :                     return pair1.first < pair2.first;
             +  +  +  + ]
     258                 :            :                 });
     259                 :            : 
     260                 :            :             /* List all proper-size fonts that _have_ this symbol          */
     261                 :          0 :             struct candidate
     262                 :            :             {
     263                 :            :                 std::string filename;
     264                 :            :                 unsigned    width;
     265                 :            :                 unsigned    height;
     266                 :            :                 char32_t    ch;
     267                 :            :                 long        score;
     268                 :            :                 std::string_view encoding;
     269                 :     137213 :             } candidate = {};
     270                 :            : 
     271   [ +  +  +  + ]:   10029276 :             for(const auto& [basescore,key]: fonts)
     272                 :            :             {
     273                 :    9956543 :                 long score = 0x7FFFFFFFl - basescore;
     274         [ +  + ]:    9956543 :                 if(score <= candidate.score) break;
     275                 :            : 
     276         [ +  + ]:    9892065 :                 const auto& data = info.find(key)->second;
     277                 :            : 
     278                 :            :                 //bool correct_size       = (key.second == std::pair(width,height));
     279   [ +  +  +  + ]:    9891958 :                 bool has_correct_symbol = data.second.size() > ch && data.second[ch];
     280                 :    9892147 :                 if(has_correct_symbol)
     281                 :            :                 {
     282   [ +  +  +  - ]:      64478 :                     if(candidate.filename.empty() || score > candidate.score)
     283                 :            :                     {
     284                 :            :                         //fprintf(stderr, "U+%04X: Chose exact U+%04X from %ux%u %s (score %ld)\n",
     285                 :            :                         //    ch, ch, key.second.first,key.second.second,key.first.c_str(),score);
     286         [ +  - ]:      64478 :                         candidate = { key.first, key.second.first, key.second.second, ch, score, data.first };
     287                 :            :                     }
     288                 :            :                 }
     289                 :            :                 else
     290                 :            :                 {
     291                 :    9827669 :                     constexpr int approx_penalty_once = 1000000;
     292                 :    9827669 :                     constexpr int approx_penalty_per  = 10;
     293                 :            : 
     294                 :    9827669 :                     long saved_score = score;
     295                 :    9827669 :                     score -= approx_penalty_once;
     296         [ +  + ]:    9827669 :                     if(candidate.score < score)
     297         [ +  + ]:    9667264 :                         for(auto i = choices.first; i != choices.second; ++i)
     298                 :            :                         {
     299   [ +  +  +  + ]:     126983 :                             if(data.second.size() > i->second && data.second[i->second])
     300                 :            :                             {
     301   [ +  +  +  + ]:       9463 :                                 if(candidate.filename.empty() || score > candidate.score)
     302                 :            :                                 {
     303                 :            :                                     //fprintf(stderr, "U+%04X: Chose approx U+%04X from %ux%u %s (score %ld)\n",
     304                 :            :                                     //    ch, i->second, key.second.first,key.second.second,key.first.c_str(),score);
     305         [ +  - ]:       7616 :                                     candidate = { key.first, key.second.first, key.second.second, i->second, score, data.first };
     306                 :            :                                 }
     307                 :            :                             }
     308                 :     127010 :                             score -= approx_penalty_per;
     309                 :            :                         }
     310                 :    9827696 :                     score = saved_score - ilseq_penalty;
     311         [ +  + ]:    8957409 :                     if(data.second.size() > ilseq && data.second[ilseq] // illseq
     312   [ +  +  +  +  :   16887755 :                     && (candidate.filename.empty() || score > candidate.score))
                   -  + ]
     313                 :            :                     {
     314                 :            :                         //fprintf(stderr, "U+%04X: Chose fail U+%04X from %ux%u %s (score %ld)\n",
     315                 :            :                         //    ch, ilseq, key.second.first,key.second.second,key.first.c_str(),score);
     316         [ +  - ]:     128940 :                         candidate = { key.first, key.second.first, key.second.second, ilseq, score, data.first };
     317                 :            :                     }
     318                 :            :                 }
     319                 :            :             }
     320                 :            : 
     321         [ -  + ]:     137211 :             if(candidate.filename.empty())
     322                 :            :             {
     323         [ #  # ]:          0 :                 std::fprintf(stderr, "No candidate (%zu fonts) for char 0x%X\n", fonts.size(), unsigned(ch));
     324                 :            :             }
     325                 :            : 
     326                 :            :             // Append bitmap
     327                 :            :             // This causes a font file load, but it is done only after we have already decided on a font file.
     328         [ +  - ]:     137211 :             std::shared_ptr<GlyphList> font = LoadFont(candidate.filename, candidate.width, candidate.height, candidate.encoding);
     329         [ -  + ]:     137215 :             assert(&*font != nullptr);
     330                 :     137215 :             auto k = font->glyphs.find(candidate.ch);
     331         [ -  + ]:     137214 :             if(k == font->glyphs.end())
     332                 :            :             {
     333                 :          0 :                 std::fprintf(stderr, "Font %s (%ux%u enc %s) doesn't contain 0x%X\n",
     334                 :            :                     candidate.filename.c_str(),
     335                 :            :                     candidate.width, candidate.height,
     336                 :            :                     candidate.encoding.data(),
     337         [ #  # ]:          0 :                     candidate.ch);
     338         [ #  # ]:          0 :                 std::fprintf(stderr, "  It contains ");
     339   [ #  #  #  # ]:          0 :                 for(auto& c: font->glyphs) std::fprintf(stderr, " %X", c.first);
     340         [ #  # ]:          0 :                 std::fprintf(stderr, "\n");
     341                 :            :             }
     342         [ -  + ]:     137214 :             assert(k != font->glyphs.end());
     343         [ -  + ]:     137214 :             const unsigned char* bitmap_pointer = &font->bitmap[ k->second ];
     344         [ -  + ]:     137214 :             assert(bitmap_pointer != nullptr);
     345                 :            : 
     346         [ +  - ]:     137214 :             bold_list[ch-firstch] = IsBoldFont(candidate.filename, candidate.width);
     347         [ +  + ]:     137210 :             bitmap_pointers[ch-firstch] = bitmap_pointer;
     348                 :            : 
     349   [ +  +  +  + ]:     137210 :             if(candidate.width == width && candidate.height == height)
     350                 :            :             {
     351                 :            :                 // Make sure this font doesn't get unloaded while it's in use
     352         [ +  + ]:       3720 :                 if(font_filenames.find(candidate.filename) == font_filenames.end())
     353                 :            :                 {
     354         [ +  - ]:         40 :                     font_filenames.insert(candidate.filename);
     355         [ +  - ]:         40 :                     loaded_fonts.emplace_back(std::move(font));
     356                 :            :                 }
     357                 :            :             }
     358                 :            :             else
     359                 :            :             {
     360                 :            :                 // Create resized bitmap
     361   [ +  -  -  - ]:     133490 :                 resize_pointers.emplace_back(ch, resized_bitmaps.size(), candidate.width,candidate.height);
     362         [ +  - ]:     133492 :                 resized_bitmaps.resize(resized_bitmaps.size() + height * ((width+7)/8));
     363                 :            : 
     364                 :            :                 // Make sure this font doesn't get unloaded while it's in use
     365         [ +  + ]:     133492 :                 if(font_filenames.find(candidate.filename) == font_filenames.end())
     366                 :            :                 {
     367         [ +  - ]:         53 :                     font_filenames.insert(candidate.filename);
     368         [ +  - ]:         53 :                     loaded_fonts.emplace_back(std::move(font));
     369                 :            :                 }
     370                 :            :             }
     371                 :     137222 :         }
     372                 :            : 
     373                 :            :         //#pragma omp parallel for
     374         [ +  + ]:     133499 :         for(std::size_t n=0; n<resize_pointers.size(); ++n)
     375                 :            :         {
     376                 :     133492 :             auto [ch, offs, swidth, sheight] = resize_pointers[n];
     377                 :     133492 :             const unsigned char* bitmap_pointer = bitmap_pointers[ch-firstch];
     378                 :            : 
     379                 :     133492 :             unsigned bytes_per_fontrow = (swidth + 7) / 8;
     380         [ +  + ]:    1076699 :             for(unsigned y=0; y<height; ++y)
     381                 :            :             {
     382                 :     943207 :                 unsigned fr_actual = (y  ) * sheight / height;
     383                 :     943207 :                 unsigned fr_next   = (y+1) * sheight / height;
     384                 :     943207 :                 unsigned long widefont = 0;
     385                 :    1412788 :                 do {
     386                 :    1412788 :                     const unsigned char* source = bitmap_pointer + fr_actual * bytes_per_fontrow;
     387                 :    1412788 :                     widefont |= Rn(source, bytes_per_fontrow);
     388         [ +  + ]:    1412788 :                 } while(++fr_actual < fr_next);
     389                 :            : 
     390                 :     943207 :                 widefont = ScaleFont(widefont, swidth, width);
     391         [ +  + ]:    1886414 :                 for(unsigned x=0; x<width; x+=8)
     392                 :     943207 :                     resized_bitmaps[offs + y*((width+7)/8) + x/8] = widefont >> x;
     393                 :            :             }
     394                 :     133492 :             bitmap_pointers[ch-firstch] = &resized_bitmaps[offs];
     395                 :            :         }
     396                 :            : 
     397                 :            :         /* After the list is updated, unload the old resources; free those which are no longer used. */
     398                 :          7 :         old_loaded_fonts.clear();
     399                 :          7 :         old_font_filenames.clear();
     400                 :            : 
     401         [ +  - ]:          7 :         {std::lock_guard<std::mutex> temp(fonts_lock); // for printing
     402         [ +  - ]:          7 :         std::fprintf(stderr, "For U+%04X..U+%04X in %ux%u Chose ", firstch,firstch+numch-1, width,height);
     403   [ +  -  +  + ]:        100 :         for(auto& f: font_filenames) fprintf(stderr, " %s", f.c_str());
     404         [ +  - ]:          7 :         std::fprintf(stderr, "\n");}
     405                 :            : 
     406         [ +  - ]:          7 :         ready = true;
     407         [ +  - ]:          7 :         lk.unlock();
     408                 :          7 :         ready_notification.notify_all();
     409         [ -  + ]:         14 :     });
     410         [ +  - ]:          7 :     updater.detach();
     411                 :          7 : }
     412                 :            : 
     413                 :     260013 : FontPlan::Glyph FontPlan::LoadGlyph(char32_t ch, unsigned scanline, unsigned render_width) const
     414                 :            : {
     415         [ +  + ]:     260013 :     if(!ready)
     416                 :            :     {
     417                 :          4 :         std::unique_lock<std::mutex> lk(working);
     418         [ +  + ]:          4 :         if(!ready)
     419         [ +  + ]:          6 :             ready_notification.wait(lk, [&]{ return ready == true; });
     420                 :          4 :     }
     421         [ +  - ]:     260013 :     unsigned bytes_per_fontrow = (std::get<0>(prev) + 7) / 8;
     422         [ +  - ]:     260013 :     const unsigned char* fontptr = bitmap_pointers[ch] + scanline * bytes_per_fontrow;
     423                 :     260013 :     unsigned long widefont = 0;
     424         [ +  - ]:     260013 :     if(bitmap_pointers[ch])
     425                 :            :     {
     426                 :     260013 :         widefont = Rn(fontptr, bytes_per_fontrow);
     427                 :            :     }
     428                 :            : 
     429                 :            :     /*if(std::get<0>(prev) != render_width)
     430                 :            :     {
     431                 :            :         if(render_width != std::get<0>(prev)*2)
     432                 :            :             fprintf(stderr, "font is %u, render %u\n",
     433                 :            :                 std::get<0>(prev), render_width);
     434                 :            :     }*/
     435                 :     260013 :     widefont = ScaleFont(widefont, std::get<0>(prev), render_width);
     436                 :            : 
     437                 :     260013 :     return {widefont, bold_list[ch]};
     438                 :            : }
     439                 :            : 
     440                 :         18 : void FontPlannerTick()
     441                 :            : {
     442   [ +  +  +  - ]:         18 :     static auto prev = std::chrono::system_clock::now();
     443                 :         18 :     auto now = std::chrono::system_clock::now();
     444                 :            :     // Every 10 seconds, free fonts that have not been used for 60 seconds
     445         [ +  + ]:         18 :     if(std::chrono::duration<double>(now - prev).count() >= unload_check_interval)
     446                 :            :     {
     447         [ +  - ]:          1 :         std::lock_guard<std::mutex> lk(fonts_lock);
     448                 :          1 :         for(auto i = loaded_fonts.begin(); i != loaded_fonts.end(); )
     449                 :            :         {
     450                 :         45 :             if(i->second.second.use_count() == 1
     451   [ +  -  -  + ]:         45 :             && std::chrono::duration<double>(now - i->second.first).count() >= unload_interval)
     452                 :            :             {
     453         [ #  # ]:          0 :                 std::fprintf(stderr, "Unloading %ux%u font %s\n",
     454         [ #  # ]:          0 :                     i->first.second.first, i->first.second.second,
     455         [ #  # ]:          0 :                     i->first.first.c_str());
     456                 :          0 :                 i = loaded_fonts.erase(i);
     457                 :            :             }
     458                 :            :             else
     459         [ +  + ]:         91 :                 ++i;
     460                 :            :         }
     461         [ +  - ]:          1 :         prev = std::move(now);
     462                 :          1 :     }
     463                 :         18 : }
     464                 :            : 
     465                 :            : #ifdef RUN_TESTS
     466                 :          1 : static void DeleteCaches()
     467                 :            : {
     468   [ +  -  -  + ]:          2 :     {auto [path, status] = FindCacheFile("similarities.dat", true);
     469                 :            :     // Erase this file and rebuild it
     470         [ -  + ]:          2 :     if(std::filesystem::exists(status))
     471                 :            :     {
     472         [ #  # ]:          0 :         std::filesystem::remove(path);
     473                 :          0 :     }}
     474   [ +  -  +  - ]:          2 :     {auto [path, status] = FindCacheFile("fonts-list.dat", true);
     475                 :            :     // Erase this file and rebuild it
     476         [ +  - ]:          1 :     if(std::filesystem::exists(status))
     477                 :            :     {
     478         [ +  - ]:          1 :         std::filesystem::remove(path);
     479                 :          1 :     }}
     480                 :          1 : }
     481                 :            : #include <iostream>
     482                 :          3 : TEST(font_planner, rebuild_test)
     483                 :            : {
     484                 :          1 :     std::cout << "Running font planner stress test. This will take a lot of time.\n" << std::flush;
     485                 :          1 :     std::cout << "- Deleting cache\n" << std::flush;
     486                 :          1 :     DeleteCaches();
     487                 :          1 :     std::cout << "- Loading fonts list\n" << std::flush;
     488                 :          1 :     GetFontsInfo();
     489                 :          1 :     std::cout << "- Building similarities\n" << std::flush;
     490                 :          1 :     GetSimilarities();
     491                 :          1 :     LoadPreferences();
     492                 :          1 : }
     493                 :          3 : TEST(font_planner, parallel_plan)
     494                 :            : {
     495                 :          1 :     std::cout << "- Testing 8x8, 8x12, 8x14, 8x16 parallel planning\n" << std::flush;
     496                 :          1 :     FontPlan tmpplan1, tmpplan2, tmpplan3, tmpplan4;
     497         [ +  - ]:          1 :     tmpplan1.Create(8,8,  0x00, 0x400);
     498         [ +  - ]:          1 :     tmpplan2.Create(8,12, 0x00, 0x400);
     499         [ +  - ]:          1 :     tmpplan3.Create(8,14, 0x00, 0x400);
     500         [ +  - ]:          1 :     tmpplan4.Create(8,16, 0x00, 0x400);
     501         [ +  - ]:          1 :     tmpplan1.LoadGlyph(0, 0, 8);
     502         [ +  - ]:          1 :     tmpplan2.LoadGlyph(0, 1, 8);
     503         [ +  - ]:          1 :     tmpplan3.LoadGlyph(0, 2, 8);
     504         [ +  - ]:          1 :     tmpplan4.LoadGlyph(0, 3, 8);
     505                 :          1 : }
     506                 :          3 : TEST(font_planner, large_plan)
     507                 :            : {
     508                 :          1 :     std::cout << "- Testing 7x7 large plan.\n" << std::flush;
     509                 :          1 :     char32_t first = 0x00;
     510                 :          1 :     char32_t last  = 0x20000;
     511                 :          1 :     FontPlan plan;
     512         [ +  - ]:          1 :     plan.Create(7,7, first, last-first+1); // Force some scaling
     513         [ +  + ]:     131074 :     for(char32_t c=first; c<=last; ++c)
     514                 :            :     {
     515         [ +  - ]:     131073 :         plan.LoadGlyph(c - first, 0, 12); // Force some scaling
     516                 :            :     }
     517                 :          1 : }
     518                 :          3 : TEST(font_planner, tick10s)
     519                 :            : {
     520                 :          1 :     unsigned backup = unload_check_interval;
     521                 :          1 :     unload_check_interval = 1;
     522                 :          1 :     std::cout << "- Running FontPlannerTick planner tick for " << unload_check_interval << " second(s).\n" << std::flush;
     523         [ +  + ]:          7 :     for(unsigned n=0; n<unload_check_interval*5+1; ++n)
     524                 :            :     {
     525                 :          6 :         FontPlannerTick();
     526                 :          6 :         usleep(200000);
     527                 :            :     }
     528                 :          1 :     unload_check_interval = backup;
     529                 :          1 : }
     530                 :          3 : TEST(font_planner, unload_fonts)
     531                 :            : {
     532                 :          1 :     unsigned backup = unload_interval;
     533                 :          1 :     unload_interval = 2;
     534                 :          1 :     std::cout << "- Running FontPlannerTick planner tick for another " << unload_interval << " seconds to trigger a font unload.\n" << std::flush;
     535         [ +  + ]:         13 :     for(unsigned n=0; n<unload_interval*5+2; ++n)
     536                 :            :     {
     537                 :         12 :         FontPlannerTick();
     538                 :         12 :         usleep(200000);
     539                 :            :     }
     540                 :          1 :     unload_interval = backup;
     541                 :          1 : }
     542                 :            : #endif

Generated by: LCOV version 1.16