177 lines
5.5 KiB
C++
177 lines
5.5 KiB
C++
// Class to store the data of a font while it is being processed.
|
|
// This class can be safely cloned using the default copy constructor.
|
|
|
|
#pragma once
|
|
#include <cstdint>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <fstream>
|
|
#include <memory>
|
|
#include <map>
|
|
|
|
namespace mcufont
|
|
{
|
|
|
|
class DataFile
|
|
{
|
|
public:
|
|
typedef std::vector<uint8_t> pixels_t;
|
|
|
|
struct dictentry_t
|
|
{
|
|
pixels_t replacement; // The expanded version of this block.
|
|
int score; // Number of bytes that having this entry saves.
|
|
bool ref_encode; // Encode using references to other dictionary entries.
|
|
|
|
dictentry_t(): score(0), ref_encode(false) {}
|
|
};
|
|
|
|
struct glyphentry_t
|
|
{
|
|
pixels_t data; // The full data of the glyph.
|
|
std::vector<int> chars; // Characters that this glyph represents.
|
|
int width; // Tracking width of the character.
|
|
};
|
|
|
|
struct fontinfo_t
|
|
{
|
|
std::string name; // Name of the typeface
|
|
int max_width; // Width of the character bounding box.
|
|
int max_height; // Height of the character bounding box.
|
|
int baseline_x; // X coordinate (from left) of the baseline.
|
|
int baseline_y; // Y coordinate (from top) of the baseline.
|
|
int line_height; // Line height (vertical advance).
|
|
int flags;
|
|
};
|
|
|
|
static const int FLAG_MONOSPACE = 0x01;
|
|
static const int FLAG_BW = 0x02;
|
|
|
|
// Construct from data in memory.
|
|
DataFile(const std::vector<dictentry_t> &dictionary,
|
|
const std::vector<glyphentry_t> &glyphs,
|
|
const fontinfo_t &fontinfo);
|
|
|
|
inline DataFile * clone() const { return new DataFile(GetDictionary(), GetGlyphTable(), GetFontInfo()); }
|
|
|
|
// Save to a file (custom format)
|
|
void Save(std::ostream &file) const;
|
|
|
|
// Load from a file (custom format)
|
|
// Returns nullptr if load fails.
|
|
static std::unique_ptr<DataFile> Load(std::istream &file);
|
|
|
|
// Get or set an entry in the dictionary. The size of the dictionary
|
|
// is constant. Entries 0 to 23 are reserved for special purposes.
|
|
static const size_t dictionarysize = 256 - 24;
|
|
const dictentry_t &GetDictionaryEntry(size_t index) const
|
|
{ return m_dictionary.at(index); }
|
|
void SetDictionaryEntry(size_t index, const dictentry_t &value);
|
|
const std::vector<dictentry_t> &GetDictionary() const
|
|
{ return m_dictionary; }
|
|
|
|
// Get the index of the dictionary entry with the lowest score.
|
|
size_t GetLowScoreIndex() const
|
|
{ return m_lowscoreindex; }
|
|
|
|
// Get an entry in the glyph table.
|
|
size_t GetGlyphCount() const
|
|
{ return m_glyphtable.size(); }
|
|
const glyphentry_t &GetGlyphEntry(size_t index) const
|
|
{ return m_glyphtable.at(index); }
|
|
const std::vector<glyphentry_t> &GetGlyphTable() const
|
|
{ return m_glyphtable; }
|
|
|
|
// Create a map of char indices to glyph indices
|
|
std::map<size_t, size_t> GetCharToGlyphMap() const;
|
|
|
|
// Get the information that applies to all glyphs.
|
|
const fontinfo_t &GetFontInfo() const
|
|
{ return m_fontinfo; }
|
|
|
|
// Show a glyph as text.
|
|
std::string GlyphToText(size_t index) const;
|
|
|
|
// Get the random generator seed
|
|
// The seed is stored in the datafile to get deterministic behaviour
|
|
// for debugging.
|
|
uint32_t GetSeed() const { return m_seed; }
|
|
void SetSeed(uint32_t seed) { m_seed = seed; }
|
|
|
|
private:
|
|
std::vector<dictentry_t> m_dictionary;
|
|
std::vector<glyphentry_t> m_glyphtable;
|
|
fontinfo_t m_fontinfo;
|
|
uint32_t m_seed;
|
|
|
|
size_t m_lowscoreindex;
|
|
|
|
void UpdateLowScoreIndex();
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const DataFile::pixels_t& str);
|
|
std::istream& operator>>(std::istream& is, DataFile::pixels_t& str);
|
|
|
|
}
|
|
|
|
#ifdef CXXTEST_RUNNING
|
|
#include <cxxtest/TestSuite.h>
|
|
|
|
using namespace mcufont;
|
|
|
|
class DataFileTests: public CxxTest::TestSuite
|
|
{
|
|
public:
|
|
void testFileLoad()
|
|
{
|
|
std::istringstream s(testfile);
|
|
std::unique_ptr<DataFile> f = DataFile::Load(s);
|
|
|
|
TS_ASSERT_EQUALS(f->GetFontInfo().name, "Sans Serif");
|
|
TS_ASSERT_EQUALS(f->GetFontInfo().max_width, 4);
|
|
TS_ASSERT_EQUALS(f->GetFontInfo().max_height, 6);
|
|
TS_ASSERT_EQUALS(f->GetDictionaryEntry(0).score, 5);
|
|
TS_ASSERT_EQUALS(f->GetDictionaryEntry(1).score, 13);
|
|
TS_ASSERT_EQUALS(f->GetGlyphCount(), 3);
|
|
|
|
DataFile::pixels_t expected = {
|
|
0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15
|
|
};
|
|
TS_ASSERT_EQUALS(f->GetGlyphEntry(0).data.size(), 24);
|
|
TS_ASSERT(f->GetGlyphEntry(0).data == expected);
|
|
}
|
|
|
|
void testFileSave()
|
|
{
|
|
std::istringstream is1(testfile);
|
|
std::unique_ptr<DataFile> f1 = DataFile::Load(is1);
|
|
|
|
std::ostringstream os;
|
|
f1->Save(os);
|
|
|
|
std::string text = os.str();
|
|
std::istringstream is2(text);
|
|
std::unique_ptr<DataFile> f2 = DataFile::Load(is2);
|
|
|
|
TS_ASSERT_EQUALS(f1->GetFontInfo().name, f2->GetFontInfo().name);
|
|
TS_ASSERT(f1->GetGlyphEntry(0).data == f2->GetGlyphEntry(0).data);
|
|
}
|
|
|
|
private:
|
|
static constexpr const char *testfile =
|
|
"Version 1\n"
|
|
"FontName Sans Serif\n"
|
|
"MaxWidth 4\n"
|
|
"MaxHeight 6\n"
|
|
"BaselineX 1\n"
|
|
"BaselineY 1\n"
|
|
"DictEntry 5 0 0F0F0\n"
|
|
"DictEntry 13 0 F0F0F0\n"
|
|
"DictEntry 1 0 F0F0F0\n"
|
|
"Glyph 1,2,3 4 0F0F0F0F0F0F0F0F0F0F0F0F\n"
|
|
"Glyph 4 4 0F0F0F0F0F0F0F0F0F0F0F0F\n"
|
|
"Glyph 5 4 0F0F0F0F0F0F0F0F0F0F0F0F\n";
|
|
};
|
|
|
|
#endif
|