GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 5.4% 336 / 0 / 6176
Functions: 9.3% 18 / 0 / 194
Branches: 6.0% 209 / 0 / 3464

core/info.cpp
Line Branch Exec Source
1 // Avisynth+
2 // https://avs-plus.net
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
17 // http://www.gnu.org/copyleft/gpl.html .
18 //
19 // Linking Avisynth statically or dynamically with other modules is making a
20 // combined work based on Avisynth. Thus, the terms and conditions of the GNU
21 // General Public License cover the whole combination.
22 //
23 // As a special exception, the copyright holders of Avisynth give you
24 // permission to link Avisynth with independent modules that communicate with
25 // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license
26 // terms of these independent modules, and to copy and distribute the
27 // resulting combined work under terms of your choice, provided that
28 // every copy of the combined work is accompanied by a complete copy of
29 // the source code of Avisynth (the version of Avisynth used to produce the
30 // combined work), being distributed under the terms of the GNU General
31 // Public License plus this exception. An independent module is a module
32 // which is not derived from or based on Avisynth, such as 3rd-party filters,
33 // import and export plugins, or graphical user interfaces.
34
35 // pinterf:
36 // high bit depth, planar RGB
37 // utf8 option, internally unicode
38 // info_h font definition reorganized, Latin-1 Supplement 00A0-00FF
39 // Configurable color
40 // Configurable halocolor (text outline)
41 // Configurable background fading
42 // Alignment
43 // multiline
44 // multiple size, multiple fonts, "Terminus", "info_h"
45 // chroma location, overlay-like weighted chroma handling
46
47 #include "info.h"
48 #include <cstring>
49 #include <sstream>
50 #include <fstream>
51 #include <unordered_map>
52 #include <array>
53 #include <iomanip>
54 #include <avs/filesystem.h>
55
56 #include <locale>
57 #include <cstdio>
58 #include <cassert>
59 #include "fonts/fixedfonts.h"
60 #include "strings.h"
61 #include "../convert/convert_helper.h"
62
63 // helper function for remapping an utf8 string to font index entry list
64 7 std::vector<int> BitmapFont::remap(const std::string& s_utf8)
65 {
66 // new vector with characters remapped to font table indexes
67 7 std::vector<int> s_remapped;
68
1/2
✓ Branch 3 → 4 taken 7 times.
✗ Branch 3 → 43 not taken.
7 const size_t real_len = str_utf8_size(s_utf8);
69
1/2
✓ Branch 4 → 5 taken 7 times.
✗ Branch 4 → 43 not taken.
7 s_remapped.resize(real_len);
70
71 7 size_t index = 0;
72 7 const char* p = s_utf8.data();
73 7 const char* end = p + s_utf8.size();
74
2/2
✓ Branch 33 → 8 taken 369 times.
✓ Branch 33 → 34 taken 7 times.
376 while (p < end) {
75 // Get the lead byte of the current UTF-8 character
76 369 unsigned char lb = static_cast<unsigned char>(*p);
77 // Determine the number of bytes in the current UTF-8 character
78 369 int n = 0;
79
1/2
✓ Branch 8 → 9 taken 369 times.
✗ Branch 8 → 10 not taken.
369 if ((lb & 0x80) == 0) n = 1; // 0xxxxxxx
80 else if ((lb & 0xE0) == 0xC0) n = 2; // 110xxxxx
81 else if ((lb & 0xF0) == 0xE0) n = 3; // 1110xxxx
82 else if ((lb & 0xF8) == 0xF0) n = 4; // 11110xxx
83 else {
84 // Invalid lead byte, skip
85 ++p;
86 continue;
87 }
88 // Create a buffer to store the UTF-8 character code
89 369 char utf8_char_buf[5] = { 0 };
90 // Copy the bytes from the string to the buffer
91 369 std::memcpy(utf8_char_buf, p, n);
92 // finds by utf8 character sequence in font index table
93
2/4
✓ Branch 19 → 20 taken 369 times.
✗ Branch 19 → 38 not taken.
✓ Branch 20 → 21 taken 369 times.
✗ Branch 20 → 36 not taken.
738 auto it = charReMapUtf8.find(utf8_char_buf);
94
1/2
✓ Branch 25 → 26 taken 369 times.
✗ Branch 25 → 29 not taken.
369 if (it != charReMapUtf8.end())
95 369 s_remapped[index] = it->second;
96 else
97 s_remapped[index] = 0; // empty neutral character (space)
98 369 index++;
99 // Advance the pointer by the number of bytes
100 369 p += n;
101 }
102 7 return s_remapped;
103 }
104
105 // Internal function! For creating source code from a previously LoadBDF'd font file
106 // see fixedfonts.cpp
107 void BitmapFont::SaveAsC(const uint16_t* _codepoints)
108 {
109 if (font_filename == "") return; // no GUS no sound :)
110
111 std::string fontname;
112 if (font_filename.substr(0, 4) == "ter-")
113 font_name = "Terminus";
114 else
115 font_name = font_filename;
116
117 std::ostringstream ss;
118 ss << "namespace fixed_font_# {" << std::endl;
119 ss << "// -- start of autogenerated text ---" << std::endl;
120 ss << "// definition section for font: " << font_filename << std::endl;
121 ss << "constexpr int CHARCOUNT = " << std::to_string(number_of_chars) << ";" << std::endl;
122 ss << "constexpr int WIDTH = " << std::to_string(global_bbx.width) << ";" << std::endl;
123 ss << "constexpr int HEIGHT = " << std::to_string(global_bbx.height) << ";" << std::endl;
124 ss << "constexpr int OFFSET_X = " << std::to_string(global_bbx.offset_x) << ";" << std::endl;
125 ss << "constexpr int OFFSET_Y = " << std::to_string(global_bbx.offset_y) << ";" << std::endl;
126 ss << "constexpr FixedFont_info_t fixedfont_info = {" << std::endl;
127 ss << " \"" << font_filename << "\", // font name" << std::endl;
128 ss << " \"" << font_filename << "\", // font name internal" << std::endl;
129 ss << " CHARCOUNT, // num of chars" << std::endl;
130 ss << " WIDTH, HEIGHT, OFFSET_X, OFFSET_Y," << std::endl;
131 ss << " " << (bold ? "true" : "false") << " // bold" << std::endl;
132 ss << "};" << std::endl;
133 ss << "// font bitmap definitions" << std::endl;
134 ss << "constexpr std::array<uint16_t, CHARCOUNT * HEIGHT> fixedfont_bitmap = {" << std::endl;
135 for (int charcount = 0; charcount < number_of_chars; charcount++)
136 {
137 constexpr int LINES_BY_N = 16; // maximum number of constants per line
138 for (int y = 0; y < global_bbx.height; y++) {
139 ss << "0x";
140 for (int x = 0; x < fontline_bytes; x++) {
141 uint8_t charline = font_bitmaps[(charcount * global_bbx.height + y) * fontline_bytes + x];
142 ss << std::setfill('0') << std::setw(2) << std::hex << charline;
143 }
144 const bool last = charcount == number_of_chars - 1 && y == global_bbx.height - 1;
145 if (!last) ss << ",";
146 if (y == global_bbx.height - 1)
147 ss << " // u" << std::setw(4) << std::hex << _codepoints[charcount];
148 if (y % LINES_BY_N == LINES_BY_N - 1) ss << std::endl; // last line of character- forced new line
149 }
150 if (global_bbx.height % LINES_BY_N != 0)
151 ss << std::endl;
152 };
153 // example output:
154 // 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
155 // 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // u0020
156 // 0x0000,0x0000,0x0000,0x0000,0x0300,0x0300,0x0300,0x0300,0x0300,0x0300,0x0300,0x0300,0x0300,0x0300,0x0300,0x0300,
157 // 0x0000,0x0000,0x0300,0x0300,0x0300,0x0300,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // u0021
158
159 ss << "};" << std::endl;
160 ss << "// codepoints array" << std::endl;
161 ss << "constexpr std::array<uint16_t, CHARCOUNT> fixedfont_codepoints = {" << std::endl;
162 for (int charcount = 0; charcount < number_of_chars; charcount++)
163 {
164 constexpr int LINES_BY_N = 16;
165 int val = _codepoints[charcount];
166 ss << "0x" << std::setfill('0') << std::setw(4) << std::hex << val;
167 const bool last = charcount == number_of_chars - 1;
168 if (!last) ss << ",";
169 if (charcount % LINES_BY_N == LINES_BY_N - 1) ss << std::endl;
170 }
171 ss << "};" << std::endl;
172
173 ss << "} // namespace" << std::endl;
174 ss << "// -- end of autogenerated text ---" << std::endl;
175 //
176 std::ofstream outFile(font_filename + ".cpp_sample");
177 outFile << ss.str();
178 outFile.close();
179
180 /* usage:
181 // Predefined fonts main table
182 constexpr int PREDEFINED_FONT_COUNT = 2;
183 static const uint16_t* font_bitmaps[PREDEFINED_FONT_COUNT] =
184 {
185 &fixed_font_1::fixedfont_bitmap[0],
186 &fixed_font_2::fixedfont_bitmap[0]
187 // ...
188 };
189 static const uint16_t* font_codepoints[PREDEFINED_FONT_COUNT] =
190 {
191 &fixed_font_1::fixedfont_codepoints[0],
192 &fixed_font_2::fixedfont_codepoints[0]
193 // ...
194 };
195 static const FixedFont_info_t* font_infos[PREDEFINED_FONT_COUNT] =
196 {
197 &fixed_font_1::fixedfont_info,
198 &fixed_font_2::fixedfont_info
199 // ...
200 };
201 */
202 }
203
204 typedef struct CharInfo { // STARTCHAR charname
205 std::string friendly_name;
206 uint16_t encoding;
207 int dwidth_x, dwidth_y;
208 // DWIDTH not supported, same as main PIXELSIZE
209 // font properties can be overridden.
210 BBX bbx;
211 int empty_lines_bottom;
212 int empty_lines_top;
213 int bits_to_shift;
214 } CharDef;
215
216 typedef struct FontProperties {
217 std::string Copyright;
218 std::string Notice;
219 std::string Family_name;
220 std::string Weight_name;
221 int pixel_size;
222 int font_ascent; // 12
223 int font_descent; // 4
224 uint16_t default_char; // 65533
225 } FontProperties;
226
227 typedef struct FontInfo {
228 std::string font; // n/a
229 int size_points, size_dpi_x, size_dpi_y; // n/a
230 // these may be overridden by individual characters.
231 BBX bbx; // FONTBOUNDINGBOX e.g. 8 16 0 -4
232 int chars; // number of characters
233 int fontline_bytes; // length of a single font line in the byte array
234 } FontInfo;
235
236 class BdfFont {
237 public:
238 std::string font_filename;
239 FontInfo font_info;
240 FontProperties font_properties;
241 std::vector<uint16_t> codepoints_array;
242 std::vector<std::string> charnames_array;
243 // each character can have different bounding box dimensions from global one
244 std::vector<BBX> bbx_array;
245 // one character line is not limited to 16/32 pixels anymore:
246 std::vector<uint8_t> font_bitmaps;
247 };
248
249 std::string UnQuote(std::string s) {
250 if (s.size() >= 2 && s.substr(0, 1) == "\"" && (s.substr(s.size() - 1, 1) == "\""))
251 return s.substr(1,s.size()-2); // zero based
252 return s;
253 }
254
255 static void vector_shl(uint8_t* buf, const size_t size, const size_t bits) {
256 const size_t whole_chunks = bits / 8;
257
258 // more bits that size
259 if (whole_chunks >= size) {
260 memset(buf, 0, size);
261 return;
262 }
263
264 if (whole_chunks) {
265 memmove(buf, buf + whole_chunks, size - whole_chunks);
266 memset(buf + size - whole_chunks, 0, whole_chunks);
267 }
268
269 const uint8_t final_shift = bits % 8;
270
271 if (final_shift) {
272 const auto right = 8 - final_shift;
273 const size_t len = size - whole_chunks - 1;
274 for (size_t i = 0; i < len; i++) {
275 buf[i] = (buf[i] << final_shift) | (buf[i + 1] >> right);
276 }
277 buf[len] = buf[len] << final_shift;
278 }
279 }
280
281 static void vector_shr(uint8_t* buf, const size_t size, const size_t bits) {
282 const size_t whole_chunks = bits / 8;
283
284 // more bits that size
285 if (whole_chunks >= size) {
286 memset(buf, 0, size);
287 return;
288 }
289
290 if (whole_chunks) {
291 memmove(buf + whole_chunks, buf, size - whole_chunks);
292 memset(buf, 0, whole_chunks);
293 }
294
295 const uint8_t final_shift = bits % 8;
296
297 if (final_shift) {
298 const auto left = 8 - final_shift;
299 const size_t len = size - whole_chunks - 1;
300 auto index = size - 1;
301 for (size_t i = 0; i < len; i++) {
302 buf[index] = (buf[index] >> final_shift) | (buf[index - 1] << left);
303 index--;
304 }
305 buf[index] = buf[index] >> final_shift;
306 }
307 }
308
309 static BdfFont LoadBMF(std::string name, bool bold) {
310
311 // https://en.wikipedia.org/wiki/Glyph_Bitmap_Distribution_Format
312 // also for make cpp source code, see BdfFont methods
313
314 enum loadstate { ls_Init, ls_StartFont, ls_StartProperties, ls_Char };
315
316 BdfFont fnt;
317 CharInfo current_char;
318
319 std::string temp;
320 //std::ifstream ss("c:\\Download\\terminus-font-4.48\\terminus-font-4.48\\ter-u16n.bdf");
321 std::ifstream ss;
322 // explicite font file name
323 auto fname = fs::path(name);
324
325 if (!fs::exists(name))
326 return fnt;
327
328 fnt.font_filename = fname.filename().generic_string();
329 ss.open(name);
330
331 loadstate state = ls_Init;
332
333 size_t line_counter = 0;
334 int char_counter = 0;
335
336 // make list governed by LF separator
337 while (std::getline(ss, temp, '\n')) {
338 std::string token;
339 std::istringstream ssline(temp);
340
341 std::getline(ssline, token, ' ');
342
343 if (token.size() == 0) continue;
344
345 switch (state) {
346 case ls_Init:
347 if (token == "STARTFONT")
348 state = ls_StartFont;
349 else {
350 // unexpected token
351 }
352 break;
353 case ls_StartFont:
354 if (token == "ENDFONT") {
355 state = ls_Init;
356 }
357 else if (token == "FONT") {
358 // FONT-xos4-Terminus-Bold-R-Normal--16-160-72-72-C-80-ISO10646-1
359 std::getline(ssline, token, ' ');
360 fnt.font_info.font = UnQuote(token);
361 }
362 else if (token == "SIZE") {
363 // SIZE 16 72 72
364 // size in points, X and Y-axis resolution
365 std::getline(ssline, token, ' ');
366 fnt.font_info.size_points = std::stoi(token);
367 std::getline(ssline, token, ' ');
368 fnt.font_info.size_dpi_x = std::stoi(token);
369 std::getline(ssline, token, ' ');
370 fnt.font_info.size_dpi_y = std::stoi(token);
371 }
372 else if (token == "FONTBOUNDINGBOX") {
373 // FONTBOUNDINGBOX 8 16 0 -4
374 // bounding box of 8 pixels wide and 16 pixels high
375 // lower left hand corner starting at x=0, y=-4. Note that although the bounding box is defined to be a 8x16 cell,
376 // this can be overridden for individual glyphs.
377
378 // e.g: Terminus: FONTBOUNDINGBOX 8 16 0 -4   Terminus
379 // later for "A": BBX 8 16 0 -4
380 // Unifont: FONTBOUNDINGBOX 16 16 0 -2  Unifont
381 // later for "A": BBX 8 16 0 -2 should override the global bounding box
382
383 std::getline(ssline, token, ' ');
384 fnt.font_info.bbx.width = std::stoi(token);
385 std::getline(ssline, token, ' ');
386 fnt.font_info.bbx.height = std::stoi(token);
387 std::getline(ssline, token, ' ');
388 fnt.font_info.bbx.offset_x = std::stoi(token);
389 std::getline(ssline, token, ' ');
390 fnt.font_info.bbx.offset_y = std::stoi(token);
391
392 // a single font line can occupy variable amount of bytes depending on font width
393 // Note: in this implementation these global font dimensions are _the_
394 // dimensions of the fixed size font
395 // Note, that this is the maximum theoretical width; some BDF fonts (unifont) contain variable width characters
396 // contrary to that it contains monospaced "C" type characters. In this case latin characters are 8 pixels wide,
397 // and use DWIDTH and BBOX as 8, less than FONTBOUNDINGBOX value of 16.
398 // but CJK characters are 16 pixels wide.
399 fnt.font_info.fontline_bytes = (fnt.font_info.bbx.width + 7) / 8;
400 }
401 else if (token == "STARTPROPERTIES") {
402 // STARTPROPERTIES 20
403 // do not check line count now
404 state = ls_StartProperties;
405 }
406 else if (token == "CHARS") {
407 std::getline(ssline, token);
408 fnt.font_info.chars = std::stoi(token);
409 // allocate area for "character_count" * "height"
410 fnt.font_bitmaps.resize(fnt.font_info.chars * fnt.font_info.bbx.height * fnt.font_info.fontline_bytes);
411 fnt.charnames_array.resize(fnt.font_info.chars);
412 fnt.codepoints_array.resize(fnt.font_info.chars);
413 fnt.bbx_array.resize(fnt.font_info.chars);
414 }
415 else if (token == "STARTCHAR") {
416 std::getline(ssline, token);
417 current_char.friendly_name = token;
418 current_char.encoding = 0;
419 current_char.dwidth_x = 0;
420 current_char.dwidth_y = 0;
421 current_char.bbx = fnt.font_info.bbx; // inherited from FONTBOUNDINGBOX
422 current_char.bits_to_shift = 0;
423 current_char.empty_lines_bottom = 0;
424 current_char.empty_lines_top = 0;
425
426 state = ls_Char;
427 }
428 else {
429 // unexpected token
430 }
431 break;
432 case ls_StartProperties:
433 if (token == "ENDPROPERTIES") {
434 state = ls_StartFont;
435 }
436 /*
437 FAMILY_NAME "Terminus"
438 FOUNDRY "xos4"
439 SETWIDTH_NAME "Normal"
440 ADD_STYLE_NAME ""
441 COPYRIGHT "Copyright (C) 2019 Dimitar Toshkov Zhekov"
442 NOTICE "Licensed under the SIL Open Font License, Version 1.1"
443 WEIGHT_NAME "Bold"
444 SLANT "R"
445 PIXEL_SIZE 16
446 POINT_SIZE 160
447 RESOLUTION_X 72
448 RESOLUTION_Y 72
449 SPACING "C"
450 AVERAGE_WIDTH 80
451 CHARSET_REGISTRY "ISO10646"
452 CHARSET_ENCODING "1"
453 MIN_SPACE 8
454 */
455 else if (token == "FAMILY_NAME") {
456 //FAMILY_NAME "Terminus"
457 std::getline(ssline, token, ' ');
458 fnt.font_properties.Family_name = UnQuote(token);
459 }
460 else if (token == "COPYRIGHT") {
461 std::getline(ssline, token, ' ');
462 fnt.font_properties.Copyright = UnQuote(token);
463 }
464 else if (token == "NOTICE") {
465 std::getline(ssline, token, ' ');
466 fnt.font_properties.Notice = UnQuote(token);
467 }
468 else if (token == "WEIGHT_NAME") {
469 // "Medium", "Bold"
470 std::getline(ssline, token, ' ');
471 fnt.font_properties.Weight_name = UnQuote(token);
472 }
473 else if (token == "PIXEL_SIZE") {
474 std::getline(ssline, token, ' ');
475 fnt.font_properties.pixel_size = std::stoi(token);
476 }
477 else if (token == "FONT_ASCENT") {
478 // FONT_ASCENT 12
479 // 12 of the 16 pixels in height are above the baseline.
480 std::getline(ssline, token, ' ');
481 fnt.font_properties.font_ascent = std::stoi(token);
482 }
483 else if (token == "FONT_DESCENT") {
484 // FONT_DESCENT 4
485 // 4 of the 16 pixels in height are below the baseline
486 std::getline(ssline, token, ' ');
487 fnt.font_properties.font_descent = std::stoi(token);
488 }
489 else if (token == "DEFAULT_CHAR") {
490 // DEFAULT_CHAR 65533
491 std::getline(ssline, token, ' ');
492 fnt.font_properties.default_char = std::stoi(token);
493 }
494 break;
495 case ls_Char:
496 if (token == "ENDCHAR") {
497 // add to vector
498 state = ls_StartFont;
499 }
500 else if (token == "DWIDTH") {
501 // DWIDTH 9 0
502 // declares the Device Width of a glyph. After the glyph is rendered, the start of the next glyph is
503 // offset 9 pixels on the X-axis and
504 // offset 0 pixels on the Y-axis from the current glyph origin.
505 // They are not necessarily equal to the width of the glyph.
506 // It is simply the offset on the X-axis to move the current point to the start of the next glyph.
507 // dwidth_y is not used in Avisynth, fonts are of fixed heights
508 std::getline(ssline, token, ' ');
509 current_char.dwidth_x = std::stoi(token);
510 std::getline(ssline, token, ' ');
511 current_char.dwidth_y = std::stoi(token);
512 }
513 else if (token == "ENCODING") {
514 // ENCODING 32
515 std::getline(ssline, token, ' ');
516 current_char.encoding = std::stoi(token);
517 }
518 else if (token == "BBX") {
519 // BBX 8 16 0 -4 bounding box. 8 pixels wide and 16 pixels tall;
520 // lower left corner is offset by 0 on the X and -4 pixels on the Y axis.
521
522 // Originally we only assumed Monospaced, uniformly fixed size fonts.
523 // But technically we can have variable width characters inside due to per character BBOX.
524 // E.g. Latin characters usually have a DWIDTH of 8, while others like CJK Unifont
525 // characters have a DWIDTH of 16. (like U + 007F and CJK glyphs). Because of these
526 // wider characters, the FONTBOUNDINGBOX must be 16 pixels wide to signal the renderer
527 // the maximum possible width for the font type (useful for buffer preallocation). But
528 // ordinarly Latin characters are still 8 bits wide.
529 // So can we support "fixed width" fonts which contains character bitmaps of different widths.
530
531 std::getline(ssline, token, ' ');
532 current_char.bbx.width = std::stoi(token);
533 std::getline(ssline, token, ' ');
534 current_char.bbx.height = std::stoi(token);
535 std::getline(ssline, token, ' ');
536 current_char.bbx.offset_x = std::stoi(token);
537 std::getline(ssline, token, ' ');
538 current_char.bbx.offset_y = std::stoi(token);
539 current_char.empty_lines_bottom = current_char.bbx.offset_y - fnt.font_info.bbx.offset_y;
540 current_char.empty_lines_top = fnt.font_info.bbx.height - (current_char.empty_lines_bottom + current_char.bbx.height);
541 current_char.bits_to_shift = current_char.bbx.width - current_char.dwidth_x; // actual per-characer dwidth_x can differ from font_info.bbx.width
542 }
543 else if (token == "BITMAP") {
544 /* space:
545 STARTCHAR space
546 ENCODING 32
547 SWIDTH 692 0
548 DWIDTH 9 0
549 BBX 0 0 0 0
550 BITMAP
551 ENDCHAR
552
553 or
554
555 6x12:
556 BITMAP
557 00
558 00
559 70
560 88
561 08
562 30
563 08
564 08
565 88
566 70
567 00
568 00
569 ENDCHAR
570
571 10x18:
572 BITMAP
573 0000
574 0000
575 0000
576 3F00
577 6180
578 6180
579 0180
580 0180
581 1F00
582 0180
583 0180
584 0180
585 6180
586 6180
587 3F00
588 0000
589 0000
590 0000
591 ENDCHAR
592 */
593 fnt.codepoints_array[char_counter] = current_char.encoding;
594 fnt.charnames_array[char_counter] = current_char.friendly_name;
595 fnt.bbx_array[char_counter] = current_char.bbx;
596 char_counter++;
597
598 // by spec: charlines are left aligned within byte boundaries
599
600 // fill empty top lines
601 for (int count = 0; count < current_char.empty_lines_top; count++) {
602 // fontline_bytes bytes per line
603 for(int i = 0; i < fnt.font_info.fontline_bytes; i++)
604 fnt.font_bitmaps[line_counter++] = 0;
605 }
606
607 // one character line is of (almost) arbitrary length, not limited to 16 or 32 bits
608 std::vector<uint8_t> charline_buffer(fnt.font_info.fontline_bytes);
609 const size_t linebuf_len = fnt.font_info.fontline_bytes;
610
611 for(int count = 0; count < current_char.bbx.height; count++)
612 {
613 std::getline(ss, temp);
614
615 size_t len = temp.length();
616 const size_t bytes_defined = len / 2;
617
618 if (len % 2) {
619 // weird, one byte must be defined on two hexadecimal characters
620 // FIXME: should give an error
621 }
622 if (bytes_defined > linebuf_len) {
623 // weird, more bytes defined that needed for the font width defined in the header
624 // FIXME: should give an error
625 len = linebuf_len * 2; // until then a safe limit
626 }
627 size_t buf_ctr = 0;
628 // two hex characters by two character then put into byte buffer
629 for (size_t i = 0; i < len; i += 2) {
630 auto ssss = temp.substr(i, 2); // next two hex chars
631 charline_buffer[buf_ctr++] = (uint8_t)std::stoul(ssss, nullptr, 16);
632 }
633 for (auto i = buf_ctr; i < linebuf_len; i++)
634 charline_buffer[i] = 0;
635
636 // shift full buffer, they are msb...lsb for increasing addresses
637 if (current_char.bits_to_shift < 0) {
638 const int bits_to_shift_left = -current_char.bits_to_shift;
639 vector_shl(charline_buffer.data(), fnt.font_info.fontline_bytes, bits_to_shift_left);
640 }
641 else if (current_char.bits_to_shift > 0) {
642 const int bits_to_shift_right = current_char.bits_to_shift;
643 vector_shr(charline_buffer.data(), fnt.font_info.fontline_bytes, bits_to_shift_right);
644 }
645
646 for(int i = 0; i < fnt.font_info.fontline_bytes; i++) {
647 fnt.font_bitmaps[line_counter++] = charline_buffer[i];
648 }
649 }
650
651 // fill empty bottom lines
652 for (int count = 0; count < current_char.empty_lines_bottom; count++) {
653 // fontline_bytes bytes per line
654 for (int i = 0; i < fnt.font_info.fontline_bytes; i++)
655 fnt.font_bitmaps[line_counter++] = 0;
656 }
657
658 }
659 break;
660 }
661 }
662
663 return fnt;
664 }
665 /*
666
667 STARTFONT 2.1
668 FONT -xos4-Terminus-Bold-R-Normal--16-160-72-72-C-80-ISO10646-1
669 SIZE 16 72 72
670 FONTBOUNDINGBOX 8 16 0 -4
671
672 STARTPROPERTIES 20
673 FAMILY_NAME "Terminus"
674 FOUNDRY "xos4"
675 SETWIDTH_NAME "Normal"
676 ADD_STYLE_NAME ""
677 COPYRIGHT "Copyright (C) 2019 Dimitar Toshkov Zhekov"
678 NOTICE "Licensed under the SIL Open Font License, Version 1.1"
679 WEIGHT_NAME "Bold"
680 SLANT "R"
681 PIXEL_SIZE 16
682 POINT_SIZE 160
683 RESOLUTION_X 72
684 RESOLUTION_Y 72
685 SPACING "C"
686 AVERAGE_WIDTH 80
687 CHARSET_REGISTRY "ISO10646"
688 CHARSET_ENCODING "1"
689 MIN_SPACE 8
690 FONT_ASCENT 12
691 FONT_DESCENT 4
692 DEFAULT_CHAR 65533
693 ENDPROPERTIES
694
695 CHARS 1354
696
697 STARTCHAR space
698 ENCODING 32
699 SWIDTH 500 0
700 DWIDTH 8 0
701 BBX 8 16 0 -4
702 BITMAP
703 ...
704 00
705 ENDCHAR
706
707 ENDFONT
708 */
709
710 static constexpr int ATA_LEFT = 1;
711 static constexpr int ATA_RIGHT = 2;
712 static constexpr int ATA_CENTER = 4;
713
714 static constexpr int ATA_TOP = 8;
715 static constexpr int ATA_BOTTOM = 16;
716 static constexpr int ATA_BASELINE = 32;
717
718 8 static int alignToBitmask(int align_1_to_9)
719 {
720 // alignment 1-9: digit positions on numeric keypad
721 8 int al = 0;
722
1/10
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 4 not taken.
✗ Branch 2 → 5 not taken.
✗ Branch 2 → 6 not taken.
✗ Branch 2 → 7 not taken.
✗ Branch 2 → 8 not taken.
✓ Branch 2 → 9 taken 8 times.
✗ Branch 2 → 10 not taken.
✗ Branch 2 → 11 not taken.
✗ Branch 2 → 12 not taken.
8 switch (align_1_to_9) // This spec where [X, Y] is relative to the text (inverted logic)
723 {
724 case 1: al = ATA_BOTTOM | ATA_LEFT; break; // .----
725 case 2: al = ATA_BOTTOM | ATA_CENTER; break; // --.--
726 case 3: al = ATA_BOTTOM | ATA_RIGHT; break; // ----.
727 case 4: al = ATA_BASELINE | ATA_LEFT; break; // .____
728 case 5: al = ATA_BASELINE | ATA_CENTER; break; // __.__
729 case 6: al = ATA_BASELINE | ATA_RIGHT; break; // ____.
730 8 case 7: al = ATA_TOP | ATA_LEFT; break; // `----
731 case 8: al = ATA_TOP | ATA_CENTER; break; // --`--
732 case 9: al = ATA_TOP | ATA_RIGHT; break; // ----`
733 default: al = ATA_BASELINE | ATA_LEFT; break; // .____
734 }
735 8 return al;
736 }
737
738 42 static int getColorForPlane(int plane, int color)
739 {
740
3/5
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 14 times.
✓ Branch 2 → 5 taken 14 times.
✓ Branch 2 → 6 taken 14 times.
✗ Branch 2 → 7 not taken.
42 switch (plane) {
741 case PLANAR_A:
742 return (color >> 24) & 0xff; break;
743 14 case PLANAR_R:
744 case PLANAR_Y:
745 14 return (color >> 16) & 0xff; break;
746 14 case PLANAR_G:
747 case PLANAR_U:
748 14 return (color >> 8) & 0xff; break;
749 14 case PLANAR_B:
750 case PLANAR_V:
751 14 return color & 0xff; break;
752 }
753 return color & 0xFF;
754 }
755
756 template<typename pixel_t, bool fadeBackground>
757 void AVS_FORCEINLINE LightOnePixelPackedRGB(const bool lightIt, BYTE* _dp, int val_color_R, int val_color_G, int val_color_B)
758 {
759 59427 pixel_t* dp = reinterpret_cast<pixel_t*>(_dp);
760 59427 if (lightIt) {
761 7902 dp[0] = val_color_B;
762 7902 dp[1] = val_color_G;
763 7902 dp[2] = val_color_R;
764 }
765 else {
766 if constexpr (fadeBackground) {
767 dp[0] = (pixel_t)((dp[0] * 7) >> 3);
768 dp[1] = (pixel_t)((dp[1] * 7) >> 3);
769 dp[2] = (pixel_t)((dp[2] * 7) >> 3);
770 }
771 }
772 59427 }
773
774 template<typename pixel_t, bool fadeBackground, bool isRGB>
775 void AVS_FORCEINLINE LightOnePixel(const bool lightIt, pixel_t* dstp, int j, pixel_t& val_color, int bits_per_pixel)
776 {
777 // some optimization hint
778 if constexpr (sizeof(pixel_t) == 1)
779 bits_per_pixel = 8;
780 else if constexpr (sizeof(pixel_t) == 4)
781 bits_per_pixel = 32;
782
783 if (lightIt) { // character definition bits aligned to msb
784 dstp[j] = val_color;
785 }
786 else {
787 // 16 = y_min
788 // speed optimization: one subtraction less, 5-8% faster
789 // (((Y - 16) * 7) >> 3) + 16 = ((Y * 7) >> 3) + 2
790 // in general: ((Y * 7) >> 3) + n, where n = range_min - ((range_min * 7) >> 3)
791 if constexpr (fadeBackground) {
792 // background darkening
793 if constexpr (isRGB) {
794 if constexpr (sizeof(pixel_t) != 4)
795 dstp[j] = (pixel_t)((dstp[j] * 7) >> 3);
796 else {
797 constexpr float factor = 7.0f / 8;
798 dstp[j] = (pixel_t)(dstp[j] * factor);
799 }
800 }
801 else {
802 if constexpr (sizeof(pixel_t) != 4) {
803 const int range_min = 16 << (bits_per_pixel - 8);
804 const int n = range_min - ((range_min * 7) >> 3);
805 dstp[j] = (pixel_t)(((dstp[j] * 7) >> 3) + n); // (_dstp[j] - range_min) * 7) >> 3) + range_min);
806 }
807 else {
808 constexpr float range_min_f = 16.0f / 255.0f;
809 dstp[j] = (pixel_t)(((dstp[j] - range_min_f) * 7 / 8) + range_min_f);
810 }
811 }
812 }
813 }
814 }
815
816 template<typename pixel_t, int logXRatioUV, int logYRatioUV, bool fadeBackground, ChromaLocationMode chromaMode>
817 static void LightOneUVPixel(pixel_t* dstpU, int j, pixel_t* dstpV, pixel_t& font_color_u, pixel_t& font_color_v, pixel_t& halo_color_u, pixel_t& halo_color_v,
818 int fontpixelcount, int halopixelcount,
819 int bits_per_pixel
820 )
821 {
822 if constexpr (!fadeBackground) {
823 if (halopixelcount == 0 && fontpixelcount == 0) return; // no change, keep background
824 }
825
826 // some optimization hint
827 if constexpr (sizeof(pixel_t) == 1)
828 bits_per_pixel = 8;
829 else if constexpr (sizeof(pixel_t) == 4)
830 bits_per_pixel = 32;
831
832 // weighed count
833 constexpr int totalpixelcount =
834 (chromaMode == LEFT_420) ? 8 : // 1-2-1 | 1-2-1
835 (chromaMode == LEFT_422) ? 4 : // 1-2-1
836 (chromaMode == CENTER_420) ? 4 : // 1-1 | 1-1
837 (chromaMode == CENTER_422) ? 2 : // 1-1
838 (chromaMode == CENTER_411) ? 4 : // 1-1-1-1
839 1; // unreached
840
841 if (fontpixelcount == totalpixelcount) {
842 dstpU[j] = font_color_u;
843 dstpV[j] = font_color_v;
844 }
845 else if (halopixelcount == totalpixelcount) {
846 dstpU[j] = halo_color_u;
847 dstpV[j] = halo_color_v;
848 }
849 else {
850 // not reached, only when subsampled
851 pixel_t actualU = dstpU[j];
852 pixel_t actualV = dstpV[j];
853 const int backgroundpixelcount = totalpixelcount - fontpixelcount - halopixelcount;
854
855 if constexpr (fadeBackground) {
856 // have to fade the existing background color
857 // speed optimization: one subtraction less
858 // (((U - 128) * 7) >> 3) + 128 = ((U * 7) >> 3) + 16
859 // in general: ((U * 7) >> 3) + n where n = range_half - ((range_half * 7) >> 3)
860 if constexpr (sizeof(pixel_t) != 4) {
861 int range_half = 1 << (bits_per_pixel - 1);
862 int n = range_half - ((range_half * 7) >> 3);
863 actualU = (pixel_t)(((actualU * 7) >> 3) + n); // ((((U - range_half) * 7) >> 3) + range_half);
864 actualV = (pixel_t)(((actualV * 7) >> 3) + n);
865 }
866 else {
867 constexpr float chroma_center = 0.0f; // ancient times this was 0.5
868 constexpr float factor = 7.0f / 8.0f;
869 actualU = (pixel_t)(((actualU - chroma_center) * factor) + chroma_center);
870 actualV = (pixel_t)(((actualV - chroma_center) * factor) + chroma_center);
871 }
872 }
873
874 // compute resulting color weighted by pixel kinds
875 if constexpr (sizeof(pixel_t) != 4) {
876 constexpr int rounder = totalpixelcount >> 1;
877 constexpr int divshift =
878 (chromaMode == LEFT_420) ? 3 :
879 (chromaMode == LEFT_422) ? 2 :
880 (chromaMode == CENTER_420) ? 2 :
881 (chromaMode == CENTER_422) ? 1 :
882 (chromaMode == CENTER_411) ? 2 :
883 0; // unreached
884
885 const int effective_color_u = (font_color_u * fontpixelcount + halo_color_u * halopixelcount + actualU * backgroundpixelcount + rounder);
886 const int effective_color_v = (font_color_v * fontpixelcount + halo_color_v * halopixelcount + actualV * backgroundpixelcount + rounder);
887 dstpU[j] = effective_color_u >> divshift;
888 dstpV[j] = effective_color_v >> divshift;
889 }
890 else {
891 constexpr float chroma_center = 0.0f;
892 float effective_color_u = (font_color_u - chroma_center) * fontpixelcount + (halo_color_u - chroma_center) * halopixelcount + (actualU - chroma_center) * backgroundpixelcount;
893 float effective_color_v = (font_color_v - chroma_center) * fontpixelcount + (halo_color_v - chroma_center) * halopixelcount + (actualV - chroma_center) * backgroundpixelcount;
894 dstpU[j] = effective_color_u / totalpixelcount + chroma_center;
895 dstpV[j] = effective_color_v / totalpixelcount + chroma_center;
896 }
897 }
898 }
899
900 // s (containing the indexes to the fontbitmap array) is modified in-place
901 7 static void adjustWriteLimits(std::vector<int>& s, const std::vector<BBX>& bbx_array, const int vi_width, const int vi_height,
902 const int FONT_WIDTH_not_used, const int FONT_HEIGHT,
903 int align,
904 const bool useHalocolor,
905 int& x, int& y, int& len, int& startindex, int& xstart, int& ystart, int& yend)
906 {
907 7 const int al = alignToBitmask(align);
908
909 // cannot calculate with len * FONT_WIDTH, because characters can have different widths
910 7 int total_width = 0;
911
2/2
✓ Branch 7 → 4 taken 369 times.
✓ Branch 7 → 8 taken 7 times.
376 for (int i = 0; i < len; i++) {
912 // bbx_array is the array of bounding boxes for each character
913 // s[i] is the index to the fontbitmap array
914 369 total_width += bbx_array[s[i]].width;
915 }
916
917 // alignment X
918
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 7 times.
7 if (al & ATA_RIGHT)
919 x -= (total_width - 1);
920
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 7 times.
7 else if (al & ATA_CENTER)
921 x -= (total_width / 2);
922
923 // alignment Y
924
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 7 times.
7 if (al & ATA_BASELINE)
925 y -= FONT_HEIGHT / 2;
926
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 7 times.
7 else if (al & ATA_BOTTOM)
927 y -= (FONT_HEIGHT - 1);
928
929
1/2
✓ Branch 16 → 17 taken 7 times.
✗ Branch 16 → 18 not taken.
7 const int final_height = useHalocolor ? FONT_HEIGHT + 2 : FONT_HEIGHT;
930
1/2
✓ Branch 19 → 20 taken 7 times.
✗ Branch 19 → 21 not taken.
7 if (useHalocolor) y = y - 1; // one more top line, adjust y (we do it after the alignment!)
931
932 // Chop text if exceed right margin
933 // keep last character of which at least one pixel can be drawn
934
935 // Fixed FONT_WIDTH calculation logic is replaced with logic which allows variable width characters
936 // Starting the truncation from the right side until the last character fits into the right margin, at least one pixel of it.
937 // bbx.width is the width of the character, not the whole font, this amount must be subtracted from the right margin.
938
5/6
✓ Branch 25 → 26 taken 8 times.
✗ Branch 25 → 30 not taken.
✓ Branch 28 → 29 taken 1 time.
✓ Branch 28 → 30 taken 7 times.
✓ Branch 31 → 22 taken 1 time.
✓ Branch 31 → 32 taken 7 times.
8 while(len > 0 && total_width > vi_width - x + bbx_array[s[len - 1]].width) {
939 // remove last character
940 1 len--;
941 1 total_width -= bbx_array[s[len]].width;
942 }
943 /* // old method left here intentionally:
944 if (len * FONT_WIDTH > width - x)
945 len = (width - x + FONT_WIDTH - 1) / FONT_WIDTH;
946 */
947
948 // FIXME: what if the character not, but its left side halo would be seen?
949
950 7 startindex = 0;
951 7 xstart = 0;
952 // Chop 1st char if exceed left margin
953
1/2
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 40 taken 7 times.
7 if (x < 0) {
954 // startindex is the first position in the string to be drawn
955 // Previously we had a global font-constant FONT_WIDTH, it was the same as x / FONT_WIDTH
956 // and the internal start position could be calculated as (-x) % FONT_WIDTH.
957 // Now that we allow that BDF originated characters can have different widths, we must
958 // calculate the startindex one-by-one from the left side.
959 while (startindex < len && x < 0) {
960 // s[startindex] is the index to the fontbitmap array
961 // bbx_array[s[startindex]].width is the width of the character
962 x += bbx_array[s[startindex]].width;
963 startindex++;
964 }
965 // x is the remaining negative offset, which is the start position of the first character
966 xstart = x; // this is the start position of the first character
967 // OLD method left here intentionally:
968 /*
969 startindex = (-x) / FONT_WIDTH;
970 xstart = (-x) % FONT_WIDTH;
971 */
972 x = 0;
973 }
974
975 7 ystart = 0;
976 7 yend = final_height;
977 // Chop font if exceed bottom margin
978
1/2
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 42 taken 7 times.
7 if (y > vi_height - final_height)
979 yend = vi_height - y;
980
981 // Chop font if exceed top margin
982
1/2
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 44 taken 7 times.
7 if (y < 0) {
983 ystart = -y;
984 y = 0;
985 }
986
987 // Roll in start index
988
1/2
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 59 taken 7 times.
7 if (startindex > 0) {
989 s.erase(s.begin(), s.begin() + startindex);
990 len -= startindex;
991 }
992 7 }
993
994 // Inserts leftmost 'bitcount' bits of fontlinebuf at the end of the byte buffer dst,
995 // Target is the 'bitposition'th bit
996 4808 static void insert_from_msb_bit(uint8_t *dst, int bitposition, const uint8_t* fontlinebuf, int fontlinebuf_size, int bitcount)
997 {
998 4808 int pos = bitposition / 8;
999 4808 int bitindex = bitposition % 8;
1000
2/2
✓ Branch 2 → 3 taken 4528 times.
✓ Branch 2 → 10 taken 280 times.
4808 if (bitindex > 0) {
1001 4528 const int usable_msb_count = 8 - bitindex;
1002 // mask FF >> (bitindex)
1003 // bitindex 1 mask = 10000000 01111111
1004 // bitindex 7 mask = 11111110 00000001
1005 4528 const uint8_t mask = (uint8_t)(0xFF >> bitindex); // leftmost (msb) N bits
1006 4528 const uint8_t partial_val = (uint8_t)((fontlinebuf[0] >> bitindex) & mask); // shift leftmost bits lsb
1007 4528 dst[pos++] |= partial_val; // or'd with existing
1008
1009 4528 bitcount -= usable_msb_count;
1010
1011 // deal with shifted buffer from now on
1012 4528 int bufpos = 0;
1013
2/2
✓ Branch 8 → 4 taken 3252 times.
✓ Branch 8 → 9 taken 4528 times.
7780 while (bitcount > 0) {
1014 uint8_t val;
1015
1/2
✓ Branch 4 → 5 taken 3252 times.
✗ Branch 4 → 6 not taken.
3252 if (bufpos <fontlinebuf_size - 1)
1016 3252 val = (fontlinebuf[bufpos] << usable_msb_count) | (fontlinebuf[bufpos + 1] >> (8 - usable_msb_count));
1017 else
1018 val = (fontlinebuf[bufpos] << usable_msb_count); // suppose that unused lsb bits were 0 in the end
1019
1020 // put actual byte into buffer
1021 3252 dst[pos++] = val;
1022 3252 bufpos++;
1023 3252 bitcount -= 8;
1024 }
1025 4528 return;
1026 }
1027
1028 280 int bufpos = 0;
1029
2/2
✓ Branch 12 → 11 taken 98 times.
✓ Branch 12 → 13 taken 280 times.
378 while (bitcount > 0) {
1030 // put actual byte into buffer
1031 98 dst[pos++] = fontlinebuf[bufpos++]; // suppose that unused lsb bits were 0 in the end
1032 98 bitcount -= 8;
1033 }
1034 }
1035
1036 59427 static uint8_t get_bit(uint8_t* src, const int bitposition)
1037 {
1038 59427 int pos = bitposition / 8;
1039 59427 int bitindex = bitposition % 8;
1040 59427 const uint8_t mask = 1 << (7 - bitindex);
1041 59427 return src[pos] & mask;
1042 }
1043
1044 static int get_bits(uint8_t* src, int bitposition, int count)
1045 {
1046 int bitcounter = 0;
1047 while (count--) {
1048 if (0 != get_bit(src, bitposition++))
1049 bitcounter++;
1050 }
1051 return bitcounter;
1052 }
1053
1054 7 void PreRendered::make_outline() {
1055 7 auto h = stringbitmap.size();
1056 7 auto w = stringbitmap[0].size();
1057
1058 // circular line buffer for holding precalculated shifted lines
1059
1/2
✓ Branch 7 → 8 taken 7 times.
✗ Branch 7 → 51 not taken.
14 std::vector<uint8_t> buf1(w);
1060
1/2
✓ Branch 11 → 12 taken 7 times.
✗ Branch 11 → 54 not taken.
14 std::vector<uint8_t> buf2(w);
1061
1/2
✓ Branch 15 → 16 taken 7 times.
✗ Branch 15 → 57 not taken.
7 std::vector<uint8_t> buf3(w);
1062
1063 // it's unnecessary to mask the last byte against valid bitcounter, they won't drawn
1064 // const uint8_t mask = ~(0xFF >> (8 - (bitcounter % 8)));
1065
1066 // shift a line left and rights and result is or'd
1067 98 auto make_dizzyLR = [](uint8_t* dst, auto src, size_t w) {
1068
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 98 times.
98 if (w == 1)
1069 {
1070 *dst = (src[0] << 1) | (src[0] >> 1);
1071 return;
1072 }
1073 // leftmost
1074 98 uint8_t left = (src[0] << 1) | (src[1] >> (8 - 1));
1075 98 uint8_t right = 0 | (src[0] >> 1);
1076 98 *dst = left | right;
1077 98 dst++;
1078 98 src++;
1079 // middle
1080
2/2
✓ Branch 6 → 5 taken 3696 times.
✓ Branch 6 → 7 taken 98 times.
3794 for (size_t i = 1; i < w - 1; ++i)
1081 {
1082 3696 left = (src[0] << 1) | (src[1] >> (8 - 1));
1083 3696 right = (src[-1] << (8 - 1)) | (src[0] >> 1);
1084 3696 *dst++ = left | right;
1085 3696 src++;
1086 }
1087 // rightmost
1088 98 left = (src[0] << 1) | 0;
1089 98 right = (src[-1] << (8 - 1)) | (src[0] >> 1);
1090 98 *dst = left | right;
1091 };
1092
1093 #if defined(_MSC_VER) && _MSC_VER < 1944
1094 // MSVC's optimizer fail (as of 17.8.4, release, optimize for speed, SSE2) would
1095 // Workaround. Bad code generated. Compiler bug.
1096 // Fixed (said) in 17.9 preview 3 (_MSC_VER 1939)
1097 // Tested again in 17.14 (_MSC_VER 1944), it's OK.
1098 // https://developercommunity.visualstudio.com/t/Bad-c-codegen-in-1784-x64-unless-se/10565370
1099 volatile uint8_t* src_prev;
1100 volatile uint8_t* src;
1101 #else
1102 uint8_t* src_prev;
1103 uint8_t* src;
1104 #endif
1105 uint8_t* src_next;
1106 uint8_t* dst;
1107
1108 uint8_t* tmp_line;
1109 7 uint8_t* prev_line_LR = buf1.data();
1110 7 uint8_t* curr_line_LR = buf2.data();
1111 7 uint8_t* next_line_LR = buf3.data();
1112
1113 // line 0, no previous line
1114 7 size_t y = 0;
1115 7 dst = stringbitmap_outline[y].data();
1116 7 src = stringbitmap[y].data();
1117 7 src_next = stringbitmap[y + 1].data();
1118 7 make_dizzyLR(curr_line_LR, src, w);
1119 7 make_dizzyLR(next_line_LR, src_next, w);
1120
2/2
✓ Branch 30 → 29 taken 278 times.
✓ Branch 30 → 31 taken 7 times.
285 for (size_t x = 0; x < w; x++) {
1121 278 dst[x] = (curr_line_LR[x] | next_line_LR[x] | src_next[x]) & ~src[x];
1122 }
1123 7 tmp_line = prev_line_LR;
1124 7 prev_line_LR = curr_line_LR;
1125 7 curr_line_LR = next_line_LR;
1126 7 next_line_LR = tmp_line;
1127
1128 7 src_prev = src;
1129 7 src = src_next;
1130 7 y++;
1131
1132 // middle lines, y runs on 1..(h-2)
1133
2/2
✓ Branch 41 → 32 taken 84 times.
✓ Branch 41 → 42 taken 7 times.
91 for (; y < h - 1; y++)
1134 {
1135 84 dst = stringbitmap_outline[y].data();
1136 84 src_next = stringbitmap[y + 1].data();
1137 84 make_dizzyLR(next_line_LR, src_next, w);
1138
2/2
✓ Branch 39 → 38 taken 3336 times.
✓ Branch 39 → 40 taken 84 times.
3420 for (size_t x = 0; x < w; x++) {
1139 3336 dst[x] = (prev_line_LR[x] | curr_line_LR[x] | next_line_LR[x] | src_prev[x] | src_next[x]) & ~src[x];
1140 }
1141 84 tmp_line = prev_line_LR;
1142 84 prev_line_LR = curr_line_LR;
1143 84 curr_line_LR = next_line_LR;
1144 84 next_line_LR = tmp_line;
1145 84 src_prev = src;
1146 84 src = src_next;
1147 }
1148 // last one, no next line
1149 7 dst = stringbitmap_outline[y].data();
1150
2/2
✓ Branch 46 → 45 taken 278 times.
✓ Branch 46 → 47 taken 7 times.
285 for (size_t x = 0; x < w; x++) {
1151 278 dst[x] = (prev_line_LR[x] | curr_line_LR[x] | src_prev[x]) & ~src[x];
1152 }
1153 7 }
1154
1155 7 PreRendered::PreRendered(
1156 const uint8_t* fonts,
1157 const int fontline_bytes_storage,
1158 const int _vi_width, const int _vi_height, // from VideoInfo
1159 int _x, int _y, // they may change
1160 std::vector<int>& s,
1161 // each character can have different width, variable bbx.width is used instead of font-level constant FONT_WIDTH
1162 const std::vector<BBX>& bbx_array,
1163 int align,
1164 const bool _useHalocolor,
1165
1166 // Each character can have a different width; variable bbx.width is used instead of the font-level constant FONT_WIDTH.
1167 //
1168 // A BDF font can contain mixed sizes. The next two parameters (FONT_WIDTH, FONT_HEIGHT) represent the global maximum
1169 // font size. A font file may contain per-character widths and heights in bbx_array.
1170 //
1171 // Regarding the wider spacing in Unifont:
1172 // This is due to how FONTBOUNDINGBOX is interpreted during rendering. In Unifont's BDF header, FONTBOUNDINGBOX is set
1173 // to 16 pixels wide. In earlier versions of Avisynth, having a BDF header with FONTBOUNDINGBOX set to 16 pixels wide
1174 // globally caused all glyphs, regardless of their actual per-character width, to be rendered with 16-pixel spacing in
1175 // Avisynth's Text() function. This resulted in visible gaps for narrower, 8-pixel wide Latin characters.
1176 //
1177 // While Unifont is conceptually monospaced, it technically includes variable-width glyphs.
1178 // For example, Latin characters have DWIDTH 8, but others (like U+007F or CJK characters) use DWIDTH 16.
1179 //
1180 // Thus, the global FONTBOUNDINGBOX only specifies a maximum size for buffer allocation and must be 16 pixels wide.
1181 // In a fully BDF-compliant renderer, the per-character DWIDTH and BBX values can override the global bounding box
1182 // for spacing and alignment.
1183 //
1184 // Since Avisynth 3.7.6, the BDF renderer implements this override behavior.
1185
1186 const int FONT_WIDTH, const int FONT_HEIGHT,
1187 const int _safety_bits_x_left,
1188 const int _safety_bits_x_right
1189 7 )
1190 :
1191 7 useHalocolor(_useHalocolor), vi_width(_vi_width), vi_height(_vi_height),
1192 7 safety_bits_x_left(_safety_bits_x_left),
1193 7 safety_bits_x_right(_safety_bits_x_right)
1194 {
1195 7 len = (int)s.size();
1196 7 x = _x;
1197 7 y = _y;
1198 7 xstart = 0;
1199 7 ystart = 0;
1200 7 yend = 0;
1201 7 text_width = 0;
1202
1203 7 int startindex = 0;
1204
1205 // optional two additional lines for top and bottom for outline
1206
1/2
✓ Branch 5 → 6 taken 7 times.
✗ Branch 5 → 7 not taken.
7 stringbitmap_height = useHalocolor ? FONT_HEIGHT + 2 : FONT_HEIGHT;
1207
1208 7 adjustWriteLimits(s, // s is modified in-place
1209 bbx_array, // bounding boxes for each character
1210 7 vi_width, vi_height,
1211 FONT_WIDTH, // not used, bbx_array contains per-character widths, but must be passed
1212 FONT_HEIGHT, // extra top and bottom for halo
1213 align,
1214 7 useHalocolor,
1215 // adjusted parameters
1216
1/2
✓ Branch 8 → 9 taken 7 times.
✗ Branch 8 → 111 not taken.
7 x, y, len, startindex, xstart, ystart, yend
1217 );
1218
1219
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 7 times.
7 if (len <= 0)
1220 return;
1221
1222 // prepare font mask and outline mask
1223
1224 // instead of FONT_WIDTH * len we have to calculate the total width of the string dynamically,
1225 // because characters can have different widths, so we use bbx_array[s[i]].width
1226 7 int total_font_width = 0;
1227
2/2
✓ Branch 15 → 12 taken 368 times.
✓ Branch 15 → 16 taken 7 times.
375 for (int i = 0; i < len; i++) {
1228 // bbx_array is the array of bounding boxes for each character
1229 // s[i] is the index to the fontbitmap array
1230 // total_font_width += bbx_array[s[i]].width;
1231 368 total_font_width += bbx_array[s[i]].width; // this is the total width of the string
1232 }
1233
1234 // left-right safety bits for horizontal subsampled cases
1235
1/2
✓ Branch 16 → 17 taken 7 times.
✗ Branch 16 → 18 not taken.
7 const int stringbitmap_width = safety_bits_x_left + total_font_width + (useHalocolor ? 2 : 0) + safety_bits_x_right;
1236 7 const int stringbitmapline_bytes = (stringbitmap_width + 7) / 8;
1237
1238 // allocate actual space
1239
1/2
✓ Branch 19 → 20 taken 7 times.
✗ Branch 19 → 111 not taken.
7 stringbitmap.resize(stringbitmap_height);
1240
3/4
✓ Branch 24 → 25 taken 98 times.
✗ Branch 24 → 109 not taken.
✓ Branch 34 → 22 taken 98 times.
✓ Branch 34 → 35 taken 7 times.
210 for (auto& subarray : stringbitmap) subarray.resize(stringbitmapline_bytes);
1241
1/2
✓ Branch 35 → 36 taken 7 times.
✗ Branch 35 → 53 not taken.
7 if (useHalocolor) {
1242
1/2
✓ Branch 36 → 37 taken 7 times.
✗ Branch 36 → 111 not taken.
7 stringbitmap_outline.resize(stringbitmap_height);
1243
3/4
✓ Branch 41 → 42 taken 98 times.
✗ Branch 41 → 110 not taken.
✓ Branch 51 → 39 taken 98 times.
✓ Branch 51 → 52 taken 7 times.
210 for (auto& subarray : stringbitmap_outline) subarray.resize(stringbitmapline_bytes);
1244 }
1245
1246 // fill matrix with fonts
1247 // optionally leave spare left-right-top-bottom pixel lines for out-of-matrix halo pixels
1248 // plus a final zero-cost bit column for safely drawing unaligned horizontal subsampling cases
1249 7 uint8_t zerobyte[1] = { 0 };
1250 7 int bitcounter = 0;
1251
1252 // safety columns for horizontal subsampling cases
1253 // fill another column with 0
1254
2/2
✓ Branch 58 → 54 taken 98 times.
✓ Branch 58 → 59 taken 7 times.
105 for (int ty = 0; ty < stringbitmap_height; ty++) {
1255 98 insert_from_msb_bit(&stringbitmap[ty][0], bitcounter, &zerobyte[0], 1, safety_bits_x_left);
1256 }
1257 7 bitcounter += safety_bits_x_left;
1258
1259
1/2
✓ Branch 59 → 60 taken 7 times.
✗ Branch 59 → 67 not taken.
7 if (useHalocolor) {
1260 // fill leftmost 1 bitcolumn with 0
1261
2/2
✓ Branch 65 → 61 taken 98 times.
✓ Branch 65 → 66 taken 7 times.
105 for (int ty = 0; ty < stringbitmap_height; ty++) {
1262 98 uint8_t zerobyte[1] = { 0 };
1263 98 insert_from_msb_bit(&stringbitmap[ty][0], 0, &zerobyte[0], 1, 1);
1264 }
1265 7 bitcounter++;
1266 }
1267
1268 // if outline needed, leave place for top outline, copy actual character to the second row
1269
1/2
✓ Branch 67 → 68 taken 7 times.
✗ Branch 67 → 69 not taken.
7 const int ypos_of_char_in_bitmap = useHalocolor ? 1 : 0;
1270
1271
2/2
✓ Branch 80 → 71 taken 368 times.
✓ Branch 80 → 81 taken 7 times.
375 for (int i = 0; i < len; i++) {
1272 368 int num = s[i];
1273 368 int actual_font_width = bbx_array[num].width; // per character width, instead of font-level constant FONT_WIDTH;
1274 368 const uint8_t* fontlinebuf = &fonts[num * FONT_HEIGHT * fontline_bytes_storage];
1275
2/2
✓ Branch 78 → 74 taken 4416 times.
✓ Branch 78 → 79 taken 368 times.
4784 for (int ty = 0; ty < FONT_HEIGHT; ty++) {
1276 // stuff FONT_WIDTH bits from fontline into bitcounter_th bit of target line
1277 4416 insert_from_msb_bit(&stringbitmap[ty + ypos_of_char_in_bitmap][0], bitcounter, fontlinebuf, fontline_bytes_storage, actual_font_width);
1278 4416 fontlinebuf += fontline_bytes_storage;
1279 }
1280 368 bitcounter += actual_font_width;
1281 }
1282
1/2
✓ Branch 81 → 82 taken 7 times.
✗ Branch 81 → 89 not taken.
7 if (useHalocolor) {
1283 // fill rightmost 'safety_bits' bitcolumn with 0
1284
2/2
✓ Branch 87 → 83 taken 98 times.
✓ Branch 87 → 88 taken 7 times.
105 for (int ty = 0; ty < stringbitmap_height; ty++) {
1285 98 insert_from_msb_bit(&stringbitmap[ty][0], bitcounter, &zerobyte[0], 1, 1);
1286 }
1287 7 bitcounter++;
1288 }
1289
1290 // safety columns for horizontal subsampling cases
1291 // fill another column with 0
1292
2/2
✓ Branch 94 → 90 taken 98 times.
✓ Branch 94 → 95 taken 7 times.
105 for (int ty = 0; ty < stringbitmap_height; ty++) {
1293 98 insert_from_msb_bit(&stringbitmap[ty][0], bitcounter, &zerobyte[0], 1, safety_bits_x_right);
1294 }
1295 7 bitcounter += safety_bits_x_right;
1296
1297
1/2
✗ Branch 95 → 96 not taken.
✓ Branch 95 → 97 taken 7 times.
7 assert(bitcounter == stringbitmap_width);
1298
1299
1/2
✓ Branch 97 → 98 taken 7 times.
✗ Branch 97 → 99 not taken.
7 if (useHalocolor)
1300
1/2
✓ Branch 98 → 99 taken 7 times.
✗ Branch 98 → 111 not taken.
7 make_outline();
1301
1302 // recalculate the text width with assuming variable character widths
1303
1304 // actual visible pixel count
1305 // Instead of (len - 1) * FONT_WIDTH, we must calculate the text width based on the actual character widths
1306 7 text_width = total_font_width - xstart;
1307
2/2
✓ Branch 99 → 100 taken 6 times.
✓ Branch 99 → 101 taken 1 time.
7 if (x + text_width > vi_width) text_width -= (x + text_width - vi_width);
1308
1309
1/2
✓ Branch 101 → 102 taken 7 times.
✗ Branch 101 → 107 not taken.
7 if (useHalocolor) {
1310 // x was calculated w/o the extra left-right padding
1311 7 xstart += 1; // worst case, skip leftmost extra halo column
1312
2/4
✓ Branch 102 → 103 taken 7 times.
✗ Branch 102 → 105 not taken.
✓ Branch 103 → 104 taken 7 times.
✗ Branch 103 → 105 not taken.
7 if (x + xstart - 1 >= 0 && x > 0) {
1313 7 x -= 1;
1314 7 xstart -= 1;
1315 7 text_width += 1;
1316 }
1317 // still good an increased text_width for last halo column?
1318
2/2
✓ Branch 105 → 106 taken 1 time.
✓ Branch 105 → 107 taken 6 times.
7 if (x + text_width + 1 < vi_width)
1319 1 text_width += 1;
1320 }
1321 }
1322
1323 template<typename pixel_t>
1324 static auto getHBDColor_UV(int color, int bits_per_pixel)
1325 {
1326 if (bits_per_pixel < 32)
1327 return (pixel_t)(color << (bits_per_pixel - 8));
1328 constexpr float shift = 0.0f;
1329 return (pixel_t)((color - 128) / 255.0f + shift);
1330 // FIXME: consistently using limited->fullscale conversion for float
1331 }
1332
1333 template<typename pixel_t>
1334 static auto getHBDColor_Y(int color, int bits_per_pixel)
1335 {
1336 if (bits_per_pixel < 32)
1337 return (pixel_t)(color << (bits_per_pixel - 8));
1338 return (pixel_t)(color / 255.0f); // 0..255 -> 0..1.0
1339 // FIXME: consistently using limited->fullscale conversion for float
1340 }
1341
1342 template<typename pixel_t>
1343 42 static auto getHBDColor_RGB(int color, int bits_per_pixel)
1344 {
1345
1/6
auto getHBDColor_RGB<float>(int, int):
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 4 not taken.
auto getHBDColor_RGB<unsigned char>(int, int):
✓ Branch 2 → 3 taken 42 times.
✗ Branch 2 → 4 not taken.
auto getHBDColor_RGB<unsigned short>(int, int):
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 4 not taken.
42 if (bits_per_pixel <= 16) {
1346 42 const int max_pixel_value = (1 << (bits_per_pixel & 31)) - 1;
1347 42 return (pixel_t)((float)color * max_pixel_value / 255); // 0..255 --> 0..1023,4095,16383,65535
1348 }
1349 return (pixel_t)(color / 255.0f); // 0..255 -> 0..1.0
1350 }
1351
1352
1353 template<typename pixel_t, bool useHalocolor, bool fadeBackground, bool isRGB>
1354 void Render1by1Planes(int bits_per_pixel, int color, int halocolor, int* pitches, BYTE** dstps, PreRendered& pre,
1355 const int planeCount, const bool is444)
1356 {
1357 // 1:1 planes, Y or planar RGB or 4:4:4 U/V
1358
1359 int planes_y[4] = { PLANAR_Y, PLANAR_U, PLANAR_V, PLANAR_A };
1360 int planes_r[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A };
1361 int* planes = isRGB ? planes_r : planes_y;
1362
1363 for (int p = 0; p < planeCount; p++)
1364 {
1365 const int plane = planes[p];
1366
1367 if (!(isRGB || plane == PLANAR_Y || ((plane == PLANAR_U || plane == PLANAR_V) && is444)))
1368 continue; // Y, R, G, B is O.K. U, V is OK if 444
1369
1370 pixel_t val_color;
1371 pixel_t val_color_outline;
1372
1373 const int planecolor = getColorForPlane(plane, color);
1374 const int planecolor_outline = getColorForPlane(plane, halocolor);
1375 if (isRGB) {
1376 val_color = getHBDColor_RGB<pixel_t>(planecolor, bits_per_pixel);
1377 val_color_outline = getHBDColor_RGB<pixel_t>(planecolor_outline, bits_per_pixel);
1378 }
1379 else if (plane == PLANAR_U || plane == PLANAR_V) {
1380 val_color = getHBDColor_UV<pixel_t>(planecolor, bits_per_pixel);
1381 val_color_outline = getHBDColor_UV<pixel_t>(planecolor_outline, bits_per_pixel);
1382 }
1383 else {// Y
1384 val_color = getHBDColor_Y<pixel_t>(planecolor, bits_per_pixel);
1385 val_color_outline = getHBDColor_Y<pixel_t>(planecolor_outline, bits_per_pixel);
1386 }
1387
1388 const int pitch = pitches[p];
1389 BYTE* dstp = dstps[p] + pre.x * sizeof(pixel_t) + pre.y * pitch;
1390
1391 // Start rendering
1392 for (int ty = pre.ystart; ty < pre.yend; ty++) {
1393 pixel_t* _dstp = reinterpret_cast<pixel_t*>(dstp);
1394 uint8_t* fontline_ptr = pre.stringbitmap[ty].data();
1395 [[maybe_unused]] uint8_t* fontoutline_ptr;
1396 if constexpr(useHalocolor)
1397 fontoutline_ptr = pre.stringbitmap_outline[ty].data();
1398 int j = 0;
1399 const int shifted_xstart = pre.safety_bits_x_left + pre.xstart;
1400 for (int tx = shifted_xstart; tx < shifted_xstart + pre.text_width; tx++)
1401 {
1402 const bool lightIt = 0 != get_bit(fontline_ptr, tx);
1403 LightOnePixel<pixel_t, fadeBackground, isRGB>(lightIt, _dstp, j, val_color, bits_per_pixel);
1404 if constexpr(useHalocolor) {
1405 if (!lightIt)
1406 {
1407 const bool lightIt_outline = 0 != get_bit(fontoutline_ptr, tx);
1408 LightOnePixel<pixel_t, fadeBackground, isRGB>(lightIt_outline, _dstp, j, val_color_outline, bits_per_pixel);
1409 }
1410 }
1411 j += 1;
1412 }
1413 dstp += pitch;
1414 }
1415 }
1416 }
1417
1418 template<typename pixel_t, bool useHalocolor, bool fadeBackground, int logXRatioUV, int logYRatioUV, ChromaLocationMode chromaMode>
1419 void RenderUV(int bits_per_pixel, int color, int halocolor, int* pitches, BYTE** dstps, PreRendered& pre)
1420 {
1421 // some optimization hint
1422 if constexpr (sizeof(pixel_t) == 1)
1423 bits_per_pixel = 8;
1424 else if constexpr (sizeof(pixel_t) == 4)
1425 bits_per_pixel = 32;
1426
1427 // draw U and V in one step
1428 pixel_t color_u = getHBDColor_UV<pixel_t>(getColorForPlane(PLANAR_U, color), bits_per_pixel);
1429 pixel_t color_v = getHBDColor_UV<pixel_t>(getColorForPlane(PLANAR_V, color), bits_per_pixel);
1430 pixel_t color_outline_u = getHBDColor_UV<pixel_t>(getColorForPlane(PLANAR_U, halocolor), bits_per_pixel);
1431 pixel_t color_outline_v = getHBDColor_UV<pixel_t>(getColorForPlane(PLANAR_V, halocolor), bits_per_pixel);
1432
1433 const int pitchUV = pitches[1];
1434 const int offset = (pre.x >> logXRatioUV) * sizeof(pixel_t) + (pre.y >> logYRatioUV) * pitchUV;
1435 BYTE* dstpU = dstps[1] + offset;
1436 BYTE* dstpV = dstps[2] + offset;
1437
1438 // .SubS = 1, 2 or 4
1439 constexpr int xSubS = 1 << logXRatioUV;
1440 constexpr int ySubS = 1 << logYRatioUV;
1441
1442 /*
1443 U and V handling, multiple luma/outline/background source for a given chroma point
1444 resulting chroma is a weighted sum of the three pixel kinds (font/outline/background)
1445 012345678901
1446 ...#OOO#....
1447 ..#O###O#...
1448 ..#O#.#O#...
1449 ..#O#.#O#...
1450 ..#O#.#O#...
1451 */
1452
1453 // unaligned x: for horizontal subsampling 420, 422 and 411:
1454 // 420, 422, 411 horizontal subsampling: one more loop because of the leftmost orphan pixel(s)
1455 // we can overaddress on the right, additional safety bit column(s) were added for the bitmap
1456 const bool unaligned_x_start = logXRatioUV > 0 && 0 != pre.x % xSubS;
1457 const int xplus = unaligned_x_start ? xSubS : 0; // extra orphan bits affecting rightmost chroma
1458 const int xshift = pre.x % xSubS; // when aligned --> 0
1459 // unaligned y: vertical subsampling 420
1460 const bool odd_y_start = logYRatioUV == 1 && 0 != pre.y % 2;
1461 const int yshift = odd_y_start ? 1 : 0;
1462
1463 constexpr bool hasVerticalSubsample = logYRatioUV > 0;
1464
1465 // safe zero array for vertical subsampled 4:2:0 case for orphan top/bottom
1466 std::vector<uint8_t> zeros;
1467 if constexpr (hasVerticalSubsample)
1468 zeros.resize(pre.stringbitmap[0].size());
1469
1470 // second array element is only valid for vertically subsampled 4:2:0
1471 uint8_t* fontlines_ptr[2] = { nullptr };
1472 uint8_t* fontoutlines_ptr[2] = { nullptr };
1473
1474 for (int ty = pre.ystart; ty < pre.yend; ty += ySubS) {
1475
1476 pixel_t* _dstpU = reinterpret_cast<pixel_t*>(dstpU);
1477 pixel_t* _dstpV = reinterpret_cast<pixel_t*>(dstpV);
1478
1479 if (hasVerticalSubsample && odd_y_start && ty == pre.ystart) {
1480 // top font line on odd y position + vertically subsampled (420)
1481 fontlines_ptr[0] = zeros.data();
1482 fontlines_ptr[1] = pre.stringbitmap[ty].data();
1483 if constexpr(useHalocolor) {
1484 fontoutlines_ptr[0] = zeros.data();
1485 fontoutlines_ptr[1] = pre.stringbitmap_outline[ty].data();
1486 }
1487 }
1488 else if (hasVerticalSubsample && ty + 1 - yshift >= pre.stringbitmap_height) {
1489 // bottom font line on even y position
1490 fontlines_ptr[0] = pre.stringbitmap[ty - yshift].data();
1491 fontlines_ptr[1] = zeros.data();
1492 if constexpr(useHalocolor) {
1493 fontoutlines_ptr[0] = pre.stringbitmap_outline[ty - yshift].data();
1494 fontoutlines_ptr[1] = zeros.data();
1495 }
1496 }
1497 else {
1498 // all font lines contributing to chroma can safely be used
1499 for (int m = 0; m < ySubS; m++)
1500 fontlines_ptr[m] = pre.stringbitmap[ty + m - yshift].data();
1501
1502 if constexpr(useHalocolor) {
1503 for (int m = 0; m < ySubS; m++)
1504 fontoutlines_ptr[m] = pre.stringbitmap_outline[ty + m - yshift].data();
1505 }
1506 }
1507
1508 // render a horizontal line
1509 int j = 0;
1510 // (pre.xstart - xshift) is always on horizontal subsample boundary
1511 const int shifted_xstart = pre.safety_bits_x_left + pre.xstart - xshift;
1512
1513 // left (mpeg2) location:
1514 int fontpixels_right = 0; // used for left (mpeg2) chroma location case
1515 int halopixels_right = 0;
1516 // For the very first chroma pixel there is no previous rightside.
1517 // Use index -1, safe because of safety columns
1518 if constexpr (chromaMode == LEFT_420 || chromaMode == LEFT_422) {
1519 for (int yy = 0; yy < ySubS; yy++) {
1520 fontpixels_right += get_bits(fontlines_ptr[yy], shifted_xstart - 1, 1);
1521 if constexpr (useHalocolor)
1522 halopixels_right += get_bits(fontoutlines_ptr[yy], shifted_xstart - 1, 1);
1523 }
1524 }
1525
1526 for (int tx = shifted_xstart; tx < shifted_xstart + pre.text_width + xplus; tx += xSubS) {
1527 int fontpixels = 0;
1528 int halopixels = 0;
1529
1530 // 411
1531 // +------+------+------+------+
1532 // | 0.25 | 0.25 | 0.25 | 0.25 |
1533 // +------+------+------+------+
1534 // 420 center (mpeg1, jpeg)
1535 // +------+------+
1536 // | 0.25 | 0.25 |
1537 // |------+------|
1538 // | 0.25 | 0.25 |
1539 // +------+------+
1540 // 422 center
1541 // +------+------+
1542 // | 0.5 | 0.5 |
1543 // +------+------+
1544 // 420 left (mpeg2)
1545 // ------+------+-------+
1546 // 0.125 | 0.25 | 0.125 |
1547 // ------|------+-------|
1548 // 0.125 | 0.25 | 0.125 |
1549 // ------+------+-------+
1550 // 422 left (mpeg2)
1551 // ------+------+-------+
1552 // 0.25 | 0.5 | 0.25 |
1553 // ------+------+-------+
1554
1555 if constexpr (chromaMode == LEFT_420 || chromaMode == LEFT_422) {
1556 int fontpixels_left = 0;
1557 int halopixels_left = 0;
1558 int fontpixels_mid = 0;
1559 int halopixels_mid = 0;
1560
1561 // shift variables
1562 fontpixels_left = fontpixels_right;
1563 if constexpr (useHalocolor)
1564 halopixels_left = halopixels_right;
1565 // gather counts
1566 fontpixels_mid = get_bits(fontlines_ptr[0], tx, 1);
1567 fontpixels_right = get_bits(fontlines_ptr[0], tx + 1, 1);
1568 if constexpr (useHalocolor) {
1569 halopixels_mid = get_bits(fontoutlines_ptr[0], tx, 1);
1570 halopixels_right = get_bits(fontoutlines_ptr[0], tx + 1, 1);
1571 }
1572 if constexpr (chromaMode == LEFT_420) {
1573 fontpixels_mid += get_bits(fontlines_ptr[1], tx, 1);
1574 fontpixels_right += get_bits(fontlines_ptr[1], tx + 1, 1);
1575 if constexpr (useHalocolor) {
1576 halopixels_mid += get_bits(fontoutlines_ptr[1], tx, 1);
1577 halopixels_right += get_bits(fontoutlines_ptr[1], tx + 1, 1);
1578 }
1579 }
1580 // 1-2-1 weight
1581 fontpixels = fontpixels_left + 2 * fontpixels_mid + fontpixels_right;
1582 if constexpr (useHalocolor)
1583 halopixels = halopixels_left + 2 * halopixels_mid + halopixels_right;
1584 }
1585 else {
1586 // center, equal weights
1587 for (int yy = 0; yy < ySubS; yy++) {
1588 fontpixels += get_bits(fontlines_ptr[yy], tx, xSubS);
1589 if constexpr (useHalocolor)
1590 halopixels += get_bits(fontoutlines_ptr[yy], tx, xSubS);
1591 }
1592 }
1593 LightOneUVPixel<pixel_t, logXRatioUV, logYRatioUV, fadeBackground, chromaMode>(_dstpU, j, _dstpV,
1594 color_u, color_v, color_outline_u, color_outline_v,
1595 fontpixels, halopixels,
1596 bits_per_pixel
1597 );
1598
1599 j += 1;
1600 }
1601
1602 dstpU += pitchUV;
1603 dstpV += pitchUV;
1604 }
1605 }
1606
1607 template<typename pixel_t, bool fadeBackground, bool isRGB>
1608 void do_DrawStringPlanar(
1609 const int width, const int height, BYTE** dstps, int* pitches, const int logXRatioUV, const int logYRatioUV, const int planeCount,
1610 int bits_per_pixel,
1611 const BitmapFont* bmfont, int x, int y, std::vector<int>& s, int color, int halocolor, int align, bool useHalocolor, int chromalocation)
1612 {
1613 // some optimization hint
1614 if constexpr (sizeof(pixel_t) == 1)
1615 bits_per_pixel = 8;
1616 else if constexpr (sizeof(pixel_t) == 4)
1617 bits_per_pixel = 32;
1618
1619 // Chroma 411 would require 3 extra bits on both left and right.
1620 // Chroma 420 and 422 need 1 bits on both left and right
1621 // Left (mpeg2) chroma placement (420, 422) requires an additional one on the left.
1622 const bool isLeftStyleChromaLoc = (logXRatioUV == 1) &&
1623 ((chromalocation == ChromaLocation_e::AVS_CHROMA_LEFT) ||
1624 (chromalocation == ChromaLocation_e::AVS_CHROMA_TOP_LEFT) || // not supported yet; for the sake of completeness
1625 (chromalocation == ChromaLocation_e::AVS_CHROMA_BOTTOM_LEFT)); // not supported yet; for the sake of completeness
1626 const int safety_bits_x_left = (1 << logXRatioUV) - 1 + (isLeftStyleChromaLoc ? 1 : 0);
1627 const int safety_bits_x_right = (1 << logXRatioUV) - 1;
1628
1629 PreRendered pre(bmfont->font_bitmaps.data(), bmfont->fontline_bytes, width, height, x, y, s, bmfont->bbx_array, align, useHalocolor,
1630 bmfont->global_bbx.width, bmfont->global_bbx.height, safety_bits_x_left, safety_bits_x_right);
1631
1632 if (pre.len <= 0)
1633 return;
1634
1635 const bool is444 = !isRGB && (planeCount >= 3) && (logXRatioUV == 0) && (logYRatioUV == 0);
1636
1637 if (useHalocolor)
1638 Render1by1Planes<pixel_t, true, fadeBackground, isRGB>(bits_per_pixel, color, halocolor, pitches, dstps, pre, planeCount, is444);
1639 else
1640 Render1by1Planes<pixel_t, false, fadeBackground, isRGB>(bits_per_pixel, color, halocolor, pitches, dstps, pre, planeCount, is444);
1641
1642 if constexpr (isRGB)
1643 return;
1644
1645 if (is444)
1646 return;
1647
1648 if (planeCount < 3)
1649 return; // Y
1650
1651 // Subsampled cases, templates help a lot
1652 // for 420 and 422 center and left supported only, what is not "center", we do the "left" method
1653 if (logXRatioUV == 2 && logYRatioUV == 0) {// 411
1654 // ignore chromalocation
1655 if (useHalocolor)
1656 RenderUV<pixel_t, true, fadeBackground, 2, 0, ChromaLocationMode::CENTER_411>(bits_per_pixel, color, halocolor, pitches, dstps, pre);
1657 else
1658 RenderUV<pixel_t, false, fadeBackground, 2, 0, ChromaLocationMode::CENTER_411>(bits_per_pixel, color, halocolor, pitches, dstps, pre);
1659 }
1660 else if (logXRatioUV == 1 && logYRatioUV == 0) {
1661 if (chromalocation == ChromaLocation_e::AVS_CHROMA_CENTER) {
1662 if (useHalocolor)
1663 RenderUV<pixel_t, true, fadeBackground, 1, 0, ChromaLocationMode::CENTER_422>(bits_per_pixel, color, halocolor, pitches, dstps, pre);
1664 else
1665 RenderUV<pixel_t, false, fadeBackground, 1, 0, ChromaLocationMode::CENTER_422>(bits_per_pixel, color, halocolor, pitches, dstps, pre);
1666 }
1667 else {
1668 if (useHalocolor)
1669 RenderUV<pixel_t, true, fadeBackground, 1, 0, ChromaLocationMode::LEFT_422>(bits_per_pixel, color, halocolor, pitches, dstps, pre);
1670 else
1671 RenderUV<pixel_t, false, fadeBackground, 1, 0, ChromaLocationMode::LEFT_422>(bits_per_pixel, color, halocolor, pitches, dstps, pre);
1672 }
1673 }
1674 else if (logXRatioUV == 1 && logYRatioUV == 1) {
1675 if (chromalocation == ChromaLocation_e::AVS_CHROMA_CENTER) {
1676 if (useHalocolor)
1677 RenderUV<pixel_t, true, fadeBackground, 1, 1, ChromaLocationMode::CENTER_420>(bits_per_pixel, color, halocolor, pitches, dstps, pre);
1678 else
1679 RenderUV<pixel_t, false, fadeBackground, 1, 1, ChromaLocationMode::CENTER_420>(bits_per_pixel, color, halocolor, pitches, dstps, pre);
1680 }
1681 else {
1682 if (useHalocolor)
1683 RenderUV<pixel_t, true, fadeBackground, 1, 1, ChromaLocationMode::LEFT_420>(bits_per_pixel, color, halocolor, pitches, dstps, pre);
1684 else
1685 RenderUV<pixel_t, false, fadeBackground, 1, 1, ChromaLocationMode::LEFT_420>(bits_per_pixel, color, halocolor, pitches, dstps, pre);
1686
1687 }
1688 }
1689 else
1690 assert(0);
1691 }
1692
1693 template<bool useHalocolor, bool fadeBackground, ChromaLocationMode chromaMode>
1694 void RenderYUY2(int color, int halocolor, int pitch, BYTE* _dstp, PreRendered& pre)
1695 {
1696 BYTE* dstp = _dstp + pre.x * 2 + pre.y * pitch;
1697 BYTE* dstpUV = _dstp + (pre.x / 2) * 4 + 1 + pre.y * pitch; // always points to U of a YUYV block
1698
1699 typedef uint8_t pixel_t;
1700
1701 pixel_t val_color = getColorForPlane(PLANAR_Y, color);
1702 pixel_t val_color_outline = getColorForPlane(PLANAR_Y, halocolor);
1703 pixel_t color_u = getColorForPlane(PLANAR_U, color);
1704 pixel_t color_outline_u = getColorForPlane(PLANAR_U, halocolor);
1705 pixel_t color_v = getColorForPlane(PLANAR_V, color);
1706 pixel_t color_outline_v = getColorForPlane(PLANAR_V, halocolor);
1707
1708 // YUY2 like 422
1709 constexpr int logXRatioUV = 1;
1710 constexpr int logYRatioUV = 0;
1711 constexpr int xSubS = 2;
1712
1713 // unaligned x: for horizontal subsampling 420, 422 (YUY2) and 411:
1714 // 420, 422, 411, YUY2 horizontal subsampling: one more loop because of the leftmost orphan pixel(s)
1715 // we can overaddress on the right, additional safety bit column(s) were added for the bitmap
1716 const bool unaligned_x_start = 0 != pre.x % xSubS;
1717 const int xplus = unaligned_x_start ? xSubS : 0; // extra orphan bits affecting rightmost chroma
1718 const int xshift = pre.x % xSubS; // when aligned --> 0
1719
1720 for (int ty = pre.ystart; ty < pre.yend; ty++) {
1721 BYTE* dp = dstp;
1722 BYTE* dpUV = dstpUV;
1723 uint8_t* fontline_ptr = pre.stringbitmap[ty].data();
1724 [[maybe_unused]] uint8_t* fontoutline_ptr;
1725 if constexpr (useHalocolor)
1726 fontoutline_ptr = pre.stringbitmap_outline[ty].data();
1727
1728 // first Y, like in 4:4:4
1729 int j = 0;
1730 int shifted_xstart = pre.safety_bits_x_left + pre.xstart;
1731 for (int tx = shifted_xstart; tx < shifted_xstart + pre.text_width; tx++) {
1732 const bool lightIt = 0 != get_bit(fontline_ptr, tx);
1733 LightOnePixel<uint8_t, fadeBackground, false>(lightIt, dp, j, val_color, 8);
1734 if constexpr (useHalocolor) {
1735 if (!lightIt)
1736 {
1737 const bool lightIt_outline = 0 != get_bit(fontoutline_ptr, tx);
1738 LightOnePixel<uint8_t, fadeBackground, false>(lightIt_outline, dp, j, val_color_outline, 8);
1739 }
1740 }
1741 j += 2; // next Y
1742 }
1743
1744 // then chroma
1745 j = 0;
1746 // (pre.xstart - xshift) is always on horizontal subsample boundary
1747 shifted_xstart = pre.safety_bits_x_left + pre.xstart - xshift;
1748
1749 // left (mpeg2) location:
1750 int fontpixels_right = 0; // used for left (mpeg2) chroma location case
1751 int halopixels_right = 0;
1752 // For the very first chroma pixel there is no previous rightside.
1753 // Use index -1, safe because of safety columns
1754 if constexpr (chromaMode == LEFT_422) {
1755 fontpixels_right = get_bits(fontline_ptr, shifted_xstart - 1, 1);
1756 if constexpr (useHalocolor)
1757 halopixels_right = get_bits(fontoutline_ptr, shifted_xstart - 1, 1);
1758 }
1759
1760 for (int tx = shifted_xstart; tx < shifted_xstart + pre.text_width + xplus; tx += xSubS) {
1761 int fontpixels = 0;
1762 int halopixels = 0;
1763 // 422 center (mpeg1, jpeg)
1764 // +------+------+
1765 // | 0.5 | 0.5 |
1766 // +------+------+
1767 // 422 left (mpeg2)
1768 // ------+------+-------+
1769 // 0.25 | 0.5 | 0.25 |
1770 // ------+------+-------+
1771
1772 if constexpr (chromaMode == LEFT_422) {
1773 int fontpixels_left = 0;
1774 int halopixels_left = 0;
1775 int fontpixels_mid = 0;
1776 int halopixels_mid = 0;
1777
1778 // shift variables
1779 fontpixels_left = fontpixels_right;
1780 if constexpr (useHalocolor)
1781 halopixels_left = halopixels_right;
1782 // gather counts
1783 fontpixels_mid = get_bits(fontline_ptr, tx, 1);
1784 fontpixels_right = get_bits(fontline_ptr, tx + 1, 1);
1785 if constexpr (useHalocolor) {
1786 halopixels_mid = get_bits(fontoutline_ptr, tx, 1);
1787 halopixels_right = get_bits(fontoutline_ptr, tx + 1, 1);
1788 }
1789 // 1-2-1 weight
1790 fontpixels = fontpixels_left + 2 * fontpixels_mid + fontpixels_right;
1791 if constexpr (useHalocolor)
1792 halopixels = halopixels_left + 2 * halopixels_mid + halopixels_right;
1793 }
1794 else {
1795 // center, equal weights
1796 fontpixels += get_bits(fontline_ptr, tx, xSubS);
1797 if constexpr (useHalocolor)
1798 halopixels += get_bits(fontoutline_ptr, tx, xSubS);
1799 }
1800
1801 LightOneUVPixel<uint8_t, logXRatioUV, logYRatioUV, fadeBackground, chromaMode>(dpUV /*U*/, j, dpUV + 2 /*V*/,
1802 color_u, color_v, color_outline_u, color_outline_v,
1803 fontpixels, halopixels,
1804 8
1805 );
1806 j += 4; // YUYV
1807 }
1808
1809 dstp += pitch;
1810 dstpUV += pitch;
1811 }
1812 }
1813
1814 template<bool fadeBackground>
1815 static void do_DrawStringYUY2(
1816 const int width, const int height, BYTE* _dstp, int pitch, const BitmapFont* bmfont, int x, int y, std::vector<int>& s,
1817 int color, int halocolor, int align, bool useHalocolor, int chromalocation)
1818 {
1819 const bool isLeftStyleChromaLoc = chromalocation == ChromaLocation_e::AVS_CHROMA_LEFT;
1820 // Like 422. Chroma subsampling would require 1 extra bit playground on both left and right
1821 const int safety_bits_x_left = 1 + (isLeftStyleChromaLoc ? 1 : 0);
1822 const int safety_bits_x_right = 1;
1823
1824 PreRendered pre(bmfont->font_bitmaps.data(), bmfont->fontline_bytes, width, height, x, y, s, bmfont->bbx_array, align, useHalocolor,
1825 bmfont->global_bbx.width, bmfont->global_bbx.height,
1826 safety_bits_x_left, safety_bits_x_right);
1827
1828 if (pre.len <= 0)
1829 return;
1830
1831 if (useHalocolor) {
1832 if (chromalocation == ChromaLocation_e::AVS_CHROMA_CENTER)
1833 RenderYUY2<true, fadeBackground, ChromaLocationMode::CENTER_422>(color, halocolor, pitch, _dstp, pre);
1834 else
1835 RenderYUY2<true, fadeBackground, ChromaLocationMode::LEFT_422>(color, halocolor, pitch, _dstp, pre);
1836 }
1837 else {
1838 if (chromalocation == ChromaLocation_e::AVS_CHROMA_CENTER)
1839 RenderYUY2<false, fadeBackground, ChromaLocationMode::CENTER_422>(color, halocolor, pitch, _dstp, pre);
1840 else
1841 RenderYUY2<false, fadeBackground, ChromaLocationMode::LEFT_422>(color, halocolor, pitch, _dstp, pre);
1842 }
1843 }
1844
1845 template<typename pixel_t, bool useHalocolor, bool fadeBackground, int rgbstep>
1846 7 static void RenderPackedRGB(int color, int halocolor, BYTE* _dstp, int pitch, int height, PreRendered& pre)
1847 {
1848 // packed: only 8 and 16 bits
1849 7 int bits_per_pixel = 0;
1850 if constexpr (sizeof(pixel_t) == 1)
1851 7 bits_per_pixel = 8;
1852 else if constexpr (sizeof(pixel_t) == 2)
1853 bits_per_pixel = 16;
1854
1855 7 const int val_color_R = getHBDColor_RGB<pixel_t>(getColorForPlane(PLANAR_R, color), bits_per_pixel);
1856 7 const int val_color_R_outline = getHBDColor_RGB<pixel_t>(getColorForPlane(PLANAR_R, halocolor), bits_per_pixel);
1857 7 const int val_color_G = getHBDColor_RGB<pixel_t>(getColorForPlane(PLANAR_G, color), bits_per_pixel);
1858 7 const int val_color_G_outline = getHBDColor_RGB<pixel_t>(getColorForPlane(PLANAR_G, halocolor), bits_per_pixel);
1859 7 const int val_color_B = getHBDColor_RGB<pixel_t>(getColorForPlane(PLANAR_B, color), bits_per_pixel);
1860 7 const int val_color_B_outline = getHBDColor_RGB<pixel_t>(getColorForPlane(PLANAR_B, halocolor), bits_per_pixel);
1861
1862 // upside down
1863 7 BYTE* dstp = _dstp + pre.x * rgbstep + (height - 1 - pre.y) * pitch;
1864
1865 // Start rendering
1866
2/32
void RenderPackedRGB<unsigned char, false, false, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 25 → 15 not taken.
✗ Branch 25 → 26 not taken.
void RenderPackedRGB<unsigned char, false, false, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 25 → 15 not taken.
✗ Branch 25 → 26 not taken.
void RenderPackedRGB<unsigned char, false, true, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 26 → 15 not taken.
✗ Branch 26 → 27 not taken.
void RenderPackedRGB<unsigned char, false, true, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 26 → 15 not taken.
✗ Branch 26 → 27 not taken.
void RenderPackedRGB<unsigned char, true, false, 3>(int, int, unsigned char*, int, int, PreRendered&):
✓ Branch 33 → 15 taken 98 times.
✓ Branch 33 → 34 taken 7 times.
void RenderPackedRGB<unsigned char, true, false, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 33 → 15 not taken.
✗ Branch 33 → 34 not taken.
void RenderPackedRGB<unsigned char, true, true, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 35 → 15 not taken.
✗ Branch 35 → 36 not taken.
void RenderPackedRGB<unsigned char, true, true, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 35 → 15 not taken.
✗ Branch 35 → 36 not taken.
void RenderPackedRGB<unsigned short, false, false, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 25 → 15 not taken.
✗ Branch 25 → 26 not taken.
void RenderPackedRGB<unsigned short, false, false, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 25 → 15 not taken.
✗ Branch 25 → 26 not taken.
void RenderPackedRGB<unsigned short, false, true, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 26 → 15 not taken.
✗ Branch 26 → 27 not taken.
void RenderPackedRGB<unsigned short, false, true, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 26 → 15 not taken.
✗ Branch 26 → 27 not taken.
void RenderPackedRGB<unsigned short, true, false, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 33 → 15 not taken.
✗ Branch 33 → 34 not taken.
void RenderPackedRGB<unsigned short, true, false, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 33 → 15 not taken.
✗ Branch 33 → 34 not taken.
void RenderPackedRGB<unsigned short, true, true, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 35 → 15 not taken.
✗ Branch 35 → 36 not taken.
void RenderPackedRGB<unsigned short, true, true, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 35 → 15 not taken.
✗ Branch 35 → 36 not taken.
105 for (int ty = pre.ystart; ty < pre.yend; ty++) {
1867 98 uint8_t* dp = dstp;
1868 98 uint8_t* fontline_ptr = pre.stringbitmap[ty].data();
1869 [[maybe_unused]] uint8_t* fontoutline_ptr;
1870 if constexpr(useHalocolor)
1871 98 fontoutline_ptr = pre.stringbitmap_outline[ty].data();
1872
1873 98 const int shifted_xstart = pre.safety_bits_x_left + pre.xstart; // though safety bit count must be 0 here
1874
2/32
void RenderPackedRGB<unsigned char, false, false, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 23 → 18 not taken.
✗ Branch 23 → 24 not taken.
void RenderPackedRGB<unsigned char, false, false, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 23 → 18 not taken.
✗ Branch 23 → 24 not taken.
void RenderPackedRGB<unsigned char, false, true, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 24 → 18 not taken.
✗ Branch 24 → 25 not taken.
void RenderPackedRGB<unsigned char, false, true, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 24 → 18 not taken.
✗ Branch 24 → 25 not taken.
void RenderPackedRGB<unsigned char, true, false, 3>(int, int, unsigned char*, int, int, PreRendered&):
✓ Branch 31 → 20 taken 30856 times.
✓ Branch 31 → 32 taken 98 times.
void RenderPackedRGB<unsigned char, true, false, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 31 → 20 not taken.
✗ Branch 31 → 32 not taken.
void RenderPackedRGB<unsigned char, true, true, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 33 → 20 not taken.
✗ Branch 33 → 34 not taken.
void RenderPackedRGB<unsigned char, true, true, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 33 → 20 not taken.
✗ Branch 33 → 34 not taken.
void RenderPackedRGB<unsigned short, false, false, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 23 → 18 not taken.
✗ Branch 23 → 24 not taken.
void RenderPackedRGB<unsigned short, false, false, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 23 → 18 not taken.
✗ Branch 23 → 24 not taken.
void RenderPackedRGB<unsigned short, false, true, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 24 → 18 not taken.
✗ Branch 24 → 25 not taken.
void RenderPackedRGB<unsigned short, false, true, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 24 → 18 not taken.
✗ Branch 24 → 25 not taken.
void RenderPackedRGB<unsigned short, true, false, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 31 → 20 not taken.
✗ Branch 31 → 32 not taken.
void RenderPackedRGB<unsigned short, true, false, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 31 → 20 not taken.
✗ Branch 31 → 32 not taken.
void RenderPackedRGB<unsigned short, true, true, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 33 → 20 not taken.
✗ Branch 33 → 34 not taken.
void RenderPackedRGB<unsigned short, true, true, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 33 → 20 not taken.
✗ Branch 33 → 34 not taken.
30954 for (int tx = shifted_xstart; tx < shifted_xstart + pre.text_width; tx++)
1875 {
1876 30856 const bool lightIt = 0 != get_bit(fontline_ptr, tx);
1877
2/32
void RenderPackedRGB<unsigned char, false, false, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
void RenderPackedRGB<unsigned char, false, false, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
void RenderPackedRGB<unsigned char, false, true, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
void RenderPackedRGB<unsigned char, false, true, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
void RenderPackedRGB<unsigned char, true, false, 3>(int, int, unsigned char*, int, int, PreRendered&):
✓ Branch 21 → 22 taken 2285 times.
✓ Branch 21 → 23 taken 28571 times.
void RenderPackedRGB<unsigned char, true, false, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
void RenderPackedRGB<unsigned char, true, true, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
void RenderPackedRGB<unsigned char, true, true, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
void RenderPackedRGB<unsigned short, false, false, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
void RenderPackedRGB<unsigned short, false, false, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
void RenderPackedRGB<unsigned short, false, true, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
void RenderPackedRGB<unsigned short, false, true, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
void RenderPackedRGB<unsigned short, true, false, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
void RenderPackedRGB<unsigned short, true, false, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
void RenderPackedRGB<unsigned short, true, true, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
void RenderPackedRGB<unsigned short, true, true, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
30856 LightOnePixelPackedRGB<pixel_t, fadeBackground>(lightIt, dp, val_color_R, val_color_G, val_color_B);
1878 if constexpr(useHalocolor) {
1879
2/16
void RenderPackedRGB<unsigned char, true, false, 3>(int, int, unsigned char*, int, int, PreRendered&):
✓ Branch 24 → 25 taken 28571 times.
✓ Branch 24 → 30 taken 2285 times.
void RenderPackedRGB<unsigned char, true, false, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 30 not taken.
void RenderPackedRGB<unsigned char, true, true, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 32 not taken.
void RenderPackedRGB<unsigned char, true, true, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 32 not taken.
void RenderPackedRGB<unsigned short, true, false, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 30 not taken.
void RenderPackedRGB<unsigned short, true, false, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 30 not taken.
void RenderPackedRGB<unsigned short, true, true, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 32 not taken.
void RenderPackedRGB<unsigned short, true, true, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 32 not taken.
30856 if (!lightIt) {
1880 28571 const bool lightIt_outline = 0 != get_bit(fontoutline_ptr, tx);
1881
2/16
void RenderPackedRGB<unsigned char, true, false, 3>(int, int, unsigned char*, int, int, PreRendered&):
✓ Branch 26 → 27 taken 5617 times.
✓ Branch 26 → 28 taken 22954 times.
void RenderPackedRGB<unsigned char, true, false, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
void RenderPackedRGB<unsigned char, true, true, 3>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
void RenderPackedRGB<unsigned char, true, true, 4>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
void RenderPackedRGB<unsigned short, true, false, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
void RenderPackedRGB<unsigned short, true, false, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
void RenderPackedRGB<unsigned short, true, true, 6>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
void RenderPackedRGB<unsigned short, true, true, 8>(int, int, unsigned char*, int, int, PreRendered&):
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
28571 LightOnePixelPackedRGB<pixel_t, fadeBackground>(lightIt_outline, dp, val_color_R_outline, val_color_G_outline, val_color_B_outline);
1882 }
1883 }
1884 30856 dp += rgbstep;
1885 }
1886 98 dstp -= pitch;
1887 }
1888 7 }
1889
1890 template<typename pixel_t, int rgbstep, bool fadeBackground>
1891 7 static void do_DrawStringPackedRGB(
1892 const int width, const int height, BYTE* _dstp, int pitch,
1893 const BitmapFont* bmfont, int x, int y, std::vector<int>& s, int color, int halocolor, int align, bool useHalocolor)
1894 {
1895 7 const int safety_bits_x_left = 0; // no horizontal subsampling
1896 7 const int safety_bits_x_right = 0; // no horizontal subsampling
1897
1/16
void do_DrawStringPackedRGB<unsigned char, 3, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✓ Branch 3 → 4 taken 7 times.
✗ Branch 3 → 18 not taken.
void do_DrawStringPackedRGB<unsigned char, 3, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 18 not taken.
void do_DrawStringPackedRGB<unsigned char, 4, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 18 not taken.
void do_DrawStringPackedRGB<unsigned char, 4, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 18 not taken.
void do_DrawStringPackedRGB<unsigned short, 6, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 18 not taken.
void do_DrawStringPackedRGB<unsigned short, 6, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 18 not taken.
void do_DrawStringPackedRGB<unsigned short, 8, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 18 not taken.
void do_DrawStringPackedRGB<unsigned short, 8, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 18 not taken.
7 PreRendered pre(bmfont->font_bitmaps.data(), bmfont->fontline_bytes, width, height, x, y, s, bmfont->bbx_array, align, useHalocolor,
1898 7 bmfont->global_bbx.width, bmfont->global_bbx.height,
1899 safety_bits_x_left, safety_bits_x_right);
1900
1901
1/16
void do_DrawStringPackedRGB<unsigned char, 3, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 7 times.
void do_DrawStringPackedRGB<unsigned char, 3, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
void do_DrawStringPackedRGB<unsigned char, 4, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
void do_DrawStringPackedRGB<unsigned char, 4, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
void do_DrawStringPackedRGB<unsigned short, 6, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
void do_DrawStringPackedRGB<unsigned short, 6, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
void do_DrawStringPackedRGB<unsigned short, 8, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
void do_DrawStringPackedRGB<unsigned short, 8, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
7 if (pre.len <= 0)
1902 return;
1903
1904
1/16
void do_DrawStringPackedRGB<unsigned char, 3, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✓ Branch 6 → 7 taken 7 times.
✗ Branch 6 → 8 not taken.
void do_DrawStringPackedRGB<unsigned char, 3, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
void do_DrawStringPackedRGB<unsigned char, 4, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
void do_DrawStringPackedRGB<unsigned char, 4, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
void do_DrawStringPackedRGB<unsigned short, 6, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
void do_DrawStringPackedRGB<unsigned short, 6, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
void do_DrawStringPackedRGB<unsigned short, 8, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
void do_DrawStringPackedRGB<unsigned short, 8, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
7 if (useHalocolor)
1905
1/16
void do_DrawStringPackedRGB<unsigned char, 3, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✓ Branch 7 → 9 taken 7 times.
✗ Branch 7 → 16 not taken.
void do_DrawStringPackedRGB<unsigned char, 3, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 7 → 9 not taken.
✗ Branch 7 → 16 not taken.
void do_DrawStringPackedRGB<unsigned char, 4, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 7 → 9 not taken.
✗ Branch 7 → 16 not taken.
void do_DrawStringPackedRGB<unsigned char, 4, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 7 → 9 not taken.
✗ Branch 7 → 16 not taken.
void do_DrawStringPackedRGB<unsigned short, 6, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 7 → 9 not taken.
✗ Branch 7 → 16 not taken.
void do_DrawStringPackedRGB<unsigned short, 6, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 7 → 9 not taken.
✗ Branch 7 → 16 not taken.
void do_DrawStringPackedRGB<unsigned short, 8, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 7 → 9 not taken.
✗ Branch 7 → 16 not taken.
void do_DrawStringPackedRGB<unsigned short, 8, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 7 → 9 not taken.
✗ Branch 7 → 16 not taken.
7 RenderPackedRGB<pixel_t, true, fadeBackground, rgbstep>(color, halocolor, _dstp, pitch, height, pre);
1906 else
1907 RenderPackedRGB<pixel_t, false, fadeBackground, rgbstep>(color, halocolor, _dstp, pitch, height, pre);
1908
1/16
void do_DrawStringPackedRGB<unsigned char, 3, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✓ Branch 11 → 12 taken 7 times.
✗ Branch 11 → 14 not taken.
void do_DrawStringPackedRGB<unsigned char, 3, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 14 not taken.
void do_DrawStringPackedRGB<unsigned char, 4, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 14 not taken.
void do_DrawStringPackedRGB<unsigned char, 4, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 14 not taken.
void do_DrawStringPackedRGB<unsigned short, 6, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 14 not taken.
void do_DrawStringPackedRGB<unsigned short, 6, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 14 not taken.
void do_DrawStringPackedRGB<unsigned short, 8, false>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 14 not taken.
void do_DrawStringPackedRGB<unsigned short, 8, true>(int, int, unsigned char*, int, BitmapFont const*, int, int, std::vector<int, std::allocator<int> >&, int, int, int, bool):
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 14 not taken.
7 }
1909
1910
1911 16 static bool strequals_i(const std::string& a, const std::string& b)
1912 {
1913 16 return std::equal(a.begin(), a.end(),
1914 b.begin(), b.end(),
1915 120 [](char a, char b) {
1916 120 return tolower(a) == tolower(b);
1917 16 });
1918 }
1919
1920 // in fixedfonts.cpp
1921 extern const uint16_t *font_bitmaps[];
1922 extern const uint16_t *font_codepoints[];
1923 extern const FixedFont_info_t *font_infos[];
1924
1925 7 std::unique_ptr<BitmapFont> GetBitmapFont(int size, const char *name, bool bold, bool debugSave) {
1926
1927 7 BitmapFont* current_font = nullptr;
1928
1929 // check internal embedded fonts
1930 7 bool found = false;
1931
1932 // find font in internal list
1933
2/2
✓ Branch 44 → 3 taken 50 times.
✓ Branch 44 → 45 taken 1 time.
51 for (int i = 0; i < PREDEFINED_FONT_COUNT; i++)
1934 {
1935 50 const FixedFont_info_t* fi = font_infos[i];
1936
17/30
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 14 taken 44 times.
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 14 not taken.
✓ Branch 7 → 8 taken 6 times.
✗ Branch 7 → 132 not taken.
✓ Branch 10 → 11 taken 6 times.
✗ Branch 10 → 132 not taken.
✓ Branch 11 → 12 taken 6 times.
✗ Branch 11 → 132 not taken.
✓ Branch 12 → 13 taken 6 times.
✗ Branch 12 → 14 not taken.
✓ Branch 15 → 16 taken 6 times.
✓ Branch 15 → 17 taken 44 times.
✓ Branch 17 → 18 taken 6 times.
✓ Branch 17 → 20 taken 44 times.
✓ Branch 20 → 21 taken 6 times.
✓ Branch 20 → 22 taken 44 times.
✓ Branch 22 → 23 taken 6 times.
✓ Branch 22 → 25 taken 44 times.
✓ Branch 25 → 26 taken 6 times.
✓ Branch 25 → 43 taken 44 times.
✗ Branch 132 → 133 not taken.
✗ Branch 132 → 134 not taken.
✗ Branch 136 → 137 not taken.
✗ Branch 136 → 139 not taken.
✗ Branch 141 → 142 not taken.
✗ Branch 141 → 143 not taken.
✗ Branch 145 → 146 not taken.
✗ Branch 145 → 148 not taken.
74 if (fi->height == size && fi->bold == bold && strequals_i(fi->fontname, name)) {
1937 6 BBX global_bbx = BBX{ fi->width, fi->height, fi->offset_x, fi->offset_y }; // BBX from FONTBOUNDINGBOX
1938 // max WIDTH, max HEIGHT, offset_x, offset_y
1939 current_font = new BitmapFont(
1940 6 fi->charcount,
1941 font_bitmaps[i], // internal one
1942 nullptr, // not a byte array from external BDF
1943 sizeof(uint16_t), // sizeof(*font_bitmaps) 2: uint16_t
1944 font_codepoints[i],
1945 nullptr, // Individual BBX for each character. For our internal characters it comes from global bbx
1946 global_bbx,
1947
1/2
✓ Branch 32 → 33 taken 6 times.
✗ Branch 32 → 152 not taken.
12 fi->fontname,
1948
2/4
✓ Branch 29 → 30 taken 6 times.
✗ Branch 29 → 158 not taken.
✗ Branch 38 → 39 not taken.
✓ Branch 38 → 41 taken 6 times.
18 "",
1949 6 fi->bold,
1950
2/4
✓ Branch 26 → 27 taken 6 times.
✗ Branch 26 → 166 not taken.
✓ Branch 33 → 34 taken 6 times.
✗ Branch 33 → 150 not taken.
12 false);
1951 6 found = true;
1952 6 break;
1953 }
1954 }
1955 // pass #2 when size does not match exactly, find nearest, but still smaller font.
1956
2/2
✓ Branch 45 → 46 taken 1 time.
✓ Branch 45 → 93 taken 6 times.
7 if (!found) {
1957 // find font i internal list
1958 1 int last_good_size = 0;
1959 1 int found_index = -1;
1960
2/2
✓ Branch 75 → 47 taken 20 times.
✓ Branch 75 → 76 taken 1 time.
21 for (int i = 0; i < PREDEFINED_FONT_COUNT; i++)
1961 {
1962 20 const FixedFont_info_t* fi = font_infos[i];
1963
17/28
✓ Branch 47 → 48 taken 10 times.
✓ Branch 47 → 57 taken 10 times.
✓ Branch 50 → 51 taken 10 times.
✗ Branch 50 → 167 not taken.
✓ Branch 53 → 54 taken 10 times.
✗ Branch 53 → 167 not taken.
✓ Branch 54 → 55 taken 10 times.
✗ Branch 54 → 167 not taken.
✓ Branch 55 → 56 taken 9 times.
✓ Branch 55 → 57 taken 1 time.
✓ Branch 58 → 59 taken 10 times.
✓ Branch 58 → 60 taken 10 times.
✓ Branch 60 → 61 taken 10 times.
✓ Branch 60 → 63 taken 10 times.
✓ Branch 63 → 64 taken 10 times.
✓ Branch 63 → 65 taken 10 times.
✓ Branch 65 → 66 taken 10 times.
✓ Branch 65 → 68 taken 10 times.
✓ Branch 68 → 69 taken 9 times.
✓ Branch 68 → 74 taken 11 times.
✗ Branch 167 → 168 not taken.
✗ Branch 167 → 169 not taken.
✗ Branch 171 → 172 not taken.
✗ Branch 171 → 174 not taken.
✗ Branch 176 → 177 not taken.
✗ Branch 176 → 178 not taken.
✗ Branch 180 → 181 not taken.
✗ Branch 180 → 183 not taken.
60 if (fi->bold == bold && strequals_i(fi->fontname, name)) {
1964
2/2
✓ Branch 69 → 70 taken 1 time.
✓ Branch 69 → 71 taken 8 times.
9 if (last_good_size == 0) {
1965 1 found_index = i;
1966 1 last_good_size = fi->height;
1967 }
1968
1/4
✗ Branch 71 → 72 not taken.
✓ Branch 71 → 74 taken 8 times.
✗ Branch 72 → 73 not taken.
✗ Branch 72 → 74 not taken.
8 else if (std::abs(fi->height - size) < std::abs(last_good_size - size) && fi->height <= size) {
1969 // has better size match and is not larger
1970 found_index = i;
1971 last_good_size = fi->height;
1972 }
1973 }
1974 }
1975
1/2
✓ Branch 76 → 77 taken 1 time.
✗ Branch 76 → 93 not taken.
1 if (found_index >= 0) {
1976 1 const FixedFont_info_t* fi = font_infos[found_index];
1977 1 BBX global_bbx = BBX{ fi->width, fi->height, fi->offset_x, fi->offset_y }; // BBX from FONTBOUNDINGBOX
1978 current_font = new BitmapFont(
1979 1 fi->charcount,
1980 font_bitmaps[found_index], // internal one
1981 nullptr, // not a byte array from external BDF
1982 sizeof(uint16_t), // sizeof(*font_bitmaps) 2: uint16_t
1983 font_codepoints[found_index],
1984 nullptr, // Individual BBX for each character. For our internal characters it comes from global bbx
1985 global_bbx,
1986
1/2
✓ Branch 83 → 84 taken 1 time.
✗ Branch 83 → 187 not taken.
2 fi->fontname,
1987
2/4
✓ Branch 80 → 81 taken 1 time.
✗ Branch 80 → 193 not taken.
✗ Branch 89 → 90 not taken.
✓ Branch 89 → 92 taken 1 time.
3 "",
1988 1 fi->bold,
1989
2/4
✓ Branch 77 → 78 taken 1 time.
✗ Branch 77 → 201 not taken.
✓ Branch 84 → 85 taken 1 time.
✗ Branch 84 → 185 not taken.
2 false);
1990 1 found = true;
1991 }
1992 }
1993
1994
1/2
✗ Branch 93 → 94 not taken.
✓ Branch 93 → 130 taken 7 times.
7 if (!found) {
1995 // fixme: make cache
1996 BdfFont bdf;
1997 bdf = LoadBMF(name, bold);
1998 if (bdf.codepoints_array.size() == 0)
1999 return nullptr;
2000
2001 current_font = new BitmapFont(
2002 bdf.font_info.chars,
2003 nullptr, // not an internal one
2004 bdf.font_bitmaps.data(), // a byte array from external BDF
2005 bdf.font_info.fontline_bytes,
2006 bdf.codepoints_array.data(),
2007 &bdf.bbx_array, // Individual BBX for each character, nullptr if not available for our internal fonts
2008 bdf.font_info.bbx, // from FONTBOUNDINGBOX, max WIDTH, HEIGHT, offset_x, offset_y
2009 bdf.font_info.font,
2010 bdf.font_filename,
2011 strequals_i(bdf.font_properties.Weight_name, "bold"),
2012 debugSave);
2013 }
2014 7 return std::unique_ptr<BitmapFont>(current_font);
2015 }
2016
2017 7 static void DrawString_internal(BitmapFont* current_font, const VideoInfo& vi, PVideoFrame& dst, int x, int y, std::string& s_utf8,
2018 int color, int halocolor, bool useHalocolor, int align, bool fadeBackground, int chromalocation)
2019 {
2020 //static BitmapFont_10_20 infoFont1020; // constructor runs once, single instance
2021
2022 // map an utf8 string to a sequence of character map indexes
2023
1/2
✓ Branch 2 → 3 taken 7 times.
✗ Branch 2 → 135 not taken.
7 auto s_remapped = current_font->remap(s_utf8); // array of font table indexes
2024
2025 //SaveBitmapSource(); // debug to generate source from original table
2026
2027
1/2
✓ Branch 3 → 4 taken 7 times.
✗ Branch 3 → 133 not taken.
7 const bool isRGB = vi.IsRGB();
2028 7 const int planes_y[4] = { PLANAR_Y, PLANAR_U, PLANAR_V, PLANAR_A };
2029 7 const int planes_r[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A };
2030
1/2
✓ Branch 4 → 5 taken 7 times.
✗ Branch 4 → 6 not taken.
7 const int* planes = isRGB ? planes_r : planes_y;
2031
2032 7 int logXRatioUV = 0;
2033 7 int logYRatioUV = 0;
2034
5/10
✓ Branch 7 → 8 taken 7 times.
✗ Branch 7 → 133 not taken.
✓ Branch 8 → 9 taken 7 times.
✗ Branch 8 → 12 not taken.
✓ Branch 9 → 10 taken 7 times.
✗ Branch 9 → 133 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 7 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 24 taken 7 times.
7 if (!vi.IsY() && !vi.IsRGB()) {
2035 logXRatioUV = vi.IsYUY2() ? 1 : vi.GetPlaneWidthSubsampling(PLANAR_U);
2036 logYRatioUV = vi.IsYUY2() ? 0 : vi.GetPlaneHeightSubsampling(PLANAR_U);
2037 }
2038
3/6
✓ Branch 24 → 25 taken 7 times.
✗ Branch 24 → 133 not taken.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 7 times.
✓ Branch 27 → 28 taken 7 times.
✗ Branch 27 → 131 not taken.
7 const int planecount = vi.IsYUY2() ? 1 : std::min(vi.NumComponents(), 3);
2039 7 BYTE* dstps[3] = { nullptr };
2040 7 int pitches[3] = { 0 };
2041
2042
2/2
✓ Branch 36 → 31 taken 21 times.
✓ Branch 36 → 37 taken 7 times.
28 for (int i = 0; i < planecount; i++)
2043 {
2044 21 int plane = planes[i];
2045
1/2
✓ Branch 32 → 33 taken 21 times.
✗ Branch 32 → 133 not taken.
21 dstps[i] = dst->GetWritePtr(plane);
2046
1/2
✓ Branch 34 → 35 taken 21 times.
✗ Branch 34 → 133 not taken.
21 pitches[i] = dst->GetPitch(plane);
2047 }
2048
2049 7 const int width = vi.width;
2050 7 const int height = vi.height;
2051
2052
1/2
✓ Branch 37 → 38 taken 7 times.
✗ Branch 37 → 133 not taken.
7 const int bits_per_pixel = vi.BitsPerComponent();
2053
2054 // narrow down valid chroma choices, ignoring and moving to default what is not supported at the moment
2055
2/4
✓ Branch 38 → 39 taken 7 times.
✗ Branch 38 → 133 not taken.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 41 taken 7 times.
7 if (vi.IsYV411()) {
2056 // ignored, always left
2057 chromalocation = ChromaLocation_e::AVS_CHROMA_LEFT;
2058 }
2059
7/14
✓ Branch 41 → 42 taken 7 times.
✗ Branch 41 → 133 not taken.
✓ Branch 42 → 43 taken 7 times.
✗ Branch 42 → 47 not taken.
✓ Branch 43 → 44 taken 7 times.
✗ Branch 43 → 133 not taken.
✓ Branch 44 → 45 taken 7 times.
✗ Branch 44 → 47 not taken.
✓ Branch 45 → 46 taken 7 times.
✗ Branch 45 → 133 not taken.
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 48 taken 7 times.
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 52 taken 7 times.
7 else if (vi.Is420() || vi.Is422() || vi.IsYUY2()) {
2060 if (chromalocation != ChromaLocation_e::AVS_CHROMA_CENTER)
2061 chromalocation = ChromaLocation_e::AVS_CHROMA_LEFT;
2062 // When CENTER is specified, do "center", all other cases fall back
2063 // to "left" (mpeg2). Option is meaningful only 420 or 422 formats, otherwise ignored.
2064 }
2065
2066 // YUY2
2067
2/4
✓ Branch 52 → 53 taken 7 times.
✗ Branch 52 → 133 not taken.
✗ Branch 53 → 54 not taken.
✓ Branch 53 → 58 taken 7 times.
7 if (vi.IsYUY2()) {
2068 if (fadeBackground)
2069 do_DrawStringYUY2<true>(width, height, dstps[0], pitches[0], current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2070 else
2071 do_DrawStringYUY2<false>(width, height, dstps[0], pitches[0], current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2072 return;
2073 }
2074
2075 // Packed RGB24/32/48/64
2076
4/8
✓ Branch 58 → 59 taken 7 times.
✗ Branch 58 → 62 not taken.
✓ Branch 59 → 60 taken 7 times.
✗ Branch 59 → 133 not taken.
✓ Branch 60 → 61 taken 7 times.
✗ Branch 60 → 62 not taken.
✓ Branch 63 → 64 taken 7 times.
✗ Branch 63 → 90 not taken.
7 if (isRGB && !vi.IsPlanar()) {
2077
1/2
✗ Branch 64 → 65 not taken.
✓ Branch 64 → 77 taken 7 times.
7 if (fadeBackground) {
2078 if (vi.IsRGB24())
2079 do_DrawStringPackedRGB<uint8_t, 3, true>(width, height, dstps[0], pitches[0], current_font, x, y, s_remapped, color, halocolor, align, useHalocolor);
2080 else if (vi.IsRGB32())
2081 do_DrawStringPackedRGB<uint8_t, 4, true>(width, height, dstps[0], pitches[0], current_font, x, y, s_remapped, color, halocolor, align, useHalocolor);
2082 else if (vi.IsRGB48())
2083 do_DrawStringPackedRGB<uint16_t, 6, true>(width, height, dstps[0], pitches[0], current_font, x, y, s_remapped, color, halocolor, align, useHalocolor);
2084 else if (vi.IsRGB64())
2085 do_DrawStringPackedRGB<uint16_t, 8, true>(width, height, dstps[0], pitches[0], current_font, x, y, s_remapped, color, halocolor, align, useHalocolor);
2086 }
2087 else {
2088
2/4
✓ Branch 77 → 78 taken 7 times.
✗ Branch 77 → 133 not taken.
✓ Branch 78 → 79 taken 7 times.
✗ Branch 78 → 80 not taken.
7 if (vi.IsRGB24())
2089
1/2
✓ Branch 79 → 89 taken 7 times.
✗ Branch 79 → 133 not taken.
7 do_DrawStringPackedRGB<uint8_t, 3, false>(width, height, dstps[0], pitches[0], current_font, x, y, s_remapped, color, halocolor, align, useHalocolor);
2090 else if (vi.IsRGB32())
2091 do_DrawStringPackedRGB<uint8_t, 4, false>(width, height, dstps[0], pitches[0], current_font, x, y, s_remapped, color, halocolor, align, useHalocolor);
2092 else if (vi.IsRGB48())
2093 do_DrawStringPackedRGB<uint16_t, 6, false>(width, height, dstps[0], pitches[0], current_font, x, y, s_remapped, color, halocolor, align, useHalocolor);
2094 else if (vi.IsRGB64())
2095 do_DrawStringPackedRGB<uint16_t, 8, false>(width, height, dstps[0], pitches[0], current_font, x, y, s_remapped, color, halocolor, align, useHalocolor);
2096 }
2097 7 return;
2098 }
2099
2100 // planar and Y
2101 if (fadeBackground) {
2102 if (isRGB) {
2103 switch (bits_per_pixel)
2104 {
2105 case 8:
2106 do_DrawStringPlanar<uint8_t, true, true>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2107 break;
2108 case 10:
2109 case 12:
2110 case 14:
2111 case 16:
2112 do_DrawStringPlanar<uint16_t, true, true>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2113 break;
2114 case 32:
2115 do_DrawStringPlanar<float, true, true>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2116 break;
2117 }
2118 }
2119 else {
2120 switch (bits_per_pixel)
2121 {
2122 case 8:
2123 do_DrawStringPlanar<uint8_t, true, false>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2124 break;
2125 case 10:
2126 case 12:
2127 case 14:
2128 case 16:
2129 do_DrawStringPlanar<uint16_t, true, false>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2130 break;
2131 case 32:
2132 do_DrawStringPlanar<float, true, false>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2133 break;
2134 }
2135 }
2136 }
2137 else {
2138 if (isRGB) {
2139 switch (bits_per_pixel)
2140 {
2141 case 8:
2142 do_DrawStringPlanar<uint8_t, false, true>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2143 break;
2144 case 10:
2145 case 12:
2146 case 14:
2147 case 16:
2148 do_DrawStringPlanar<uint16_t, false, true>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2149 break;
2150 case 32:
2151 do_DrawStringPlanar<float, false, true>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2152 break;
2153 }
2154 }
2155 else {
2156 switch (bits_per_pixel)
2157 {
2158 case 8:
2159 do_DrawStringPlanar<uint8_t, false, false>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2160 break;
2161 case 10:
2162 case 12:
2163 case 14:
2164 case 16:
2165 do_DrawStringPlanar<uint16_t, false, false>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2166 break;
2167 case 32:
2168 do_DrawStringPlanar<float, false, false>(width, height, dstps, pitches, logXRatioUV, logYRatioUV, planecount, bits_per_pixel, current_font, x, y, s_remapped, color, halocolor, align, useHalocolor, chromalocation);
2169 break;
2170 }
2171 }
2172 }
2173
1/2
✗ Branch 126 → 127 not taken.
✓ Branch 126 → 129 taken 7 times.
7 }
2174
2175 7 void SimpleTextOutW(BitmapFont *current_font, const VideoInfo& vi, PVideoFrame& frame, int real_x, int real_y, std::string& text_utf8,
2176 bool fadeBackground, int textcolor, int halocolor, bool useHaloColor, int align, int chromalocation)
2177 {
2178 7 DrawString_internal(current_font, vi, frame, real_x, real_y, text_utf8, textcolor, halocolor, useHaloColor, align, fadeBackground, chromalocation); // fully transparent background
2179 7 }
2180
2181 // additional parameter: lsp line spacing
2182 1 void SimpleTextOutW_multi(BitmapFont *current_font, const VideoInfo& vi, PVideoFrame& frame, int real_x, int real_y, std::string& text_utf8,
2183 bool fadeBackground, int textcolor, int halocolor, bool useHaloColor,
2184 int align, int lsp, int chromalocation)
2185 {
2186
2187 // make list governed by LF separator
2188 1 std::string temp;
2189 1 std::vector<std::string> parts;
2190
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 44 not taken.
1 std::stringstream ss(text_utf8);
2191
4/6
✓ Branch 8 → 9 taken 8 times.
✗ Branch 8 → 42 not taken.
✓ Branch 9 → 10 taken 8 times.
✗ Branch 9 → 42 not taken.
✓ Branch 10 → 7 taken 7 times.
✓ Branch 10 → 11 taken 1 time.
8 while (std::getline(ss, temp, '\n'))
2192
1/2
✓ Branch 7 → 8 taken 7 times.
✗ Branch 7 → 42 not taken.
7 parts.push_back(temp); // still in utf8
2193 // It doesn't result in a new line if the last character is \n and is followed by nothing.
2194 // "Line1\nLine2" is the same as "Line1\nLine2\n"
2195 // Like in SubTitle
2196 /*
2197 if(!text.empty())
2198 {
2199 if( *text.rbegin() == '\n')
2200 parts.push_back("");
2201 }
2202 */
2203 1 const int fontSize = current_font->global_bbx.height;
2204
2205 // when multiline, bottom and vertically centered cases affect starting y
2206 // lsp units are in 1/8 pixels by definition
2207 1 int al = alignToBitmask(align);
2208
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 15 taken 1 time.
1 if (al & ATA_BOTTOM)
2209 real_y -= (int)((fontSize + lsp / 8.0) * ((int)parts.size() - 1) + 0.5);
2210
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 18 taken 1 time.
1 else if (al & ATA_BASELINE)
2211 real_y -= (int)((fontSize + lsp / 8.0) * (int)(parts.size() - 1) / 2.0 + 0.5);
2212
2213 1 const int orig_real_y = real_y;
2214 1 int linecount = 0;
2215
3/4
✓ Branch 22 → 23 taken 7 times.
✗ Branch 22 → 41 not taken.
✓ Branch 34 → 20 taken 7 times.
✓ Branch 34 → 35 taken 1 time.
16 for (auto s_utf8 : parts) {
2216 7 real_y = orig_real_y + fontSize * linecount + (int)(lsp / 8.0 * linecount + 0.5);
2217
1/2
✓ Branch 23 → 24 taken 7 times.
✗ Branch 23 → 39 not taken.
7 SimpleTextOutW(current_font, vi, frame, real_x, real_y, s_utf8, fadeBackground, textcolor, halocolor, useHaloColor, align, chromalocation);
2218 7 linecount++;
2219 7 }
2220 1 }
2221
2222 // Old legacy info.h functions, but with utf8 mode
2223 // w/o outline, originally with ASCII input, background fading
2224 // Despite name Planar, it works for all formats
2225 void DrawStringPlanar(VideoInfo& vi, PVideoFrame& dst, int x, int y, const char* s)
2226 {
2227 int color;
2228 if (vi.IsRGB())
2229 color = (250 << 16) + (250 << 8) + (250);
2230 else
2231 color = (230 << 16) + (128 << 8) + (128);
2232
2233 // fadeBackground = true: background letter area is faded instead not being untouched.
2234
2235 std::string s_utf8 = charToUtf8(s, false);
2236
2237 int halocolor = 0;
2238
2239 std::unique_ptr<BitmapFont> current_font = GetBitmapFont(20, "info_h", false, false); // 10x20
2240
2241 if (current_font == nullptr)
2242 return;
2243
2244 DrawString_internal(current_font.get(), vi, dst, x, y, s_utf8,
2245 color,
2246 halocolor,
2247 false, // don't use halocolor
2248 0 /* no align */,
2249 true, // fadeBackGround
2250 ChromaLocation_e::AVS_CHROMA_LEFT
2251 );
2252
2253 }
2254
2255 void DrawStringYUY2(VideoInfo& vi, PVideoFrame& dst, int x, int y, const char* s)
2256 {
2257 DrawStringPlanar(vi, dst, x, y, s);
2258 }
2259
2260 // legacy function w/o outline, originally with ASCII input, background fading
2261 void DrawStringRGB24(VideoInfo &vi, PVideoFrame& dst, int x, int y, const char* s)
2262 {
2263 DrawStringPlanar(vi, dst, x, y, s);
2264 }
2265
2266 // legacy function w/o outline, originally with ASCII input, background fading
2267 void DrawStringRGB32(VideoInfo& vi, PVideoFrame& dst, int x, int y, const char* s)
2268 {
2269 DrawStringPlanar(vi, dst, x, y, s);
2270 }
2271