Smooth font rendering
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(cd /c/Users/mta/projects/autosample && ./nob.exe debug 2>&1)",
|
||||
"Bash(cd /c/Users/mta/projects/autosample && rm -f build/*.pdb build/*.obj build/*.ilk && ./nob.exe debug 2>&1)"
|
||||
"Bash(cd /c/Users/mta/projects/autosample && rm -f build/*.pdb build/*.obj build/*.ilk && ./nob.exe debug 2>&1)",
|
||||
"Bash(cd /c/Users/mta/projects/autosample && ./nob.exe 2>&1)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,12 +407,17 @@ static void ensure_measure_font(Renderer *r, F32 font_size) {
|
||||
}
|
||||
|
||||
static bool create_font_atlas(Renderer *r, F32 font_size) {
|
||||
const int SS = 2; // supersample factor
|
||||
F32 render_size = font_size * SS;
|
||||
int render_w = FONT_ATLAS_W * SS;
|
||||
int render_h = FONT_ATLAS_H * SS;
|
||||
|
||||
r->font_atlas_size = font_size;
|
||||
|
||||
// Create a GDI bitmap to render glyphs into
|
||||
// Create a GDI bitmap to render glyphs at supersampled resolution
|
||||
HDC dc = CreateCompatibleDC(nullptr);
|
||||
HFONT font = CreateFontW(
|
||||
-(int)(font_size + 0.5f), 0, 0, 0,
|
||||
-(int)(render_size + 0.5f), 0, 0, 0,
|
||||
FW_NORMAL, FALSE, FALSE, FALSE,
|
||||
DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
|
||||
@@ -420,16 +425,16 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
|
||||
);
|
||||
SelectObject(dc, font);
|
||||
|
||||
// Get line height
|
||||
// Get line height (at supersample scale, divide back to get 1x)
|
||||
TEXTMETRICW tm;
|
||||
GetTextMetricsW(dc, &tm);
|
||||
r->font_line_height = (F32)tm.tmHeight;
|
||||
r->font_line_height = (F32)tm.tmHeight / SS;
|
||||
|
||||
// Create DIB section for rendering
|
||||
// Create DIB section at supersampled resolution
|
||||
BITMAPINFO bmi = {};
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = FONT_ATLAS_W;
|
||||
bmi.bmiHeader.biHeight = -FONT_ATLAS_H; // top-down
|
||||
bmi.bmiHeader.biWidth = render_w;
|
||||
bmi.bmiHeader.biHeight = -render_h; // top-down
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 32;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
@@ -440,13 +445,13 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
|
||||
SelectObject(dc, font);
|
||||
|
||||
// Clear to black
|
||||
memset(dib_bits, 0, FONT_ATLAS_W * FONT_ATLAS_H * 4);
|
||||
memset(dib_bits, 0, render_w * render_h * 4);
|
||||
|
||||
SetTextColor(dc, RGB(255, 255, 255));
|
||||
SetBkMode(dc, TRANSPARENT);
|
||||
|
||||
// Render each glyph
|
||||
int pen_x = 1, pen_y = 1;
|
||||
// Render each glyph at supersampled resolution
|
||||
int pen_x = SS, pen_y = SS;
|
||||
int row_height = 0;
|
||||
|
||||
for (int i = 0; i < GLYPH_COUNT; i++) {
|
||||
@@ -454,41 +459,49 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
|
||||
SIZE ch_size = {};
|
||||
GetTextExtentPoint32A(dc, &ch, 1, &ch_size);
|
||||
|
||||
int gw = ch_size.cx + 2; // 1px padding each side
|
||||
int gh = ch_size.cy + 2;
|
||||
int gw = ch_size.cx + 2 * SS; // padding scaled by SS
|
||||
int gh = ch_size.cy + 2 * SS;
|
||||
|
||||
if (pen_x + gw >= FONT_ATLAS_W) {
|
||||
pen_x = 1;
|
||||
pen_y += row_height + 1;
|
||||
if (pen_x + gw >= render_w) {
|
||||
pen_x = SS;
|
||||
pen_y += row_height + SS;
|
||||
row_height = 0;
|
||||
}
|
||||
|
||||
if (pen_y + gh >= FONT_ATLAS_H) break; // out of space
|
||||
if (pen_y + gh >= render_h) break; // out of space
|
||||
|
||||
TextOutA(dc, pen_x + 1, pen_y + 1, &ch, 1);
|
||||
TextOutA(dc, pen_x + SS, pen_y + SS, &ch, 1);
|
||||
|
||||
r->glyphs[i].u0 = (F32)pen_x / (F32)FONT_ATLAS_W;
|
||||
r->glyphs[i].v0 = (F32)pen_y / (F32)FONT_ATLAS_H;
|
||||
r->glyphs[i].u1 = (F32)(pen_x + gw) / (F32)FONT_ATLAS_W;
|
||||
r->glyphs[i].v1 = (F32)(pen_y + gh) / (F32)FONT_ATLAS_H;
|
||||
r->glyphs[i].w = (F32)gw;
|
||||
r->glyphs[i].h = (F32)gh;
|
||||
r->glyphs[i].x_advance = (F32)ch_size.cx;
|
||||
// UVs are fractional — pen_x/render_w == (pen_x/SS)/FONT_ATLAS_W
|
||||
r->glyphs[i].u0 = (F32)pen_x / (F32)render_w;
|
||||
r->glyphs[i].v0 = (F32)pen_y / (F32)render_h;
|
||||
r->glyphs[i].u1 = (F32)(pen_x + gw) / (F32)render_w;
|
||||
r->glyphs[i].v1 = (F32)(pen_y + gh) / (F32)render_h;
|
||||
r->glyphs[i].w = (F32)gw / SS; // store at 1x scale
|
||||
r->glyphs[i].h = (F32)gh / SS;
|
||||
r->glyphs[i].x_advance = (F32)ch_size.cx / SS;
|
||||
|
||||
if (gh > row_height) row_height = gh;
|
||||
pen_x += gw + 1;
|
||||
pen_x += gw + SS;
|
||||
}
|
||||
|
||||
GdiFlush();
|
||||
|
||||
// Convert BGRA bitmap to single-channel alpha
|
||||
// Box-filter downsample from supersampled resolution to atlas resolution
|
||||
U8 *atlas_data = (U8 *)malloc(FONT_ATLAS_W * FONT_ATLAS_H);
|
||||
U8 *src = (U8 *)dib_bits;
|
||||
for (int y = 0; y < FONT_ATLAS_H; y++) {
|
||||
for (int x = 0; x < FONT_ATLAS_W; x++) {
|
||||
int idx = (y * FONT_ATLAS_W + x);
|
||||
U8 r_val = src[idx * 4 + 2]; // BGRA layout: R=2
|
||||
atlas_data[idx] = r_val;
|
||||
int sum = 0;
|
||||
for (int sy = 0; sy < SS; sy++) {
|
||||
for (int sx = 0; sx < SS; sx++) {
|
||||
int src_idx = ((y * SS + sy) * render_w + (x * SS + sx)) * 4;
|
||||
sum += src[src_idx + 2]; // R channel from BGRA
|
||||
}
|
||||
}
|
||||
float a = (float)sum / (float)(SS * SS * 255);
|
||||
a = powf(a, 0.55f);
|
||||
atlas_data[y * FONT_ATLAS_W + x] = (U8)(a * 255.0f + 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user