aboutsummaryrefslogtreecommitdiffstats
path: root/tools/mcufontencoder/src/export_bwfont.cc
blob: 9b97fb9c4b702b21fee907861c014bf9a3f79a53 (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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#include "export_bwfont.hh"
#include <vector>
#include <iomanip>
#include <map>
#include <set>
#include <algorithm>
#include <string>
#include <cctype>
#include "exporttools.hh"
#include "importtools.hh"
#include "ccfixes.hh"

#define BWFONT_FORMAT_VERSION 4

namespace mcufont {
namespace bwfont {

static void encode_glyph(const DataFile::glyphentry_t &glyph,
                         const DataFile::fontinfo_t &fontinfo,
                         std::vector<unsigned> &dest,
                         int num_cols)
{
    const int threshold = 8;
    
    if (glyph.data.size() == 0)
        return;
    
    // Find the number of columns in the glyph data
    if (num_cols == 0)
    {
        for (int x = 0; x < fontinfo.max_width; x++)
        {
            for (int y = 0; y < fontinfo.max_height; y++)
            {
                size_t index = y * fontinfo.max_width + x;
                if (glyph.data.at(index) >= threshold)
                    num_cols = x + 1;
            }
        }
    }
    
    // Write the bits that compose the glyph
    for (int x = 0; x < num_cols; x++)
    {
        for (int y = 0; y < fontinfo.max_height; y+= 8)
        {
            size_t remain = std::min(8, fontinfo.max_height - y);
            uint8_t byte = 0;
            for (size_t i = 0; i < remain; i++)
            {
                size_t index = (y + i) * fontinfo.max_width + x;
                if (glyph.data.at(index) >= threshold)
                {
                    byte |= (1 << i);
                }
            }
            dest.push_back(byte);
        }
    }
}

struct cropinfo_t
{
    size_t offset_x;
    size_t offset_y;
    size_t height_bytes;
    size_t height_pixels;
    size_t width;
};

static void encode_character_range(std::ostream &out,
                                   const std::string &name,
                                   const DataFile &datafile,
                                   const char_range_t &range,
                                   unsigned range_index,
                                   cropinfo_t &cropinfo)
{
    std::vector<DataFile::glyphentry_t> glyphs;
    bool constant_width = true;
    int width = datafile.GetGlyphEntry(range.glyph_indices[0]).width;
    
    // Copy all the glyphs in this range for the purpose of cropping them.
    for (int glyph_index: range.glyph_indices)
    {
        if (glyph_index < 0)
        {
            // Missing glyph
            DataFile::glyphentry_t dummy = {};
            glyphs.push_back(dummy);
        }
        else
        {
            auto glyph = datafile.GetGlyphEntry(glyph_index);
            glyphs.push_back(glyph);
            
            if (glyph.width != width)
            {
                constant_width = false;
                width = 0;
            }
        }
    }
    
    // Crop the glyphs in this range. Getting rid of a few rows at top
    // or left can save a bunch of bytes with minimal cost.
    DataFile::fontinfo_t old_fi = datafile.GetFontInfo();
    DataFile::fontinfo_t new_fi = old_fi;
    crop_glyphs(glyphs, new_fi);
    
    if (new_fi.max_width != width)
    {
        constant_width = false;
        width = 0;
    }
    
    // Fill in the crop information
    cropinfo.offset_x = old_fi.baseline_x - new_fi.baseline_x;
    cropinfo.offset_y = old_fi.baseline_y - new_fi.baseline_y;
    cropinfo.height_pixels = new_fi.max_height;
    cropinfo.height_bytes = (cropinfo.height_pixels + 7) / 8;
    cropinfo.width = width;
    
    // Then format and write out the glyph data
    std::vector<unsigned> offsets;
    std::vector<unsigned> data;
    std::vector<unsigned> widths;
    size_t stride = cropinfo.height_bytes;
    
    for (const DataFile::glyphentry_t &g : glyphs)
    {
        offsets.push_back(data.size() / stride);
        widths.push_back(g.width);
        encode_glyph(g, new_fi, data, width);
    }    
    offsets.push_back(data.size() / stride);
    
    write_const_table(out, data, "uint8_t", "mf_bwfont_" + name + "_glyph_data_" + std::to_string(range_index));
    
    if (!constant_width)
    {
        write_const_table(out, offsets, "uint16_t", "mf_bwfont_" + name + "_glyph_offsets_" + std::to_string(range_index), 4);
        write_const_table(out, widths, "uint8_t", "mf_bwfont_" + name + "_glyph_widths_" + std::to_string(range_index));
    }
}
    
void write_source(std::ostream &out, std::string name, const DataFile &datafile)
{
    name = filename_to_identifier(name);
    
    out << std::endl;
    out << std::endl;
    out << "/* Start of automatically generated font definition for " << name << ". */" << std::endl;
    out << std::endl;
    
    out << "#ifndef MF_BWFONT_INTERNALS" << std::endl;
    out << "#define MF_BWFONT_INTERNALS" << std::endl;
    out << "#endif" << std::endl;
    out << "#include \"mf_bwfont.h\"" << std::endl;
    out << std::endl;
    
    out << "#ifndef MF_BWFONT_VERSION_" << BWFONT_FORMAT_VERSION << "_SUPPORTED" << std::endl;
    out << "#error The font file is not compatible with this version of mcufont." << std::endl;
    out << "#endif" << std::endl;
    out << std::endl;
    
    // Split the characters into ranges
    DataFile::fontinfo_t f = datafile.GetFontInfo();
    size_t glyph_size = f.max_width * ((f.max_height + 7) / 8);
    auto get_glyph_size = [=](size_t i) { return glyph_size; };
    std::vector<char_range_t> ranges = compute_char_ranges(datafile,
        get_glyph_size, 65536, 16);

    // Write out glyph data for character ranges
    std::vector<cropinfo_t> crops;
    for (size_t i = 0; i < ranges.size(); i++)
    {
        cropinfo_t cropinfo;
        encode_character_range(out, name, datafile, ranges.at(i), i, cropinfo);
        crops.push_back(cropinfo);
    }
    
    // Write out a table describing the character ranges
    out << "static const struct mf_bwfont_char_range_s mf_bwfont_" + name + "_char_ranges[] = {" << std::endl;
    for (size_t i = 0; i < ranges.size(); i++)
    {
        std::string offsets = (crops[i].width) ? "0" : "mf_bwfont_" + name + "_glyph_offsets_" + std::to_string(i);
        std::string widths = (crops[i].width) ? "0" : "mf_bwfont_" + name + "_glyph_widths_" + std::to_string(i);
        
        out << "    {" << std::endl;
        out << "        " << ranges.at(i).first_char << ", /* first char */" << std::endl;
        out << "        " << ranges.at(i).char_count << ", /* char count */" << std::endl;
        out << "        " << crops[i].offset_x << ", /* offset x */" << std::endl;
        out << "        " << crops[i].offset_y << ", /* offset y */" << std::endl;
        out << "        " << crops[i].height_bytes << ", /* height in bytes */" << std::endl;
        out << "        " << crops[i].height_pixels << ", /* height in pixels */" << std::endl;
        out << "        " << crops[i].width << ", /* width */" << std::endl;
        out << "        " << widths << ", /* glyph widths */" << std::endl;
        out << "        " << offsets << ", /* glyph offsets */" << std::endl;
        out << "        " << "mf_bwfont_" << name << "_glyph_data_" << i << ", /* glyph data */" << std::endl;
        out << "    }," << std::endl;
    }
    out << "};" << std::endl;
    out << std::endl;
    
    // Fonts in this format are always black & white
    int flags = datafile.GetFontInfo().flags | DataFile::FLAG_BW;
    
    // Pull it all together in the rlefont_s structure.
    out << "const struct mf_bwfont_s mf_bwfont_" << name << " = {" << std::endl;
    out << "    {" << std::endl;
    out << "    " << "\"" << datafile.GetFontInfo().name << "\"," << std::endl;
    out << "    " << "\"" << name << "\"," << std::endl;
    out << "    " << datafile.GetFontInfo().max_width << ", /* width */" << std::endl;
    out << "    " << datafile.GetFontInfo().max_height << ", /* height */" << std::endl;
    out << "    " << get_min_x_advance(datafile) << ", /* min x advance */" << std::endl;
    out << "    " << get_max_x_advance(datafile) << ", /* max x advance */" << std::endl;
    out << "    " << datafile.GetFontInfo().baseline_x << ", /* baseline x */" << std::endl;
    out << "    " << datafile.GetFontInfo().baseline_y << ", /* baseline y */" << std::endl;
    out << "    " << datafile.GetFontInfo().line_height << ", /* line height */" << std::endl;
    out << "    " << flags << ", /* flags */" << std::endl;
    out << "    " << select_fallback_char(datafile) << ", /* fallback character */" << std::endl;
    out << "    " << "&mf_bwfont_character_width," << std::endl;
    out << "    " << "&mf_bwfont_render_character," << std::endl;
    out << "    }," << std::endl;
    
    out << "    " << BWFONT_FORMAT_VERSION << ", /* version */" << std::endl;
    out << "    " << ranges.size() << ", /* char range count */" << std::endl;
    out << "    " << "mf_bwfont_" << name << "_char_ranges," << std::endl;
    out << "};" << std::endl;
    
    // Write the font lookup structure
    out << std::endl;
    out << "#ifdef MF_INCLUDED_FONTS" << std::endl;
    out << "/* List entry for searching fonts by name. */" << std::endl;
    out << "static const struct mf_font_list_s mf_bwfont_" << name << "_listentry = {" << std::endl;
    out << "    MF_INCLUDED_FONTS," << std::endl;
    out << "    (struct mf_font_s*)&mf_bwfont_" << name << std::endl;
    out << "};" << std::endl;
    out << "#undef MF_INCLUDED_FONTS" << std::endl;
    out << "#define MF_INCLUDED_FONTS (&mf_bwfont_" << name << "_listentry)" << std::endl;
    out << "#endif" << std::endl;
    
    out << std::endl;
    out << std::endl;
    out << "/* End of automatically generated font definition for " << name << ". */" << std::endl;
    out << std::endl;
}
    
    
}}