lovr/src/graphics/font.c

269 lines
6.6 KiB
C
Raw Normal View History

2017-02-03 23:16:30 +00:00
#include "graphics/font.h"
#include "graphics/graphics.h"
#include "graphics/texture.h"
#include "math/math.h"
#include "loaders/font.h"
#include "loaders/texture.h"
2017-02-06 09:54:11 +00:00
#include "util.h"
2017-02-03 23:16:30 +00:00
#include <string.h>
2017-02-06 09:54:11 +00:00
#include <stdlib.h>
#include <stdio.h>
2017-02-03 23:16:30 +00:00
Font* lovrFontCreate(FontData* fontData) {
Font* font = lovrAlloc(sizeof(Font), lovrFontDestroy);
if (!font) return NULL;
2017-02-06 04:30:17 +00:00
font->fontData = fontData;
vec_init(&font->vertices);
2017-02-08 06:11:20 +00:00
map_init(&font->kerning);
2017-02-06 04:30:17 +00:00
// Atlas
2017-02-05 06:21:41 +00:00
int padding = 1;
font->atlas.x = padding;
font->atlas.y = padding;
2017-02-09 02:57:47 +00:00
font->atlas.width = 256;
font->atlas.height = 256;
2017-02-05 06:21:41 +00:00
font->atlas.padding = padding;
map_init(&font->atlas.glyphs);
2017-02-09 02:57:47 +00:00
while (font->atlas.width < 4 * fontData->size) {
font->atlas.width <<= 1;
}
2017-02-06 04:30:17 +00:00
// Texture
TextureData* textureData = lovrTextureDataGetBlank(font->atlas.width, font->atlas.height, 0x0, FORMAT_RG);
font->texture = lovrTextureCreate(textureData);
lovrTextureSetWrap(font->texture, WRAP_CLAMP, WRAP_CLAMP);
int swizzle[4] = { GL_RED, GL_RED, GL_RED, GL_GREEN };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
2017-02-03 23:16:30 +00:00
return font;
}
void lovrFontDestroy(const Ref* ref) {
Font* font = containerof(ref, Font);
lovrFontDataDestroy(font->fontData);
2017-02-05 06:21:41 +00:00
lovrRelease(&font->texture->ref);
map_deinit(&font->atlas.glyphs);
2017-02-08 06:11:20 +00:00
map_deinit(&font->kerning);
2017-02-05 06:21:41 +00:00
vec_deinit(&font->vertices);
2017-02-03 23:16:30 +00:00
free(font);
}
void lovrFontPrint(Font* font, const char* str) {
2017-02-05 06:21:41 +00:00
FontAtlas* atlas = &font->atlas;
2017-02-03 23:16:30 +00:00
float x = 0;
2017-02-10 00:30:24 +00:00
float y = -lovrFontGetHeight(font);
2017-02-06 04:30:17 +00:00
float u = atlas->width;
float v = atlas->height;
2017-02-03 23:16:30 +00:00
2017-02-06 09:54:11 +00:00
int length = strlen(str);
const char* end = str + length;
size_t bytes;
2017-02-08 06:11:20 +00:00
unsigned int previous = '\0';
2017-02-06 09:54:11 +00:00
unsigned int codepoint;
vec_reserve(&font->vertices, length * 30);
vec_clear(&font->vertices);
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
2017-02-08 06:11:20 +00:00
// Newlines
2017-02-06 09:54:11 +00:00
if (codepoint == '\n') {
2017-02-06 04:30:17 +00:00
x = 0;
y -= font->fontData->size;
2017-02-08 06:11:20 +00:00
previous = '\0';
2017-02-06 09:54:11 +00:00
str += bytes;
2017-02-06 04:30:17 +00:00
continue;
}
2017-02-08 06:11:20 +00:00
// Kerning
x += lovrFontGetKerning(font, previous, codepoint);
previous = codepoint;
2017-02-05 06:21:41 +00:00
2017-02-08 06:11:20 +00:00
// Glyph geometry
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
2017-02-06 04:30:17 +00:00
int gx = glyph->x;
int gy = glyph->y;
int gw = glyph->w;
int gh = glyph->h;
2017-02-08 06:11:20 +00:00
int dx = glyph->dx;
int dy = glyph->dy;
2017-02-06 04:30:17 +00:00
2017-02-08 06:11:20 +00:00
// Triangles
2017-02-06 04:30:17 +00:00
if (gw > 0 && gh > 0) {
2017-02-08 06:11:20 +00:00
float x1 = x + dx;
float y1 = y + dy;
2017-02-06 04:30:17 +00:00
float x2 = x1 + gw;
float y2 = y1 - gh;
float s1 = gx / u;
float t1 = gy / v;
float s2 = (gx + gw) / u;
float t2 = (gy + gh) / v;
2017-02-05 06:21:41 +00:00
float v[30] = {
2017-02-06 04:30:17 +00:00
x1, y1, 0, s1, t1,
x1, y2, 0, s1, t2,
x2, y1, 0, s2, t1,
x2, y1, 0, s2, t1,
x1, y2, 0, s1, t2,
x2, y2, 0, s2, t2
2017-02-05 06:21:41 +00:00
};
vec_pusharr(&font->vertices, v, 30);
}
2017-02-08 06:11:20 +00:00
// Advance cursor
2017-02-05 06:21:41 +00:00
x += glyph->advance;
2017-02-06 09:54:11 +00:00
str += bytes;
2017-02-03 23:16:30 +00:00
}
// Set depth test to LEQUAL to prevent blending issues with glyphs, not great
CompareMode oldCompareMode = lovrGraphicsGetDepthTest();
lovrGraphicsSetDepthTest(COMPARE_LEQUAL);
2017-02-05 06:21:41 +00:00
lovrGraphicsSetShapeData(font->vertices.data, font->vertices.length);
2017-02-03 23:16:30 +00:00
lovrGraphicsDrawPrimitive(GL_TRIANGLES, font->texture, 0, 1, 0);
lovrGraphicsSetDepthTest(oldCompareMode);
2017-02-03 23:16:30 +00:00
}
2017-02-10 00:30:24 +00:00
int lovrFontGetWidth(Font* font, const char* str) {
float width = 0;
float x = 0;
const char* end = str + strlen(str);
size_t bytes;
unsigned int previous = '\0';
unsigned int codepoint;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
// Newlines
if (codepoint == '\n') {
width = MAX(width, x);
x = 0;
previous = '\0';
str += bytes;
continue;
}
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
x += glyph->advance + lovrFontGetKerning(font, previous, codepoint);
previous = codepoint;
str += bytes;
}
return MAX(x, width);
}
2017-02-06 05:05:09 +00:00
int lovrFontGetHeight(Font* font) {
return font->fontData->height;
}
int lovrFontGetAscent(Font* font) {
return font->fontData->ascent;
}
int lovrFontGetDescent(Font* font) {
return font->fontData->descent;
}
2017-02-10 00:35:19 +00:00
int lovrFontGetBaseline(Font* font) {
return font->fontData->height / 1.25f;
}
2017-02-08 06:11:20 +00:00
int lovrFontGetKerning(Font* font, unsigned int left, unsigned int right) {
char key[12];
snprintf(key, 12, "%d,%d", left, right);
int* entry = map_get(&font->kerning, key);
if (entry) {
return *entry;
}
int kerning = lovrFontDataGetKerning(font->fontData, left, right);
map_set(&font->kerning, key, kerning);
return kerning;
}
2017-02-06 09:54:11 +00:00
Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) {
char key[6];
snprintf(key, 6, "%d", codepoint);
2017-02-05 06:21:41 +00:00
FontAtlas* atlas = &font->atlas;
vec_glyph_t* glyphs = &atlas->glyphs;
Glyph* glyph = map_get(glyphs, key);
2017-02-03 23:16:30 +00:00
2017-02-06 04:30:17 +00:00
// Add the glyph to the atlas if it isn't there
2017-02-05 06:21:41 +00:00
if (!glyph) {
Glyph g;
2017-02-06 09:54:11 +00:00
lovrFontDataLoadGlyph(font->fontData, codepoint, &g);
2017-02-05 06:21:41 +00:00
map_set(glyphs, key, g);
glyph = map_get(glyphs, key);
2017-02-06 04:30:17 +00:00
lovrFontAddGlyph(font, glyph);
2017-02-05 06:21:41 +00:00
}
2017-02-03 23:16:30 +00:00
2017-02-05 06:21:41 +00:00
return glyph;
}
2017-02-03 23:16:30 +00:00
2017-02-06 04:30:17 +00:00
void lovrFontAddGlyph(Font* font, Glyph* glyph) {
FontAtlas* atlas = &font->atlas;
// Don't waste space on empty glyphs
if (glyph->w == 0 && glyph->h == 0) {
return;
2017-02-05 06:21:41 +00:00
}
2017-02-03 23:16:30 +00:00
2017-02-06 04:30:17 +00:00
// If the glyph does not fit, you must acquit (new row)
if (atlas->x + glyph->w > atlas->width - 2 * atlas->padding) {
atlas->x = atlas->padding;
atlas->y += atlas->rowHeight + atlas->padding;
atlas->rowHeight = 0;
}
// Expand the texture if needed. Expanding the texture re-adds all the glyphs, so we can return.
if (atlas->y + glyph->h > atlas->height - 2 * atlas->padding) {
if (atlas->width == atlas->height) {
atlas->width <<= 1;
} else {
atlas->height <<= 1;
}
lovrFontExpandTexture(font);
return;
}
// Keep track of glyph's position in the atlas
glyph->x = atlas->x;
glyph->y = atlas->y;
// Paste glyph into texture
lovrGraphicsBindTexture(font->texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, atlas->x, atlas->y, glyph->w, glyph->h, GL_RG, GL_UNSIGNED_BYTE, glyph->data);
// Advance atlas cursor
atlas->x += glyph->w + atlas->padding;
atlas->rowHeight = MAX(atlas->rowHeight, glyph->h);
}
void lovrFontExpandTexture(Font* font) {
2017-02-05 06:21:41 +00:00
FontAtlas* atlas = &font->atlas;
2017-02-06 04:30:17 +00:00
// Resize the texture storage
lovrTextureDataResize(font->texture->textureData, atlas->width, atlas->height, 0x0);
lovrTextureRefresh(font->texture);
// Reset the cursor
atlas->x = atlas->padding;
atlas->y = atlas->padding;
atlas->rowHeight = 0;
// Re-pack all the glyphs
const char* key;
map_iter_t iter = map_iter(&atlas->glyphs);
while ((key = map_next(&atlas->glyphs, &iter)) != NULL) {
Glyph* glyph = map_get(&atlas->glyphs, key);
lovrFontAddGlyph(font, glyph);
}
2017-02-03 23:16:30 +00:00
}