#define STB_TRUETYPE_IMPLEMENTATION #include "stb_truetype.h" #include "dat.h" #include "fn.h" typedef struct Glyph Glyph; typedef struct Font Font; struct Glyph { uchar *bmp; int w, h, ox, oy, base; }; struct Font { stbtt_fontinfo info; uchar *data; float scale; int base; }; enum { Maxfonts = 4 }; static Font fonts[Maxfonts]; static int nfonts; static Glyph cache[Nglyphs]; static u32int blendtab[2][256]; static u32int blend(u32int bg, u32int fg, int a) { int r, g, b, inv; inv = 255 - a; r = ((bg >> 16 & 0xff) * inv + (fg >> 16 & 0xff) * a) / 255; g = ((bg >> 8 & 0xff) * inv + (fg >> 8 & 0xff) * a) / 255; b = ((bg & 0xff) * inv + (fg & 0xff) * a) / 255; return (r << 16) | (g << 8) | b; } static int loadfont(char *path) { Font *f; int ascent, descent, gap, fd; long sz, n; fd = open(path, OREAD); if(fd < 0){ fprint(2, "strans: popup: can't open font: %s\n", path); return 0; } sz = seek(fd, 0, 2); seek(fd, 0, 0); if(sz <= 0){ fprint(2, "strans: popup: empty font: %s\n", path); close(fd); return 0; } f = &fonts[nfonts]; f->data = emalloc(sz); n = readn(fd, f->data, sz); close(fd); if(n != sz){ fprint(2, "strans: popup: can't read font: %s\n", path); free(f->data); f->data = nil; return 0; } if(!stbtt_InitFont(&f->info, f->data, stbtt_GetFontOffsetForIndex(f->data, 0))){ fprint(2, "strans: popup: can't init font: %s\n", path); free(f->data); f->data = nil; return 0; } f->scale = stbtt_ScaleForPixelHeight(&f->info, Fontsz); stbtt_GetFontVMetrics(&f->info, &ascent, &descent, &gap); (void)descent; (void)gap; f->base = (int)(ascent * f->scale); nfonts++; return 1; } void fontinit(char *dir) { static char *names[] = { "NotoSans-Regular.ttf", "NotoSansMonoCJKjp-Regular.otf", "NotoEmoji-Regular.ttf", }; int i, a; char path[256]; nfonts = 0; memset(cache, 0, sizeof cache); for(i = 0; i < nelem(names); i++){ if(strlen(dir) + 1 + strlen(names[i]) + 1 > sizeof path){ fprint(2, "strans: popup: font path is too long\n"); continue; } snprint(path, sizeof path, "%s/%s", dir, names[i]); loadfont(path); } for(a = 0; a < 256; a++){ blendtab[0][a] = blend(Colbg, Colfg, a); blendtab[1][a] = blend(Colsel, Colfg, a); } } int fontready(void) { return nfonts != 0; } void putfont(u32int *buf, int w, int h, int px, int py, Rune r) { Glyph *g; int i, j, a, sel, f; int y0, j0, j1, x0, i0, i1; u32int *p; if(r >= Nglyphs) return; g = &cache[r]; if(g->bmp == nil){ for(f = 0; f < nfonts; f++){ if(stbtt_FindGlyphIndex(&fonts[f].info, r) == 0) continue; g->bmp = stbtt_GetCodepointBitmap(&fonts[f].info, fonts[f].scale, fonts[f].scale, r, &g->w, &g->h, &g->ox, &g->oy); if(g->bmp != nil){ g->base = fonts[f].base; break; } } if(g->bmp == nil) return; } y0 = py + g->base + g->oy; j0 = y0 < 0 ? -y0 : 0; j1 = y0 + g->h > h ? h - y0 : g->h; x0 = px + g->ox; i0 = x0 < 0 ? -x0 : 0; i1 = x0 + g->w > w ? w - x0 : g->w; for(j = j0; j < j1; j++){ for(i = i0; i < i1; i++){ a = g->bmp[j * g->w + i]; if(a > 0){ p = &buf[(y0 + j) * w + x0 + i]; sel = (*p == Colsel) ? 1 : 0; *p = blendtab[sel][a]; } } } }