version 2.8
datafile.cc
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 }