aboutsummaryrefslogtreecommitdiffstats
path: root/tools/mcufontencoder/src/datafile.hh
blob: 460e60393f92bc7f107cf23ad8bd5f65218d1748 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Class to store the data of a font while it is being processed.
// This class can be safely cloned using the default copy constructor.

#pragma once
#include <cstdint>
#include <vector>
#include <string>
#include <fstream>
#include <memory>
#include <map>

namespace mcufont
{

class DataFile
{
public:
    typedef std::vector<uint8_t> pixels_t;
    
    struct dictentry_t
    {
        pixels_t replacement; // The expanded version of this block.
        int score; // Number of bytes that having this entry saves.
        bool ref_encode; // Encode using references to other dictionary entries.
        
        dictentry_t(): score(0), ref_encode(false) {}
    };
    
    struct glyphentry_t
    {
        pixels_t data; // The full data of the glyph.
        std::vector<int> chars; // Characters that this glyph represents.
        int width; // Tracking width of the character.
    };
    
    struct fontinfo_t
    {
        std::string name; // Name of the typeface
        int max_width; // Width of the character bounding box.
        int max_height; // Height of the character bounding box.
        int baseline_x; // X coordinate (from left) of the baseline.
        int baseline_y; // Y coordinate (from top) of the baseline.
        int line_height; // Line height (vertical advance).
        int flags;
    };
    
    static const int FLAG_MONOSPACE = 0x01;
    static const int FLAG_BW = 0x02;
    
    // Construct from data in memory.
    DataFile(const std::vector<dictentry_t> &dictionary,
             const std::vector<glyphentry_t> &glyphs,
             const fontinfo_t &fontinfo);
    
    // Save to a file (custom format)
    void Save(std::ostream &file) const;

    // Load from a file (custom format)
    // Returns nullptr if load fails.
    static std::unique_ptr<DataFile> Load(std::istream &file);
    
    // Get or set an entry in the dictionary. The size of the dictionary
    // is constant. Entries 0 to 23 are reserved for special purposes.
    static const size_t dictionarysize = 256 - 24;
    const dictentry_t &GetDictionaryEntry(size_t index) const
        { return m_dictionary.at(index); }
    void SetDictionaryEntry(size_t index, const dictentry_t &value);
    const std::vector<dictentry_t> &GetDictionary() const
        { return m_dictionary; }
        
    // Get the index of the dictionary entry with the lowest score.
    size_t GetLowScoreIndex() const
        { return m_lowscoreindex; }
    
    // Get an entry in the glyph table.
    size_t GetGlyphCount() const
        { return m_glyphtable.size(); }
    const glyphentry_t &GetGlyphEntry(size_t index) const
        { return m_glyphtable.at(index); }
    const std::vector<glyphentry_t> &GetGlyphTable() const
        { return m_glyphtable; }
    
    // Create a map of char indices to glyph indices
    std::map<size_t, size_t> GetCharToGlyphMap() const;
    
    // Get the information that applies to all glyphs.
    const fontinfo_t &GetFontInfo() const
        { return m_fontinfo; }
    
    // Show a glyph as text.
    std::string GlyphToText(size_t index) const;
    
    // Get the random generator seed
    // The seed is stored in the datafile to get deterministic behaviour
    // for debugging.
    uint32_t GetSeed() const { return m_seed; }
    void SetSeed(uint32_t seed) { m_seed = seed; }
    
private:
    std::vector<dictentry_t> m_dictionary;
    std::vector<glyphentry_t> m_glyphtable;
    fontinfo_t m_fontinfo;
    uint32_t m_seed;
    
    size_t m_lowscoreindex;
    
    void UpdateLowScoreIndex();
};

std::ostream& operator<<(std::ostream& os, const DataFile::pixels_t& str);
std::istream& operator>>(std::istream& is, DataFile::pixels_t& str);

}

#ifdef CXXTEST_RUNNING
#include <cxxtest/TestSuite.h>

using namespace mcufont;

class DataFileTests: public CxxTest::TestSuite
{
public:
    void testFileLoad()
    {
        std::istringstream s(testfile);
        std::unique_ptr<DataFile> f = DataFile::Load(s);
        
        TS_ASSERT_EQUALS(f->GetFontInfo().name, "Sans Serif");
        TS_ASSERT_EQUALS(f->GetFontInfo().max_width, 4);
        TS_ASSERT_EQUALS(f->GetFontInfo().max_height, 6);
        TS_ASSERT_EQUALS(f->GetDictionaryEntry(0).score, 5);
        TS_ASSERT_EQUALS(f->GetDictionaryEntry(1).score, 13);
        TS_ASSERT_EQUALS(f->GetGlyphCount(), 3);
        
        DataFile::pixels_t expected = {
            0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15
        };
        TS_ASSERT_EQUALS(f->GetGlyphEntry(0).data.size(), 24);
        TS_ASSERT(f->GetGlyphEntry(0).data == expected);
    }
    
    void testFileSave()
    {
        std::istringstream is1(testfile);
        std::unique_ptr<DataFile> f1 = DataFile::Load(is1);
        
        std::ostringstream os;
        f1->Save(os);
        
        std::string text = os.str();
        std::istringstream is2(text);
        std::unique_ptr<DataFile> f2 = DataFile::Load(is2);
        
        TS_ASSERT_EQUALS(f1->GetFontInfo().name, f2->GetFontInfo().name);
        TS_ASSERT(f1->GetGlyphEntry(0).data == f2->GetGlyphEntry(0).data);
    }
    
private:
    static constexpr const char *testfile =
        "Version 1\n"
        "FontName Sans Serif\n"
        "MaxWidth 4\n"
        "MaxHeight 6\n"
        "BaselineX 1\n"
        "BaselineY 1\n"
        "DictEntry 5 0 0F0F0\n"
        "DictEntry 13 0 F0F0F0\n"
        "DictEntry 1 0 F0F0F0\n"
        "Glyph 1,2,3 4 0F0F0F0F0F0F0F0F0F0F0F0F\n"
        "Glyph 4 4 0F0F0F0F0F0F0F0F0F0F0F0F\n"
        "Glyph 5 4 0F0F0F0F0F0F0F0F0F0F0F0F\n";
};

#endif