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)) {
|
2017-08-10 08:05:04 +00:00
|
|
|
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);
|
2017-07-14 14:57:45 +00:00
|
|
|
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);
|
2017-08-10 08:05:04 +00:00
|
|
|
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);
|
2017-07-14 14:57:45 +00:00
|
|
|
}
|
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);
|
2017-08-10 08:05:04 +00:00
|
|
|
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;
|
|
|
|
}
|