#include "datafile.hh" #include #include #include #include #include "ccfixes.hh" #define DATAFILE_FORMAT_VERSION 1 namespace mcufont { DataFile::DataFile(const std::vector &dictionary, const std::vector &glyphs, const fontinfo_t &fontinfo): m_dictionary(dictionary), m_glyphtable(glyphs), m_fontinfo(fontinfo) { dictentry_t dummy = {}; while (m_dictionary.size() < dictionarysize) m_dictionary.push_back(dummy); UpdateLowScoreIndex(); } void DataFile::Save(std::ostream &file) const { file << "Version " << DATAFILE_FORMAT_VERSION << std::endl; file << "FontName " << m_fontinfo.name << std::endl; file << "MaxWidth " << m_fontinfo.max_width << std::endl; file << "MaxHeight " << m_fontinfo.max_height << std::endl; file << "BaselineX " << m_fontinfo.baseline_x << std::endl; file << "BaselineY " << m_fontinfo.baseline_y << std::endl; file << "LineHeight " << m_fontinfo.line_height << std::endl; file << "Flags " << m_fontinfo.flags << std::endl; file << "RandomSeed " << m_seed << std::endl; for (const dictentry_t &d : m_dictionary) { if (d.replacement.size() != 0) { file << "DictEntry " << d.score << " "; file << d.ref_encode << " " << d.replacement << std::endl; } } for (const glyphentry_t &g : m_glyphtable) { file << "Glyph "; for (size_t i = 0; i < g.chars.size(); i++) { if (i != 0) file << ','; file << g.chars.at(i); } file << " " << g.width << " " << g.data << std::endl; } } std::unique_ptr DataFile::Load(std::istream &file) { fontinfo_t fontinfo = {}; std::vector dictionary; std::vector glyphtable; uint32_t seed = 1234; int version = -1; std::string line; while (std::getline(file, line)) { std::istringstream input(line); std::string tag; input >> tag; if (tag == "Version") { input >> version; } else if (tag == "FontName") { while (std::isspace(input.peek())) input.get(); std::getline(input, fontinfo.name); } else if (tag == "MaxWidth") { input >> fontinfo.max_width; } else if (tag == "MaxHeight") { input >> fontinfo.max_height; } else if (tag == "BaselineX") { input >> fontinfo.baseline_x; } else if (tag == "BaselineY") { input >> fontinfo.baseline_y; } else if (tag == "LineHeight") { input >> fontinfo.line_height; } else if (tag == "RandomSeed") { input >> seed; } else if (tag == "Flags") { input >> fontinfo.flags; } else if (tag == "DictEntry" && dictionary.size() < dictionarysize) { dictentry_t d = {}; input >> d.score >> d.ref_encode >> d.replacement; dictionary.push_back(d); } else if (tag == "Glyph") { glyphentry_t g = {}; std::string chars; input >> chars >> g.width >> g.data; if ((int)g.data.size() != fontinfo.max_width * fontinfo.max_height) throw std::runtime_error("wrong glyph data length: " + std::to_string(g.data.size())); size_t pos = 0; while (pos < chars.size()) { size_t p; g.chars.push_back(std::stoi(chars.substr(pos), &p)); pos += p + 1; } glyphtable.push_back(g); } } if (version != DATAFILE_FORMAT_VERSION) { return std::unique_ptr(nullptr); } std::unique_ptr result(new DataFile(dictionary, glyphtable, fontinfo)); result->SetSeed(seed); return result; } void DataFile::SetDictionaryEntry(size_t index, const dictentry_t &value) { m_dictionary.at(index) = value; if (index == m_lowscoreindex || m_dictionary.at(m_lowscoreindex).score > value.score) { UpdateLowScoreIndex(); } } std::map DataFile::GetCharToGlyphMap() const { std::map char_to_glyph; for (size_t i = 0; i < m_glyphtable.size(); i++) { for (size_t c: m_glyphtable[i].chars) { char_to_glyph[c] = i; } } return char_to_glyph; } std::string DataFile::GlyphToText(size_t index) const { std::ostringstream os; const char glyphchars[] = "....,,,,----XXXX"; for (int y = 0; y < m_fontinfo.max_height; y++) { for (int x = 0; x < m_fontinfo.max_width; x++) { size_t pos = y * m_fontinfo.max_width + x; os << glyphchars[m_glyphtable.at(index).data.at(pos)]; } os << std::endl; } return os.str(); } void DataFile::UpdateLowScoreIndex() { auto comparison = [](const dictentry_t &a, const dictentry_t &b) { return a.score < b.score; }; auto iter = std::min_element(m_dictionary.begin(), m_dictionary.end(), comparison); m_lowscoreindex = iter - m_dictionary.begin(); } std::ostream& operator<<(std::ostream& os, const DataFile::pixels_t& str) { for (uint8_t p: str) { if (p <= 9) os << (char)(p + '0'); else if (p <= 15) os << (char)(p - 10 + 'A'); else throw std::logic_error("invalid pixel alpha: " + std::to_string(p)); } return os; } std::istream& operator>>(std::istream& is, DataFile::pixels_t& str) { char c; str.clear(); while (isspace(is.peek())) is.get(); while (is.get(c)) { if (c >= '0' && c <= '9') str.push_back(c - '0'); else if (c >= 'A' && c <= 'F') str.push_back(c - 'A' + 10); else break; } return is; } }