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
|