218 lines
4.7 KiB
C
218 lines
4.7 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 == 0) {
|
|
break;
|
|
}
|
|
|
|
if(cp == 10) {
|
|
x = 0;
|
|
y += sz;
|
|
}
|
|
|
|
struct k3FontGlyph *g = k3FontGetGlyph(this, cp);
|
|
|
|
if(!g) continue;
|
|
|
|
if(x + g->width * this->lineScale * sz > 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, int alignment, vec4 color) {
|
|
if(wall < 0) {
|
|
wall = HUGE_VALF;
|
|
}
|
|
|
|
float y = yStart;
|
|
|
|
while(1) {
|
|
size_t lineLength = 0;
|
|
|
|
float lineWidth = 0;
|
|
|
|
uint32_t cp2;
|
|
const char *txt2 = txt;
|
|
while(1) {
|
|
cp2 = read_utf8(&txt2);
|
|
if(cp2 == 0 || cp2 == 10) {
|
|
break;
|
|
}
|
|
|
|
struct k3FontGlyph *g = k3FontGetGlyph(this, cp2);
|
|
if(g) {
|
|
if(lineWidth + g->width * this->lineScale * sz > wall) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lineLength++;
|
|
if(g) {
|
|
lineWidth += g->xadvance * this->lineScale * sz;
|
|
}
|
|
}
|
|
|
|
if(lineLength == 0 && cp2 == 0) {
|
|
break;
|
|
}
|
|
|
|
float x = 0;
|
|
if(alignment == 0) {
|
|
x = xStart;
|
|
} else if(alignment == 1) {
|
|
x = xStart + (wall - lineWidth) / 2;
|
|
} else if(alignment == 2) {
|
|
x = xStart + wall - lineWidth;
|
|
}
|
|
|
|
for(size_t i = 0; i < lineLength; i++) {
|
|
uint32_t cp = read_utf8(&txt);
|
|
|
|
struct k3FontGlyph *g = k3FontGetGlyph(this, cp);
|
|
|
|
if(!g) continue;
|
|
|
|
struct k3Tex *tex = this->pages[g->page];
|
|
size_t texW = this->texW;
|
|
size_t texH = this->texH;
|
|
|
|
k3BatchAdd(tex,
|
|
(struct k3RectF) {(float) (g->x + 0.5) / texW, (float) (g->y + 0.5) / texH, (float) (g->width - 1) / texW, (float) (g->height - 1) / 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;
|
|
}
|
|
|
|
// If the line break was caused directly by a LF, skip over it for next line
|
|
if(cp2 == 10) {
|
|
read_utf8(&txt);
|
|
}
|
|
|
|
y -= sz;
|
|
}
|
|
k3BatchFlush();
|
|
}
|
|
|
|
struct k3FontGlyph *k3FontGetGlyph(struct k3Font *this, uint32_t cp) {
|
|
return bsearch(&cp, this->glyphs, this->glyphCount, sizeof(*this->glyphs), cmpglyph);
|
|
}
|