lovr/src/modules/data/rasterizer.c

242 lines
7.1 KiB
C
Raw Normal View History

2018-01-22 16:28:33 +00:00
#include "data/rasterizer.h"
2019-04-05 11:27:48 +00:00
#include "data/blob.h"
#include "data/image.h"
2018-12-12 07:49:48 +00:00
#include "resources/VarelaRound.ttf.h"
2022-03-22 07:13:21 +00:00
#include "util.h"
2018-07-05 08:55:09 +00:00
#include "lib/stb/stb_truetype.h"
2019-06-10 10:39:53 +00:00
#include <msdfgen-c.h>
2019-04-05 11:27:48 +00:00
#include <stdlib.h>
#include <string.h>
2018-07-05 08:55:09 +00:00
#include <math.h>
2017-07-14 15:18:25 +00:00
2021-02-08 17:58:39 +00:00
struct Rasterizer {
2021-02-11 23:37:55 +00:00
uint32_t ref;
2021-02-08 17:58:39 +00:00
stbtt_fontinfo font;
struct Blob* blob;
float size;
float scale;
int glyphCount;
int height;
int advance;
int ascent;
int descent;
};
Rasterizer* lovrRasterizerCreate(Blob* blob, float size) {
Rasterizer* rasterizer = calloc(1, sizeof(Rasterizer));
lovrAssert(rasterizer, "Out of memory");
rasterizer->ref = 1;
2018-07-05 08:55:09 +00:00
stbtt_fontinfo* font = &rasterizer->font;
const unsigned char* data = blob ? blob->data : src_resources_VarelaRound_ttf;
2018-07-05 08:55:09 +00:00
if (!stbtt_InitFont(font, data, stbtt_GetFontOffsetForIndex(data, 0))) {
lovrThrow("Problem loading font");
2017-04-02 12:55:21 +00:00
}
2018-07-29 08:26:12 +00:00
lovrRetain(blob);
2018-01-22 16:28:33 +00:00
rasterizer->blob = blob;
rasterizer->size = size;
2018-07-05 08:55:09 +00:00
rasterizer->scale = stbtt_ScaleForMappingEmToPixels(font, size);
rasterizer->glyphCount = font->numGlyphs;
2017-02-06 05:05:09 +00:00
2018-07-05 08:55:09 +00:00
int ascent, descent, linegap;
stbtt_GetFontVMetrics(font, &ascent, &descent, &linegap);
rasterizer->ascent = roundf(ascent * rasterizer->scale);
rasterizer->descent = roundf(descent * rasterizer->scale);
rasterizer->height = roundf((ascent - descent + linegap) * rasterizer->scale);
int x0, y0, x1, y1;
stbtt_GetFontBoundingBox(font, &x0, &y0, &x1, &y1);
rasterizer->advance = roundf(x1 * rasterizer->scale);
2017-02-06 05:05:09 +00:00
2018-01-22 16:28:33 +00:00
return rasterizer;
2017-01-30 02:37:56 +00:00
}
2018-02-26 08:59:03 +00:00
void lovrRasterizerDestroy(void* ref) {
Rasterizer* rasterizer = ref;
2021-02-09 00:52:26 +00:00
lovrRelease(rasterizer->blob, lovrBlobDestroy);
free(rasterizer);
2017-02-06 04:30:17 +00:00
}
2021-02-08 17:58:39 +00:00
float lovrRasterizerGetSize(Rasterizer* rasterizer) {
return rasterizer->size;
}
int lovrRasterizerGetGlyphCount(Rasterizer* rasterizer) {
return rasterizer->glyphCount;
}
int lovrRasterizerGetHeight(Rasterizer* rasterizer) {
return rasterizer->height;
}
int lovrRasterizerGetAdvance(Rasterizer* rasterizer) {
return rasterizer->advance;
}
int lovrRasterizerGetAscent(Rasterizer* rasterizer) {
return rasterizer->ascent;
}
int lovrRasterizerGetDescent(Rasterizer* rasterizer) {
return rasterizer->descent;
}
2018-01-23 02:49:45 +00:00
bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t character) {
2018-07-05 08:55:09 +00:00
return stbtt_FindGlyphIndex(&rasterizer->font, character) != 0;
2018-01-23 02:49:45 +00:00
}
bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str) {
2019-03-17 07:58:01 +00:00
const char* end = str + strlen(str);
2018-01-23 02:49:45 +00:00
unsigned int codepoint;
size_t bytes;
bool hasGlyphs = true;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
hasGlyphs &= lovrRasterizerHasGlyph(rasterizer, codepoint);
str += bytes;
}
return hasGlyphs;
}
void lovrRasterizerLoadGlyph(Rasterizer* rasterizer, uint32_t character, uint32_t padding, double spread, Glyph* glyph) {
2018-07-05 08:55:09 +00:00
int glyphIndex = stbtt_FindGlyphIndex(&rasterizer->font, character);
lovrAssert(glyphIndex, "No font glyph found for character code %d, try using Rasterizer:hasGlyphs", character);
2017-01-30 02:37:56 +00:00
int advance, bearing;
stbtt_GetGlyphHMetrics(&rasterizer->font, glyphIndex, &advance, &bearing);
if (stbtt_IsGlyphEmpty(&rasterizer->font, glyphIndex)) {
memset(glyph, 0, sizeof(Glyph));
glyph->advance = roundf(advance * rasterizer->scale);
return;
}
2018-07-05 08:55:09 +00:00
// Trace glyph outline
stbtt_vertex* vertices;
int vertexCount = stbtt_GetGlyphShape(&rasterizer->font, glyphIndex, &vertices);
2017-07-14 15:18:25 +00:00
msShape* shape = msShapeCreate();
2018-07-05 08:55:09 +00:00
msContour* contour = NULL;
2019-04-21 00:02:48 +00:00
float x = 0.f;
float y = 0.f;
2018-07-05 08:55:09 +00:00
for (int i = 0; i < vertexCount; i++) {
stbtt_vertex vertex = vertices[i];
float x2 = vertex.x * rasterizer->scale;
float y2 = vertex.y * rasterizer->scale;
switch (vertex.type) {
case STBTT_vmove:
contour = msShapeAddContour(shape);
break;
case STBTT_vline:
msContourAddLinearEdge(contour, x, y, x2, y2);
break;
case STBTT_vcurve: {
float cx = vertex.cx * rasterizer->scale;
float cy = vertex.cy * rasterizer->scale;
msContourAddQuadraticEdge(contour, x, y, cx, cy, x2, y2);
break;
}
case STBTT_vcubic: {
float cx1 = vertex.cx * rasterizer->scale;
float cy1 = vertex.cy * rasterizer->scale;
float cx2 = vertex.cx1 * rasterizer->scale;
float cy2 = vertex.cy1 * rasterizer->scale;
msContourAddCubicEdge(contour, x, y, cx1, cy1, cx2, cy2, x2, y2);
break;
}
}
x = x2;
y = y2;
}
2019-12-20 12:13:38 +00:00
stbtt_FreeShape(&rasterizer->font, vertices);
2018-07-05 08:55:09 +00:00
int x0, y0, x1, y1;
stbtt_GetGlyphBox(&rasterizer->font, glyphIndex, &x0, &y0, &x1, &y1);
2017-02-03 23:16:30 +00:00
2018-07-05 08:55:09 +00:00
// Initialize glyph data
2017-02-05 06:21:41 +00:00
glyph->x = 0;
glyph->y = 0;
glyph->w = ceilf((x1 - x0) * rasterizer->scale);
glyph->h = ceilf((y1 - y0) * rasterizer->scale);
glyph->tw = glyph->w + 2 * padding;
glyph->th = glyph->h + 2 * padding;
glyph->dx = roundf(bearing * rasterizer->scale);
glyph->dy = roundf(y1 * rasterizer->scale);
2018-07-05 08:55:09 +00:00
glyph->advance = roundf(advance * rasterizer->scale);
glyph->data = lovrImageCreate(glyph->tw, glyph->th, NULL, 0, FORMAT_RGBA32F);
2017-02-03 23:16:30 +00:00
2017-07-14 15:18:25 +00:00
// Render SDF
float tx = (float) padding + -glyph->dx;
float ty = (float) padding + (float) glyph->h - glyph->dy;
2017-07-14 15:18:25 +00:00
msShapeNormalize(shape);
2019-04-21 00:02:48 +00:00
msEdgeColoringSimple(shape, 3., 0);
msGenerateMTSDF(glyph->data->blob->data, glyph->tw, glyph->th, shape, spread, 1.f, 1.f, tx, ty);
2017-07-14 15:18:25 +00:00
msShapeDestroy(shape);
2017-01-30 02:37:56 +00:00
}
2017-02-08 06:11:20 +00:00
2019-05-21 03:35:07 +00:00
int32_t lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t left, uint32_t right) {
2018-07-05 08:55:09 +00:00
return stbtt_GetCodepointKernAdvance(&rasterizer->font, left, right) * rasterizer->scale;
2017-02-08 06:11:20 +00:00
}
2021-10-11 22:31:43 +00:00
2021-12-20 15:12:39 +00:00
void lovrRasterizerMeasure(Rasterizer* rasterizer, const char* str, size_t length, float wrap, float* width, float* lastLineWidth, float* height, uint32_t* lineCount, uint32_t* glyphCount) {
2021-10-11 22:31:43 +00:00
float x = 0.f;
const char* end = str + length;
size_t bytes;
unsigned int previous = '\0';
unsigned int codepoint;
*width = 0.f;
2021-12-20 15:12:39 +00:00
*lastLineWidth = 0.f;
2021-10-11 22:31:43 +00:00
*lineCount = 0;
*glyphCount = 0;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
if (codepoint == '\n' || (wrap && x > wrap && (codepoint == ' ' || previous == ' '))) {
2021-10-11 22:31:43 +00:00
*width = MAX(*width, x);
(*lineCount)++;
x = 0.f;
previous = '\0';
if (codepoint == ' ' || codepoint == '\n') {
str += bytes;
continue;
}
2021-10-11 22:31:43 +00:00
}
// Tabs
if (codepoint == '\t') {
int glyphIndex = stbtt_FindGlyphIndex(&rasterizer->font, ' ');
int advance, bearing;
stbtt_GetGlyphHMetrics(&rasterizer->font, glyphIndex, &advance, &bearing);
x += advance * 4.f;
str += bytes;
continue;
}
int glyphIndex = stbtt_FindGlyphIndex(&rasterizer->font, codepoint);
int advance, bearing;
stbtt_GetGlyphHMetrics(&rasterizer->font, glyphIndex, &advance, &bearing);
int x0, y0, x1, y1;
if (stbtt_GetGlyphBox(&rasterizer->font, glyphIndex, &x0, &y0, &x1, &y1)) {
float w = ceilf((x1 - x0) * rasterizer->scale);
float h = ceilf((y1 - y0) * rasterizer->scale);
if (w > 0 && h > 0) {
(*glyphCount)++;
}
2021-10-11 22:31:43 +00:00
}
x += roundf(advance * rasterizer->scale) + lovrRasterizerGetKerning(rasterizer, previous, codepoint);
previous = codepoint;
str += bytes;
}
*width = MAX(*width, x);
2021-12-20 15:12:39 +00:00
*lastLineWidth = x;
2021-10-11 22:31:43 +00:00
*height = ((*lineCount + 1) * lovrRasterizerGetHeight(rasterizer));
}