libui: Add PSF font loading and rendering
This commit is contained in:
parent
d78cc52d22
commit
d0b8068a7c
BIN
base/usr/share/fonts/Tamsyn-Bold.psf
Normal file
BIN
base/usr/share/fonts/Tamsyn-Bold.psf
Normal file
Binary file not shown.
BIN
base/usr/share/fonts/Tamsyn-Regular.psf
Normal file
BIN
base/usr/share/fonts/Tamsyn-Regular.psf
Normal file
Binary file not shown.
@ -6,9 +6,11 @@ set(SOURCES
|
|||||||
${HEADERS}
|
${HEADERS}
|
||||||
src/Canvas.cpp
|
src/Canvas.cpp
|
||||||
src/Rect.cpp
|
src/Rect.cpp
|
||||||
|
src/Font.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(ui ${SOURCES})
|
add_library(ui ${SOURCES})
|
||||||
target_compile_options(ui PRIVATE ${COMMON_FLAGS})
|
target_compile_options(ui PRIVATE ${COMMON_FLAGS})
|
||||||
target_include_directories(ui PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/)
|
target_include_directories(ui PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/)
|
||||||
target_include_directories(ui PUBLIC ${LUNA_BASE}/usr/include)
|
target_include_directories(ui PUBLIC ${LUNA_BASE}/usr/include)
|
||||||
|
target_link_libraries(ui PUBLIC os)
|
||||||
|
55
libui/include/ui/Font.h
Normal file
55
libui/include/ui/Font.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <luna/Buffer.h>
|
||||||
|
#include <luna/SharedPtr.h>
|
||||||
|
#include <os/Path.h>
|
||||||
|
#include <ui/Canvas.h>
|
||||||
|
|
||||||
|
#define PSF_FONT_MAGIC 0x864ab572
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
class Font : public Shareable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum FontWeight
|
||||||
|
{
|
||||||
|
Regular,
|
||||||
|
Bold,
|
||||||
|
};
|
||||||
|
|
||||||
|
static Result<SharedPtr<Font>> load(const os::Path& path);
|
||||||
|
static Result<SharedPtr<Font>> load_builtin(StringView name, FontWeight weight);
|
||||||
|
|
||||||
|
static SharedPtr<Font> default_font();
|
||||||
|
static SharedPtr<Font> default_bold_font();
|
||||||
|
|
||||||
|
void render(wchar_t codepoint, ui::Color color, ui::Canvas& canvas);
|
||||||
|
void render(const wchar_t* text, ui::Color color, ui::Canvas& canvas);
|
||||||
|
|
||||||
|
int width() const
|
||||||
|
{
|
||||||
|
return m_psf_header.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int height() const
|
||||||
|
{
|
||||||
|
return m_psf_header.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct PSFHeader
|
||||||
|
{
|
||||||
|
u32 magic;
|
||||||
|
u32 version; // zero
|
||||||
|
u32 headersize;
|
||||||
|
u32 flags; // 0 if there's no unicode table
|
||||||
|
u32 numglyph;
|
||||||
|
u32 bytesperglyph;
|
||||||
|
int height;
|
||||||
|
int width;
|
||||||
|
};
|
||||||
|
|
||||||
|
PSFHeader m_psf_header;
|
||||||
|
Buffer m_font_data;
|
||||||
|
};
|
||||||
|
};
|
117
libui/src/Font.cpp
Normal file
117
libui/src/Font.cpp
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#include <luna/String.h>
|
||||||
|
#include <os/File.h>
|
||||||
|
#include <ui/Font.h>
|
||||||
|
|
||||||
|
SharedPtr<ui::Font> s_default_font = {};
|
||||||
|
SharedPtr<ui::Font> s_default_bold_font = {};
|
||||||
|
|
||||||
|
constexpr static int BYTES_PER_PIXEL = (int)sizeof(ui::Color);
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
Result<SharedPtr<Font>> Font::load(const os::Path& path)
|
||||||
|
{
|
||||||
|
auto font = TRY(make_shared<Font>());
|
||||||
|
|
||||||
|
auto file = TRY(os::File::open(path, os::File::ReadOnly));
|
||||||
|
|
||||||
|
TRY(file->read_typed(font->m_psf_header));
|
||||||
|
|
||||||
|
if (font->m_psf_header.magic != PSF_FONT_MAGIC)
|
||||||
|
{
|
||||||
|
os::eprintln("ui::Font::load(%s) failed: font magic does not match PSF2 magic", path.name().chars());
|
||||||
|
return err(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font->m_psf_header.version != 0)
|
||||||
|
{
|
||||||
|
os::eprintln("ui::Font::load(%s) failed: font version is unsupported", path.name().chars());
|
||||||
|
return err(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font->m_psf_header.flags)
|
||||||
|
{
|
||||||
|
os::eprintln("ui::Font::load(%s) warning: font has a unicode table, which we're ignoring",
|
||||||
|
path.name().chars());
|
||||||
|
// todo(); // Font has a unicode table, oh no!
|
||||||
|
}
|
||||||
|
|
||||||
|
font->m_font_data = TRY(file->read_all()); // Read the rest of the file into the font data buffer.
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<SharedPtr<Font>> Font::load_builtin(StringView name, FontWeight weight)
|
||||||
|
{
|
||||||
|
auto path = TRY(String::format("/usr/share/fonts/%s-%s.psf"_sv, name.chars(),
|
||||||
|
weight == FontWeight::Bold ? "Bold" : "Regular"));
|
||||||
|
|
||||||
|
return load(path.view());
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<Font> Font::default_font()
|
||||||
|
{
|
||||||
|
if (s_default_font) return s_default_font;
|
||||||
|
s_default_font = load("/usr/share/fonts/Tamsyn-Regular.psf").release_value();
|
||||||
|
return s_default_font;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<Font> Font::default_bold_font()
|
||||||
|
{
|
||||||
|
if (s_default_bold_font) return s_default_bold_font;
|
||||||
|
s_default_bold_font = load("/usr/share/fonts/Tamsyn-Bold.psf").release_value();
|
||||||
|
return s_default_bold_font;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::render(wchar_t codepoint, ui::Color color, ui::Canvas& canvas)
|
||||||
|
{
|
||||||
|
check(canvas.width == m_psf_header.width && canvas.height == m_psf_header.height);
|
||||||
|
|
||||||
|
const wchar_t str[] = { codepoint, 0 };
|
||||||
|
render(str, color, canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::render(const wchar_t* text, ui::Color color, ui::Canvas& canvas)
|
||||||
|
{
|
||||||
|
usize len = wcslen(text);
|
||||||
|
|
||||||
|
int height = m_psf_header.height;
|
||||||
|
int width = m_psf_header.width;
|
||||||
|
int last_char_width = width;
|
||||||
|
|
||||||
|
if (canvas.width < (m_psf_header.width * static_cast<int>(len)))
|
||||||
|
{
|
||||||
|
len = (canvas.width / width) + 1;
|
||||||
|
last_char_width = canvas.width % width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvas.height < height) height = canvas.height;
|
||||||
|
|
||||||
|
const int bytes_per_line = (m_psf_header.width + 7) / 8;
|
||||||
|
|
||||||
|
for (usize i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (i + 1 == len) width = last_char_width;
|
||||||
|
wchar_t codepoint = text[i];
|
||||||
|
|
||||||
|
u8* glyph =
|
||||||
|
m_font_data.data() + (codepoint > 0 && codepoint < (wchar_t)m_psf_header.numglyph ? codepoint : 0) *
|
||||||
|
m_psf_header.bytesperglyph;
|
||||||
|
|
||||||
|
u32 offset = (u32)i * (m_psf_header.width + 1) * BYTES_PER_PIXEL;
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
u32 line = offset;
|
||||||
|
int mask = 1 << (m_psf_header.width - 1);
|
||||||
|
for (int x = 0; x < m_psf_header.width; x++)
|
||||||
|
{
|
||||||
|
if (*((u32*)glyph) & mask) *(u32*)(canvas.ptr + line) = color.raw;
|
||||||
|
mask >>= 1;
|
||||||
|
line += BYTES_PER_PIXEL;
|
||||||
|
}
|
||||||
|
glyph += bytes_per_line;
|
||||||
|
offset += canvas.stride * BYTES_PER_PIXEL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user