Font.cpp
1// Copyright 2019 Arthur Sonzogni. All rights reserved.
2// Use of this source code is governed by the MIT license that can be found in
3// the LICENSE file.
4
5#include <ft2build.h>
6
7#include <iostream>
8#include <smk/Font.hpp>
9#include <vector>
10#include FT_FREETYPE_H
11
12namespace smk {
13
14Font::Glyph* Font::FetchGlyph(wchar_t in) {
15 // Load from cache.
16 auto character = glyphs_.find(in);
17 if (character != glyphs_.end()) {
18 return character->second.get();
19 }
20
21 // Load from file.
22 if (line_height_ == 0.F) {
23 LoadGlyphs({in});
24 auto character = glyphs_.find(in);
25 if (character != glyphs_.end()) {
26 return character->second.get();
27 }
28 }
29
30 // Fallback.
31 return nullptr;
32}
33
34Font& Font::operator=(Font&& other) noexcept {
35 glyphs_.insert(std::make_move_iterator(begin(other.glyphs_)),
36 std::make_move_iterator(end(other.glyphs_)));
37 filename_ = other.filename_;
38 line_height_ = other.line_height_;
39 baseline_position_ = other.baseline_position_;
40 return *this;
41}
42
43Font::Font(std::string filename, float line_height)
44 : filename_(std::move(filename)), line_height_(line_height) {
45 std::vector<wchar_t> preloaded_characters;
46 const int character_to_preload = 256;
47 for (wchar_t c = 0; c < character_to_preload; ++c) {
48 preloaded_characters.push_back(c);
49 }
50 LoadGlyphs(preloaded_characters);
51}
52
53void Font::LoadGlyphs(const std::vector<wchar_t>& chars) {
54 FT_Library ft = {};
55 if (FT_Init_FreeType(&ft)) {
56 std::cerr << "SMK > FreeType: Could not init FreeType Library" << std::endl;
57 }
58
59 FT_Face face = {};
60 if (FT_New_Face(ft, filename_.c_str(), 0, &face)) {
61 std::cerr << "SMK > FreeType: Failed to load" << filename_ << std::endl;
62 }
63
64 baseline_position_ =
65 line_height_ *
66 ((float(face->ascender) / float(face->ascender - face->descender)));
67 FT_Set_Pixel_Sizes(face, FT_UInt(line_height_), FT_UInt(line_height_));
68 // NOLINTNEXTLINE
69 if (FT_Load_Char(face, 'X', FT_LOAD_RENDER)) {
70 std::cerr << "ERROR::FREETYTPE: Failed to load Glyph for file " << filename_
71 << std::endl;
72 }
73
74 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Disable byte-alignment restriction
75
76 for (auto c : chars) {
77 // Load character glyph
78 // NOLINTNEXTLINE
79 if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
80 std::wcout << L"SMK > FreeType: Failed to load Glyph: \"" << c << "\""
81 << std::endl;
82 continue;
83 }
84
85 int width = int(face->glyph->bitmap.width);
86 int height = int(face->glyph->bitmap.rows);
87 auto character = std::make_unique<Glyph>();
88 character->bearing =
89 glm::ivec2(+face->glyph->bitmap_left, -face->glyph->bitmap_top);
90 const float advance_scale = 1.F / 64.F;
91 character->advance = float(face->glyph->advance.x) * advance_scale;
92
93 if (width * height != 0) {
94 std::vector<uint8_t> buffer_rgba(width * height * 4);
95 {
96 int j = 0;
97 for (int i = 0; i < width * height; ++i) {
98 const uint8_t v = face->glyph->bitmap.buffer[i]; // NOLINT
99 buffer_rgba[j++] = 255; // NOLINT
100 buffer_rgba[j++] = 255; // NOLINT
101 buffer_rgba[j++] = 255; // NOLINT
102 buffer_rgba[j++] = v; // NOLINT
103 }
104 }
105 character->texture =
106 smk::Texture(buffer_rgba.data(), width, height);
107 }
108
109 glyphs_[c] = std::move(character);
110 }
111
112 FT_Done_Face(face);
113 FT_Done_FreeType(ft);
114}
115
116} // namespace smk