1/*
2 * Copyright (C) 2020-2022 The opuntiaOS Project Authors.
3 * + Contributed by Nikita Melekhin <nimelehin@gmail.com>
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include <fcntl.h>
10#include <libfoundation/Logger.h>
11#include <libg/Font.h>
12#include <new>
13#include <string.h>
14#include <sys/mman.h>
15#include <sys/stat.h>
16#include <unistd.h>
17
18namespace LG {
19
20/* SerenityOS font header */
21struct [[gnu::packed]] FontFileHeader {
22 char magic[4];
23 uint8_t glyph_width;
24 uint8_t glyph_height;
25 uint16_t range_mask_size;
26 uint8_t is_variable_width;
27 uint8_t glyph_spacing;
28 uint8_t baseline;
29 uint8_t mean_line;
30 uint8_t presentation_size;
31 uint16_t weight;
32 uint8_t slope;
33 char name[32];
34 char family[32];
35};
36
37Font& Font::system_font(int size)
38{
39 const int buf_size = 40;
40 char buf[buf_size];
41 static Font* s_system_font_ptr[SystemMaxSize + 1];
42 const static char* s_system_font_path = "/res/fonts/system.font/%d/regular.font";
43 snprintf(buf, buf_size, s_system_font_path, size);
44 if (!s_system_font_ptr[size]) {
45 s_system_font_ptr[size] = Font::load_from_file(buf);
46 }
47 return *s_system_font_ptr[size];
48}
49
50Font& Font::system_bold_font(int size)
51{
52 const int buf_size = 40;
53 char buf[buf_size];
54 static Font* s_system_bold_font_ptr[SystemMaxSize + 1];
55 const static char* s_system_bold_font_path = "/res/fonts/system.font/%d/bold.font";
56 snprintf(buf, buf_size, s_system_bold_font_path, size);
57 if (!s_system_bold_font_ptr[size]) {
58 s_system_bold_font_ptr[size] = Font::load_from_file(buf);
59 }
60 return *s_system_bold_font_ptr[size];
61}
62
63Font::Font(uint32_t* raw_data, uint8_t* width_data, uint8_t width, uint8_t height, size_t count, bool dynamic_width, uint8_t glyph_spacing)
64 : m_raw_data(raw_data)
65 , m_width_data(width_data)
66 , m_width(width)
67 , m_height(height)
68 , m_count(count)
69 , m_dynamic_width(dynamic_width)
70 , m_spacing(glyph_spacing)
71{
72}
73
74Font* Font::load_from_file(const char* path)
75{
76 int fd = open(path, O_RDONLY);
77 if (fd < 0) {
78 return nullptr;
79 }
80
81 fstat_t stat;
82 fstat(fd, &stat);
83
84 uint8_t* ptr = (uint8_t*)mmap(NULL, stat.size, PROT_READ, MAP_PRIVATE, fd, 0);
85 auto* res = Font::load_from_mem(ptr);
86
87 close(fd);
88 return res;
89}
90
91Font* Font::load_from_mem(uint8_t* font_data)
92{
93 if (!font_data) {
94 return nullptr;
95 }
96
97 FontFileHeader& header = *(FontFileHeader*)font_data;
98
99 if (memcmp((uint8_t*)header.magic, (const uint8_t*)"!Fnt", 4)) {
100 Logger::debug << "Font unsupported" << std::endl;
101 return nullptr;
102 }
103
104 size_t count = 0;
105 uint8_t* range_mask = const_cast<uint8_t*>(font_data + sizeof(FontFileHeader));
106 for (size_t i = 0; i < header.range_mask_size; ++i)
107 count += 256 * __builtin_popcount(range_mask[i]);
108
109 size_t bytes_per_glyph = sizeof(uint32_t) * header.glyph_height;
110
111 uint32_t* raw_data = (uint32_t*)(range_mask + header.range_mask_size);
112 uint8_t* width_data = nullptr;
113 if (header.is_variable_width) {
114 width_data = (uint8_t*)((uint8_t*)(raw_data) + count * bytes_per_glyph);
115 }
116
117 return new Font(raw_data, width_data, header.glyph_width, header.glyph_height, count, header.is_variable_width, header.glyph_spacing);
118}
119
120GlyphBitmap Font::glyph_bitmap(size_t ch) const
121{
122 return GlyphBitmap(&m_raw_data[ch * m_height], glyph_width(ch), m_height);
123}
124
125} // namespace LG