k3/src/k3font.c

180 lines
3.9 KiB
C

#include"k3font.h"
#include"k3batch.h"
#include<string.h>
#include"gl.h"
struct k3Font *k3FontCreate() {
struct k3Font *ret = calloc(sizeof(*ret), 1);
return ret;
}
static int cmpglyph(const void *a, const void *b) {
return *(const uint32_t*) a - *(const uint32_t*) b;
}
int k3FontLoad(struct k3Font *this, const uint8_t *buf, size_t len, k3FontTexLoader texldr) {
if(*(uint32_t*) buf != 0x03464D42) {
return 0;
}
const uint8_t *end = buf + len;
buf += 4;
uint16_t pages = 0;
while(buf + 5 < end) {
uint8_t blockType = *buf;
uint32_t blockSize = *(uint32_t*) (buf + 1);
buf += 5;
if(blockType == 1) { //Info block
buf += 14;
while(*buf) buf++;
buf++;
} else if(blockType == 2) { //Common block
this->lineScale = 1.f / *(uint16_t*) buf;
buf += 2;
this->baseline = *(uint16_t*) buf;
buf += 2;
this->texW = *(uint16_t*) buf;
buf += 2;
this->texH = *(uint16_t*) buf;
buf += 2;
pages = *(uint16_t*) buf;
buf += 7;
} else if(blockType == 3) { //Pages block
if(pages == 0) return 0;
this->pageCount = pages;
this->pages = malloc(sizeof(*this->pages) * this->pageCount);
for(size_t i = 0; i < this->pageCount; i++) {
this->pages[i] = texldr(this, buf);
buf += strlen(buf) + 1;
}
} else if(blockType == 4) { //Chars block
size_t num = blockSize / 20;
this->glyphs = calloc(sizeof(*this->glyphs), this->glyphCount = num);
memcpy(this->glyphs, buf, num * 20);
qsort(this->glyphs, num, sizeof(*this->glyphs), cmpglyph);
buf += blockSize;
} else if(blockType == 5) { //Kerning block
// Ignore kerning for now
buf += blockSize;
}
}
return 1;
}
static uint32_t read_utf8(const char **txt_) {
const uint8_t **txt = (void*) txt_;
if(**txt == 0) {
return 0;
} else if(**txt < 0x80) {
return *(*txt)++;
} else if(**txt < 0xE0) {
uint32_t ret = ((*(*txt)++) & 0x1F) << 6;
ret |= (*(*txt)++) & 0x3F;
return ret;
} else if(**txt < 0xF0) {
uint32_t ret = ((*(*txt)++) & 0x0F) << 12;
ret |= ((*(*txt)++) & 0x3F) << 6;
ret |= (*(*txt)++) & 0x3F;
return ret;
} else {
uint32_t ret = ((*(*txt)++) & 0x07) << 18;
ret |= ((*(*txt)++) & 0x3F) << 12;
ret |= ((*(*txt)++) & 0x3F) << 6;
ret |= (*(*txt)++) & 0x3F;
return ret;
}
}
void k3FontSz(struct k3Font *this, float sz, const char *txt, float wall, struct k3RectF *ret) {
if(wall < 0) {
wall = HUGE_VALF;
}
float maxX = 0;
float x = 0, y = sz;
while(1) {
uint32_t cp = read_utf8(&txt);
if(!cp) break;
if(cp == 10) {
x = 0;
y += sz;
}
struct k3FontGlyph *g = k3FontGetGlyph(this, cp);
if(!g) continue;
if(x + g->width > wall) {
x = 0;
y += sz;
}
x += g->xadvance * this->lineScale * sz;
maxX = fmaxf(maxX, x);
}
ret->w = maxX;
ret->h = y;
}
void k3FontDraw(struct k3Font *this, float xStart, float yStart, float sz, const char *txt, float wall, vec4 color) {
if(wall < 0) {
wall = HUGE_VALF;
}
float x = xStart, y = yStart;
while(1) {
uint32_t cp = read_utf8(&txt);
if(!cp) break;
if(cp == 10) {
x = xStart;
y -= sz;
}
struct k3FontGlyph *g = k3FontGetGlyph(this, cp);
if(!g) continue;
if(x + g->width - xStart > wall) {
x = xStart;
y -= sz;
}
struct k3Tex *tex = this->pages[g->page];
size_t texW = this->texW;
size_t texH = this->texH;
k3BatchAdd(tex,
(struct k3RectF) {(float) g->x / texW, (float) g->y / texH, (float) g->width / texW, (float) g->height / texH},
(struct k3RectF) {
x + g->xoffset * this->lineScale * sz,
y + ((-g->height - g->yoffset) * this->lineScale + 1) * sz,
g->width * this->lineScale * sz,
g->height * this->lineScale * sz
}, 0, color, 0);
x += g->xadvance * this->lineScale * sz;
}
k3BatchFlush();
}
struct k3FontGlyph *k3FontGetGlyph(struct k3Font *this, uint32_t cp) {
return bsearch(&cp, this->glyphs, this->glyphCount, sizeof(*this->glyphs), cmpglyph);
}