86 lines
3.0 KiB
C++
86 lines
3.0 KiB
C++
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
#include <msdfgen.h>
|
|
#include <ext/import-font.h>
|
|
|
|
static unsigned char clamp_channel(float v) {
|
|
if (v <= 0.0f) return 0;
|
|
if (v >= 1.0f) return 255;
|
|
return static_cast<unsigned char>(std::lround(v * 255.0f));
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
if (argc < 3) {
|
|
std::fprintf(stderr, "Usage: %s <font-file> <codepoint>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
const char *fontPath = argv[1];
|
|
unsigned codepoint = std::strtoul(argv[2], nullptr, 0);
|
|
auto *ft = msdfgen::initializeFreetype();
|
|
if (!ft) return 1;
|
|
auto *font = msdfgen::loadFont(ft, fontPath);
|
|
if (!font) return 1;
|
|
msdfgen::Shape shape;
|
|
double advance = 0.0;
|
|
if (!msdfgen::loadGlyph(shape, font, msdfgen::GlyphIndex(codepoint), msdfgen::FONT_SCALING_EM_NORMALIZED, &advance)) {
|
|
std::fprintf(stderr, "Failed to load glyph %u\n", codepoint);
|
|
return 1;
|
|
}
|
|
shape.normalize();
|
|
msdfgen::edgeColoringSimple(shape, 3.0);
|
|
auto bounds = shape.getBounds();
|
|
|
|
double emScale = 48.0;
|
|
double pxRange = 4.0;
|
|
int bmp_w = std::max(1, (int)std::ceil((bounds.r - bounds.l) * emScale + 2.0 * pxRange));
|
|
int bmp_h = std::max(1, (int)std::ceil((bounds.t - bounds.b) * emScale + 2.0 * pxRange));
|
|
|
|
msdfgen::Bitmap<float, 3> bitmap(bmp_w, bmp_h);
|
|
msdfgen::Vector2 scale(emScale, emScale);
|
|
msdfgen::Vector2 translate(-bounds.l * emScale + pxRange, -bounds.b * emScale + pxRange);
|
|
msdfgen::generateMSDF(bitmap, shape, pxRange, scale, translate);
|
|
|
|
std::vector<unsigned char> buffer(static_cast<size_t>(bmp_w) * bmp_h * 4);
|
|
for (int y = 0; y < bmp_h; ++y) {
|
|
int dst_y = bmp_h - 1 - y;
|
|
for (int x = 0; x < bmp_w; ++x) {
|
|
const float *px = bitmap(x, y);
|
|
size_t idx = (static_cast<size_t>(dst_y) * bmp_w + x) * 4;
|
|
buffer[idx + 0] = clamp_channel(px[0]);
|
|
buffer[idx + 1] = clamp_channel(px[1]);
|
|
buffer[idx + 2] = clamp_channel(px[2]);
|
|
buffer[idx + 3] = 255;
|
|
}
|
|
}
|
|
|
|
int minX = bmp_w, minY = bmp_h, maxX = -1, maxY = -1;
|
|
for (int y = 0; y < bmp_h; ++y) {
|
|
for (int x = 0; x < bmp_w; ++x) {
|
|
size_t idx = (static_cast<size_t>(y) * bmp_w + x) * 4;
|
|
unsigned char r = buffer[idx];
|
|
unsigned char g = buffer[idx + 1];
|
|
unsigned char b = buffer[idx + 2];
|
|
bool interesting = !(r == 0 && g == 0 && b == 0) && !(r == 255 && g == 255 && b == 255);
|
|
if (interesting) {
|
|
if (x < minX) minX = x;
|
|
if (x > maxX) maxX = x;
|
|
if (y < minY) minY = y;
|
|
if (y > maxY) maxY = y;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::printf("bmp %dx%d\n", bmp_w, bmp_h);
|
|
if (maxX >= minX && maxY >= minY) {
|
|
std::printf("interesting bbox: x=[%d,%d] y=[%d,%d]\n", minX, maxX, minY, maxY);
|
|
}
|
|
|
|
msdfgen::destroyFont(font);
|
|
msdfgen::deinitializeFreetype(ft);
|
|
return 0;
|
|
}
|