163 lines
3.1 KiB
C
163 lines
3.1 KiB
C
#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];
|
|
}
|
|
}
|
|
}
|
|
}
|