lovr/src/data/rasterizer.c

172 lines
5.0 KiB
C
Raw Normal View History

2018-01-22 16:28:33 +00:00
#include "data/rasterizer.h"
2017-12-10 20:29:29 +00:00
#include "resources/Cabin.ttf.h"
2017-01-30 02:37:56 +00:00
#include "util.h"
2017-07-14 15:18:25 +00:00
#include "msdfgen-c.h"
2017-01-30 02:37:56 +00:00
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
2017-07-14 15:18:25 +00:00
#include FT_OUTLINE_H
2017-01-30 02:37:56 +00:00
static FT_Library ft = NULL;
2017-07-14 15:18:25 +00:00
typedef struct {
float x;
float y;
msShape* shape;
msContour* contour;
} ftContext;
static int ftMoveTo(const FT_Vector* to, void* userdata) {
ftContext* context = userdata;
context->contour = msShapeAddContour(context->shape);
context->x = to->x / 64.;
context->y = to->y / 64.;
return 0;
}
static int ftLineTo(const FT_Vector* to, void* userdata) {
ftContext* context = userdata;
float x = to->x / 64.;
float y = to->y / 64.;
msContourAddLinearEdge(context->contour, context->x, context->y, x, y);
context->x = x;
context->y = y;
return 0;
}
static int ftConicTo(const FT_Vector* control, const FT_Vector* to, void* userdata) {
ftContext* context = userdata;
float cx = control->x / 64.;
float cy = control->y / 64.;
float x = to->x / 64.;
float y = to->y / 64.;
msContourAddQuadraticEdge(context->contour, context->x, context->y, cx, cy, x, y);
context->x = x;
context->y = y;
return 0;
}
static int ftCubicTo(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* userdata) {
ftContext* context = userdata;
float c1x = control1->x / 64.;
float c1y = control1->y / 64.;
float c2x = control2->x / 64.;
float c2y = control2->y / 64.;
float x = to->x / 64.;
float y = to->y / 64.;
msContourAddCubicEdge(context->contour, context->x, context->y, c1x, c1y, c2x, c2y, x, y);
context->x = x;
context->y = y;
return 0;
}
2018-01-22 16:28:33 +00:00
Rasterizer* lovrRasterizerCreate(Blob* blob, int size) {
2017-01-30 02:37:56 +00:00
if (!ft && FT_Init_FreeType(&ft)) {
lovrThrow("Error initializing FreeType");
2017-01-30 02:37:56 +00:00
}
2017-02-06 05:05:09 +00:00
FT_Face face = NULL;
2017-02-06 04:30:17 +00:00
FT_Error err = FT_Err_Ok;
2017-04-02 12:55:21 +00:00
if (blob) {
err = err || FT_New_Memory_Face(ft, blob->data, blob->size, 0, &face);
lovrRetain(&blob->ref);
2017-04-02 12:55:21 +00:00
} else {
err = err || FT_New_Memory_Face(ft, Cabin_ttf, Cabin_ttf_len, 0, &face);
}
err = err || FT_Set_Pixel_Sizes(face, 0, size);
lovrAssert(!err, "Problem loading font");
2017-01-30 02:37:56 +00:00
2018-01-22 16:28:33 +00:00
Rasterizer* rasterizer = lovrAlloc(sizeof(Rasterizer), lovrRasterizerDestroy);
rasterizer->ftHandle = face;
rasterizer->blob = blob;
rasterizer->size = size;
2018-01-23 02:49:45 +00:00
rasterizer->glyphCount = face->num_glyphs;
2017-02-06 05:05:09 +00:00
FT_Size_Metrics metrics = face->size->metrics;
2018-01-22 16:28:33 +00:00
rasterizer->height = metrics.height >> 6;
2018-01-23 02:49:45 +00:00
rasterizer->advance = metrics.max_advance >> 6;
2018-01-22 16:28:33 +00:00
rasterizer->ascent = metrics.ascender >> 6;
rasterizer->descent = metrics.descender >> 6;
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-01-22 16:28:33 +00:00
void lovrRasterizerDestroy(const Ref* ref) {
2018-02-26 07:19:39 +00:00
Rasterizer* rasterizer = (Rasterizer*) ref;
2018-01-22 16:28:33 +00:00
FT_Done_Face(rasterizer->ftHandle);
if (rasterizer->blob) {
lovrRelease(&rasterizer->blob->ref);
}
2018-01-22 16:28:33 +00:00
free(rasterizer);
2017-02-06 04:30:17 +00:00
}
2018-01-23 02:49:45 +00:00
bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t character) {
FT_Face face = rasterizer->ftHandle;
return FT_Get_Char_Index(face, character) != 0;
}
bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str) {
int len = strlen(str);
const char* end = str + len;
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;
}
2018-01-22 16:28:33 +00:00
void lovrRasterizerLoadGlyph(Rasterizer* rasterizer, uint32_t character, Glyph* glyph) {
FT_Face face = rasterizer->ftHandle;
2017-02-03 23:16:30 +00:00
FT_Error err = FT_Err_Ok;
2017-02-06 04:30:17 +00:00
FT_Glyph_Metrics* metrics;
2017-07-14 15:18:25 +00:00
FT_Outline_Funcs callbacks = {
.move_to = ftMoveTo,
.line_to = ftLineTo,
.conic_to = ftConicTo,
.cubic_to = ftCubicTo
};
2017-01-30 02:37:56 +00:00
2017-07-14 15:18:25 +00:00
msShape* shape = msShapeCreate();
ftContext context = { .x = 0, .y = 0, .shape = shape, .contour = NULL };
2017-02-03 23:16:30 +00:00
2017-07-14 15:18:25 +00:00
err = err || FT_Load_Glyph(face, FT_Get_Char_Index(face, character), FT_LOAD_DEFAULT);
err = err || FT_Outline_Decompose(&face->glyph->outline, &callbacks, &context);
lovrAssert(!err, "Error loading glyph");
2017-02-03 23:16:30 +00:00
2017-02-06 04:30:17 +00:00
metrics = &face->glyph->metrics;
2017-02-03 23:16:30 +00:00
2017-02-06 04:30:17 +00:00
// Initialize glyph
2017-02-05 06:21:41 +00:00
glyph->x = 0;
glyph->y = 0;
glyph->w = metrics->width >> 6;
glyph->h = metrics->height >> 6;
2017-07-14 16:53:05 +00:00
glyph->tw = glyph->w + 2 * GLYPH_PADDING;
glyph->th = glyph->h + 2 * GLYPH_PADDING;
2017-02-06 04:30:17 +00:00
glyph->dx = metrics->horiBearingX >> 6;
glyph->dy = metrics->horiBearingY >> 6;
2017-02-05 06:21:41 +00:00
glyph->advance = metrics->horiAdvance >> 6;
2017-07-14 16:53:05 +00:00
glyph->data = malloc(glyph->tw * glyph->th * 3 * sizeof(uint8_t));
2017-02-03 23:16:30 +00:00
2017-07-14 15:18:25 +00:00
// Render SDF
2017-07-14 16:53:05 +00:00
float tx = GLYPH_PADDING + -glyph->dx;
float ty = GLYPH_PADDING + glyph->h - glyph->dy;
2017-07-14 15:18:25 +00:00
msShapeNormalize(shape);
msEdgeColoringSimple(shape, 3.0, 0);
2017-07-14 16:53:05 +00:00
msGenerateMSDF(glyph->data, glyph->tw, glyph->th, shape, 4., 1, 1, 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
2018-01-22 16:28:33 +00:00
int lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t left, uint32_t right) {
FT_Face face = rasterizer->ftHandle;
2017-02-08 06:11:20 +00:00
FT_Vector kerning;
left = FT_Get_Char_Index(face, left);
right = FT_Get_Char_Index(face, right);
FT_Get_Kerning(face, left, right, FT_KERNING_DEFAULT, &kerning);
return kerning.x >> 6;
}