First Commit
This commit is contained in:
414
3rdparty/plutosvg/plutovg/source/plutovg-font.c
vendored
Normal file
414
3rdparty/plutosvg/plutovg/source/plutovg-font.c
vendored
Normal file
@@ -0,0 +1,414 @@
|
||||
#include "plutovg.h"
|
||||
#include "plutovg-utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define STBTT_STATIC
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include "plutovg-stb-truetype.h"
|
||||
|
||||
static int plutovg_text_iterator_length(const void* data, int length, plutovg_text_encoding_t encoding)
|
||||
{
|
||||
if(length != -1)
|
||||
return length;
|
||||
length = 0;
|
||||
switch(encoding) {
|
||||
case PLUTOVG_TEXT_ENCODING_UTF8:
|
||||
case PLUTOVG_TEXT_ENCODING_LATIN1: {
|
||||
const uint8_t* text = data;
|
||||
while(*text++)
|
||||
length++;
|
||||
break;
|
||||
} case PLUTOVG_TEXT_ENCODING_UTF16: {
|
||||
const uint16_t* text = data;
|
||||
while(*text++)
|
||||
length++;
|
||||
break;
|
||||
} case PLUTOVG_TEXT_ENCODING_UTF32: {
|
||||
const uint32_t* text = data;
|
||||
while(*text++)
|
||||
length++;
|
||||
break;
|
||||
} default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void plutovg_text_iterator_init(plutovg_text_iterator_t* it, const void* text, int length, plutovg_text_encoding_t encoding)
|
||||
{
|
||||
it->text = text;
|
||||
it->length = plutovg_text_iterator_length(text, length, encoding);
|
||||
it->encoding = encoding;
|
||||
it->index = 0;
|
||||
}
|
||||
|
||||
bool plutovg_text_iterator_has_next(const plutovg_text_iterator_t* it)
|
||||
{
|
||||
return it->index < it->length;
|
||||
}
|
||||
|
||||
plutovg_codepoint_t plutovg_text_iterator_next(plutovg_text_iterator_t* it)
|
||||
{
|
||||
plutovg_codepoint_t codepoint = 0;
|
||||
switch(it->encoding) {
|
||||
case PLUTOVG_TEXT_ENCODING_UTF8: {
|
||||
static const uint8_t trailing[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
|
||||
};
|
||||
|
||||
static const uint32_t offsets[6] = {
|
||||
0x00000000, 0x00003080, 0x000E2080, 0x03C82080, 0xFA082080, 0x82082080
|
||||
};
|
||||
|
||||
const uint8_t* text = it->text;
|
||||
int trailing_bytes = trailing[text[it->index]];
|
||||
if(it->index + trailing_bytes >= it->length)
|
||||
trailing_bytes = 0;
|
||||
switch(trailing_bytes) {
|
||||
case 5: codepoint += text[it->index++]; codepoint <<= 6;
|
||||
case 4: codepoint += text[it->index++]; codepoint <<= 6;
|
||||
case 3: codepoint += text[it->index++]; codepoint <<= 6;
|
||||
case 2: codepoint += text[it->index++]; codepoint <<= 6;
|
||||
case 1: codepoint += text[it->index++]; codepoint <<= 6;
|
||||
case 0: codepoint += text[it->index++];
|
||||
}
|
||||
|
||||
codepoint -= offsets[trailing_bytes];
|
||||
break;
|
||||
} case PLUTOVG_TEXT_ENCODING_UTF16: {
|
||||
const uint16_t* text = it->text;
|
||||
codepoint = text[it->index++];
|
||||
if(((codepoint) & 0xfffffc00) == 0xd800) {
|
||||
if(it->index < it->length && (((codepoint) & 0xfffffc00) == 0xdc00)) {
|
||||
uint16_t trail = text[it->index++];
|
||||
codepoint = (codepoint << 10) + trail - ((0xD800u << 10) - 0x10000u + 0xDC00u);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
} case PLUTOVG_TEXT_ENCODING_UTF32: {
|
||||
const uint32_t* text = it->text;
|
||||
codepoint = text[it->index++];
|
||||
break;
|
||||
} case PLUTOVG_TEXT_ENCODING_LATIN1: {
|
||||
const uint8_t* text = it->text;
|
||||
codepoint = text[it->index++];
|
||||
break;
|
||||
} default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
stbtt_vertex* vertices;
|
||||
int nvertices;
|
||||
int index;
|
||||
int advance_width;
|
||||
int left_side_bearing;
|
||||
int x1;
|
||||
int y1;
|
||||
int x2;
|
||||
int y2;
|
||||
} glyph_t;
|
||||
|
||||
#define GLYPH_CACHE_SIZE 256
|
||||
struct plutovg_font_face {
|
||||
int ref_count;
|
||||
int ascent;
|
||||
int descent;
|
||||
int line_gap;
|
||||
int x1;
|
||||
int y1;
|
||||
int x2;
|
||||
int y2;
|
||||
stbtt_fontinfo info;
|
||||
glyph_t** glyphs[GLYPH_CACHE_SIZE];
|
||||
plutovg_destroy_func_t destroy_func;
|
||||
void* closure;
|
||||
};
|
||||
|
||||
plutovg_font_face_t* plutovg_font_face_load_from_file(const char* filename, int ttcindex)
|
||||
{
|
||||
FILE* fp = fopen(filename, "rb");
|
||||
if(fp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long length = ftell(fp);
|
||||
if(length == -1L) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* data = malloc(length);
|
||||
if(data == NULL) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
size_t nread = fread(data, 1, length, fp);
|
||||
fclose(fp);
|
||||
|
||||
if(nread != length) {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return plutovg_font_face_load_from_data(data, length, ttcindex, free, data);
|
||||
}
|
||||
|
||||
plutovg_font_face_t* plutovg_font_face_load_from_data(const void* data, unsigned int length, int ttcindex, plutovg_destroy_func_t destroy_func, void* closure)
|
||||
{
|
||||
stbtt_fontinfo info;
|
||||
int offset = stbtt_GetFontOffsetForIndex(data, ttcindex);
|
||||
if(offset == -1 || !stbtt_InitFont(&info, data, offset)) {
|
||||
if(destroy_func)
|
||||
destroy_func(closure);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
plutovg_font_face_t* face = malloc(sizeof(plutovg_font_face_t));
|
||||
face->ref_count = 1;
|
||||
face->info = info;
|
||||
stbtt_GetFontVMetrics(&face->info, &face->ascent, &face->descent, &face->line_gap);
|
||||
stbtt_GetFontBoundingBox(&face->info, &face->x1, &face->y1, &face->x2, &face->y2);
|
||||
memset(face->glyphs, 0, sizeof(face->glyphs));
|
||||
face->destroy_func = destroy_func;
|
||||
face->closure = closure;
|
||||
return face;
|
||||
}
|
||||
|
||||
plutovg_font_face_t* plutovg_font_face_reference(plutovg_font_face_t* face)
|
||||
{
|
||||
if(face == NULL)
|
||||
return NULL;
|
||||
++face->ref_count;
|
||||
return face;
|
||||
}
|
||||
|
||||
void plutovg_font_face_destroy(plutovg_font_face_t* face)
|
||||
{
|
||||
if(face == NULL)
|
||||
return;
|
||||
if(--face->ref_count == 0) {
|
||||
for(int i = 0; i < GLYPH_CACHE_SIZE; i++) {
|
||||
if(face->glyphs[i] == NULL)
|
||||
continue;
|
||||
for(int j = 0; j < GLYPH_CACHE_SIZE; j++) {
|
||||
glyph_t* glyph = face->glyphs[i][j];
|
||||
if(glyph == NULL)
|
||||
continue;
|
||||
stbtt_FreeShape(&face->info, glyph->vertices);
|
||||
free(glyph);
|
||||
}
|
||||
|
||||
free(face->glyphs[i]);
|
||||
}
|
||||
|
||||
if(face->destroy_func)
|
||||
face->destroy_func(face->closure);
|
||||
free(face);
|
||||
}
|
||||
}
|
||||
|
||||
int plutovg_font_face_get_reference_count(const plutovg_font_face_t* face)
|
||||
{
|
||||
if(face)
|
||||
return face->ref_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static float plutovg_font_face_get_scale(const plutovg_font_face_t* face, float size)
|
||||
{
|
||||
return stbtt_ScaleForMappingEmToPixels(&face->info, size);
|
||||
}
|
||||
|
||||
void plutovg_font_face_get_metrics(const plutovg_font_face_t* face, float size, float* ascent, float* descent, float* line_gap, plutovg_rect_t* extents)
|
||||
{
|
||||
float scale = plutovg_font_face_get_scale(face, size);
|
||||
if(ascent) *ascent = face->ascent * scale;
|
||||
if(descent) *descent = face->descent * scale;
|
||||
if(line_gap) *line_gap = face->line_gap * scale;
|
||||
if(extents) {
|
||||
extents->x = face->x1 * scale;
|
||||
extents->y = face->y2 * -scale;
|
||||
extents->w = (face->x2 - face->x1) * scale;
|
||||
extents->h = (face->y1 - face->y2) * -scale;
|
||||
}
|
||||
}
|
||||
|
||||
static glyph_t* plutovg_font_face_get_glyph(plutovg_font_face_t* face, plutovg_codepoint_t codepoint)
|
||||
{
|
||||
unsigned int msb = (codepoint >> 8) & 0xFF;
|
||||
if(face->glyphs[msb] == NULL) {
|
||||
face->glyphs[msb] = calloc(GLYPH_CACHE_SIZE, sizeof(glyph_t*));
|
||||
}
|
||||
|
||||
unsigned int lsb = codepoint & 0xFF;
|
||||
if(face->glyphs[msb][lsb] == NULL) {
|
||||
glyph_t* glyph = malloc(sizeof(glyph_t));
|
||||
glyph->index = stbtt_FindGlyphIndex(&face->info, codepoint);
|
||||
glyph->nvertices = stbtt_GetGlyphShape(&face->info, glyph->index, &glyph->vertices);
|
||||
stbtt_GetGlyphHMetrics(&face->info, glyph->index, &glyph->advance_width, &glyph->left_side_bearing);
|
||||
if(!stbtt_GetGlyphBox(&face->info, glyph->index, &glyph->x1, &glyph->y1, &glyph->x2, &glyph->y2))
|
||||
glyph->x1 = glyph->y1 = glyph->x2 = glyph->y2 = 0;
|
||||
face->glyphs[msb][lsb] = glyph;
|
||||
}
|
||||
|
||||
return face->glyphs[msb][lsb];
|
||||
}
|
||||
|
||||
void plutovg_font_face_get_glyph_metrics(plutovg_font_face_t* face, float size, plutovg_codepoint_t codepoint, float* advance_width, float* left_side_bearing, plutovg_rect_t* extents)
|
||||
{
|
||||
float scale = plutovg_font_face_get_scale(face, size);
|
||||
glyph_t* glyph = plutovg_font_face_get_glyph(face, codepoint);
|
||||
if(advance_width) *advance_width = glyph->advance_width * scale;
|
||||
if(left_side_bearing) *left_side_bearing = glyph->left_side_bearing * scale;
|
||||
if(extents) {
|
||||
extents->x = glyph->x1 * scale;
|
||||
extents->y = glyph->y2 * -scale;
|
||||
extents->w = (glyph->x2 - glyph->x1) * scale;
|
||||
extents->h = (glyph->y1 - glyph->y2) * -scale;
|
||||
}
|
||||
}
|
||||
|
||||
static void glyph_traverse_func(void* closure, plutovg_path_command_t command, const plutovg_point_t* points, int npoints)
|
||||
{
|
||||
plutovg_path_t* path = (plutovg_path_t*)(closure);
|
||||
switch(command) {
|
||||
case PLUTOVG_PATH_COMMAND_MOVE_TO:
|
||||
plutovg_path_move_to(path, points[0].x, points[0].y);
|
||||
break;
|
||||
case PLUTOVG_PATH_COMMAND_LINE_TO:
|
||||
plutovg_path_line_to(path, points[0].x, points[0].y);
|
||||
break;
|
||||
case PLUTOVG_PATH_COMMAND_CUBIC_TO:
|
||||
plutovg_path_cubic_to(path, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
|
||||
break;
|
||||
case PLUTOVG_PATH_COMMAND_CLOSE:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
float plutovg_font_face_get_glyph_path(plutovg_font_face_t* face, float size, float x, float y, plutovg_codepoint_t codepoint, plutovg_path_t* path)
|
||||
{
|
||||
return plutovg_font_face_traverse_glyph_path(face, size, x, y, codepoint, glyph_traverse_func, path);
|
||||
}
|
||||
|
||||
float plutovg_font_face_traverse_glyph_path(plutovg_font_face_t* face, float size, float x, float y, plutovg_codepoint_t codepoint, plutovg_path_traverse_func_t traverse_func, void* closure)
|
||||
{
|
||||
float scale = plutovg_font_face_get_scale(face, size);
|
||||
plutovg_matrix_t matrix;
|
||||
plutovg_matrix_init_translate(&matrix, x, y);
|
||||
plutovg_matrix_scale(&matrix, scale, -scale);
|
||||
|
||||
plutovg_point_t points[3];
|
||||
plutovg_point_t current_point = {0, 0};
|
||||
glyph_t* glyph = plutovg_font_face_get_glyph(face, codepoint);
|
||||
for(int i = 0; i < glyph->nvertices; i++) {
|
||||
switch(glyph->vertices[i].type) {
|
||||
case STBTT_vmove:
|
||||
points[0].x = glyph->vertices[i].x;
|
||||
points[0].y = glyph->vertices[i].y;
|
||||
current_point = points[0];
|
||||
plutovg_matrix_map_points(&matrix, points, points, 1);
|
||||
traverse_func(closure, PLUTOVG_PATH_COMMAND_MOVE_TO, points, 1);
|
||||
break;
|
||||
case STBTT_vline:
|
||||
points[0].x = glyph->vertices[i].x;
|
||||
points[0].y = glyph->vertices[i].y;
|
||||
current_point = points[0];
|
||||
plutovg_matrix_map_points(&matrix, points, points, 1);
|
||||
traverse_func(closure, PLUTOVG_PATH_COMMAND_LINE_TO, points, 1);
|
||||
break;
|
||||
case STBTT_vcurve:
|
||||
points[0].x = 2.f / 3.f * glyph->vertices[i].cx + 1.f / 3.f * current_point.x;
|
||||
points[0].y = 2.f / 3.f * glyph->vertices[i].cy + 1.f / 3.f * current_point.y;
|
||||
points[1].x = 2.f / 3.f * glyph->vertices[i].cx + 1.f / 3.f * glyph->vertices[i].x;
|
||||
points[1].y = 2.f / 3.f * glyph->vertices[i].cy + 1.f / 3.f * glyph->vertices[i].y;
|
||||
points[2].x = glyph->vertices[i].x;
|
||||
points[2].y = glyph->vertices[i].y;
|
||||
current_point = points[2];
|
||||
plutovg_matrix_map_points(&matrix, points, points, 3);
|
||||
traverse_func(closure, PLUTOVG_PATH_COMMAND_CUBIC_TO, points, 3);
|
||||
break;
|
||||
case STBTT_vcubic:
|
||||
points[0].x = glyph->vertices[i].cx;
|
||||
points[0].y = glyph->vertices[i].cy;
|
||||
points[1].x = glyph->vertices[i].cx1;
|
||||
points[1].y = glyph->vertices[i].cy1;
|
||||
points[2].x = glyph->vertices[i].x;
|
||||
points[2].y = glyph->vertices[i].y;
|
||||
current_point = points[2];
|
||||
plutovg_matrix_map_points(&matrix, points, points, 3);
|
||||
traverse_func(closure, PLUTOVG_PATH_COMMAND_CUBIC_TO, points, 3);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return glyph->advance_width * scale;
|
||||
}
|
||||
|
||||
float plutovg_font_face_text_extents(plutovg_font_face_t* face, float size, const void* text, int length, plutovg_text_encoding_t encoding, plutovg_rect_t* extents)
|
||||
{
|
||||
plutovg_text_iterator_t it;
|
||||
plutovg_text_iterator_init(&it, text, length, encoding);
|
||||
plutovg_rect_t* text_extents = NULL;
|
||||
float total_advance_width = 0.f;
|
||||
while(plutovg_text_iterator_has_next(&it)) {
|
||||
plutovg_codepoint_t codepoint = plutovg_text_iterator_next(&it);
|
||||
|
||||
float advance_width;
|
||||
if(extents == NULL) {
|
||||
plutovg_font_face_get_glyph_metrics(face, size, codepoint, &advance_width, NULL, NULL);
|
||||
total_advance_width += advance_width;
|
||||
continue;
|
||||
}
|
||||
|
||||
plutovg_rect_t glyph_extents;
|
||||
plutovg_font_face_get_glyph_metrics(face, size, codepoint, &advance_width, NULL, &glyph_extents);
|
||||
|
||||
glyph_extents.x += total_advance_width;
|
||||
total_advance_width += advance_width;
|
||||
if(text_extents == NULL) {
|
||||
text_extents = extents;
|
||||
*text_extents = glyph_extents;
|
||||
continue;
|
||||
}
|
||||
|
||||
float x1 = plutovg_min(text_extents->x, glyph_extents.x);
|
||||
float y1 = plutovg_min(text_extents->y, glyph_extents.y);
|
||||
float x2 = plutovg_max(text_extents->x + text_extents->w, glyph_extents.x + glyph_extents.w);
|
||||
float y2 = plutovg_max(text_extents->y + text_extents->h, glyph_extents.y + glyph_extents.h);
|
||||
|
||||
text_extents->x = x1;
|
||||
text_extents->y = y1;
|
||||
text_extents->w = x2 - x1;
|
||||
text_extents->h = y2 - y1;
|
||||
}
|
||||
|
||||
if(extents && !text_extents) {
|
||||
extents->x = 0;
|
||||
extents->y = 0;
|
||||
extents->w = 0;
|
||||
extents->h = 0;
|
||||
}
|
||||
|
||||
return total_advance_width;
|
||||
}
|
||||
Reference in New Issue
Block a user