aboutsummaryrefslogtreecommitdiffstats
path: root/tools/mcufontencoder/src/freetype_import.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mcufontencoder/src/freetype_import.cc')
-rw-r--r--tools/mcufontencoder/src/freetype_import.cc177
1 files changed, 177 insertions, 0 deletions
diff --git a/tools/mcufontencoder/src/freetype_import.cc b/tools/mcufontencoder/src/freetype_import.cc
new file mode 100644
index 00000000..27a0734d
--- /dev/null
+++ b/tools/mcufontencoder/src/freetype_import.cc
@@ -0,0 +1,177 @@
+#include "freetype_import.hh"
+#include "importtools.hh"
+#include <map>
+#include <string>
+#include <stdexcept>
+#include <iostream>
+#include "ccfixes.hh"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#undef __FTERRORS_H__
+#define FT_ERRORDEF( e, v, s ) std::make_pair( e, s ),
+#define FT_ERROR_START_LIST static const std::map<FT_Error, std::string> ft_errors {
+#define FT_ERROR_END_LIST };
+#include FT_ERRORS_H
+
+namespace mcufont {
+
+static void checkFT(FT_Error error)
+{
+ if (error != 0)
+ {
+ if (ft_errors.count(error))
+ throw std::runtime_error("libfreetype error " +
+ std::to_string(error) + ": " + ft_errors.at(error));
+ else
+ throw std::runtime_error("unknown libfreetype error " +
+ std::to_string(error));
+ }
+}
+
+// Automatically allocated & freed wrapper for FT_Library
+class _FT_Library
+{
+public:
+ _FT_Library() { checkFT(FT_Init_FreeType(&m_lib)); }
+ ~_FT_Library() { checkFT(FT_Done_FreeType(m_lib)); }
+ operator FT_Library() { return m_lib; }
+
+private:
+ FT_Library m_lib;
+};
+
+// Automatically allocated & freed wrapper for FT_Face
+class _FT_Face
+{
+public:
+ _FT_Face(FT_Library lib, const std::vector<char> &data)
+ {
+ checkFT(FT_New_Memory_Face(lib, (const unsigned char *)&data[0],
+ data.size(), 0, &m_face));
+ }
+ ~_FT_Face() { checkFT(FT_Done_Face(m_face)); }
+ operator FT_Face() { return m_face; }
+ FT_Face operator->() { return m_face; }
+
+private:
+ FT_Face m_face;
+};
+
+// Read all the data from a file into a memory buffer.
+static void readfile(std::istream &file, std::vector<char> &data)
+{
+ while (file.good())
+ {
+ const size_t blocksize = 4096;
+ size_t oldsize = data.size();
+ data.resize(oldsize + blocksize);
+ file.read(&data[oldsize], blocksize);
+ data.resize(oldsize + file.gcount());
+ }
+}
+
+std::unique_ptr<DataFile> LoadFreetype(std::istream &file, int size, bool bw)
+{
+ std::vector<char> data;
+ readfile(file, data);
+
+ _FT_Library lib;
+ _FT_Face face(lib, data);
+
+ checkFT(FT_Set_Pixel_Sizes(face, size, size));
+
+ DataFile::fontinfo_t fontinfo = {};
+ std::vector<DataFile::glyphentry_t> glyphtable;
+ std::vector<DataFile::dictentry_t> dictionary;
+
+ // Convert size to pixels and round to nearest.
+ int u_per_em = face->units_per_EM;
+ auto topx = [size, u_per_em](int s) { return (s * size + u_per_em / 2) / u_per_em; };
+
+ fontinfo.name = std::string(face->family_name) + " " +
+ std::string(face->style_name) + " " +
+ std::to_string(size);
+
+ // Reserve 4 pixels on each side for antialiasing + hinting.
+ // They will be cropped off later.
+ fontinfo.max_width = topx(face->bbox.xMax - face->bbox.xMin) + 8;
+ fontinfo.max_height = topx(face->bbox.yMax - face->bbox.yMin) + 8;
+ fontinfo.baseline_x = topx(-face->bbox.xMin) + 4;
+ fontinfo.baseline_y = topx(face->bbox.yMax) + 4;
+ fontinfo.line_height = topx(face->height);
+
+ FT_Int32 loadmode = FT_LOAD_TARGET_NORMAL | FT_LOAD_RENDER;
+
+ if (bw)
+ loadmode = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME | FT_LOAD_RENDER;
+
+ FT_ULong charcode;
+ FT_UInt gindex;
+ charcode = FT_Get_First_Char(face, &gindex);
+ while (gindex)
+ {
+ try
+ {
+ checkFT(FT_Load_Glyph(face, gindex, loadmode));
+ }
+ catch (std::runtime_error &e)
+ {
+ std::cerr << "Skipping glyph " << gindex << ": " << e.what() << std::endl;
+ charcode = FT_Get_Next_Char(face, charcode, &gindex);
+ }
+
+ DataFile::glyphentry_t glyph;
+ glyph.width = (face->glyph->advance.x + 32) / 64;
+ glyph.chars.push_back(charcode);
+ glyph.data.resize(fontinfo.max_width * fontinfo.max_height);
+
+ int w = face->glyph->bitmap.width;
+ int dw = fontinfo.max_width;
+ int dx = fontinfo.baseline_x + face->glyph->bitmap_left;
+ int dy = fontinfo.baseline_y - face->glyph->bitmap_top;
+
+ /* Some combining diacritics seem to exceed the bounding box.
+ * We don't support them all that well anyway, so just move
+ * them inside the box in order not to crash.. */
+ if (dy < 0)
+ dy = 0;
+ if (dy + face->glyph->bitmap.rows > fontinfo.max_height)
+ dy = fontinfo.max_height - face->glyph->bitmap.rows;
+
+ size_t s = face->glyph->bitmap.pitch;
+ for (int y = 0; y < face->glyph->bitmap.rows; y++)
+ {
+ for (int x = 0; x < face->glyph->bitmap.width; x++)
+ {
+ size_t index = (y + dy) * dw + x + dx;
+
+ if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
+ {
+ uint8_t byte = face->glyph->bitmap.buffer[s * y + x / 8];
+ byte <<= x % 8;
+ glyph.data.at(index) = (byte & 0x80) ? 15 : 0;
+ }
+ else
+ {
+ glyph.data.at(index) =
+ (face->glyph->bitmap.buffer[w * y + x] + 8) / 17;
+ }
+ }
+ }
+ glyphtable.push_back(glyph);
+
+ charcode = FT_Get_Next_Char(face, charcode, &gindex);
+ }
+
+ eliminate_duplicates(glyphtable);
+ crop_glyphs(glyphtable, fontinfo);
+ detect_flags(glyphtable, fontinfo);
+
+ std::unique_ptr<DataFile> result(new DataFile(
+ dictionary, glyphtable, fontinfo));
+ return result;
+}
+
+}