From 0ec1a5e4da8815f6d7c31cf7941b76f3d9fd5e28 Mon Sep 17 00:00:00 2001 From: inmarket Date: Sun, 22 Nov 2015 18:42:11 +1000 Subject: Add the mcufont encoder to the tools (including a win32 build) with binaries --- tools/mcufontencoder/src/main.cc | 480 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 480 insertions(+) create mode 100644 tools/mcufontencoder/src/main.cc (limited to 'tools/mcufontencoder/src/main.cc') diff --git a/tools/mcufontencoder/src/main.cc b/tools/mcufontencoder/src/main.cc new file mode 100644 index 00000000..19c9c6f7 --- /dev/null +++ b/tools/mcufontencoder/src/main.cc @@ -0,0 +1,480 @@ +#include "datafile.hh" +#include "importtools.hh" +#include "bdf_import.hh" +#include "freetype_import.hh" +#include "export_rlefont.hh" +#include "encode_rlefont.hh" +#include "optimize_rlefont.hh" +#include "export_bwfont.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccfixes.hh" + +using namespace mcufont; + +static std::string strip_extension(std::string filename) +{ + size_t pos = filename.find_last_of('.'); + + if (pos == std::string::npos) + { + return filename; + } + else + { + return filename.substr(0, pos); + } +} + +static std::unique_ptr load_dat(std::string src) +{ + std::ifstream infile(src); + + if (!infile.good()) + { + std::cerr << "Could not open " << src << std::endl; + return nullptr; + } + + std::unique_ptr f = DataFile::Load(infile); + if (!f) + { + std::cerr << "Invalid format for .dat file: " << src << std::endl; + return nullptr; + } + + return f; +} + +static bool save_dat(std::string dest, DataFile *f) +{ + std::ofstream outfile(dest); + + if (!outfile.good()) + { + std::cerr << "Could not open " << dest << std::endl; + return false; + } + + f->Save(outfile); + + if (!outfile.good()) + { + std::cerr << "Could not write to " << dest << std::endl; + return false; + } + + return true; +} + +enum status_t +{ + STATUS_OK = 0, // All good + STATUS_INVALID = 1, // Invalid command or args + STATUS_ERROR = 2 // Error when executing command +}; + +static status_t cmd_import_ttf(const std::vector &args) +{ + if (args.size() != 3 && args.size() != 4) + return STATUS_INVALID; + + std::string src = args.at(1); + int size = std::stoi(args.at(2)); + bool bw = (args.size() == 4 && args.at(3) == "bw"); + std::string dest = strip_extension(src) + std::to_string(size) + (bw ? "bw" : "") + ".dat"; + std::ifstream infile(src); + + if (!infile.good()) + { + std::cerr << "Could not open " << src << std::endl; + return STATUS_ERROR; + } + + std::cout << "Importing " << src << " to " << dest << std::endl; + + std::unique_ptr f = LoadFreetype(infile, size, bw); + + mcufont::rlefont::init_dictionary(*f); + + if (!save_dat(dest, f.get())) + return STATUS_ERROR; + + std::cout << "Done: " << f->GetGlyphCount() << " unique glyphs." << std::endl; + return STATUS_OK; +} + +static status_t cmd_import_bdf(const std::vector &args) +{ + if (args.size() != 2) + return STATUS_INVALID; + + std::string src = args.at(1); + std::string dest = strip_extension(args.at(1)) + ".dat"; + std::ifstream infile(src); + + if (!infile.good()) + { + std::cerr << "Could not open " << src << std::endl; + return STATUS_ERROR; + } + + std::cout << "Importing " << src << " to " << dest << std::endl; + + std::unique_ptr f = LoadBDF(infile); + + mcufont::rlefont::init_dictionary(*f); + + if (!save_dat(dest, f.get())) + return STATUS_ERROR; + + std::cout << "Done: " << f->GetGlyphCount() << " unique glyphs." << std::endl; + return STATUS_OK; +} + +static status_t cmd_filter(const std::vector &args) +{ + if (args.size() < 3) + return STATUS_INVALID; + + std::set allowed; + + // Parse arguments + for (size_t i = 2; i < args.size(); i++) + { + std::string s = args.at(i); + size_t pos = s.find('-'); + if (pos == std::string::npos) + { + // Single char + allowed.insert(std::stoi(s, nullptr, 0)); + } + else + { + // Range + int start = std::stoi(s.substr(0, pos), nullptr, 0); + int end = std::stoi(s.substr(pos + 1), nullptr, 0); + + for (int j = start; j <= end; j++) + { + allowed.insert(j); + } + } + } + + std::string src = args.at(1); + std::unique_ptr f = load_dat(src); + if (!f) + return STATUS_ERROR; + + std::cout << "Font originally had " << f->GetGlyphCount() << " glyphs." << std::endl; + + // Filter the glyphs + std::vector newglyphs; + for (size_t i = 0; i < f->GetGlyphCount(); i++) + { + DataFile::glyphentry_t g = f->GetGlyphEntry(i); + + for (size_t j = 0; j < g.chars.size(); j++) + { + if (!allowed.count(g.chars.at(j))) + { + g.chars.erase(g.chars.begin() + j); + j--; + } + } + + if (g.chars.size()) + { + newglyphs.push_back(g); + } + } + + DataFile::fontinfo_t fontinfo = f->GetFontInfo(); + crop_glyphs(newglyphs, fontinfo); + detect_flags(newglyphs, fontinfo); + + f.reset(new DataFile(f->GetDictionary(), newglyphs, fontinfo)); + std::cout << "After filtering, " << f->GetGlyphCount() << " glyphs remain." << std::endl; + + if (!save_dat(src, f.get())) + return STATUS_ERROR; + + return STATUS_OK; +} + +static status_t cmd_show_glyph(const std::vector &args) +{ + if (args.size() != 3) + return STATUS_INVALID; + + std::string src = args.at(1); + std::unique_ptr f = load_dat(src); + + if (!f) + return STATUS_ERROR; + + size_t index = 0; + if (args.at(2) == "largest") + { + std::unique_ptr e = + mcufont::rlefont::encode_font(*f, false); + size_t maxlen = 0; + size_t i = 0; + for (mcufont::rlefont::encoded_font_t::refstring_t g : e->glyphs) + { + if (g.size() > maxlen) + { + maxlen = g.size(); + index = i; + } + i++; + } + + std::cout << "Index " << index << ", length " << maxlen << std::endl; + } + else + { + index = strtol(args.at(2).c_str(), nullptr, 0); + } + + if (index < 0 || index >= f->GetGlyphCount()) + { + std::cerr << "No such glyph " << index << std::endl; + return STATUS_ERROR; + } + + std::cout << "Width: " << f->GetGlyphEntry(index).width << std::endl; + std::cout << "Chars: "; + for (int c: f->GetGlyphEntry(index).chars) std::cout << c << " "; + std::cout << std::endl; + + std::cout << f->GlyphToText(index); + return STATUS_OK; +} + +static status_t cmd_rlefont_export(const std::vector &args) +{ + if (args.size() != 2 && args.size() != 3) + return STATUS_INVALID; + + std::string src = args.at(1); + std::string dst = (args.size() == 2) ? strip_extension(src) + ".c" : args.at(2); + std::unique_ptr f = load_dat(src); + + if (!f) + return STATUS_ERROR; + + { + std::ofstream source(dst); + mcufont::rlefont::write_source(source, dst, *f); + std::cout << "Wrote " << dst << std::endl; + } + + return STATUS_OK; +} + +static status_t cmd_rlefont_size(const std::vector &args) +{ + if (args.size() != 2) + return STATUS_INVALID; + + std::string src = args.at(1); + std::unique_ptr f = load_dat(src); + + if (!f) + return STATUS_ERROR; + + size_t size = mcufont::rlefont::get_encoded_size(*f); + + std::cout << "Glyph count: " << f->GetGlyphCount() << std::endl; + std::cout << "Glyph bbox: " << f->GetFontInfo().max_width << "x" + << f->GetFontInfo().max_height << " pixels" << std::endl; + std::cout << "Uncompressed size: " << f->GetGlyphCount() * + f->GetFontInfo().max_width * f->GetFontInfo().max_height / 2 + << " bytes" << std::endl; + std::cout << "Compressed size: " << size << " bytes" << std::endl; + std::cout << "Bytes per glyph: " << size / f->GetGlyphCount() << std::endl; + return STATUS_OK; +} + +static status_t cmd_rlefont_optimize(const std::vector &args) +{ + if (args.size() != 2 && args.size() != 3) + return STATUS_INVALID; + + std::string src = args.at(1); + std::unique_ptr f = load_dat(src); + + if (!f) + return STATUS_ERROR; + + size_t oldsize = mcufont::rlefont::get_encoded_size(*f); + + std::cout << "Original size is " << oldsize << " bytes" << std::endl; + std::cout << "Press ctrl-C at any time to stop." << std::endl; + std::cout << "Results are saved automatically after each iteration." << std::endl; + + int limit = 100; + if (args.size() == 3) + { + limit = std::stoi(args.at(2)); + } + + if (limit > 0) + std::cout << "Limit is " << limit << " iterations" << std::endl; + + int i = 0; + time_t oldtime = time(NULL); + while (!limit || i < limit) + { + mcufont::rlefont::optimize(*f); + + size_t newsize = mcufont::rlefont::get_encoded_size(*f); + time_t newtime = time(NULL); + + int bytes_per_min = (oldsize - newsize) * 60 / (newtime - oldtime + 1); + + i++; + std::cout << "iteration " << i << ", size " << newsize + << " bytes, speed " << bytes_per_min << " B/min" + << std::endl; + + { + if (!save_dat(src, f.get())) + return STATUS_ERROR; + } + } + + return STATUS_OK; +} + +static status_t cmd_rlefont_show_encoded(const std::vector &args) +{ + if (args.size() != 2) + return STATUS_INVALID; + + std::string src = args.at(1); + std::unique_ptr f = load_dat(src); + + if (!f) + return STATUS_ERROR; + + std::unique_ptr e = + mcufont::rlefont::encode_font(*f, false); + + int i = 0; + for (mcufont::rlefont::encoded_font_t::rlestring_t d : e->rle_dictionary) + { + std::cout << "Dict RLE " << 24 + i++ << ": "; + for (uint8_t v : d) + std::cout << std::setfill('0') << std::setw(2) << std::hex << (int)v << " "; + std::cout << std::endl; + } + + for (mcufont::rlefont::encoded_font_t::refstring_t d : e->ref_dictionary) + { + std::cout << "Dict Ref " << 24 + i++ << ": "; + for (uint8_t v : d) + std::cout << std::setfill('0') << std::setw(2) << std::hex << (int)v << " "; + std::cout << std::endl; + } + + i = 0; + for (mcufont::rlefont::encoded_font_t::refstring_t g : e->glyphs) + { + std::cout << "Glyph " << i++ << ": "; + for (uint8_t v : g) + std::cout << std::setfill('0') << std::setw(2) << std::hex << (int)v << " "; + std::cout << std::endl; + } + + return STATUS_OK; +} + +static status_t cmd_bwfont_export(const std::vector &args) +{ + if (args.size() != 2 && args.size() != 3) + return STATUS_INVALID; + + std::string src = args.at(1); + std::string dst = (args.size() == 2) ? strip_extension(src) + ".c" : args.at(2); + std::unique_ptr f = load_dat(src); + + if (!f) + return STATUS_ERROR; + + if (!(f->GetFontInfo().flags & DataFile::FLAG_BW)) + { + std::cout << "Warning: font is not black and white" << std::endl; + } + + { + std::ofstream source(dst); + mcufont::bwfont::write_source(source, dst, *f); + std::cout << "Wrote " << dst << std::endl; + } + + return STATUS_OK; +} + + +static const char *usage_msg = + "Usage: mcufont [options] ...\n" + "Commands for importing:\n" + " import_ttf [bw] Import a .ttf font into a data file.\n" + " import_bdf Import a .bdf font into a data file.\n" + "\n" + "Commands for inspecting and editing data files:\n" + " filter ... Remove everything except specified characters.\n" + " show_glyph Show the glyph at index.\n" + "\n" + "Commands specific to rlefont format:\n" + " rlefont_size Check the encoded size of the data file.\n" + " rlefont_optimize Perform an optimization pass on the data file.\n" + " rlefont_export [outfile] Export to .c source code.\n" + " rlefont_show_encoded Show the encoded data for debugging.\n" + "\n" + "Commands specific to bwfont format:\n" + " bwfont_export [outfile Export to .c source code.\n" + ""; + +typedef status_t (*cmd_t)(const std::vector &args); +static const std::map command_list { + {"import_ttf", cmd_import_ttf}, + {"import_bdf", cmd_import_bdf}, + {"filter", cmd_filter}, + {"show_glyph", cmd_show_glyph}, + {"rlefont_size", cmd_rlefont_size}, + {"rlefont_optimize", cmd_rlefont_optimize}, + {"rlefont_export", cmd_rlefont_export}, + {"rlefont_show_encoded", cmd_rlefont_show_encoded}, + {"bwfont_export", cmd_bwfont_export}, +}; + +int main(int argc, char **argv) +{ + std::vector args; + for (int i = 1; i < argc; i++) + args.push_back(argv[i]); + + status_t status = STATUS_INVALID; + if (args.size() >= 1 && command_list.count(args.at(0))) + { + status = command_list.find(args.at(0))->second(args); + } + + if (status == STATUS_INVALID) + { + std::cout << usage_msg << std::endl; + } + + return status; +} -- cgit v1.2.3