Skip to content

Instantly share code, notes, and snippets.

@dan-oak
Created December 13, 2020 12:07
Show Gist options
  • Save dan-oak/0c0d518d4304d0b8bbdfeff3481bec9a to your computer and use it in GitHub Desktop.
Save dan-oak/0c0d518d4304d0b8bbdfeff3481bec9a to your computer and use it in GitHub Desktop.
Usage example of stb_truetype.h in C++
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <stb/image_write.hpp>
#include <stb/truetype.hpp>
#include <cmath>
#include <map>
#include <fmt/core.h>
const std::string dir = ".";
typedef unsigned char byte;
typedef std::vector<byte> buffer;
buffer read_file (const std::string &filename) {
std::ifstream file(filename, std::ios::binary);
return buffer(
std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>()
);
}
void make_rgba_from_single_channel (buffer &data) {
auto src_sz = data.size();
auto dst_sz = src_sz * 4;
data.resize(dst_sz);
byte *src, *dst;
for (src = &data[src_sz - 1], dst = &data[dst_sz - 4]; dst >= &data[0]; src--, dst -= 4) {
dst[0] = dst[1] = dst[2] = (*src > 0) ? 255 : 0;
dst[3] = *src;
}
}
struct pos {
int x;
int y;
};
struct box {
pos a;
pos b;
};
template <>
struct fmt::formatter<pos> {
char presentation = 'f';
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const pos& p, FormatContext& ctx) {
return fmt::format_to(ctx.out(), "[{},{}]", p.x, p.y);
}
};
int main () {
char c = 'g';
fmt::print("character = {}\n", c);
buffer font_buf = read_file(dir + "/nasalization-rg.ttf");
stbtt_fontinfo font_info; {
if (!stbtt_InitFont(&font_info, font_buf.data(), 0))
fmt::print("Failed to load font\n");
}
int pixel_height = 32;
float scalar = stbtt_ScaleForPixelHeight(&font_info, pixel_height);
fmt::print("scalar = {}\n", scalar);
auto scale = [scalar](int i) {
return int(roundf((float)i * scalar));
};
int ascent, descent, line_gap; {
int ascenti, descenti, line_gapi;
stbtt_GetFontVMetrics(&font_info, &ascenti, &descenti, &line_gapi);
ascent = scale(ascenti);
descent = scale(descenti);
line_gap = scale(line_gapi);
}
fmt::print("ascent = {}\n", ascent);
fmt::print("descent = {}\n", descent);
fmt::print("line_gap = {}\n", line_gap);
int adv_x;
int l_bearing; {
int adv_xi;
int l_bearingi;
stbtt_GetCodepointHMetrics(&font_info, c, &adv_xi, &l_bearingi);
adv_x = scale(adv_xi);
l_bearing = scale(l_bearingi);
}
fmt::print("adv_x = {}\n", adv_x);
fmt::print("l_bearing = {}\n", l_bearing);
pos size; {
size.x = adv_x;
size.y = ascent - descent;
}
fmt::print("size = {}\n", size);
box bb; {
stbtt_GetCodepointBitmapBox(&font_info, c, scalar, scalar,
&bb.a.x, &bb.a.y, &bb.b.x, &bb.b.y);
}
fmt::print("bb.a = {}\n", bb.a);
fmt::print("bb.b = {}\n", bb.b);
pos b_size = {
.x = bb.b.x - bb.a.x,
.y = bb.b.y - bb.a.y,
};
fmt::print("b_size = {}\n", b_size);
int y = ascent + bb.a.y;
fmt::print("y = {}\n", y);
buffer bitmap(size.x * size.y);
int bitmap_offset = y*size.x + l_bearing;
byte *output = bitmap.data() + bitmap_offset;
stbtt_MakeCodepointBitmap(&font_info, output, b_size.x, b_size.y,
size.x, scalar, scalar, c);
for (int i = 0; i < size.y; ++i) {
for (int j = 0; j < size.x; ++j)
std::cout << " .:ioVM@"[bitmap[i*size.x + j]>>5];
std::cout << "\n";
}
stbi_write_png((dir+"/out/glyph.png").c_str(),
size.x, size.y, 1, bitmap.data(), size.x);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment