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