Browse Source

Add the mcufont encoder to the tools (including a win32 build) with binaries

inmarket 3 years ago
parent
commit
0ec1a5e4da
29 changed files with 3694 additions and 0 deletions
  1. 2 0
      3rdparty/freetype-2.6.1/readme.txt
  2. BIN
      tools/mcufontencoder/binaries/linux/mcufont
  3. BIN
      tools/mcufontencoder/binaries/windows/freetype6.dll
  4. BIN
      tools/mcufontencoder/binaries/windows/libgcc_s_dw2-1.dll
  5. BIN
      tools/mcufontencoder/binaries/windows/mcufont.exe
  6. BIN
      tools/mcufontencoder/binaries/windows/zlib1.dll
  7. 43 0
      tools/mcufontencoder/src/Makefile
  8. 44 0
      tools/mcufontencoder/src/Makefile.mingw32
  9. 156 0
      tools/mcufontencoder/src/bdf_import.cc
  10. 80 0
      tools/mcufontencoder/src/bdf_import.hh
  11. 152 0
      tools/mcufontencoder/src/ccfixes.hh
  12. 239 0
      tools/mcufontencoder/src/datafile.cc
  13. 174 0
      tools/mcufontencoder/src/datafile.hh
  14. 735 0
      tools/mcufontencoder/src/encode_rlefont.cc
  15. 126 0
      tools/mcufontencoder/src/encode_rlefont.hh
  16. 247 0
      tools/mcufontencoder/src/export_bwfont.cc
  17. 16 0
      tools/mcufontencoder/src/export_bwfont.hh
  18. 181 0
      tools/mcufontencoder/src/export_rlefont.cc
  19. 15 0
      tools/mcufontencoder/src/export_rlefont.hh
  20. 179 0
      tools/mcufontencoder/src/exporttools.cc
  21. 52 0
      tools/mcufontencoder/src/exporttools.hh
  22. 177 0
      tools/mcufontencoder/src/freetype_import.cc
  23. 10 0
      tools/mcufontencoder/src/freetype_import.hh
  24. 134 0
      tools/mcufontencoder/src/importtools.cc
  25. 20 0
      tools/mcufontencoder/src/importtools.hh
  26. BIN
      tools/mcufontencoder/src/libfreetype.dll.a
  27. 480 0
      tools/mcufontencoder/src/main.cc
  28. 417 0
      tools/mcufontencoder/src/optimize_rlefont.cc
  29. 15 0
      tools/mcufontencoder/src/optimize_rlefont.hh

+ 2 - 0
3rdparty/freetype-2.6.1/readme.txt

@@ -0,0 +1,2 @@
1
+freetype-2.6.1 source code has not been included in this repository so that you are fully
2
+aware of the license types and restrictions before using it with uGFX.

BIN
tools/mcufontencoder/binaries/linux/mcufont


BIN
tools/mcufontencoder/binaries/windows/freetype6.dll


BIN
tools/mcufontencoder/binaries/windows/libgcc_s_dw2-1.dll


BIN
tools/mcufontencoder/binaries/windows/mcufont.exe


BIN
tools/mcufontencoder/binaries/windows/zlib1.dll


+ 43 - 0
tools/mcufontencoder/src/Makefile

@@ -0,0 +1,43 @@
1
+CXXFLAGS = -O2 -Wall -Werror -Wno-unused-function -Wno-sign-compare -std=c++0x
2
+CXXFLAGS += -ggdb
3
+LDFLAGS += -pthread
4
+
5
+# Libfreetype
6
+CXXFLAGS += $(shell freetype-config --cflags)
7
+LDFLAGS += $(shell freetype-config --libs)
8
+
9
+# Class to represent font data internally
10
+OBJS = datafile.o
11
+
12
+# Utility functions
13
+OBJS += importtools.o exporttools.o
14
+
15
+# Import formats
16
+OBJS += bdf_import.o freetype_import.o
17
+
18
+# rlefont export format
19
+OBJS += encode_rlefont.o optimize_rlefont.o export_rlefont.o
20
+
21
+# bwfont export format
22
+OBJS += export_bwfont.o
23
+
24
+
25
+all: run_unittests mcufont
26
+
27
+clean:
28
+	rm -f unittests unittests.cc mcufont $(OBJS)
29
+
30
+mcufont: main.o $(OBJS)
31
+	g++ $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
32
+
33
+unittests.cc: *.hh
34
+	cxxtestgen --have-eh --error-printer -o unittests.cc $^
35
+
36
+unittests: unittests.cc $(OBJS)
37
+	g++ $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
38
+
39
+run_unittests: unittests
40
+	./unittests
41
+
42
+%.o: %.cc *.hh
43
+	g++ $(CXXFLAGS) -c $<

+ 44 - 0
tools/mcufontencoder/src/Makefile.mingw32

@@ -0,0 +1,44 @@
1
+ARCH     = i686-pc-mingw32-
2
+CXXFLAGS = -O2 -Wall -Werror -Wno-unused-function -Wno-sign-compare -std=c++0x
3
+CXXFLAGS += -ggdb
4
+LDFLAGS += -pthread --static
5
+
6
+# Libfreetype
7
+CXXFLAGS += $(shell freetype-config --cflags)
8
+LDFLAGS += $(shell freetype-config --libs)
9
+#FREETYPE2_LIB = ../ugfx/3rdparty/freetype-2.6.1
10
+#CXXFLAGS += -I$(FREETYPE2_LIB)/include
11
+#LDFLAGS += -I$(FREETYPE2_LIB)/lib -lfreetype
12
+
13
+# compiler fixes for mingw32
14
+CXXFLAGS += -DNEED_STRING_FIXES -DNEED_THREAD_FIXES
15
+
16
+# Class to represent font data internally
17
+OBJS = datafile.o
18
+
19
+# Utility functions
20
+OBJS += importtools.o exporttools.o
21
+
22
+# Import formats
23
+OBJS += bdf_import.o freetype_import.o
24
+
25
+# rlefont export format
26
+OBJS += encode_rlefont.o optimize_rlefont.o export_rlefont.o
27
+
28
+# bwfont export format
29
+OBJS += export_bwfont.o
30
+
31
+
32
+all: mcufont
33
+
34
+strip: mcufont.exe
35
+	strip mcufont.exe
36
+
37
+clean:
38
+	rm -f mcufont $(OBJS)
39
+
40
+mcufont: main.o $(OBJS)
41
+	$(ARCH)g++ $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
42
+
43
+%.o: %.cc *.hh
44
+	$(ARCH)g++ $(CXXFLAGS) -c $<

+ 156 - 0
tools/mcufontencoder/src/bdf_import.cc

@@ -0,0 +1,156 @@
1
+#include "bdf_import.hh"
2
+#include "importtools.hh"
3
+#include <sstream>
4
+#include <string>
5
+#include <cctype>
6
+#include <stdexcept>
7
+
8
+namespace mcufont {
9
+
10
+static std::string toupper(const std::string &input)
11
+{
12
+    std::string result;
13
+    for (char c: input) result.push_back(::toupper(c));
14
+    return result;
15
+}
16
+
17
+static int hextoint(char c)
18
+{
19
+    if (c >= '0' && c <= '9') return c - '0';
20
+    if (c >= 'A' && c <= 'F') return c - 'A' + 10;
21
+    throw std::domain_error("Hex digit not in range");
22
+}
23
+
24
+static void parse_fontinfo(std::istream &file, DataFile::fontinfo_t &fontinfo)
25
+{
26
+    std::string line;
27
+    while (std::getline(file, line))
28
+    {
29
+        std::istringstream s(line);
30
+        std::string tag;
31
+        s >> tag;
32
+        tag = toupper(tag);
33
+        
34
+        if (tag == "FONT")
35
+        {
36
+            while (isspace(s.peek())) s.get();
37
+            std::getline(s, fontinfo.name);
38
+        }
39
+        else if (tag == "FONTBOUNDINGBOX")
40
+        {
41
+            int x, y;
42
+            s >> fontinfo.max_width >> fontinfo.max_height;
43
+            s >> x >> y;
44
+            fontinfo.baseline_x = - x;
45
+            fontinfo.baseline_y = fontinfo.max_height + y;
46
+        }
47
+        else if (tag == "STARTCHAR")
48
+        {
49
+            break;
50
+        }
51
+    }
52
+}
53
+
54
+static bool parse_glyph(std::istream &file, DataFile::glyphentry_t &glyph,
55
+                        const DataFile::fontinfo_t &fontinfo)
56
+{
57
+    glyph.chars.clear();
58
+    glyph.width = 0;
59
+    
60
+    // Initialize the character contents to all 0 with proper size.
61
+    glyph.data.clear();
62
+    glyph.data.resize(fontinfo.max_width * fontinfo.max_height, 0);
63
+    
64
+    int bbx_w = fontinfo.max_width;
65
+    int bbx_h = fontinfo.max_height;
66
+    int bbx_x = - fontinfo.baseline_x;
67
+    int bbx_y = fontinfo.baseline_y - fontinfo.max_height;
68
+    
69
+    // Read glyph metadata
70
+    std::string line;
71
+    std::string tag;
72
+    while (std::getline(file, line))
73
+    {
74
+        std::istringstream s(line);
75
+        s >> tag;
76
+        tag = toupper(tag);
77
+        
78
+        if (tag == "ENCODING")
79
+        {
80
+            int c;
81
+            s >> c;
82
+            glyph.chars.push_back(c);
83
+        }
84
+        else if (tag == "DWIDTH")
85
+        {
86
+            s >> glyph.width;
87
+        }
88
+        else if (tag == "BBX")
89
+        {
90
+            s >> bbx_w >> bbx_h >> bbx_x >> bbx_y;
91
+        }
92
+        else if (tag == "BITMAP")
93
+        {
94
+            break;
95
+        }
96
+    }
97
+    
98
+    if (tag != "BITMAP")
99
+        return false;
100
+    
101
+    // Read glyph bits
102
+    int x0 = fontinfo.baseline_x + bbx_x;
103
+    int y = fontinfo.baseline_y - bbx_y - bbx_h;
104
+    for (int i = 0; i < bbx_h; i++)
105
+    {
106
+        std::getline(file, line);
107
+        line = toupper(line);
108
+        
109
+        for (int x = 0; x < bbx_w; x++)
110
+        {
111
+            int nibble = hextoint(line.at(x / 4));
112
+            uint8_t pixel = 0;
113
+            if (nibble & (8 >> (x % 4)))
114
+                pixel = 15;
115
+            
116
+            glyph.data.at(y * fontinfo.max_width + x0 + x) = pixel;
117
+        }
118
+        
119
+        y++;
120
+    }
121
+    
122
+    std::getline(file, line);
123
+    line = toupper(line);
124
+    if (line.compare(0, 7, "ENDCHAR") == 0)
125
+        return true;
126
+    else
127
+        return false;
128
+}
129
+
130
+std::unique_ptr<DataFile> LoadBDF(std::istream &file)
131
+{
132
+    DataFile::fontinfo_t fontinfo = {};
133
+    std::vector<DataFile::glyphentry_t> glyphtable;
134
+    std::vector<DataFile::dictentry_t> dictionary;
135
+   
136
+    parse_fontinfo(file, fontinfo);
137
+    
138
+    while (file)
139
+    {
140
+        DataFile::glyphentry_t glyph = {};
141
+        if (parse_glyph(file, glyph, fontinfo))
142
+            glyphtable.push_back(glyph);
143
+    }
144
+    
145
+    eliminate_duplicates(glyphtable);
146
+    crop_glyphs(glyphtable, fontinfo);
147
+    detect_flags(glyphtable, fontinfo);
148
+    
149
+    fontinfo.line_height = fontinfo.max_height;
150
+    
151
+    std::unique_ptr<DataFile> result(new DataFile(
152
+        dictionary, glyphtable, fontinfo));
153
+    return result;
154
+}
155
+
156
+}

+ 80 - 0
tools/mcufontencoder/src/bdf_import.hh

@@ -0,0 +1,80 @@
1
+// Function for importing .BDF fonts as data files.
2
+
3
+#pragma once
4
+#include "datafile.hh"
5
+
6
+namespace mcufont
7
+{
8
+
9
+std::unique_ptr<DataFile> LoadBDF(std::istream &file);
10
+
11
+}
12
+
13
+#ifdef CXXTEST_RUNNING
14
+#include <cxxtest/TestSuite.h>
15
+
16
+using namespace mcufont;
17
+
18
+class BDFTests: public CxxTest::TestSuite
19
+{
20
+public:
21
+    void testLoadBDF()
22
+    {
23
+        std::istringstream s(testfile);
24
+        std::unique_ptr<DataFile> f = LoadBDF(s);
25
+        
26
+        TS_ASSERT_EQUALS(f->GetFontInfo().name, "-Misc-Fixed-Medium-R-Normal--14-130-75-75-C-70-ISO8859-15");
27
+        TS_ASSERT_EQUALS(f->GetFontInfo().max_width, 6);
28
+        TS_ASSERT_EQUALS(f->GetFontInfo().max_height, 11);
29
+        TS_ASSERT_EQUALS(f->GetGlyphCount(), 1);
30
+        TS_ASSERT_EQUALS(f->GetGlyphEntry(0).chars.size(), 2);
31
+    }
32
+    
33
+private:
34
+    static constexpr const char *testfile = 
35
+        "STARTFONT 2.1\n"
36
+        "FONT -Misc-Fixed-Medium-R-Normal--14-130-75-75-C-70-ISO8859-15\n"
37
+        "FONTBOUNDINGBOX 7 14 0 -2\n"
38
+        "STARTCHAR defaultchar\n"
39
+        "ENCODING 0\n"
40
+        "DWIDTH 7 0\n"
41
+        "BBX 7 14 0 -2\n"
42
+        "BITMAP\n"
43
+        "00\n"
44
+        "B4\n"
45
+        "84\n"
46
+        "00\n"
47
+        "84\n"
48
+        "84\n"
49
+        "00\n"
50
+        "84\n"
51
+        "84\n"
52
+        "00\n"
53
+        "84\n"
54
+        "B4\n"
55
+        "00\n"
56
+        "00\n"
57
+        "ENDCHAR\n"
58
+        "STARTCHAR copychar\n"
59
+        "ENCODING 2\n"
60
+        "DWIDTH 7 0\n"
61
+        "BBX 7 14 0 -2\n"
62
+        "BITMAP\n"
63
+        "00\n"
64
+        "B4\n"
65
+        "84\n"
66
+        "00\n"
67
+        "84\n"
68
+        "84\n"
69
+        "00\n"
70
+        "84\n"
71
+        "84\n"
72
+        "00\n"
73
+        "84\n"
74
+        "B4\n"
75
+        "00\n"
76
+        "00\n"
77
+        "ENDCHAR\n";
78
+
79
+};
80
+#endif

+ 152 - 0
tools/mcufontencoder/src/ccfixes.hh

@@ -0,0 +1,152 @@
1
+
2
+#ifdef NEED_STRING_FIXES
3
+	#include <string>
4
+	#include <sstream>
5
+	#include <stdexcept>
6
+	#include <limits>
7
+	#include <cstdlib>
8
+
9
+	namespace std {
10
+		template <typename T> inline std::string to_string(T value)
11
+		{
12
+			std::ostringstream os ;
13
+			os << value ;
14
+			return os.str() ;
15
+		}
16
+
17
+		inline int stoi( const std::string& str, std::size_t* pos = 0, int base = 10 )
18
+		{
19
+			const char* begin = str.c_str() ;
20
+			char* end = nullptr ;
21
+			long value = std::strtol( begin, &end, base ) ;
22
+
23
+			if( errno == ERANGE || value > std::numeric_limits<int>::max() )
24
+				throw std::out_of_range( "stoi: out ofrange" ) ;
25
+
26
+			if( end == str.c_str() )
27
+				throw std::invalid_argument( "stoi: invalid argument" ) ;
28
+
29
+			if(pos) *pos = end - begin ;
30
+
31
+			return value ;
32
+		}
33
+	}
34
+#endif
35
+
36
+#ifdef NEED_THREAD_FIXES
37
+#ifndef WIN32STDTHREAD_H
38
+	#define WIN32STDTHREAD_H
39
+
40
+	#include <windows.h>
41
+	#include <functional>
42
+	#include <memory>
43
+	#include <chrono>
44
+	#include <system_error>
45
+	#include <process.h>
46
+
47
+	#define _STD_THREAD_INVALID_HANDLE 0
48
+	namespace std
49
+	{
50
+
51
+
52
+		class thread
53
+		{
54
+		public:
55
+			class id
56
+			{
57
+				DWORD mId;
58
+				void clear() {mId = 0;}
59
+				friend class thread;
60
+			public:
61
+				id(DWORD aId=0):mId(aId){}
62
+				bool operator==(const id& other) const {return mId == other.mId;}
63
+			};
64
+		protected:
65
+			HANDLE mHandle;
66
+			id mThreadId;
67
+		public:
68
+			typedef HANDLE native_handle_type;
69
+			id get_id() const noexcept {return mThreadId;}
70
+			native_handle_type native_handle() const {return mHandle;}
71
+			thread(): mHandle(_STD_THREAD_INVALID_HANDLE){}
72
+			thread(thread& other)
73
+			:mHandle(other.mHandle), mThreadId(other.mThreadId)
74
+			{
75
+				other.mHandle = _STD_THREAD_INVALID_HANDLE;
76
+				other.mThreadId.clear();
77
+			}
78
+			template<class Function, class... Args>
79
+			explicit thread(Function&& f, Args&&... args)
80
+			{
81
+				typedef decltype(std::bind(f, args...)) Call;
82
+				Call* call = new Call(std::bind(f, args...));
83
+				mHandle = (HANDLE)_beginthreadex(NULL, 0, threadfunc<Call>,
84
+					(LPVOID)call, 0, (unsigned*)&(mThreadId.mId));
85
+			}
86
+			template <class Call>
87
+			static unsigned int __stdcall threadfunc(void* arg)
88
+			{
89
+				std::unique_ptr<Call> upCall(static_cast<Call*>(arg));
90
+				(*upCall)();
91
+				return (unsigned long)0;
92
+			}
93
+			bool joinable() const {return mHandle != _STD_THREAD_INVALID_HANDLE;}
94
+			void join()
95
+			{
96
+				if (get_id() == GetCurrentThreadId())
97
+					throw system_error(EDEADLK, generic_category());
98
+				if (mHandle == _STD_THREAD_INVALID_HANDLE)
99
+					throw system_error(ESRCH, generic_category());
100
+				if (!joinable())
101
+					throw system_error(EINVAL, generic_category());
102
+				WaitForSingleObject(mHandle, INFINITE);
103
+				CloseHandle(mHandle);
104
+				mHandle = _STD_THREAD_INVALID_HANDLE;
105
+				mThreadId.clear();
106
+			}
107
+
108
+			~thread()
109
+			{
110
+				if (joinable())
111
+					std::terminate();
112
+			}
113
+			thread& operator=(const thread&) = delete;
114
+			thread& operator=(thread&& other) noexcept
115
+			{
116
+				if (joinable())
117
+				  std::terminate();
118
+				swap(std::forward<thread>(other));
119
+				return *this;
120
+			}
121
+			void swap(thread&& other) noexcept
122
+			{
123
+				std::swap(mHandle, other.mHandle);
124
+				std::swap(mThreadId.mId, other.mThreadId.mId);
125
+			}
126
+			static unsigned int hardware_concurrency() noexcept {return 1;}
127
+			void detach()
128
+			{
129
+				if (!joinable())
130
+					throw system_error();
131
+				mHandle = _STD_THREAD_INVALID_HANDLE;
132
+				mThreadId.clear();
133
+			}
134
+		};
135
+		namespace this_thread
136
+		{
137
+			inline thread::id get_id() {return thread::id(GetCurrentThreadId());}
138
+			inline void yield() {Sleep(0);}
139
+			template< class Rep, class Period >
140
+			void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration)
141
+			{
142
+				Sleep(chrono::duration_cast<chrono::milliseconds>(sleep_duration).count());
143
+			}
144
+			template <class Clock, class Duration>
145
+			void sleep_until(const std::chrono::time_point<Clock,Duration>& sleep_time)
146
+			{
147
+				sleep_for(sleep_time-Clock::now());
148
+		   }
149
+		}
150
+	}
151
+#endif
152
+#endif

+ 239 - 0
tools/mcufontencoder/src/datafile.cc

@@ -0,0 +1,239 @@
1
+#include "datafile.hh"
2
+#include <sstream>
3
+#include <algorithm>
4
+#include <cctype>
5
+#include <stdexcept>
6
+#include "ccfixes.hh"
7
+
8
+#define DATAFILE_FORMAT_VERSION 1
9
+
10
+namespace mcufont {
11
+
12
+DataFile::DataFile(const std::vector<dictentry_t> &dictionary,
13
+                   const std::vector<glyphentry_t> &glyphs,
14
+                   const fontinfo_t &fontinfo):
15
+    m_dictionary(dictionary), m_glyphtable(glyphs), m_fontinfo(fontinfo)
16
+{
17
+    dictentry_t dummy = {};
18
+    while (m_dictionary.size() < dictionarysize)
19
+        m_dictionary.push_back(dummy);
20
+    
21
+    UpdateLowScoreIndex();
22
+}
23
+
24
+void DataFile::Save(std::ostream &file) const
25
+{
26
+    file << "Version " << DATAFILE_FORMAT_VERSION << std::endl;
27
+    file << "FontName " << m_fontinfo.name << std::endl;
28
+    file << "MaxWidth " << m_fontinfo.max_width << std::endl;
29
+    file << "MaxHeight " << m_fontinfo.max_height << std::endl;
30
+    file << "BaselineX " << m_fontinfo.baseline_x << std::endl;
31
+    file << "BaselineY " << m_fontinfo.baseline_y << std::endl;
32
+    file << "LineHeight " << m_fontinfo.line_height << std::endl;
33
+    file << "Flags " << m_fontinfo.flags << std::endl;
34
+    file << "RandomSeed " << m_seed << std::endl;
35
+    
36
+    for (const dictentry_t &d : m_dictionary)
37
+    {
38
+        if (d.replacement.size() != 0)
39
+        {
40
+            file << "DictEntry " << d.score << " ";
41
+            file << d.ref_encode << " " << d.replacement << std::endl;
42
+        }
43
+    }
44
+    
45
+    for (const glyphentry_t &g : m_glyphtable)
46
+    {
47
+        file << "Glyph ";
48
+        for (size_t i = 0; i < g.chars.size(); i++)
49
+        {
50
+            if (i != 0) file << ',';
51
+            file << g.chars.at(i);
52
+        }
53
+        file << " " << g.width << " " << g.data << std::endl;
54
+    }
55
+}
56
+
57
+std::unique_ptr<DataFile> DataFile::Load(std::istream &file)
58
+{
59
+    fontinfo_t fontinfo = {};
60
+    std::vector<dictentry_t> dictionary;
61
+    std::vector<glyphentry_t> glyphtable;
62
+    uint32_t seed = 1234;
63
+    int version = -1;
64
+    
65
+    std::string line;
66
+    while (std::getline(file, line))
67
+    {
68
+        std::istringstream input(line);
69
+        std::string tag;
70
+        
71
+        input >> tag;
72
+        
73
+        if (tag == "Version")
74
+        {
75
+            input >> version;
76
+        }
77
+        else if (tag == "FontName")
78
+        {
79
+            while (std::isspace(input.peek())) input.get();
80
+            std::getline(input, fontinfo.name);
81
+        }
82
+        else if (tag == "MaxWidth")
83
+        {
84
+            input >> fontinfo.max_width;
85
+        }
86
+        else if (tag == "MaxHeight")
87
+        {
88
+            input >> fontinfo.max_height;
89
+        }
90
+        else if (tag == "BaselineX")
91
+        {
92
+            input >> fontinfo.baseline_x;
93
+        }
94
+        else if (tag == "BaselineY")
95
+        {
96
+            input >> fontinfo.baseline_y;
97
+        }
98
+        else if (tag == "LineHeight")
99
+        {
100
+            input >> fontinfo.line_height;
101
+        }
102
+        else if (tag == "RandomSeed")
103
+        {
104
+            input >> seed;
105
+        }
106
+        else if (tag == "Flags")
107
+        {
108
+            input >> fontinfo.flags;
109
+        }
110
+        else if (tag == "DictEntry" && dictionary.size() < dictionarysize)
111
+        {
112
+            dictentry_t d = {};
113
+            input >> d.score >> d.ref_encode >> d.replacement;
114
+            dictionary.push_back(d);
115
+        }
116
+        else if (tag == "Glyph")
117
+        {
118
+            glyphentry_t g = {};
119
+            std::string chars;
120
+            input >> chars >> g.width >> g.data;
121
+            
122
+            if ((int)g.data.size() != fontinfo.max_width * fontinfo.max_height)
123
+                throw std::runtime_error("wrong glyph data length: " + std::to_string(g.data.size()));
124
+            
125
+            size_t pos = 0;
126
+            while (pos < chars.size()) {
127
+                size_t p;
128
+                g.chars.push_back(std::stoi(chars.substr(pos), &p));
129
+                pos += p + 1;
130
+            }
131
+            
132
+            glyphtable.push_back(g);
133
+        }
134
+    }
135
+    
136
+    if (version != DATAFILE_FORMAT_VERSION)
137
+    {
138
+        return std::unique_ptr<DataFile>(nullptr);
139
+    }
140
+    
141
+    std::unique_ptr<DataFile> result(new DataFile(dictionary, glyphtable, fontinfo));
142
+    result->SetSeed(seed);
143
+    return result;
144
+}
145
+
146
+void DataFile::SetDictionaryEntry(size_t index, const dictentry_t &value)
147
+{
148
+    m_dictionary.at(index) = value;
149
+    
150
+    if (index == m_lowscoreindex ||
151
+        m_dictionary.at(m_lowscoreindex).score > value.score)
152
+    {
153
+        UpdateLowScoreIndex();
154
+    }
155
+}
156
+
157
+std::map<size_t, size_t> DataFile::GetCharToGlyphMap() const
158
+{
159
+    std::map<size_t, size_t> char_to_glyph;
160
+    
161
+    for (size_t i = 0; i < m_glyphtable.size(); i++)
162
+    {
163
+        for (size_t c: m_glyphtable[i].chars)
164
+        {
165
+            char_to_glyph[c] = i;
166
+        }
167
+    }
168
+    
169
+    return char_to_glyph;
170
+}
171
+
172
+std::string DataFile::GlyphToText(size_t index) const
173
+{
174
+    std::ostringstream os;
175
+    
176
+    const char glyphchars[] = "....,,,,----XXXX";
177
+    
178
+    for (int y = 0; y < m_fontinfo.max_height; y++)
179
+    {
180
+        for (int x = 0; x < m_fontinfo.max_width; x++)
181
+        {
182
+            size_t pos = y * m_fontinfo.max_width + x;
183
+            os << glyphchars[m_glyphtable.at(index).data.at(pos)];
184
+        }
185
+        os << std::endl;
186
+    }
187
+    
188
+    return os.str();
189
+}
190
+
191
+void DataFile::UpdateLowScoreIndex()
192
+{
193
+    auto comparison = [](const dictentry_t &a, const dictentry_t &b)
194
+    {
195
+        return a.score < b.score;
196
+    };
197
+    
198
+    auto iter = std::min_element(m_dictionary.begin(),
199
+                                 m_dictionary.end(),
200
+                                 comparison);
201
+    
202
+    m_lowscoreindex = iter - m_dictionary.begin();
203
+}
204
+
205
+std::ostream& operator<<(std::ostream& os, const DataFile::pixels_t& str)
206
+{
207
+    for (uint8_t p: str)
208
+    {
209
+        if (p <= 9)
210
+            os << (char)(p + '0');
211
+        else if (p <= 15)
212
+            os << (char)(p - 10 + 'A');
213
+        else
214
+            throw std::logic_error("invalid pixel alpha: " + std::to_string(p));
215
+    }
216
+    return os;
217
+}
218
+
219
+std::istream& operator>>(std::istream& is, DataFile::pixels_t& str)
220
+{
221
+    char c;
222
+    str.clear();
223
+    
224
+    while (isspace(is.peek())) is.get();
225
+    
226
+    while (is.get(c))
227
+    {
228
+        if (c >= '0' && c <= '9')
229
+            str.push_back(c - '0');
230
+        else if (c >= 'A' && c <= 'F')
231
+            str.push_back(c - 'A' + 10);
232
+        else
233
+            break;
234
+    }
235
+    
236
+    return is;
237
+}
238
+
239
+}

+ 174 - 0
tools/mcufontencoder/src/datafile.hh

@@ -0,0 +1,174 @@
1
+// Class to store the data of a font while it is being processed.
2
+// This class can be safely cloned using the default copy constructor.
3
+
4
+#pragma once
5
+#include <cstdint>
6
+#include <vector>
7
+#include <string>
8
+#include <fstream>
9
+#include <memory>
10
+#include <map>
11
+
12
+namespace mcufont
13
+{
14
+
15
+class DataFile
16
+{
17
+public:
18
+    typedef std::vector<uint8_t> pixels_t;
19
+    
20
+    struct dictentry_t
21
+    {
22
+        pixels_t replacement; // The expanded version of this block.
23
+        int score; // Number of bytes that having this entry saves.
24
+        bool ref_encode; // Encode using references to other dictionary entries.
25
+        
26
+        dictentry_t(): score(0), ref_encode(false) {}
27
+    };
28
+    
29
+    struct glyphentry_t
30
+    {
31
+        pixels_t data; // The full data of the glyph.
32
+        std::vector<int> chars; // Characters that this glyph represents.
33
+        int width; // Tracking width of the character.
34
+    };
35
+    
36
+    struct fontinfo_t
37
+    {
38
+        std::string name; // Name of the typeface
39
+        int max_width; // Width of the character bounding box.
40
+        int max_height; // Height of the character bounding box.
41
+        int baseline_x; // X coordinate (from left) of the baseline.
42
+        int baseline_y; // Y coordinate (from top) of the baseline.
43
+        int line_height; // Line height (vertical advance).
44
+        int flags;
45
+    };
46
+    
47
+    static const int FLAG_MONOSPACE = 0x01;
48
+    static const int FLAG_BW = 0x02;
49
+    
50
+    // Construct from data in memory.
51
+    DataFile(const std::vector<dictentry_t> &dictionary,
52
+             const std::vector<glyphentry_t> &glyphs,
53
+             const fontinfo_t &fontinfo);
54
+    
55
+    // Save to a file (custom format)
56
+    void Save(std::ostream &file) const;
57
+
58
+    // Load from a file (custom format)
59
+    // Returns nullptr if load fails.
60
+    static std::unique_ptr<DataFile> Load(std::istream &file);
61
+    
62
+    // Get or set an entry in the dictionary. The size of the dictionary
63
+    // is constant. Entries 0 to 23 are reserved for special purposes.
64
+    static const size_t dictionarysize = 256 - 24;
65
+    const dictentry_t &GetDictionaryEntry(size_t index) const
66
+        { return m_dictionary.at(index); }
67
+    void SetDictionaryEntry(size_t index, const dictentry_t &value);
68
+    const std::vector<dictentry_t> &GetDictionary() const
69
+        { return m_dictionary; }
70
+        
71
+    // Get the index of the dictionary entry with the lowest score.
72
+    size_t GetLowScoreIndex() const
73
+        { return m_lowscoreindex; }
74
+    
75
+    // Get an entry in the glyph table.
76
+    size_t GetGlyphCount() const
77
+        { return m_glyphtable.size(); }
78
+    const glyphentry_t &GetGlyphEntry(size_t index) const
79
+        { return m_glyphtable.at(index); }
80
+    const std::vector<glyphentry_t> &GetGlyphTable() const
81
+        { return m_glyphtable; }
82
+    
83
+    // Create a map of char indices to glyph indices
84
+    std::map<size_t, size_t> GetCharToGlyphMap() const;
85
+    
86
+    // Get the information that applies to all glyphs.
87
+    const fontinfo_t &GetFontInfo() const
88
+        { return m_fontinfo; }
89
+    
90
+    // Show a glyph as text.
91
+    std::string GlyphToText(size_t index) const;
92
+    
93
+    // Get the random generator seed
94
+    // The seed is stored in the datafile to get deterministic behaviour
95
+    // for debugging.
96
+    uint32_t GetSeed() const { return m_seed; }
97
+    void SetSeed(uint32_t seed) { m_seed = seed; }
98
+    
99
+private:
100
+    std::vector<dictentry_t> m_dictionary;
101
+    std::vector<glyphentry_t> m_glyphtable;
102
+    fontinfo_t m_fontinfo;
103
+    uint32_t m_seed;
104
+    
105
+    size_t m_lowscoreindex;
106
+    
107
+    void UpdateLowScoreIndex();
108
+};
109
+
110
+std::ostream& operator<<(std::ostream& os, const DataFile::pixels_t& str);
111
+std::istream& operator>>(std::istream& is, DataFile::pixels_t& str);
112
+
113
+}
114
+
115
+#ifdef CXXTEST_RUNNING
116
+#include <cxxtest/TestSuite.h>
117
+
118
+using namespace mcufont;
119
+
120
+class DataFileTests: public CxxTest::TestSuite
121
+{
122
+public:
123
+    void testFileLoad()
124
+    {
125
+        std::istringstream s(testfile);
126
+        std::unique_ptr<DataFile> f = DataFile::Load(s);
127
+        
128
+        TS_ASSERT_EQUALS(f->GetFontInfo().name, "Sans Serif");
129
+        TS_ASSERT_EQUALS(f->GetFontInfo().max_width, 4);
130
+        TS_ASSERT_EQUALS(f->GetFontInfo().max_height, 6);
131
+        TS_ASSERT_EQUALS(f->GetDictionaryEntry(0).score, 5);
132
+        TS_ASSERT_EQUALS(f->GetDictionaryEntry(1).score, 13);
133
+        TS_ASSERT_EQUALS(f->GetGlyphCount(), 3);
134
+        
135
+        DataFile::pixels_t expected = {
136
+            0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15
137
+        };
138
+        TS_ASSERT_EQUALS(f->GetGlyphEntry(0).data.size(), 24);
139
+        TS_ASSERT(f->GetGlyphEntry(0).data == expected);
140
+    }
141
+    
142
+    void testFileSave()
143
+    {
144
+        std::istringstream is1(testfile);
145
+        std::unique_ptr<DataFile> f1 = DataFile::Load(is1);
146
+        
147
+        std::ostringstream os;
148
+        f1->Save(os);
149
+        
150
+        std::string text = os.str();
151
+        std::istringstream is2(text);
152
+        std::unique_ptr<DataFile> f2 = DataFile::Load(is2);
153
+        
154
+        TS_ASSERT_EQUALS(f1->GetFontInfo().name, f2->GetFontInfo().name);
155
+        TS_ASSERT(f1->GetGlyphEntry(0).data == f2->GetGlyphEntry(0).data);
156
+    }
157
+    
158
+private:
159
+    static constexpr const char *testfile =
160
+        "Version 1\n"
161
+        "FontName Sans Serif\n"
162
+        "MaxWidth 4\n"
163
+        "MaxHeight 6\n"
164
+        "BaselineX 1\n"
165
+        "BaselineY 1\n"
166
+        "DictEntry 5 0 0F0F0\n"
167
+        "DictEntry 13 0 F0F0F0\n"
168
+        "DictEntry 1 0 F0F0F0\n"
169
+        "Glyph 1,2,3 4 0F0F0F0F0F0F0F0F0F0F0F0F\n"
170
+        "Glyph 4 4 0F0F0F0F0F0F0F0F0F0F0F0F\n"
171
+        "Glyph 5 4 0F0F0F0F0F0F0F0F0F0F0F0F\n";
172
+};
173
+
174
+#endif

+ 735 - 0
tools/mcufontencoder/src/encode_rlefont.cc

@@ -0,0 +1,735 @@
1
+#include "encode_rlefont.hh"
2
+#include <algorithm>
3
+#include <stdexcept>
4
+#include "ccfixes.hh"
5
+
6
+// Number of reserved codes before the dictionary entries.
7
+#define DICT_START 24
8
+
9
+// Special reference to mean "fill with zeros to the end of the glyph"
10
+#define REF_FILLZEROS 16
11
+
12
+// RLE codes
13
+#define RLE_CODEMASK    0xC0
14
+#define RLE_VALMASK     0x3F
15
+#define RLE_ZEROS       0x00 // 0 to 63 zeros
16
+#define RLE_64ZEROS     0x40 // (1 to 64) * 64 zeros
17
+#define RLE_ONES        0x80 // 1 to 64 full alphas
18
+#define RLE_SHADE       0xC0 // 1 to 4 partial alphas
19
+
20
+// Dictionary "fill entries" for encoding bits directly.
21
+#define DICT_START7BIT  4
22
+#define DICT_START6BIT  132
23
+#define DICT_START5BIT  196
24
+#define DICT_START4BIT  228
25
+#define DICT_START3BIT  244
26
+#define DICT_START2BIT  252
27
+
28
+namespace mcufont {
29
+namespace rlefont {
30
+
31
+// Get bit count for the "fill entries"
32
+static size_t fillentry_bitcount(size_t index)
33
+{
34
+    if (index >= DICT_START2BIT)
35
+        return 2;
36
+    else if (index >= DICT_START3BIT)
37
+        return 3;
38
+    else if (index >= DICT_START4BIT)
39
+        return 4;
40
+    else if (index >= DICT_START5BIT)
41
+        return 5;
42
+    else if (index >= DICT_START6BIT)
43
+        return 6;
44
+    else
45
+        return 7;
46
+}
47
+
48
+// Count the number of equal pixels at the beginning of the pixelstring.
49
+static size_t prefix_length(const DataFile::pixels_t &pixels, size_t pos)
50
+{
51
+    uint8_t pixel = pixels.at(pos);
52
+    size_t count = 1;
53
+    while (pos + count < pixels.size() &&
54
+            pixels.at(pos + count) == pixel)
55
+    {
56
+        count++;
57
+    }
58
+    return count;
59
+}
60
+
61
+// Perform the RLE encoding for a dictionary entry.
62
+static encoded_font_t::rlestring_t encode_rle(const DataFile::pixels_t &pixels)
63
+{
64
+    encoded_font_t::rlestring_t result;
65
+    
66
+    size_t pos = 0;
67
+    while (pos < pixels.size())
68
+    {
69
+        uint8_t pixel = pixels.at(pos);
70
+        size_t count = prefix_length(pixels, pos);
71
+        pos += count;
72
+        
73
+        if (pixel == 0)
74
+        {
75
+            // Up to 63 zeros can be encoded with RLE_ZEROS. If there are more,
76
+            // encode using RLE_64ZEROS, and then whatever remains with RLE_ZEROS.
77
+            while (count >= 64)
78
+            {
79
+                size_t c = (count > 4096) ? 64 : (count / 64);
80
+                result.push_back(RLE_64ZEROS | (c - 1));
81
+                count -= c * 64;
82
+            }
83
+            
84
+            if (count)
85
+            {
86
+                result.push_back(RLE_ZEROS | count);
87
+            }
88
+        }
89
+        else if (pixel == 15)
90
+        {
91
+            // Encode ones.
92
+            while (count)
93
+            {
94
+                size_t c = (count > 64) ? 64 : count;
95
+                result.push_back(RLE_ONES | (c - 1));
96
+                count -= c;
97
+            }
98
+        }
99
+        else
100
+        {
101
+            // Encode shades.
102
+            while (count)
103
+            {
104
+                size_t c = (count > 4) ? 4 : count;
105
+                result.push_back(RLE_SHADE | ((c - 1) << 4) | pixel);
106
+                count -= c;
107
+            }
108
+        }
109
+    }
110
+    
111
+    return result;
112
+}
113
+
114
+// We use a tree structure to represent the dictionary entries.
115
+// Using this tree, we can perform a combined Aho-Corasick string matching
116
+// and breadth-first search to find the optimal encoding of glyph data.
117
+class DictTreeNode
118
+{
119
+public:
120
+    constexpr DictTreeNode():
121
+        m_index(-1),
122
+        m_ref(false),
123
+        m_length(0),
124
+        m_child0(nullptr),
125
+        m_child15(nullptr),
126
+        m_suffix(nullptr)
127
+        {}
128
+    
129
+    void SetChild(uint8_t p, DictTreeNode *child)
130
+    {
131
+        if (p == 0)
132
+            m_child0 = child;
133
+        else if (p == 15)
134
+            m_child15 = child;
135
+        else if (p > 15)
136
+            throw std::logic_error("invalid pixel alpha: " + std::to_string(p));
137
+        else
138
+        {
139
+            if (!m_children)
140
+            {
141
+                m_children.reset(new DictTreeNode*[14]());
142
+            }
143
+            m_children[p - 1] = child;
144
+        }
145
+    }
146
+    
147
+    DictTreeNode* GetChild(uint8_t p) const
148
+    { 
149
+        if (p == 0)
150
+            return m_child0;
151
+        else if (p == 15)
152
+            return m_child15;
153
+        else if (p > 15)
154
+            throw std::logic_error("invalid pixel alpha: " + std::to_string(p));
155
+        else if (!m_children)
156
+            return nullptr;
157
+        else
158
+            return m_children[p - 1];
159
+    }
160
+    
161
+    bool HasIntermediateChildren() const { return m_children != nullptr; }
162
+    
163
+    int GetIndex() const { return m_index; }
164
+    void SetIndex(int index) { m_index = index; }
165
+    bool GetRef() const { return m_ref; }
166
+    void SetRef(bool ref) { m_ref = ref; }
167
+    size_t GetLength() const { return m_length; }
168
+    void SetLength(size_t length) { m_length = length; }
169
+    DictTreeNode *GetSuffix() const { return m_suffix; }
170
+    void SetSuffix(DictTreeNode *suffix) { m_suffix = suffix; }
171
+    
172
+private:
173
+    // Index of dictionary entry or -1 if just a intermediate node.
174
+    int m_index;
175
+    
176
+    // True for ref-encoded dictionary entries. Used to avoid recursion when
177
+    // encoding them.
178
+    bool m_ref;
179
+    
180
+    // Length of the corresponding dictionary entry replacement.
181
+    // Equals the distance from the tree root.
182
+    size_t m_length;
183
+    
184
+    // Most tree nodes will only ever contains children for 0 or 15.
185
+    // Therefore the array for other nodes is allocated only on demand.
186
+    DictTreeNode *m_child0;
187
+    DictTreeNode *m_child15;
188
+    std::unique_ptr<DictTreeNode*[]> m_children;
189
+    
190
+    // Pointer to the longest suffix of this entry that exists in the
191
+    // dictionary.
192
+    DictTreeNode *m_suffix;
193
+};
194
+
195
+// Preallocated array for tree nodes
196
+class TreeAllocator
197
+{
198
+public:
199
+    TreeAllocator(size_t count)
200
+    {
201
+        m_storage.reset(new DictTreeNode[count]);
202
+        m_next = m_storage.get();
203
+        m_left = count;
204
+    }
205
+    
206
+    DictTreeNode *allocate()
207
+    {
208
+        if (m_left == 0)
209
+            throw std::logic_error("Ran out of preallocated entries");
210
+        
211
+        m_left--;
212
+        return m_next++;
213
+    }
214
+    
215
+private:
216
+    std::unique_ptr<DictTreeNode[]> m_storage;
217
+    DictTreeNode *m_next;
218
+    size_t m_left;
219
+};
220
+
221
+// Add a new dictionary entry to the tree. Adds the intermediate nodes, but
222
+// does not yet fill the suffix pointers.
223
+static DictTreeNode* add_tree_entry(const DataFile::pixels_t &entry, int index,
224
+                                    bool ref_encoded, DictTreeNode *root,
225
+                                    TreeAllocator &storage)
226
+{
227
+    DictTreeNode* node = root;
228
+    for (uint8_t p : entry)
229
+    {
230
+        DictTreeNode* branch = node->GetChild(p);
231
+        if (!branch)
232
+        {
233
+            branch = storage.allocate();
234
+            node->SetChild(p, branch);
235
+        }
236
+        
237
+        node = branch;
238
+    }
239
+    
240
+    // Replace the entry if it either does not yet have an encoding, or if
241
+    // the new entry is non-ref (i.e. can be used in more situations).
242
+    if (node->GetIndex() < 0 || (node->GetRef() && !ref_encoded))
243
+    {
244
+        node->SetIndex(index);
245
+        node->SetRef(ref_encoded);
246
+        node->SetLength(entry.size());
247
+    }
248
+    
249
+    return node;
250
+}
251
+
252
+// Walk the tree and find if the entry exists in the tree. If it does,
253
+// returns a pointer to it, otherwise nullptr.
254
+static DictTreeNode *find_tree_node(DataFile::pixels_t::const_iterator begin,
255
+                                    DataFile::pixels_t::const_iterator end,
256
+                                    DictTreeNode *root)
257
+{
258
+    DictTreeNode* node = root;
259
+    while (begin != end)
260
+    {
261
+        uint8_t pixel = *begin++;
262
+        node = node->GetChild(pixel);
263
+        
264
+        if (!node)
265
+            return nullptr;
266
+    }
267
+    
268
+    return node;
269
+}
270
+
271
+// Fill in the suffix pointers recursively for the given subtree.
272
+static void fill_tree_suffixes(DictTreeNode *root, DictTreeNode *subtree,
273
+    const DataFile::pixels_t &entry)
274
+{
275
+    for (size_t i = 1; i < entry.size(); i++)
276
+    {
277
+        DictTreeNode *node = find_tree_node(entry.begin() + i, entry.end(), root);
278
+        if (node)
279
+        {
280
+            subtree->SetSuffix(node);
281
+            break;
282
+        }
283
+    }
284
+    
285
+    if (!subtree->GetSuffix())
286
+        subtree->SetSuffix(root);
287
+    
288
+    DataFile::pixels_t newentry(entry);
289
+    newentry.resize(entry.size() + 1);
290
+    for (uint8_t i = 0; i < 16; i++)
291
+    {
292
+        // Speed-up for the common case of 0 and 15 alphas.
293
+        if (i == 1 && !subtree->HasIntermediateChildren())
294
+            i += 14;
295
+        
296
+        DictTreeNode *child = subtree->GetChild(i);
297
+        if (child)
298
+        {
299
+            newentry.at(entry.size()) = i;
300
+            fill_tree_suffixes(root, child, newentry);
301
+        }
302
+    }
303
+}
304
+
305
+// Construct a lookup tree from the dictionary entries.
306
+static DictTreeNode* construct_tree(const std::vector<DataFile::dictentry_t> &dictionary,
307
+                                    TreeAllocator &storage, bool fast)
308
+{
309
+    DictTreeNode* root = storage.allocate();
310
+    
311
+    // Populate the hardcoded entries for 0 to 15 alpha.
312
+    for (int j = 0; j < 16; j++)
313
+    {
314
+        DictTreeNode *node = storage.allocate();
315
+        node->SetIndex(j);
316
+        node->SetRef(false);
317
+        node->SetLength(1);
318
+        root->SetChild(j, node);
319
+    }
320
+    
321
+    // Populate the actual dictionary entries
322
+    size_t i = DICT_START;
323
+    for (DataFile::dictentry_t d : dictionary)
324
+    {
325
+        if (!d.replacement.size())
326
+            break;
327
+        
328
+        add_tree_entry(d.replacement, i, d.ref_encode, root, storage);
329
+        i++;
330
+    }
331
+    
332
+    if (!fast)
333
+    {
334
+        // Populate the fill entries for rest of dictionary
335
+        for (; i < 256; i++)
336
+        {
337
+            DataFile::pixels_t pixels;
338
+            size_t bitcount = fillentry_bitcount(i);
339
+            uint8_t byte = i - DICT_START7BIT;
340
+            for (size_t j = 0; j < bitcount; j++)
341
+            {
342
+                uint8_t p = (byte & (1 << j)) ? 15 : 0;
343
+                pixels.push_back(p);
344
+            }
345
+            
346
+            add_tree_entry(pixels, i, false, root, storage);
347
+        }
348
+        
349
+        // Fill in the suffix pointers for optimal encoding
350
+        DataFile::pixels_t nullentry;
351
+        fill_tree_suffixes(root, root, nullentry);
352
+    }
353
+    
354
+    return root;
355
+}
356
+
357
+// Structure for keeping track of the shortest encoding to reach particular
358
+// point of the pixel string.
359
+struct encoding_link_t
360
+{
361
+    // Index of the position prior to the last dictionary entry.
362
+    size_t previous;
363
+    
364
+    // Index of the dictionary entry that brings us to this point.
365
+    int index;
366
+    
367
+    // Number of links to get here from the start of the string.
368
+    size_t length;
369
+    
370
+    constexpr encoding_link_t(): previous(0), index(-1), length(9999999) {}
371
+};
372
+
373
+// Perform the reference encoding for a glyph entry (optimal version).
374
+// Uses a modified Aho-Corasick algorithm combined with breadth first search
375
+// to find the shortest representation.
376
+static encoded_font_t::refstring_t encode_ref_slow(const DataFile::pixels_t &pixels,
377
+                                                   const DictTreeNode *root,
378
+                                                   bool is_glyph)
379
+{
380
+    // Chain of encodings. Each entry in this array corresponds to a position
381
+    // in the pixel string.
382
+    std::unique_ptr<encoding_link_t[]> chain(new encoding_link_t[pixels.size() + 1]);
383
+    
384
+    chain[0].previous = 0;
385
+    chain[0].index = 0;
386
+    chain[0].length = 0;
387
+    
388
+    // Read the pixels one-by-one and update the encoding links accordingly.
389
+    const DictTreeNode *node = root;
390
+    for (size_t pos = 0; pos < pixels.size(); pos++)
391
+    {
392
+        uint8_t pixel = pixels.at(pos);
393
+        const DictTreeNode *branch = node->GetChild(pixel);
394
+        
395
+        while (!branch)
396
+        {
397
+            // Cannot expand this sequence, defer to suffix.
398
+            node = node->GetSuffix();
399
+            branch = node->GetChild(pixel);
400
+        }
401
+        
402
+        node = branch;
403
+        
404
+        // We have arrived at a new node, add it and any proper suffixes to
405
+        // the link chain.
406
+        const DictTreeNode *suffix = node;
407
+        while (suffix != root)
408
+        {
409
+            if (suffix->GetIndex() >= 0 && (is_glyph || !suffix->GetRef()))
410
+            {
411
+                encoding_link_t link;
412
+                link.previous = pos + 1 - suffix->GetLength();
413
+                link.index = suffix->GetIndex();
414
+                link.length = chain[link.previous].length + 1;
415
+                
416
+                if (link.length < chain[pos + 1].length)
417
+                    chain[pos + 1] = link;
418
+            }
419
+            suffix = suffix->GetSuffix();
420
+        }
421
+    }
422
+    
423
+    // Check if we can shorten the final encoding using REF_FILLZEROS.
424
+    if (is_glyph)
425
+    {
426
+        for (size_t pos = pixels.size() - 1; pos > 0; pos--)
427
+        {
428
+            if (pixels.at(pos) != 0)
429
+                break;
430
+            
431
+            encoding_link_t link;
432
+            link.previous = pos;
433
+            link.index = REF_FILLZEROS;
434
+            link.length = chain[pos].length + 1;
435
+            
436
+            if (link.length <= chain[pixels.size()].length)
437
+                chain[pixels.size()] = link;
438
+        }
439
+    }
440
+    
441
+    // Backtrack from the final link back to the start and construct the
442
+    // encoded string.
443
+    encoded_font_t::refstring_t result;
444
+    size_t len = chain[pixels.size()].length;
445
+    result.resize(len);
446
+    
447
+    size_t pos = pixels.size();
448
+    for (size_t i = len; i > 0; i--)
449
+    {
450
+        result.at(i - 1) = chain[pos].index;
451
+        pos = chain[pos].previous;
452
+    }
453
+    
454
+    return result;
455
+}
456
+
457
+// Walk the tree as far as possible following the given pixel string iterator.
458
+// Returns number of pixels encoded, and index is set to the dictionary reference.
459
+static size_t walk_tree(const DictTreeNode *tree,
460
+                        DataFile::pixels_t::const_iterator pixels,
461
+                        DataFile::pixels_t::const_iterator pixelsend,
462
+                        int &index, bool is_glyph)
463
+{
464
+    size_t best_length = 0;
465
+    size_t length = 0;
466
+    index = -1;
467
+    
468
+    const DictTreeNode* node = tree;
469
+    while (pixels != pixelsend)
470
+    {
471
+        uint8_t pixel = *pixels++;
472
+        node = node->GetChild(pixel);
473
+        
474
+        if (!node)
475
+            break;
476
+        
477
+        length++;
478
+        
479
+        if (is_glyph || !node->GetRef())
480
+        {
481
+            if (node->GetIndex() >= 0)
482
+            {
483
+                index = node->GetIndex();
484
+                best_length = length;
485
+            }
486
+        }
487
+    }
488
+    
489
+    if (index < 0)
490
+        throw std::logic_error("walk_tree failed to find a valid encoding");
491
+    
492
+    return best_length;
493
+}
494
+
495
+// Perform the reference encoding for a glyph entry (fast version).
496
+// Uses a simple greedy search to find select the encodings.
497
+static encoded_font_t::refstring_t encode_ref_fast(const DataFile::pixels_t &pixels,
498
+                                                   const DictTreeNode *tree,
499
+                                                   bool is_glyph)
500
+{
501
+    encoded_font_t::refstring_t result;
502
+    
503
+    // Strip any zeroes from end
504
+    size_t end = pixels.size();
505
+    
506
+    if (is_glyph)
507
+    {
508
+        while (end > 0 && pixels.at(end - 1) == 0) end--;
509
+    }
510
+    
511
+    size_t i = 0;
512
+    while (i < end)
513
+    {
514
+        int index;
515
+        i += walk_tree(tree, pixels.begin() + i, pixels.end(), index, is_glyph);
516
+        result.push_back(index);
517
+    }
518
+    
519
+    if (i < pixels.size())
520
+        result.push_back(REF_FILLZEROS);
521
+    
522
+    return result;
523
+}
524
+
525
+static encoded_font_t::refstring_t encode_ref(const DataFile::pixels_t &pixels,
526
+                                              const DictTreeNode *tree,
527
+                                              bool is_glyph, bool fast)
528
+{
529
+    if (fast)
530
+        return encode_ref_fast(pixels, tree, is_glyph);
531
+    else
532
+        return encode_ref_slow(pixels, tree, is_glyph);
533
+}
534
+
535
+// Compare dictionary entries by their coding type.
536
+// Sorts RLE-encoded entries first and any empty entries last.
537
+static bool cmp_dict_coding(const DataFile::dictentry_t &a,
538
+                            const DataFile::dictentry_t &b)
539
+{
540
+    if (a.replacement.size() == 0 && b.replacement.size() != 0)
541
+        return false;
542
+    else if (a.replacement.size() != 0 && b.replacement.size() == 0)
543
+        return true;
544
+    else if (a.ref_encode == false && b.ref_encode == true)
545
+        return true;
546
+    else
547
+        return false;
548
+}
549
+
550
+size_t estimate_tree_node_count(const std::vector<DataFile::dictentry_t> &dict)
551
+{
552
+    size_t count = DICT_START; // Preallocated entries
553
+    for (const DataFile::dictentry_t &d: dict)
554
+    {
555
+        count += d.replacement.size();
556
+    }
557
+    count += 128 * 7; // Fill entries
558
+    return count;
559
+}
560
+
561
+std::unique_ptr<encoded_font_t> encode_font(const DataFile &datafile,
562
+                                            bool fast)
563
+{
564
+    std::unique_ptr<encoded_font_t> result(new encoded_font_t);
565
+    
566
+    // Sort the dictionary so that RLE-coded entries come first.
567
+    // This way the two are easy to distinguish based on index.
568
+    std::vector<DataFile::dictentry_t> sorted_dict = datafile.GetDictionary();
569
+    std::stable_sort(sorted_dict.begin(), sorted_dict.end(), cmp_dict_coding);
570
+    
571
+    // Build the binary tree for looking up references.
572
+    size_t count = estimate_tree_node_count(sorted_dict);
573
+    TreeAllocator allocator(count);
574
+    DictTreeNode* tree = construct_tree(sorted_dict, allocator, fast);
575
+    
576
+    // Encode the dictionary entries, using either RLE or reference method.
577
+    for (const DataFile::dictentry_t &d : sorted_dict)
578
+    {
579
+        if (d.replacement.size() == 0)
580
+        {
581
+            continue;
582
+        }
583
+        else if (d.ref_encode)
584
+        {
585
+            result->ref_dictionary.push_back(encode_ref(d.replacement, tree, false, fast));
586
+        }
587
+        else
588
+        {
589
+            result->rle_dictionary.push_back(encode_rle(d.replacement));
590
+        }
591
+    }
592
+    
593
+    // Then reference-encode the glyphs
594
+    for (const DataFile::glyphentry_t &g : datafile.GetGlyphTable())
595
+    {
596
+        result->glyphs.push_back(encode_ref(g.data, tree, true, fast));
597
+    }
598
+    
599
+    // Optionally verify that the encoding was correct.
600
+    if (!fast)
601
+    {
602
+        for (size_t i = 0; i < datafile.GetGlyphCount(); i++)
603
+        {
604
+            std::unique_ptr<DataFile::pixels_t> decoded = 
605
+                decode_glyph(*result, i, datafile.GetFontInfo());
606
+            if (*decoded != datafile.GetGlyphEntry(i).data)
607
+            {
608
+                auto iter = std::mismatch(decoded->begin(), decoded->end(),
609
+                                          datafile.GetGlyphEntry(i).data.begin());
610
+                size_t pos = iter.first - decoded->begin();
611
+                throw std::logic_error("verification of glyph " + std::to_string(i) +
612
+                    " failed at position " + std::to_string(pos));
613
+            }
614
+        }
615
+    }
616
+    
617
+    return result;
618
+}
619
+
620
+size_t get_encoded_size(const encoded_font_t &encoded)
621
+{
622
+    size_t total = 0;
623
+    for (const encoded_font_t::rlestring_t &r : encoded.rle_dictionary)
624
+    {
625
+        total += r.size();
626
+        
627
+        if (r.size() != 0)
628
+            total += 2; // Offset table entry
629
+    }
630
+    for (const encoded_font_t::refstring_t &r : encoded.ref_dictionary)
631
+    {
632
+        total += r.size();
633
+        
634
+        if (r.size() != 0)
635
+            total += 2; // Offset table entry
636
+    }
637
+    for (const encoded_font_t::refstring_t &r : encoded.glyphs)
638
+    {
639
+        total += r.size();
640
+        total += 2; // Offset table entry
641
+        total += 1; // Width table entry
642
+    }
643
+    return total;
644
+}
645
+
646
+std::unique_ptr<DataFile::pixels_t> decode_glyph(
647
+    const encoded_font_t &encoded,
648
+    const encoded_font_t::refstring_t &refstring,
649
+    const DataFile::fontinfo_t &fontinfo)
650
+{
651
+    std::unique_ptr<DataFile::pixels_t> result(new DataFile::pixels_t);
652
+    
653
+    for (uint8_t ref : refstring)
654
+    {
655
+        if (ref <= 15)
656
+        {
657
+            result->push_back(ref);
658
+        }
659
+        else if (ref == REF_FILLZEROS)
660
+        {
661
+            result->resize(fontinfo.max_width * fontinfo.max_height, 0);
662
+        }
663
+        else if (ref < DICT_START)
664
+        {
665
+            throw std::logic_error("unknown code: " + std::to_string(ref));
666
+        }
667
+        else if (ref - DICT_START < (int)encoded.rle_dictionary.size())
668
+        {
669
+            for (uint8_t rle : encoded.rle_dictionary.at(ref - DICT_START))
670
+            {
671
+                if ((rle & RLE_CODEMASK) == RLE_ZEROS)
672
+                {
673
+                    for (int i = 0; i < (rle & RLE_VALMASK); i++)
674
+                    {
675
+                        result->push_back(0);
676
+                    }
677
+                }
678
+                else if ((rle & RLE_CODEMASK) == RLE_64ZEROS)
679
+                {
680
+                    for (int i = 0; i < ((rle & RLE_VALMASK) + 1) * 64; i++)
681
+                    {
682
+                        result->push_back(0);
683
+                    }
684
+                }
685
+                else if ((rle & RLE_CODEMASK) == RLE_ONES)
686
+                {
687
+                    for (int i = 0; i < (rle & RLE_VALMASK) + 1; i++)
688
+                    {
689
+                        result->push_back(15);
690
+                    }
691
+                }
692
+                else if ((rle & RLE_CODEMASK) == RLE_SHADE)
693
+                {
694
+                    uint8_t count, alpha;
695
+                    count = ((rle & RLE_VALMASK) >> 4) + 1;
696
+                    alpha = ((rle & RLE_VALMASK) & 0xF);
697
+                    for (int i = 0; i < count; i++)
698
+                    {
699
+                        result->push_back(alpha);
700
+                    }
701
+                }
702
+            }
703
+        }
704
+        else if (ref - DICT_START - encoded.rle_dictionary.size() < encoded.ref_dictionary.size())
705
+        {
706
+            size_t index = ref - DICT_START - encoded.rle_dictionary.size();
707
+            std::unique_ptr<DataFile::pixels_t> part =
708
+                decode_glyph(encoded, encoded.ref_dictionary.at(index),
709
+                             fontinfo);
710
+            result->insert(result->end(), part->begin(), part->end());
711
+        }
712
+        else
713
+        {
714
+            size_t bitcount = fillentry_bitcount(ref);
715
+            
716
+            uint8_t byte = ref - DICT_START7BIT;
717
+            for (size_t i = 0; i < bitcount; i++)
718
+            {
719
+                uint8_t p = (byte & (1 << i)) ? 15 : 0;
720
+                result->push_back(p);
721
+            }
722
+        }
723
+    }
724
+    
725
+    return result;
726
+}
727
+
728
+std::unique_ptr<DataFile::pixels_t> decode_glyph(
729
+    const encoded_font_t &encoded, size_t index,
730
+    const DataFile::fontinfo_t &fontinfo)
731
+{
732
+    return decode_glyph(encoded, encoded.glyphs.at(index), fontinfo);
733
+}
734
+
735
+}}

+ 126 - 0
tools/mcufontencoder/src/encode_rlefont.hh

@@ -0,0 +1,126 @@
1
+// Given a dictionary and glyphs, encode the data for all the glyphs.
2
+
3
+#pragma once
4
+
5
+#include "datafile.hh"
6
+#include <vector>
7
+#include <memory>
8
+
9
+namespace mcufont {
10
+namespace rlefont {
11
+
12
+struct encoded_font_t
13
+{
14
+    // RLE-encoded format for storing the dictionary entries.
15
+    // Each item is a byte. Top bit means the value in the original bitstream,
16
+    // and the bottom 7 bits store the repetition count.
17
+    typedef std::vector<uint8_t> rlestring_t;
18
+    
19
+    // Reference encoded format for storing the glyphs.
20
+    // Each item is a reference to the dictionary.
21
+    // Values 0 and 1 are hardcoded to mean 0 and 1.
22
+    // All other values mean dictionary entry at (i-2).
23
+    typedef std::vector<uint8_t> refstring_t;
24
+    
25
+    std::vector<rlestring_t> rle_dictionary;
26
+    std::vector<refstring_t> ref_dictionary;
27
+    std::vector<refstring_t> glyphs;
28
+};
29
+
30
+// Encode all the glyphs.
31
+std::unique_ptr<encoded_font_t> encode_font(const DataFile &datafile,
32
+                                            bool fast = true);
33
+
34
+// Sum up the total size of the encoded glyphs + dictionary.
35
+size_t get_encoded_size(const encoded_font_t &encoded);
36
+
37
+inline size_t get_encoded_size(const DataFile &datafile, bool fast = true)
38
+{
39
+    std::unique_ptr<encoded_font_t> e = encode_font(datafile, fast);
40
+    return get_encoded_size(*e);
41
+}
42
+
43
+// Decode a single glyph (for verification).
44
+std::unique_ptr<DataFile::pixels_t> decode_glyph(
45
+    const encoded_font_t &encoded,
46
+    const encoded_font_t::refstring_t &refstring,
47
+    const DataFile::fontinfo_t &fontinfo);
48
+
49
+// Decode a single glyph (for verification).
50
+std::unique_ptr<DataFile::pixels_t> decode_glyph(
51
+    const encoded_font_t &encoded, size_t index,
52
+    const DataFile::fontinfo_t &fontinfo);
53
+
54
+}}
55
+
56
+
57
+#ifdef CXXTEST_RUNNING
58
+#include <cxxtest/TestSuite.h>
59
+
60
+using namespace mcufont;
61
+using namespace mcufont::rlefont;
62
+
63
+class RLEFontEncodeTests: public CxxTest::TestSuite
64
+{
65
+public:
66
+    void testEncode()
67
+    {
68
+        std::istringstream s(testfile);
69
+        std::unique_ptr<DataFile> f = DataFile::Load(s);
70
+        std::unique_ptr<encoded_font_t> e = encode_font(*f, false);
71
+        
72
+        TS_ASSERT_EQUALS(e->glyphs.size(), 3);
73
+        
74
+        // Expected values for dictionary
75
+        encoded_font_t::rlestring_t dict0 = {0x01, 0xCE, 0x01, 0xCE};
76
+        encoded_font_t::rlestring_t dict1 = {0x0C};
77
+        encoded_font_t::rlestring_t dict2 = {0xFE};
78
+        encoded_font_t::refstring_t dict3 = {24, 24};
79
+        
80
+        TS_ASSERT(e->rle_dictionary.at(0) == dict0);
81
+        TS_ASSERT(e->rle_dictionary.at(1) == dict1);
82
+        TS_ASSERT(e->rle_dictionary.at(2) == dict2);
83
+        TS_ASSERT(e->ref_dictionary.at(0) == dict3);
84
+        
85
+        // Expected values for glyphs
86
+        encoded_font_t::refstring_t glyph0 = {27, 27, 27};
87
+        encoded_font_t::refstring_t glyph1 = {24, 0, 132, 25, 14};
88
+        encoded_font_t::refstring_t glyph2 = {228, 26, 244, 14, 14, 14, 228, 26, 16};
89
+        
90
+        TS_ASSERT_EQUALS(e->glyphs.at(0), glyph0);
91
+        TS_ASSERT_EQUALS(e->glyphs.at(1), glyph1);
92
+        TS_ASSERT_EQUALS(e->glyphs.at(2), glyph2);
93
+    }
94
+    
95
+    void testDecode()
96
+    {
97
+        std::istringstream s(testfile);
98
+        std::unique_ptr<DataFile> f = DataFile::Load(s);
99
+        std::unique_ptr<encoded_font_t> e = encode_font(*f, false);
100
+        
101
+        for (size_t i = 0; i < 3; i++)
102
+        {
103
+            std::unique_ptr<DataFile::pixels_t> dec;
104
+            dec = decode_glyph(*e, i, f->GetFontInfo());
105
+            
106
+            TS_ASSERT_EQUALS(*dec, f->GetGlyphEntry(i).data);
107
+        }
108
+    }
109
+    
110
+private:
111
+    static constexpr const char *testfile =
112
+        "Version 1\n"
113
+        "FontName Sans Serif\n"
114
+        "MaxWidth 4\n"
115
+        "MaxHeight 6\n"
116
+        "BaselineX 1\n"
117
+        "BaselineY 1\n"
118
+        "DictEntry 1 0 0E0E\n"
119
+        "DictEntry 1 0 000000000000\n"
120
+        "DictEntry 1 0 EEEE\n"
121
+        "DictEntry 1 1 0E0E0E0E\n"
122
+        "Glyph 0 4 0E0E0E0E0E0E0E0E0E0E0E0E\n"
123
+        "Glyph 1 4 0E0E0000000000000000000E\n"
124
+        "Glyph 2 4 0000EEEE000EEE0000EEEE00\n";
125
+};
126
+#endif

+ 247 - 0
tools/mcufontencoder/src/export_bwfont.cc

@@ -0,0 +1,247 @@
1
+#include "export_bwfont.hh"
2
+#include <vector>
3
+#include <iomanip>
4
+#include <map>
5
+#include <set>
6
+#include <algorithm>
7
+#include <string>
8
+#include <cctype>
9
+#include "exporttools.hh"
10
+#include "importtools.hh"
11
+#include "ccfixes.hh"
12
+
13
+#define BWFONT_FORMAT_VERSION 4
14
+
15
+namespace mcufont {
16
+namespace bwfont {
17
+
18
+static void encode_glyph(const DataFile::glyphentry_t &glyph,
19
+                         const DataFile::fontinfo_t &fontinfo,
20
+                         std::vector<unsigned> &dest,
21
+                         int num_cols)
22
+{
23
+    const int threshold = 8;
24
+    
25
+    // Find the number of columns in the glyph data
26
+    if (num_cols == 0)
27
+    {
28
+        for (int x = 0; x < fontinfo.max_width; x++)
29
+        {
30
+            for (int y = 0; y < fontinfo.max_height; y++)
31
+            {
32
+                size_t index = y * fontinfo.max_width + x;
33
+                if (glyph.data.at(index) >= threshold)
34
+                    num_cols = x + 1;
35
+            }
36
+        }
37
+    }
38
+    
39
+    // Write the bits that compose the glyph
40
+    for (int x = 0; x < num_cols; x++)
41
+    {
42
+        for (int y = 0; y < fontinfo.max_height; y+= 8)
43
+        {
44
+            size_t remain = std::min(8, fontinfo.max_height - y);
45
+            uint8_t byte = 0;
46
+            for (size_t i = 0; i < remain; i++)
47
+            {
48
+                size_t index = (y + i) * fontinfo.max_width + x;
49
+                if (glyph.data.at(index) >= threshold)
50
+                {
51
+                    byte |= (1 << i);
52
+                }
53
+            }
54
+            dest.push_back(byte);
55
+        }
56
+    }
57
+}
58
+
59
+struct cropinfo_t
60
+{
61
+    size_t offset_x;
62
+    size_t offset_y;
63
+    size_t height_bytes;
64
+    size_t height_pixels;
65
+    size_t width;
66
+};
67
+
68
+static void encode_character_range(std::ostream &out,
69
+                                   const std::string &name,
70
+                                   const DataFile &datafile,
71
+                                   const char_range_t &range,
72
+                                   unsigned range_index,
73
+                                   cropinfo_t &cropinfo)
74
+{
75
+    std::vector<DataFile::glyphentry_t> glyphs;
76
+    bool constant_width = true;
77
+    int width = datafile.GetGlyphEntry(range.glyph_indices[0]).width;
78
+    
79
+    // Copy all the glyphs in this range for the purpose of cropping them.
80
+    for (int glyph_index: range.glyph_indices)
81
+    {
82
+        if (glyph_index < 0)
83
+        {
84
+            // Missing glyph
85
+            DataFile::glyphentry_t dummy = {};
86
+            glyphs.push_back(dummy);
87
+        }
88
+        else
89
+        {
90
+            auto glyph = datafile.GetGlyphEntry(glyph_index);
91
+            glyphs.push_back(glyph);
92
+            
93
+            if (glyph.width != width)
94
+            {
95
+                constant_width = false;
96
+                width = 0;
97
+            }
98
+        }
99
+    }
100
+    
101
+    // Crop the glyphs in this range. Getting rid of a few rows at top
102
+    // or left can save a bunch of bytes with minimal cost.
103
+    DataFile::fontinfo_t old_fi = datafile.GetFontInfo();
104
+    DataFile::fontinfo_t new_fi = old_fi;
105
+    crop_glyphs(glyphs, new_fi);
106
+    
107
+    if (new_fi.max_width != width)
108
+    {
109
+        constant_width = false;
110
+        width = 0;
111
+    }
112
+    
113
+    // Fill in the crop information
114
+    cropinfo.offset_x = old_fi.baseline_x - new_fi.baseline_x;
115
+    cropinfo.offset_y = old_fi.baseline_y - new_fi.baseline_y;
116
+    cropinfo.height_pixels = new_fi.max_height;
117
+    cropinfo.height_bytes = (cropinfo.height_pixels + 7) / 8;
118
+    cropinfo.width = width;
119
+    
120
+    // Then format and write out the glyph data
121
+    std::vector<unsigned> offsets;
122
+    std::vector<unsigned> data;
123
+    std::vector<unsigned> widths;
124
+    size_t stride = cropinfo.height_bytes;
125
+    
126
+    for (const DataFile::glyphentry_t &g : glyphs)
127
+    {
128
+        offsets.push_back(data.size() / stride);
129
+        widths.push_back(g.width);
130
+        encode_glyph(g, new_fi, data, width);
131
+    }    
132
+    offsets.push_back(data.size() / stride);
133
+    
134
+    write_const_table(out, data, "uint8_t", "mf_bwfont_" + name + "_glyph_data_" + std::to_string(range_index));
135
+    
136
+    if (!constant_width)
137
+    {
138
+        write_const_table(out, offsets, "uint16_t", "mf_bwfont_" + name + "_glyph_offsets_" + std::to_string(range_index), 4);
139
+        write_const_table(out, widths, "uint8_t", "mf_bwfont_" + name + "_glyph_widths_" + std::to_string(range_index));
140
+    }
141
+}
142
+    
143
+void write_source(std::ostream &out, std::string name, const DataFile &datafile)
144
+{
145
+    name = filename_to_identifier(name);
146
+    
147
+    out << std::endl;
148
+    out << std::endl;
149
+    out << "/* Start of automatically generated font definition for " << name << ". */" << std::endl;
150
+    out << std::endl;
151
+    
152
+    out << "#ifndef MF_BWFONT_INTERNALS" << std::endl;
153
+    out << "#define MF_BWFONT_INTERNALS" << std::endl;
154
+    out << "#endif" << std::endl;
155
+    out << "#include \"mf_bwfont.h\"" << std::endl;
156
+    out << std::endl;
157
+    
158
+    out << "#ifndef MF_BWFONT_VERSION_" << BWFONT_FORMAT_VERSION << "_SUPPORTED" << std::endl;
159
+    out << "#error The font file is not compatible with this version of mcufont." << std::endl;
160
+    out << "#endif" << std::endl;
161
+    out << std::endl;
162
+    
163
+    // Split the characters into ranges
164
+    DataFile::fontinfo_t f = datafile.GetFontInfo();
165
+    size_t glyph_size = f.max_width * ((f.max_height + 7) / 8);
166
+    auto get_glyph_size = [=](size_t i) { return glyph_size; };
167
+    std::vector<char_range_t> ranges = compute_char_ranges(datafile,
168
+        get_glyph_size, 65536, 16);
169
+
170
+    // Write out glyph data for character ranges
171
+    std::vector<cropinfo_t> crops;
172
+    for (size_t i = 0; i < ranges.size(); i++)
173
+    {
174
+        cropinfo_t cropinfo;
175
+        encode_character_range(out, name, datafile, ranges.at(i), i, cropinfo);
176
+        crops.push_back(cropinfo);
177
+    }
178
+    
179
+    // Write out a table describing the character ranges
180
+    out << "static const struct mf_bwfont_char_range_s mf_bwfont_" + name + "_char_ranges[] = {" << std::endl;
181
+    for (size_t i = 0; i < ranges.size(); i++)
182
+    {
183
+        std::string offsets = (crops[i].width) ? "0" : "mf_bwfont_" + name + "_glyph_offsets_" + std::to_string(i);
184
+        std::string widths = (crops[i].width) ? "0" : "mf_bwfont_" + name + "_glyph_widths_" + std::to_string(i);
185
+        
186
+        out << "    {" << std::endl;
187
+        out << "        " << ranges.at(i).first_char << ", /* first char */" << std::endl;
188
+        out << "        " << ranges.at(i).char_count << ", /* char count */" << std::endl;
189
+        out << "        " << crops[i].offset_x << ", /* offset x */" << std::endl;
190
+        out << "        " << crops[i].offset_y << ", /* offset y */" << std::endl;
191
+        out << "        " << crops[i].height_bytes << ", /* height in bytes */" << std::endl;
192
+        out << "        " << crops[i].height_pixels << ", /* height in pixels */" << std::endl;
193
+        out << "        " << crops[i].width << ", /* width */" << std::endl;
194
+        out << "        " << widths << ", /* glyph widths */" << std::endl;
195
+        out << "        " << offsets << ", /* glyph offsets */" << std::endl;
196
+        out << "        " << "mf_bwfont_" << name << "_glyph_data_" << i << ", /* glyph data */" << std::endl;
197
+        out << "    }," << std::endl;
198
+    }
199
+    out << "};" << std::endl;
200
+    out << std::endl;
201
+    
202
+    // Fonts in this format are always black & white
203
+    int flags = datafile.GetFontInfo().flags | DataFile::FLAG_BW;
204
+    
205
+    // Pull it all together in the rlefont_s structure.
206
+    out << "const struct mf_bwfont_s mf_bwfont_" << name << " = {" << std::endl;
207
+    out << "    {" << std::endl;
208
+    out << "    " << "\"" << datafile.GetFontInfo().name << "\"," << std::endl;
209
+    out << "    " << "\"" << name << "\"," << std::endl;
210
+    out << "    " << datafile.GetFontInfo().max_width << ", /* width */" << std::endl;
211
+    out << "    " << datafile.GetFontInfo().max_height << ", /* height */" << std::endl;
212
+    out << "    " << get_min_x_advance(datafile) << ", /* min x advance */" << std::endl;
213
+    out << "    " << get_max_x_advance(datafile) << ", /* max x advance */" << std::endl;
214
+    out << "    " << datafile.GetFontInfo().baseline_x << ", /* baseline x */" << std::endl;
215
+    out << "    " << datafile.GetFontInfo().baseline_y << ", /* baseline y */" << std::endl;
216
+    out << "    " << datafile.GetFontInfo().line_height << ", /* line height */" << std::endl;
217
+    out << "    " << flags << ", /* flags */" << std::endl;
218
+    out << "    " << select_fallback_char(datafile) << ", /* fallback character */" << std::endl;
219
+    out << "    " << "&mf_bwfont_character_width," << std::endl;
220
+    out << "    " << "&mf_bwfont_render_character," << std::endl;
221
+    out << "    }," << std::endl;
222
+    
223
+    out << "    " << BWFONT_FORMAT_VERSION << ", /* version */" << std::endl;
224
+    out << "    " << ranges.size() << ", /* char range count */" << std::endl;
225
+    out << "    " << "mf_bwfont_" << name << "_char_ranges," << std::endl;
226
+    out << "};" << std::endl;
227
+    
228
+    // Write the font lookup structure
229
+    out << std::endl;
230
+    out << "#ifdef MF_INCLUDED_FONTS" << std::endl;
231
+    out << "/* List entry for searching fonts by name. */" << std::endl;
232
+    out << "static const struct mf_font_list_s mf_bwfont_" << name << "_listentry = {" << std::endl;
233
+    out << "    MF_INCLUDED_FONTS," << std::endl;
234
+    out << "    (struct mf_font_s*)&mf_bwfont_" << name << std::endl;
235
+    out << "};" << std::endl;
236
+    out << "#undef MF_INCLUDED_FONTS" << std::endl;
237
+    out << "#define MF_INCLUDED_FONTS (&mf_bwfont_" << name << "_listentry)" << std::endl;
238
+    out << "#endif" << std::endl;
239
+    
240
+    out << std::endl;
241
+    out << std::endl;
242
+    out << "/* End of automatically generated font definition for " << name << ". */" << std::endl;
243
+    out << std::endl;
244
+}
245
+    
246
+    
247
+}}

+ 16 - 0
tools/mcufontencoder/src/export_bwfont.hh

@@ -0,0 +1,16 @@
1
+// Write out the encoded data in C source code files for mf_bwfont format.
2
+
3
+#pragma once
4
+
5
+#include "datafile.hh"
6
+#include <iostream>
7
+
8
+namespace mcufont {
9
+namespace bwfont {
10
+
11
+void write_header(std::ostream &out, std::string name, const DataFile &datafile);
12
+
13
+void write_source(std::ostream &out, std::string name, const DataFile &datafile);
14
+
15
+} }
16
+

+ 181 - 0
tools/mcufontencoder/src/export_rlefont.cc

@@ -0,0 +1,181 @@
1
+#include "export_rlefont.hh"
2
+#include <vector>
3
+#include <iomanip>
4
+#include <map>
5
+#include <set>
6
+#include <algorithm>
7
+#include <string>
8
+#include <cctype>
9
+#include "exporttools.hh"
10
+#include "ccfixes.hh"
11
+
12
+#define RLEFONT_FORMAT_VERSION 4
13
+
14
+namespace mcufont {
15
+namespace rlefont {
16
+
17
+// Encode the dictionary entries and the offsets to them.
18
+// Generates tables dictionary_data and dictionary_offsets.
19
+static void encode_dictionary(std::ostream &out,
20
+                              const std::string &name,
21
+                              const DataFile &datafile,
22
+                              const encoded_font_t &encoded)
23
+{
24
+    std::vector<unsigned> offsets;
25
+    std::vector<unsigned> data;
26
+    for (const encoded_font_t::rlestring_t &r : encoded.rle_dictionary)
27
+    {
28
+        offsets.push_back(data.size());
29
+        data.insert(data.end(), r.begin(), r.end());
30
+    }
31
+    
32
+    for (const encoded_font_t::refstring_t &r : encoded.ref_dictionary)
33
+    {
34
+        offsets.push_back(data.size());
35
+        data.insert(data.end(), r.begin(), r.end());
36
+    }
37
+    offsets.push_back(data.size());
38
+    
39
+    write_const_table(out, data, "uint8_t", "mf_rlefont_" + name + "_dictionary_data");
40
+    write_const_table(out, offsets, "uint16_t", "mf_rlefont_" + name + "_dictionary_offsets", 4);
41
+}
42
+
43
+// Encode the data tables for a single character range.
44
+// Generates tables glyph_data_i and glyph_offsets_i.
45
+static void encode_character_range(std::ostream &out,
46
+                              const std::string &name,
47
+                              const DataFile &datafile,
48
+                              const encoded_font_t& encoded,
49
+                              const char_range_t& range,
50
+                              unsigned range_index)
51
+{
52
+    std::vector<unsigned> offsets;
53
+    std::vector<unsigned> data;
54
+    std::map<size_t, unsigned> already_encoded;
55
+    
56
+    for (int glyph_index : range.glyph_indices)
57
+    {
58
+        if (already_encoded.count(glyph_index))
59
+        {
60
+            offsets.push_back(already_encoded[glyph_index]);
61
+        }
62
+        else
63
+        {
64
+            encoded_font_t::refstring_t r;
65
+            int width = 0;
66
+            
67
+            if (glyph_index >= 0)
68
+            {
69
+                r = encoded.glyphs[glyph_index];
70
+                width = datafile.GetGlyphEntry(glyph_index).width;
71
+            }
72
+            
73
+            offsets.push_back(data.size());
74
+            already_encoded[glyph_index] = data.size();
75
+            
76
+            data.push_back(width);
77
+            data.insert(data.end(), r.begin(), r.end());
78
+        }
79
+    }
80
+    
81
+    write_const_table(out, data, "uint8_t", "mf_rlefont_" + name + "_glyph_data_" + std::to_string(range_index));
82
+    write_const_table(out, offsets, "uint16_t", "mf_rlefont_" + name + "_glyph_offsets_" + std::to_string(range_index), 4);
83
+}
84
+
85
+void write_source(std::ostream &out, std::string name, const DataFile &datafile)
86
+{
87
+    name = filename_to_identifier(name);
88
+    std::unique_ptr<encoded_font_t> encoded = encode_font(datafile, false);
89
+    
90
+    out << std::endl;
91
+    out << std::endl;
92
+    out << "/* Start of automatically generated font definition for " << name << ". */" << std::endl;
93
+    out << std::endl;
94
+    
95
+    out << "#ifndef MF_RLEFONT_INTERNALS" << std::endl;
96
+    out << "#define MF_RLEFONT_INTERNALS" << std::endl;
97
+    out << "#endif" << std::endl;
98
+    out << "#include \"mf_rlefont.h\"" << std::endl;
99
+    out << std::endl;
100
+    
101
+    out << "#ifndef MF_RLEFONT_VERSION_" << RLEFONT_FORMAT_VERSION << "_SUPPORTED" << std::endl;
102
+    out << "#error The font file is not compatible with this version of mcufont." << std::endl;
103
+    out << "#endif" << std::endl;
104
+    out << std::endl;
105
+    
106
+    // Write out the dictionary entries
107
+    encode_dictionary(out, name, datafile, *encoded);
108
+    
109
+    // Split the characters into ranges
110
+    auto get_glyph_size = [&encoded](size_t i)
111
+    {
112
+        return encoded->glyphs[i].size();
113
+    };
114
+    std::vector<char_range_t> ranges = compute_char_ranges(datafile,
115
+        get_glyph_size, 65536, 16);
116
+
117
+    // Write out glyph data for character ranges
118
+    for (size_t i = 0; i < ranges.size(); i++)
119
+    {
120
+        encode_character_range(out, name, datafile, *encoded, ranges.at(i), i);
121
+    }
122
+    
123
+    // Write out a table describing the character ranges
124
+    out << "static const struct mf_rlefont_char_range_s mf_rlefont_" << name << "_char_ranges[] = {" << std::endl;
125
+    for (size_t i = 0; i < ranges.size(); i++)
126
+    {
127
+        out << "    {" << ranges.at(i).first_char
128
+            << ", " << ranges.at(i).char_count
129
+            << ", mf_rlefont_" << name << "_glyph_offsets_" << i
130
+            << ", mf_rlefont_" << name << "_glyph_data_" << i << "}," << std::endl; 
131
+    }
132
+    out << "};" << std::endl;
133
+    out << std::endl;
134
+    
135
+    // Pull it all together in the rlefont_s structure.
136
+    out << "const struct mf_rlefont_s mf_rlefont_" << name << " = {" << std::endl;
137
+    out << "    {" << std::endl;
138
+    out << "    " << "\"" << datafile.GetFontInfo().name << "\"," << std::endl;
139
+    out << "    " << "\"" << name << "\"," << std::endl;
140
+    out << "    " << datafile.GetFontInfo().max_width << ", /* width */" << std::endl;
141
+    out << "    " << datafile.GetFontInfo().max_height << ", /* height */" << std::endl;
142
+    out << "    " << get_min_x_advance(datafile) << ", /* min x advance */" << std::endl;
143
+    out << "    " << get_max_x_advance(datafile) << ", /* max x advance */" << std::endl;
144
+    out << "    " << datafile.GetFontInfo().baseline_x << ", /* baseline x */" << std::endl;
145
+    out << "    " << datafile.GetFontInfo().baseline_y << ", /* baseline y */" << std::endl;
146
+    out << "    " << datafile.GetFontInfo().line_height << ", /* line height */" << std::endl;
147
+    out << "    " << datafile.GetFontInfo().flags << ", /* flags */" << std::endl;
148
+    out << "    " << select_fallback_char(datafile) << ", /* fallback character */" << std::endl;
149
+    out << "    " << "&mf_rlefont_character_width," << std::endl;
150
+    out << "    " << "&mf_rlefont_render_character," << std::endl;
151
+    out << "    }," << std::endl;
152
+    
153
+    out << "    " << RLEFONT_FORMAT_VERSION << ", /* version */" << std::endl;
154
+    out << "    " << "mf_rlefont_" << name << "_dictionary_data," << std::endl;
155
+    out << "    " << "mf_rlefont_" << name << "_dictionary_offsets," << std::endl;
156
+    out << "    " << encoded->rle_dictionary.size() << ", /* rle dict count */" << std::endl;
157
+    out << "    " << encoded->ref_dictionary.size() + encoded->rle_dictionary.size() << ", /* total dict count */" << std::endl;
158
+    out << "    " << ranges.size() << ", /* char range count */" << std::endl;
159
+    out << "    " << "mf_rlefont_" << name << "_char_ranges," << std::endl;
160
+    out << "};" << std::endl;
161
+    
162
+    // Write the font lookup structure
163
+    out << std::endl;
164
+    out << "#ifdef MF_INCLUDED_FONTS" << std::endl;
165
+    out << "/* List entry for searching fonts by name. */" << std::endl;
166
+    out << "static const struct mf_font_list_s mf_rlefont_" << name << "_listentry = {" << std::endl;
167
+    out << "    MF_INCLUDED_FONTS," << std::endl;
168
+    out << "    (struct mf_font_s*)&mf_rlefont_" << name << std::endl;
169
+    out << "};" << std::endl;
170
+    out << "#undef MF_INCLUDED_FONTS" << std::endl;
171
+    out << "#define MF_INCLUDED_FONTS (&mf_rlefont_" << name << "_listentry)" << std::endl;
172
+    out << "#endif" << std::endl;
173
+    
174
+    out << std::endl;
175
+    out << std::endl;
176
+    out << "/* End of automatically generated font definition for " << name << ". */" << std::endl;
177
+    out << std::endl;
178
+}
179
+
180
+}}
181
+

+ 15 - 0
tools/mcufontencoder/src/export_rlefont.hh

@@ -0,0 +1,15 @@
1
+// Write out the encoded data in C source code files for the mf_rlefont format.
2
+
3
+#pragma once
4
+
5
+#include "datafile.hh"
6
+#include "encode_rlefont.hh"
7
+#include <iostream>
8
+
9
+namespace mcufont {
10
+namespace rlefont {
11
+
12
+void write_source(std::ostream &out, std::string name, const DataFile &datafile);
13
+
14
+} }
15
+

+ 179 - 0
tools/mcufontencoder/src/exporttools.cc

@@ -0,0 +1,179 @@
1
+#include "exporttools.hh"
2
+#include <iomanip>
3
+#include <set>
4
+
5
+namespace mcufont {
6
+    
7
+    
8
+// Convert a file name to a valid C identifier
9
+std::string filename_to_identifier(std::string name)
10
+{
11
+    // If the name contains path separators (/ or \), take only the last part.
12
+    size_t pos = name.find_last_of("/\\");
13
+    if (pos != std::string::npos)
14
+        name = name.substr(pos + 1);
15
+    
16
+    // If the name contains a file extension, strip it.
17
+    pos = name.find_first_of(".");
18
+    if (pos != std::string::npos)
19
+        name = name.substr(0, pos);
20
+    
21
+    // Replace any special characters with _.
22
+    for (pos = 0; pos < name.size(); pos++)
23
+    {
24
+        if (!isalnum(name.at(pos)))
25
+            name.at(pos) = '_';
26
+    }
27
+    
28
+    return name;
29
+}
30
+
31
+// Write a vector of integers as line-wrapped hex/integer data for initializing const array.
32
+void wordwrap_vector(std::ostream &out, const std::vector<unsigned> &data,
33
+                     const std::string &prefix, size_t width)
34
+{
35
+    int values_per_column = (width <= 2) ? 16 : 8;
36
+    
37
+    std::ios::fmtflags flags(out.flags());
38
+    out << prefix;
39
+    out << std::hex << std::setfill('0');
40
+    for (size_t i = 0; i < data.size(); i++)
41
+    {
42
+        if (i % values_per_column == 0 && i != 0)
43
+            out << std::endl << prefix;
44
+        
45
+        out << "0x" << std::setw(width) << (int)data.at(i) << ", ";
46
+    }
47
+    out.flags(flags);
48
+}
49
+
50
+// Write a vector of integers as a C constant array of given datatype.
51
+ void write_const_table(std::ostream &out, const std::vector<unsigned> &data,
52
+                        const std::string &datatype, const std::string &tablename,
53
+                        size_t width)
54
+{
55
+    out << "static const " << datatype << " " << tablename;
56
+    out << "[" << data.size() << "] = {" << std::endl;
57
+    wordwrap_vector(out, data, "    ", width);
58
+    out << std::endl << "};" << std::endl;
59
+    out << std::endl;
60
+}    
61
+
62
+int get_min_x_advance(const DataFile &datafile)
63
+{
64
+    int min = datafile.GetGlyphEntry(0).width;
65
+    
66
+    for (const DataFile::glyphentry_t &g : datafile.GetGlyphTable())
67
+    {
68
+        if (min > g.width)
69
+            min = g.width;
70
+    }
71
+    
72
+    return min;
73
+}
74
+
75
+int get_max_x_advance(const DataFile &datafile)
76
+{
77
+    int max = 0;
78
+    
79
+    for (const DataFile::glyphentry_t &g : datafile.GetGlyphTable())
80
+    {
81
+        if (max < g.width)
82
+            max = g.width;
83
+    }
84
+    
85
+    return max;
86
+}
87
+
88
+// Select the character to use as a fallback.
89
+int select_fallback_char(const DataFile &datafile)
90
+{
91
+    std::set<int> chars;
92
+    
93
+    size_t i = 0;
94
+    for (const DataFile::glyphentry_t &g: datafile.GetGlyphTable())
95
+    {
96
+        for (size_t c: g.chars)
97
+        {
98
+            chars.insert(c);
99
+        }
100
+        i++;
101
+    }
102
+    
103
+    if (chars.count(0xFFFD))
104
+        return 0xFFFD; // Unicode replacement character
105
+    
106
+    if (chars.count(0))
107
+        return 0; // Used by many BDF fonts as replacement char
108
+    
109
+    if (chars.count('?'))
110
+        return '?';
111
+    
112
+    return ' ';
113
+}
114
+
115
+// Decide how to best divide the characters in the font into ranges.
116
+// Limitations are:
117
+//  - Gaps longer than minimum_gap should result in separate ranges.
118
+//  - Each range can have encoded data size of at most maximum_size.
119
+std::vector<char_range_t> compute_char_ranges(const DataFile &datafile,
120
+    std::function<size_t(size_t)> get_encoded_glyph_size,
121
+    size_t maximum_size,
122
+    size_t minimum_gap)
123
+{
124
+    std::vector<char_range_t> result;
125
+    std::map<size_t, size_t> char_to_glyph = datafile.GetCharToGlyphMap();
126
+    std::vector<size_t> chars;
127
+    
128
+    // Get list of all characters in numeric order.
129
+    for (auto iter : char_to_glyph)
130
+        chars.push_back(iter.first);
131
+    
132
+    // Pick out ranges until we have processed all characters
133
+    size_t i = 0;
134
+    while (i < chars.size())
135
+    {
136
+        char_range_t range;
137
+        range.first_char = chars.at(i);
138
+        
139
+        // Find the point where there is a gap larger than minimum_gap.
140
+        i++;
141
+        while (i < chars.size() && chars.at(i) - chars.at(i - 1) < minimum_gap)
142
+            i++;
143
+        
144
+        uint16_t last_char = chars.at(i - 1);
145
+        
146
+        // Then store the indices of glyphs for each character
147
+        size_t data_length = 0;
148
+        for (size_t j = range.first_char; j <= last_char; j++)
149
+        {
150
+            if (char_to_glyph.count(j) == 0)
151
+            {
152
+                // Missing character
153
+                range.glyph_indices.push_back(-1);
154
+                continue;
155
+            }
156
+            
157
+            int glyph_index = char_to_glyph[j];
158
+            
159
+            // Monitor the amount of the data in the range and split it
160
+            // if it grows too large.
161
+            data_length += get_encoded_glyph_size(glyph_index);
162
+            if (data_length > maximum_size)
163
+            {
164
+                last_char = j - 1;
165
+                break;
166
+            }
167
+            
168
+            range.glyph_indices.push_back(glyph_index);
169
+        }
170
+        
171
+        range.char_count = last_char - range.first_char + 1;
172
+        result.push_back(range);
173
+    }
174
+    
175
+    return result;
176
+}
177
+    
178
+    
179
+}

+ 52 - 0
tools/mcufontencoder/src/exporttools.hh

@@ -0,0 +1,52 @@
1
+// Utility functions for exporting to C source code files.
2
+
3
+#pragma once
4
+#include <string>
5
+#include <vector>
6
+#include <iostream>
7
+#include <functional>
8
+#include "datafile.hh"
9
+
10
+namespace mcufont {
11
+    
12
+// Convert a file name to a valid C identifier
13
+std::string filename_to_identifier(std::string name);
14
+
15
+// Write a vector of integers as line-wrapped hex/integer data for initializing const array.
16
+void wordwrap_vector(std::ostream &out, const std::vector<unsigned> &data,
17
+                     const std::string &prefix, size_t width = 2);
18
+
19
+// Write a vector of integers as a C constant array of given datatype.
20
+void write_const_table(std::ostream &out, const std::vector<unsigned> &data,
21
+                       const std::string &datatype, const std::string &tablename,
22
+                       size_t width = 2);
23
+
24
+// Get minimum tracking width of font
25
+int get_min_x_advance(const DataFile &datafile);
26
+
27
+// Get maximum tracking width of font
28
+int get_max_x_advance(const DataFile &datafile);
29
+
30
+// Select the character to use as a fallback.
31
+int select_fallback_char(const DataFile &datafile);
32
+
33
+// Structure to represent one consecutive range of characters.
34
+struct char_range_t
35
+{
36
+    uint16_t first_char;
37
+    uint16_t char_count;
38
+    std::vector<int> glyph_indices;
39
+    
40
+    char_range_t(): first_char(0), char_count(0) {}
41
+};
42
+
43
+// Decide how to best divide the characters in the font into ranges.
44
+// Limitations are:
45
+//  - Gaps longer than minimum_gap should result in separate ranges.
46
+//  - Each range can have encoded data size of at most maximum_size.
47
+std::vector<char_range_t> compute_char_ranges(const DataFile &datafile,
48
+    std::function<size_t(size_t)> get_encoded_glyph_size,
49
+    size_t maximum_size,
50
+    size_t minimum_gap);
51
+
52
+}

+ 177 - 0
tools/mcufontencoder/src/freetype_import.cc

@@ -0,0 +1,177 @@
1
+#include "freetype_import.hh"
2
+#include "importtools.hh"
3
+#include <map>
4
+#include <string>
5
+#include <stdexcept>
6
+#include <iostream>
7
+#include "ccfixes.hh"
8
+
9
+#include <ft2build.h>
10
+#include FT_FREETYPE_H
11
+
12
+#undef __FTERRORS_H__
13
+#define FT_ERRORDEF( e, v, s )  std::make_pair( e, s ),
14
+#define FT_ERROR_START_LIST static const std::map<FT_Error, std::string> ft_errors {
15
+#define FT_ERROR_END_LIST };    
16
+#include FT_ERRORS_H
17
+
18
+namespace mcufont {
19
+
20
+static void checkFT(FT_Error error)
21
+{
22
+    if (error != 0)
23
+    {
24
+        if (ft_errors.count(error))
25
+            throw std::runtime_error("libfreetype error " +
26
+                std::to_string(error) + ": " + ft_errors.at(error));
27
+        else
28
+            throw std::runtime_error("unknown libfreetype error " +
29
+                std::to_string(error));
30
+    }
31
+}
32
+
33
+// Automatically allocated & freed wrapper for FT_Library
34
+class _FT_Library
35
+{
36
+public:
37
+    _FT_Library() { checkFT(FT_Init_FreeType(&m_lib)); }
38
+    ~_FT_Library() { checkFT(FT_Done_FreeType(m_lib)); }
39
+    operator FT_Library() { return m_lib; }
40
+
41
+private:
42
+    FT_Library m_lib;
43
+};
44
+
45
+// Automatically allocated & freed wrapper for FT_Face
46
+class _FT_Face
47
+{
48
+public:
49
+    _FT_Face(FT_Library lib, const std::vector<char> &data)
50
+    {
51
+        checkFT(FT_New_Memory_Face(lib, (const unsigned char *)&data[0],
52
+                                   data.size(), 0, &m_face));
53
+    }
54
+    ~_FT_Face() { checkFT(FT_Done_Face(m_face)); }
55
+    operator FT_Face() { return m_face; }
56
+    FT_Face operator->() { return m_face; }
57
+
58
+private:
59
+    FT_Face m_face;
60
+};
61
+
62
+// Read all the data from a file into a memory buffer.
63
+static void readfile(std::istream &file, std::vector<char> &data)
64
+{
65
+    while (file.good())
66
+    {
67
+        const size_t blocksize = 4096;
68
+        size_t oldsize = data.size();
69
+        data.resize(oldsize + blocksize);
70
+        file.read(&data[oldsize], blocksize);
71
+        data.resize(oldsize + file.gcount());
72
+    }
73
+}
74
+
75
+std::unique_ptr<DataFile> LoadFreetype(std::istream &file, int size, bool bw)
76
+{
77
+    std::vector<char> data;
78
+    readfile(file, data);
79
+    
80
+    _FT_Library lib;
81
+    _FT_Face face(lib, data);
82
+    
83
+    checkFT(FT_Set_Pixel_Sizes(face, size, size));
84
+    
85
+    DataFile::fontinfo_t fontinfo = {};
86
+    std::vector<DataFile::glyphentry_t> glyphtable;
87
+    std::vector<DataFile::dictentry_t> dictionary;
88
+   
89
+    // Convert size to pixels and round to nearest.
90
+    int u_per_em = face->units_per_EM;
91
+    auto topx = [size, u_per_em](int s) { return (s * size + u_per_em / 2) / u_per_em; };
92
+    
93
+    fontinfo.name = std::string(face->family_name) + " " +
94
+                    std::string(face->style_name) + " " +
95
+                    std::to_string(size);
96
+    
97
+    // Reserve 4 pixels on each side for antialiasing + hinting.
98
+    // They will be cropped off later.
99
+    fontinfo.max_width = topx(face->bbox.xMax - face->bbox.xMin) + 8;
100
+    fontinfo.max_height = topx(face->bbox.yMax - face->bbox.yMin) + 8;
101
+    fontinfo.baseline_x = topx(-face->bbox.xMin) + 4;
102
+    fontinfo.baseline_y = topx(face->bbox.yMax) + 4;
103
+    fontinfo.line_height = topx(face->height);
104
+    
105
+    FT_Int32 loadmode = FT_LOAD_TARGET_NORMAL | FT_LOAD_RENDER;
106
+    
107
+    if (bw)
108
+        loadmode = FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME | FT_LOAD_RENDER;
109
+    
110
+    FT_ULong charcode;
111
+    FT_UInt gindex;
112
+    charcode = FT_Get_First_Char(face, &gindex);
113
+    while (gindex)
114
+    {
115
+        try
116
+        {
117
+            checkFT(FT_Load_Glyph(face, gindex, loadmode));
118
+        }
119
+        catch (std::runtime_error &e)
120
+        {
121
+            std::cerr << "Skipping glyph " << gindex << ": " << e.what() << std::endl;        
122
+            charcode = FT_Get_Next_Char(face, charcode, &gindex);
123
+        }
124
+        
125
+        DataFile::glyphentry_t glyph;
126
+        glyph.width = (face->glyph->advance.x + 32) / 64;
127
+        glyph.chars.push_back(charcode);
128
+        glyph.data.resize(fontinfo.max_width * fontinfo.max_height);
129
+        
130
+        int w = face->glyph->bitmap.width;
131
+        int dw = fontinfo.max_width;
132
+        int dx = fontinfo.baseline_x + face->glyph->bitmap_left;
133
+        int dy = fontinfo.baseline_y - face->glyph->bitmap_top;
134
+        
135
+        /* Some combining diacritics seem to exceed the bounding box.
136
+         * We don't support them all that well anyway, so just move
137
+         * them inside the box in order not to crash.. */
138
+        if (dy < 0)
139
+            dy = 0;
140
+        if (dy + face->glyph->bitmap.rows > fontinfo.max_height)
141
+            dy = fontinfo.max_height - face->glyph->bitmap.rows;
142
+        
143
+        size_t s = face->glyph->bitmap.pitch;
144
+        for (int y = 0; y < face->glyph->bitmap.rows; y++)
145
+        {
146
+            for (int x = 0; x < face->glyph->bitmap.width; x++)
147
+            {
148
+                size_t index = (y + dy) * dw + x + dx;
149
+                
150
+                if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
151
+                {
152
+                    uint8_t byte = face->glyph->bitmap.buffer[s * y + x / 8];
153
+                    byte <<= x % 8;
154
+                    glyph.data.at(index) = (byte & 0x80) ? 15 : 0;
155
+                }
156
+                else
157
+                {
158
+                    glyph.data.at(index) =
159
+                        (face->glyph->bitmap.buffer[w * y + x] + 8) / 17;
160
+                }
161
+            }
162
+        }
163
+        glyphtable.push_back(glyph);
164
+        
165
+        charcode = FT_Get_Next_Char(face, charcode, &gindex);
166
+    }
167
+    
168
+    eliminate_duplicates(glyphtable);
169
+    crop_glyphs(glyphtable, fontinfo);
170
+    detect_flags(glyphtable, fontinfo);
171
+    
172
+    std::unique_ptr<DataFile> result(new DataFile(
173
+        dictionary, glyphtable, fontinfo));
174
+    return result;
175
+}
176
+
177
+}

+ 10 - 0
tools/mcufontencoder/src/freetype_import.hh

@@ -0,0 +1,10 @@
1
+// Function for importing any font supported by libfreetype.
2
+
3
+#pragma once
4
+#include "datafile.hh"
5
+
6
+namespace mcufont {
7
+
8
+std::unique_ptr<DataFile> LoadFreetype(std::istream &file, int size, bool bw);
9
+
10
+}

+ 134 - 0
tools/mcufontencoder/src/importtools.cc

<
@@ -0,0 +1,134 @@
1
+#include "importtools.hh"
2
+#include <limits>
3
+
4
+namespace mcufont {
5
+
6
+void eliminate_duplicates(std::vector<DataFile::glyphentry_t> &glyphtable)
7
+{
8
+    for (size_t i = 0; i + 1 < glyphtable.size(); i++)
9
+    {
10
+        for (size_t j = i + 1; j < glyphtable.size(); j++)
11
+        {
12
+            if (glyphtable.at(i).data == glyphtable.at(j).data &&
13
+                glyphtable.at(i).width == glyphtable.at(j).width)
14
+            {
15
+                for (int c : glyphtable.at(j).chars)
16
+                    glyphtable.at(i).chars.push_back(c);
17
+                
18
+                glyphtable.erase(glyphtable.begin() + j);
19
+                j--;
20
+            }
21
+        }
22
+    }
23
+}
24
+
25
+struct bbox_t
26
+{
27
+    int left;
28
+    int top;
29
+    int right;
30
+    int bottom;
31
+    
32
+    bbox_t()
33
+    {
34
+        left = std::numeric_limits<int>::max();
35
+        top = std::numeric_limits<int>::max();
36
+        right = std::numeric_limits<int>::min();
37
+        bottom = std::numeric_limits<int>::min();
38
+    }
39
+    
40
+    void update(int x, int y)
41
+    {
42
+        if (x < left) left = x;
43
+        if (x > right) right = x;
44
+        if (y < top) top = y;
45
+        if (y > bottom) bottom = y;
46
+    }
47
+};
48
+
49
+void crop_glyphs(std::vector<DataFile::glyphentry_t> &glyphtable,
50
+                 DataFile::fontinfo_t &fontinfo)
51
+{
52
+    // Find out the maximum bounding box
53
+    bbox_t bbox;
54
+    for (DataFile::glyphentry_t &glyph : glyphtable)
55
+    {
56
+        for (int y = 0; y < fontinfo.max_height; y++)
57
+        {