blob: 4a1c3885c37d7b7bd58948e854b42c2872024606 [file] [log] [blame]
#include <cassert>
#include "font.h"
#include "include/core/SkFont.h"
uint16_t RiveFont::charToGlyph(SkUnichar u) const {
for (size_t i = 0; i < fCMap.size(); ++i) {
if (fCMap[i].fChar == u) {
return fCMap[i].fGlyph;
}
}
return 0;
}
float RiveFont::advance(uint16_t glyph) const {
assert(glyph < fGlyphs.size());
return fGlyphs[glyph].fAdvance;
}
const SkPath* RiveFont::path(uint16_t glyph) const {
assert(glyph < fGlyphs.size());
const SkPath& p = fGlyphs[glyph].fPath;
return p.isEmpty() ? nullptr : &p;
}
#define kSignature 0x23581321
#define kVersion 1
constexpr uint32_t Tag(unsigned a, unsigned b, unsigned c, unsigned d) {
assert((a & 0xFF) == a);
assert((b & 0xFF) == b);
assert((c & 0xFF) == c);
assert((d & 0xFF) == d);
return (a << 24) | (b << 16) | (c << 8) | d;
}
static inline void tag_to_str(uint32_t tag, char str[5]) {
str[0] = (tag >> 24) & 0xFF;
str[1] = (tag >> 16) & 0xFF;
str[2] = (tag >> 8) & 0xFF;
str[3] = (tag >> 0) & 0xFF;
str[4] = 0;
}
constexpr uint32_t kOffsets_TableTag = Tag('p', 'o', 'f', 'f');
constexpr uint32_t kPaths_TableTag = Tag('p', 'a', 't', 'h');
constexpr uint32_t kCMap_TableTag = Tag('c', 'm', 'a', 'p');
constexpr uint32_t kInfo_TableTag = Tag('i', 'n', 'f', 'o');
constexpr uint32_t kAdvances_TableTag = Tag('h', 'a', 'd', 'v');
struct TableDir {
uint32_t tag;
uint32_t offset;
uint32_t length;
};
struct FontHead {
uint32_t signature;
uint32_t version;
uint32_t tableCount;
// TableDir[dirCount]
const TableDir* dir() const { return (const TableDir*)(this + 1); }
const void* findTable(uint32_t tag) const {
auto dir = this->dir();
for (unsigned i = 0; i < this->tableCount; ++i) {
if (dir[i].tag == tag) {
return (char*)this + dir[i].offset;
}
}
return nullptr;
}
};
struct InfoTable {
uint16_t glyphCount;
uint16_t upem;
};
struct GlyphOffsetTable {
// uint32_t offsets[glyphCount+1]; // relative to the glyphdata table
};
struct GlyphRec {
// uint16_t verbCount;
// uint16_t pointCount; // if verbCount > 0
// uint8_t verbs; // Move, Line, Quad, Cubic, Close
// pad16[]
// uint16_t x,y x,y x,y
};
void RiveFont::load(sk_sp<SkTypeface> tf, const char str[], size_t len) {
this->clear();
SkFont font(std::move(tf), 1.0f);
uint16_t glyphIDs[len];
int glyphCount = font.textToGlyphs(str, len, SkTextEncoding::kUTF8, glyphIDs, len);
assert(glyphCount == (int)len);
struct Rec {
uint16_t charCode;
uint16_t srcGlyph;
uint16_t dstGlyph;
};
std::vector<Rec> rec;
uint16_t newDstGlyphID = 1; // leave room for glyphID==0 for missing glyph
// build vector of unique chars
for (size_t i = 0; i < len; ++i) {
uint16_t code = str[i];
auto iter = std::find_if(
rec.begin(), rec.end(), [code](const auto& r) { return r.charCode == code; });
if (iter == rec.end()) {
// gonna add code -- now see if its glyph is unique
uint16_t srcGlyph = glyphIDs[i];
auto it2 = std::find_if(rec.begin(), rec.end(), [srcGlyph](const auto& r) {
return r.srcGlyph == srcGlyph;
});
uint16_t dstGlyph;
if (it2 == rec.end()) {
// srcGlyph is unique (or zero)
dstGlyph = srcGlyph ? newDstGlyphID++ : 0;
} else {
dstGlyph = it2->dstGlyph; // reuse prev dstGlyph
}
rec.push_back({code, srcGlyph, dstGlyph});
}
}
std::sort(
rec.begin(), rec.end(), [](const Rec& a, const Rec& b) { return a.charCode < b.charCode; });
for (const auto& r : rec) {
printf("'%c' [%d] %d -> %d\n", r.charCode, r.charCode, r.srcGlyph, r.dstGlyph);
fCMap.push_back({r.charCode, r.dstGlyph});
}
std::sort(
rec.begin(), rec.end(), [](const Rec& a, const Rec& b) { return a.dstGlyph < b.dstGlyph; });
font.setLinearMetrics(true);
auto append_glyph = [&](uint16_t srcGlyph) {
float width;
font.getWidths(&srcGlyph, 1, &width);
SkPath path;
font.getPath(srcGlyph, &path); // returns false if glyph is bitmap
fGlyphs.push_back({std::move(path), width});
};
append_glyph(0); // missing glyph
for (int i = 1; i < newDstGlyphID; ++i) { // walk through our glyphs
auto iter =
std::find_if(rec.begin(), rec.end(), [i](const auto& r) { return r.dstGlyph == i; });
assert(iter != rec.end());
append_glyph(iter->srcGlyph);
}
}
struct ByteBuilder {
std::vector<uint8_t> bytes;
uint32_t length() const { return bytes.size(); }
uint8_t add8(size_t x) {
assert((x & 0xFF) == x);
uint8_t b = (uint8_t)x;
bytes.push_back(b);
return b;
}
int16_t addS16(ssize_t x) {
int16_t s = (int16_t)x;
assert(s == x);
this->add(&s, 2);
return s;
}
uint16_t addU16(size_t x) {
assert((x & 0xFFFF) == x);
uint16_t u = (uint16_t)x;
this->add(&u, 2);
return u;
}
uint32_t addU32(size_t x) {
assert((x & 0xFFFFFFFF) == x);
uint32_t u = (uint32_t)x;
this->add(&u, 4);
return u;
}
void add(const void* src, size_t n) {
size_t size = bytes.size();
bytes.resize(size + n);
memcpy(bytes.data() + size, src, n);
}
template <typename T> void add(const std::vector<T>& v) {
this->add(v.data(), v.size() * sizeof(T));
}
void add(const ByteBuilder& bb) { this->add(bb.bytes.data(), bb.bytes.size()); }
void add(sk_sp<SkData> data) { this->add(data->data(), data->size()); }
void padTo16() {
if (bytes.size() & 1) {
this->add8(0);
}
}
void padTo32() {
while (bytes.size() & 3) {
this->add8(0);
}
}
void set32(size_t offset, uint32_t value) {
assert(offset + 4 <= bytes.size());
assert((offset & 3) == 0);
memcpy(bytes.data() + offset, &value, 4);
}
sk_sp<SkData> detach() {
auto data = SkData::MakeWithCopy(bytes.data(), bytes.size());
bytes.clear();
return data;
}
};
void encode_path(ByteBuilder* bb, const SkPath& path, float scale) {
std::vector<uint8_t> varray;
std::vector<uint16_t> parray;
auto add_point = [&](SkPoint p) {
parray.push_back(SkScalarRoundToInt(p.fX * scale));
parray.push_back(SkScalarRoundToInt(p.fY * scale));
};
SkPath::RawIter iter(path);
for (;;) {
SkPoint pts[4];
auto verb = iter.next(pts);
if (verb == SkPath::kDone_Verb) {
break;
}
varray.push_back(verb);
switch ((SkPathVerb)verb) {
case SkPathVerb::kMove:
add_point(pts[0]);
break;
case SkPathVerb::kLine:
add_point(pts[1]);
break;
case SkPathVerb::kQuad:
add_point(pts[1]);
add_point(pts[2]);
break;
case SkPathVerb::kCubic:
add_point(pts[1]);
add_point(pts[2]);
add_point(pts[3]);
break;
case SkPathVerb::kClose:
break;
default:
assert(false); // unsupported
}
}
assert((int)varray.size() == path.countVerbs());
assert((int)parray.size() == path.countPoints() * 2);
auto no_useful_verbs = [](const std::vector<uint8_t>& verbs) {
for (auto v : verbs) {
switch ((SkPathVerb)v) {
case SkPathVerb::kLine:
case SkPathVerb::kQuad:
case SkPathVerb::kCubic:
return false;
default:
break;
}
}
return true;
};
if (no_useful_verbs(varray)) {
return; // we signal empty paths with the offset table
}
bb->addU16(varray.size());
assert((parray.size() & 1) == 0);
bb->addU16(parray.size() / 2); // #points == 2 * #values
bb->add(varray);
bb->padTo16();
bb->add(parray);
}
sk_sp<SkData> RiveFont::encode() const {
sk_sp<SkData> infoD, cmapD, offsetsD, pathsD;
const int upem = 2048;
const float scale = upem;
struct DirRec {
uint32_t tag;
sk_sp<SkData> data;
};
std::vector<DirRec> dir;
{
InfoTable itable;
itable.glyphCount = fGlyphs.size();
itable.upem = upem;
dir.push_back({kInfo_TableTag, SkData::MakeWithCopy(&itable, sizeof(itable))});
}
{
ByteBuilder cmap;
// start with #pairs
cmap.addU16(fCMap.size());
for (const auto& cm : fCMap) {
cmap.addU16(cm.fChar);
cmap.addU16(cm.fGlyph);
}
dir.push_back({kCMap_TableTag, cmap.detach()});
}
// todo: store offset[i] for glyph[i+1] since first offset is always 0
{
ByteBuilder paths, advances;
std::vector<uint32_t> offsets;
for (const auto& g : fGlyphs) {
offsets.push_back(paths.length());
encode_path(&paths, g.fPath, scale);
paths.padTo16();
advances.addU16(SkScalarRoundToInt(g.fAdvance * scale));
}
offsets.push_back(paths.length()); // store N+1 offsets
dir.push_back(
{kOffsets_TableTag, SkData::MakeWithCopy(offsets.data(), offsets.size() * 4)});
dir.push_back({kPaths_TableTag, paths.detach()});
dir.push_back({kAdvances_TableTag, advances.detach()});
}
std::sort(
dir.begin(), dir.end(), [](const DirRec& a, const DirRec& b) { return a.tag < b.tag; });
ByteBuilder header;
header.addU32(kSignature);
header.addU32(kVersion);
header.addU32(dir.size());
for (auto& d : dir) {
header.addU32(d.tag);
header.addU32(0); // offset -- fill in later
header.addU32(d.data->size());
}
size_t offsetToDirEntry = sizeof(FontHead);
for (auto& d : dir) {
// +4 to skip the tag field of the dir entry
header.set32(offsetToDirEntry + 4, header.length());
offsetToDirEntry += sizeof(TableDir);
header.add(d.data);
header.padTo32();
}
return header.detach();
}
struct Reader {
const char* fStart;
const char* fCurr;
const char* fStop;
Reader(const void* data, size_t length) {
fStart = (const char*)data;
fCurr = fStart;
fStop = fStart + length;
}
bool isAvailable(size_t n) const { return fCurr + n <= fStop; }
size_t available() const { return fStop - fCurr; }
template <typename T> const T* skip(size_t size) {
assert(this->isAvailable(size));
const char* p = fCurr;
fCurr += size;
return reinterpret_cast<const T*>(p);
}
template <typename T> const T* skip() { return this->skip<T>(sizeof(T)); }
void skip(size_t size) {}
uint8_t u8() {
assert(this->isAvailable(1));
return *fCurr++;
}
int16_t s16() {
assert(this->isAvailable(2));
int16_t s;
memcpy(&s, fCurr, 2);
fCurr += 2;
return s;
}
uint16_t u16() {
assert(this->isAvailable(2));
uint16_t s;
memcpy(&s, fCurr, 2);
fCurr += 2;
return s;
}
uint32_t u32() {
assert(this->isAvailable(4));
uint32_t s;
memcpy(&s, fCurr, 4);
fCurr += 4;
return s;
}
void read(void* dst, size_t size) {
assert(this->isAvailable(size));
memcpy(dst, fCurr, size);
fCurr += size;
}
void skipPad16() {
size_t amount = fCurr - fStart;
if (amount & 1) {
assert(this->isAvailable(1));
fCurr += 1;
}
}
};
static int compute_point_count(const uint8_t verbs[], int verbCount) {
int count = 0;
for (int i = 0; i < verbCount; ++i) {
switch ((SkPathVerb)verbs[i]) {
case SkPathVerb::kMove:
count += 1;
break;
case SkPathVerb::kLine:
count += 1;
break;
case SkPathVerb::kQuad:
count += 2;
break;
case SkPathVerb::kCubic:
count += 3;
break;
case SkPathVerb::kClose:
count += 0;
break;
default:
assert(false);
return -1;
}
}
return count;
}
static SkPath decode_path(const void* data, size_t length, float scale) {
Reader reader(data, length);
const int verbCount = reader.u16();
assert(verbCount > 0);
const int pointCount = reader.u16();
auto verbs = reader.skip<uint8_t>(verbCount);
reader.skipPad16();
const int computedPointCount = compute_point_count(verbs, verbCount);
assert(pointCount == computedPointCount);
auto pts16 = reader.skip<int16_t>(pointCount * 2 * sizeof(int16_t));
assert(reader.available() == 0);
SkPoint pts[pointCount];
for (int i = 0; i < pointCount; ++i) {
pts[i] = {pts16[0] * scale, pts16[1] * scale};
pts16 += 2;
}
return SkPath::Make(pts, pointCount, verbs, verbCount, nullptr, 0, SkPathFillType::kWinding);
}
bool RiveFont::decode(const void* data, size_t length) {
Reader reader(data, length);
auto font = reader.skip<FontHead>();
assert(font->signature == kSignature);
assert(font->version == kVersion);
assert(font->tableCount >= 0);
assert(reader.isAvailable(font->tableCount * sizeof(TableDir)));
if (true) {
auto dir = font->dir();
for (unsigned i = 0; i < font->tableCount; ++i) {
char str[5];
tag_to_str(dir[i].tag, str);
printf("[%d] {%0x08 %s %10d} %d %d\n",
i,
dir[i].tag,
str,
dir[i].tag,
dir[i].offset,
dir[i].length);
}
}
auto info = (const InfoTable*)font->findTable(kInfo_TableTag);
auto cmap = (const uint16_t*)font->findTable(kCMap_TableTag);
auto paths = (const char*)font->findTable(kPaths_TableTag);
auto offsets = (const uint32_t*)font->findTable(kOffsets_TableTag);
auto advances = (const uint16_t*)font->findTable(kAdvances_TableTag);
const int glyphCount = info->glyphCount;
this->clear();
const int pairCount = *cmap++;
for (int i = 0; i < pairCount; ++i) {
uint16_t c = *cmap++;
uint16_t g = *cmap++;
assert(g < glyphCount);
fCMap.push_back({c, g});
}
const float scale = 1.0f / (float)info->upem;
for (int i = 0; i < glyphCount; ++i) {
float adv = advances[i] * scale;
uint32_t start = offsets[i];
uint32_t end = offsets[i + 1];
assert(start <= end);
fGlyphs.push_back(
{(start == end) ? SkPath() : decode_path(paths + start, end - start, scale), adv});
}
return true;
}