[RELEASE] u22-v03

This version is submitted to U22 breau.
This commit is contained in:
2020-09-14 00:00:00 +00:00
parent 360595de37
commit 84c3a02b9a
357 changed files with 29223 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
add_library(glyphas
aligner.c
block.c
cache.c
context.c
drawer.c
face.c
glyph.c
)
target_any_sources(glyphas
drawer.vshader
drawer.fshader
)
target_link_libraries(glyphas
Freetype::Freetype
GLEW::GLEW
OpenGL::GL
chaos
container
conv
gleasy
math
memory
)
if (BUILD_TESTING)
add_executable(glyphas-test test.c)
target_link_libraries(glyphas-test
SDL2::SDL2
glyphas)
endif()

4
util/glyphas/README.md Normal file
View File

@@ -0,0 +1,4 @@
glyphas
====
GLYPH texture atlAS library wrapping FreeType

78
util/glyphas/aligner.c Normal file
View File

@@ -0,0 +1,78 @@
#include "./aligner.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "util/gleasy/atlas.h"
#include "util/math/algorithm.h"
#include "./cache.h"
void glyphas_aligner_initialize(
glyphas_aligner_t* aligner,
glyphas_aligner_direction_t dir,
int32_t lineheight,
int32_t maxpos) {
assert(aligner != NULL);
assert(maxpos > 0);
*aligner = (typeof(*aligner)) {
.dir = dir,
.lineheight = lineheight,
.maxpos = maxpos,
.line = lineheight,
};
}
void glyphas_aligner_deinitialize(glyphas_aligner_t* aligner) {
assert(aligner != NULL);
}
void glyphas_aligner_reset(glyphas_aligner_t* aligner) {
assert(aligner != NULL);
aligner->line = aligner->lineheight;
aligner->pos = 0;
}
void glyphas_aligner_push_character(
glyphas_aligner_t* aligner,
int32_t* x,
int32_t* y,
const glyphas_cache_glyph_t* g) {
assert(aligner != NULL);
assert(x != NULL);
assert(y != NULL);
assert(g != NULL);
switch (aligner->dir) {
case GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL:
if (aligner->pos + g->hmetrics.advance > aligner->maxpos) {
glyphas_aligner_break_line(aligner);
}
*x = aligner->pos + g->hmetrics.bear_x;
*y = g->hmetrics.bear_y + aligner->line;
aligner->pos += g->hmetrics.advance;
break;
case GLYPHAS_ALIGNER_DIRECTION_VERTICAL:
if (aligner->pos - g->vmetrics.advance < -aligner->maxpos) {
glyphas_aligner_break_line(aligner);
}
*x = aligner->pos - g->vmetrics.bear_x + aligner->line;
*y = -g->vmetrics.bear_y;
aligner->pos -= g->vmetrics.advance;
break;
default:
assert(false);
}
}
void glyphas_aligner_break_line(glyphas_aligner_t* aligner) {
assert(aligner != NULL);
aligner->pos = 0;
aligner->line += aligner->lineheight;
}

52
util/glyphas/aligner.h Normal file
View File

@@ -0,0 +1,52 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "./cache.h"
typedef enum {
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
GLYPHAS_ALIGNER_DIRECTION_VERTICAL,
} glyphas_aligner_direction_t;
typedef struct {
glyphas_aligner_direction_t dir;
int32_t lineheight;
int32_t maxpos;
int32_t pos;
int32_t line;
} glyphas_aligner_t;
void
glyphas_aligner_initialize(
glyphas_aligner_t* aligner,
glyphas_aligner_direction_t dir,
int32_t lineheight,
int32_t maxpos
);
void
glyphas_aligner_deinitialize(
glyphas_aligner_t* aligner
);
void
glyphas_aligner_reset(
glyphas_aligner_t* aligner
);
void
glyphas_aligner_push_character(
glyphas_aligner_t* aligner,
int32_t* x,
int32_t* y,
const glyphas_cache_glyph_t* g
);
void
glyphas_aligner_break_line(
glyphas_aligner_t* aligner
);

216
util/glyphas/block.c Normal file
View File

@@ -0,0 +1,216 @@
#include "./block.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "util/chaos/xorshift.h"
#include "util/conv/charcode.h"
#include "util/math/algorithm.h"
#include "util/math/vector.h"
#include "util/gleasy/atlas.h"
#include "util/memory/memory.h"
#include "./cache.h"
struct glyphas_block_t {
glyphas_aligner_t aligner;
size_t reserve;
size_t length;
glyphas_block_item_t items[1];
};
glyphas_block_t* glyphas_block_new(
glyphas_aligner_direction_t dir,
int32_t lineheight,
int32_t maxpos,
size_t reserve) {
assert(maxpos > 0);
assert(reserve > 0);
glyphas_block_t* block =
memory_new(sizeof(*block) + (reserve-1)*sizeof(block->items[0]));
*block = (typeof(*block)) {
.reserve = reserve,
};
glyphas_aligner_initialize(&block->aligner, dir, lineheight, maxpos);
return block;
}
void glyphas_block_delete(glyphas_block_t* block) {
if (block == NULL) return;
glyphas_aligner_deinitialize(&block->aligner);
memory_delete(block);
}
void glyphas_block_clear(glyphas_block_t* block) {
assert(block != NULL);
glyphas_aligner_reset(&block->aligner);
block->length = 0;
}
void glyphas_block_add_character(
glyphas_block_t* block,
const glyphas_cache_glyph_t* glyph,
const vec4_t* color) {
assert(block != NULL);
assert(glyph != NULL);
assert(vec4_valid(color));
if (block->length >= block->reserve) {
fprintf(stderr, "glyphas block overflow\n");
abort();
}
int32_t x, y;
glyphas_aligner_push_character(&block->aligner, &x, &y, glyph);
if (glyph->geometry.left >= glyph->geometry.right) return;
block->items[block->length++] = (glyphas_block_item_t) {
.pos = vec2(x, y),
.size = vec2(glyph->width, glyph->height),
.color = *color,
.uv = glyph->geometry,
};
}
void glyphas_block_add_characters(
glyphas_block_t* block,
glyphas_cache_t* cache,
const vec4_t* color,
const char* str,
size_t len) {
assert(block != NULL);
assert(cache != NULL);
assert(vec4_valid(color));
const char* end = str + len;
while (str < end) {
uint32_t c;
const size_t consumed = conv_charcode_utf8_to_utf32(&c, str, end-str);
str += MATH_MAX(consumed, 1);
if (consumed == 0) continue;
if (c == '\n') {
glyphas_block_break_line(block);
continue;
}
const glyphas_cache_glyph_t* g = glyphas_cache_add_glyph(cache, c);
if (g == NULL) continue;
glyphas_block_add_character(block, g, color);
}
}
void glyphas_block_break_line(glyphas_block_t* block) {
assert(block != NULL);
glyphas_aligner_break_line(&block->aligner);
}
void glyphas_block_scale(glyphas_block_t* block, const vec2_t* s) {
assert(block != NULL);
assert(vec2_valid(s));
for (size_t i = 0; i < block->length; ++i) {
vec2_t* pos = &block->items[i].pos;
vec2_t* sz = &block->items[i].size;
pos->x *= s->x;
pos->y *= s->y;
sz->x *= s->x;
sz->y *= s->y;
}
}
void glyphas_block_translate(glyphas_block_t* block, const vec2_t* v) {
assert(block != NULL);
assert(vec2_valid(v));
for (size_t i = 0; i < block->length; ++i) {
vec2_addeq(&block->items[i].pos, v);
}
}
void glyphas_block_set_origin(glyphas_block_t* block, const vec2_t* r) {
assert(block !=NULL);
assert(vec2_valid(r));
vec2_t offset, size;
glyphas_block_calculate_geometry(block, &offset, &size);
size.x *= r->x;
size.y *= r->y;
vec2_addeq(&offset, &size);
vec2_muleq(&offset, -1);
glyphas_block_translate(block, &offset);
}
void glyphas_block_set_alpha(glyphas_block_t* block, float a) {
assert(block != NULL);
for (size_t i = 0; i < block->length; ++i) {
block->items[i].color.w = a;
}
}
void glyphas_block_make_glitched(glyphas_block_t* block, uint64_t seed) {
assert(block != NULL);
# define randf() (seed = chaos_xorshift(seed), seed%1000/1000.f)
for (size_t i = 0; i < block->length; ++i) {
const vec2_t* sz = &block->items[i].size;
const vec2_t shift = vec2((randf()-.5f)*sz->x*.5f, (randf()-.5f)*sz->y*.5f);
const float scale = randf()*.4f + .8f;
vec2_addeq(&block->items[i].pos, &shift);
vec2_muleq(&block->items[i].size, scale);
}
# undef randf
}
void glyphas_block_calculate_geometry(
const glyphas_block_t* block, vec2_t* offset, vec2_t* size) {
assert(block != NULL);
assert(offset != NULL);
assert(size != NULL);
vec2_t lb = vec2(0, 0), rt = vec2(0, 0);
for (size_t i = 0; i < block->length; ++i) {
const glyphas_block_item_t* item = &block->items[i];
const float l = item->pos.x;
const float r = l + item->size.x;
const float t = item->pos.y;
const float b = t - item->size.y;
lb.x = MATH_MIN(lb.x, l);
lb.y = MATH_MIN(lb.y, b);
rt.x = MATH_MAX(rt.x, r);
rt.y = MATH_MAX(rt.y, t);
}
*offset = vec2(lb.x, rt.y);
vec2_sub(size, &rt, &lb);
}
const glyphas_block_item_t* glyphas_block_get_items(
const glyphas_block_t* block, size_t* len) {
assert(block != NULL);
assert(len != NULL);
*len = block->length;
return block->items;
}

102
util/glyphas/block.h Normal file
View File

@@ -0,0 +1,102 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "util/math/vector.h"
#include "util/gleasy/atlas.h"
#include "./aligner.h"
#include "./cache.h"
struct glyphas_block_t;
typedef struct glyphas_block_t glyphas_block_t;
typedef struct {
vec2_t pos;
vec2_t size;
vec4_t color;
gleasy_atlas_geometry_t uv;
} glyphas_block_item_t;
glyphas_block_t*
glyphas_block_new(
glyphas_aligner_direction_t dir,
int32_t lineheight,
int32_t maxpos,
size_t reserve
);
void
glyphas_block_delete(
glyphas_block_t* block
);
void
glyphas_block_clear(
glyphas_block_t* block
);
void
glyphas_block_add_character(
glyphas_block_t* block,
const glyphas_cache_glyph_t* glyph,
const vec4_t* color
);
void
glyphas_block_add_characters(
glyphas_block_t* block,
glyphas_cache_t* cache,
const vec4_t* color,
const char* str,
size_t len
);
void
glyphas_block_break_line(
glyphas_block_t* block
);
void
glyphas_block_scale(
glyphas_block_t* block,
const vec2_t* translation
);
void
glyphas_block_translate(
glyphas_block_t* block,
const vec2_t* translation
);
void
glyphas_block_set_origin(
glyphas_block_t* block,
const vec2_t* r
);
void
glyphas_block_set_alpha(
glyphas_block_t* block,
float a
);
void
glyphas_block_make_glitched(
glyphas_block_t* block,
uint64_t seed
);
void
glyphas_block_calculate_geometry(
const glyphas_block_t* block,
vec2_t* offset,
vec2_t* size
);
const glyphas_block_item_t*
glyphas_block_get_items(
const glyphas_block_t* block,
size_t* len
);

111
util/glyphas/cache.c Normal file
View File

@@ -0,0 +1,111 @@
#include "./cache.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <GL/glew.h>
#include "util/container/array.h"
#include "util/gleasy/atlas.h"
#include "util/memory/memory.h"
#include "./face.h"
struct glyphas_cache_t {
gleasy_atlas_t* atlas;
glyphas_face_t* face;
int32_t char_width;
int32_t char_height;
/* TODO(catfoot): linear search isn't efficient. :( */
CONTAINER_ARRAY glyphas_cache_glyph_t* items;
};
glyphas_cache_t* glyphas_cache_new(
gleasy_atlas_t* atlas,
glyphas_face_t* face,
int32_t char_width,
int32_t char_height) {
assert(atlas != NULL);
assert(face != NULL);
assert(char_width > 0);
assert(char_height > 0);
glyphas_cache_t* cache = memory_new(sizeof(*cache));
*cache = (typeof(*cache)) {
.atlas = atlas,
.face = face,
.char_width = char_width,
.char_height = char_height,
};
return cache;
}
void glyphas_cache_delete(glyphas_cache_t* cache) {
if (cache == NULL) return;
container_array_delete(cache->items);
memory_delete(cache);
}
const glyphas_cache_glyph_t* glyphas_cache_add_glyph(
glyphas_cache_t* cache, uint32_t unicode) {
assert(cache != NULL);
const glyphas_cache_glyph_t* found =
glyphas_cache_lookup_glyph(cache, unicode);
if (found != NULL) return found;
if (!glyphas_face_set_pixel_size(
cache->face, cache->char_width, cache->char_height)) {
return NULL;
}
if (!glyphas_face_render_glyph(cache->face, unicode)) {
return NULL;
}
gleasy_atlas_geometry_t geo = {0};
const glyphas_glyph_t* rendered = &cache->face->glyph;
if (rendered->bitmap.width > 0) {
const gleasy_atlas_bitmap_t bmp = {
.width = rendered->bitmap.width,
.height = rendered->bitmap.height,
.buffer = rendered->bitmap.buffer,
.format = GL_RED,
.type = GL_UNSIGNED_BYTE,
};
if (!gleasy_atlas_add(cache->atlas, &geo, &bmp)) {
return NULL;
}
}
const size_t index = container_array_get_length(cache->items);
container_array_insert(cache->items, index);
glyphas_cache_glyph_t* g = &cache->items[index];
*g = (typeof(*g)) {
.unicode = unicode,
.width = rendered->bitmap.width,
.height = rendered->bitmap.height,
.geometry = geo,
.hmetrics = rendered->hmetrics,
.vmetrics = rendered->vmetrics,
};
return g;
}
const glyphas_cache_glyph_t* glyphas_cache_lookup_glyph(
const glyphas_cache_t* cache, uint32_t unicode) {
assert(cache != NULL);
const size_t len = container_array_get_length(cache->items);
for (size_t i = 0; i < len; ++i) {
const glyphas_cache_glyph_t* g = &cache->items[i];
if (g->unicode == unicode) return g;
}
return NULL;
}

47
util/glyphas/cache.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include <stdint.h>
#include "util/gleasy/atlas.h"
#include "./face.h"
#include "./glyph.h"
typedef struct {
uint32_t unicode;
int32_t width;
int32_t height;
gleasy_atlas_geometry_t geometry;
glyphas_glyph_metrics_t hmetrics;
glyphas_glyph_metrics_t vmetrics;
} glyphas_cache_glyph_t;
struct glyphas_cache_t;
typedef struct glyphas_cache_t glyphas_cache_t;
glyphas_cache_t*
glyphas_cache_new(
gleasy_atlas_t* atlas,
glyphas_face_t* face,
int32_t char_width,
int32_t char_height
);
void
glyphas_cache_delete(
glyphas_cache_t* cache
);
const glyphas_cache_glyph_t* /* NULLABLE */
glyphas_cache_add_glyph(
glyphas_cache_t* cache,
uint32_t unicode
);
const glyphas_cache_glyph_t* /* NULLABLE */
glyphas_cache_lookup_glyph(
const glyphas_cache_t* cache,
uint32_t unicode
);

21
util/glyphas/context.c Normal file
View File

@@ -0,0 +1,21 @@
#include "./context.h"
#include <assert.h>
#include <stddef.h>
#include <ft2build.h>
#include FT_FREETYPE_H
void glyphas_context_initialize(glyphas_context_t* ctx) {
assert(ctx != NULL);
*ctx = (typeof(*ctx)) {0};
FT_Init_FreeType(&ctx->ft);
}
void glyphas_context_deinitialize(glyphas_context_t* ctx) {
assert(ctx != NULL);
FT_Done_FreeType(ctx->ft);
}

18
util/glyphas/context.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include <ft2build.h>
#include FT_FREETYPE_H
typedef struct {
FT_Library ft;
} glyphas_context_t;
void
glyphas_context_initialize(
glyphas_context_t* ctx
);
void
glyphas_context_deinitialize(
glyphas_context_t* ctx
);

164
util/glyphas/drawer.c Normal file
View File

@@ -0,0 +1,164 @@
#include "./drawer.h"
#include <assert.h>
#include <stddef.h>
#include <GL/glew.h>
#include "util/gleasy/buffer.h"
#include "util/gleasy/texture.h"
#include "util/gleasy/program.h"
#include "util/math/vector.h"
#include "util/memory/memory.h"
#include "./block.h"
/* resources */
#include "anysrc/drawer.vshader.h"
#include "anysrc/drawer.fshader.h"
#define GLYPHAS_DRAWER_UNIFORM_TEX 0
#define GLYPHAS_DRAWER_VSHADER_IN_POS 0
#define GLYPHAS_DRAWER_VSHADER_IN_SIZE 1
#define GLYPHAS_DRAWER_VSHADER_IN_UV_POS 2
#define GLYPHAS_DRAWER_VSHADER_IN_UV_SIZE 3
#define GLYPHAS_DRAWER_VSHADER_IN_COLOR 4
struct glyphas_drawer_t {
gleasy_texture_2d_t tex;
GLuint vao;
gleasy_buffer_array_t instances;
size_t instances_reserved;
size_t instances_length;
};
#pragma pack(push, 1)
typedef struct {
vec2_t pos;
vec2_t size;
vec2_t uv_pos;
vec2_t uv_size;
vec4_t color;
} glyphas_drawer_instance_t;
#pragma pack(pop)
static void glyphas_drawer_setup_vao_(
gleasy_buffer_array_t instances) {
assert(instances != 0);
glBindBuffer(GL_ARRAY_BUFFER, instances);
# define enable_attrib_(NAME, name, dim, type) do { \
glEnableVertexAttribArray(GLYPHAS_DRAWER_VSHADER_IN_##NAME); \
glVertexAttribPointer( \
GLYPHAS_DRAWER_VSHADER_IN_##NAME, dim, type, GL_FALSE, \
sizeof(glyphas_drawer_instance_t), \
NULL + offsetof(glyphas_drawer_instance_t, name)); \
glVertexAttribDivisor(GLYPHAS_DRAWER_VSHADER_IN_##NAME, 1); \
} while (0)
enable_attrib_(POS, pos, 2, GL_FLOAT);
enable_attrib_(SIZE, size, 2, GL_FLOAT);
enable_attrib_(UV_POS, uv_pos, 2, GL_FLOAT);
enable_attrib_(UV_SIZE, uv_size, 2, GL_FLOAT);
enable_attrib_(COLOR, color, 4, GL_FLOAT);
# undef enable_attrib_
}
gleasy_program_t glyphas_drawer_create_default_program(void) {
return gleasy_program_new("", 0,
glyphas_drawer_vshader_, sizeof(glyphas_drawer_vshader_),
glyphas_drawer_fshader_, sizeof(glyphas_drawer_fshader_));
}
glyphas_drawer_t* glyphas_drawer_new(void) {
glyphas_drawer_t* drawer = memory_new(sizeof(*drawer));
*drawer = (typeof(*drawer)) {0};
glCreateVertexArrays(1, &drawer->vao);
glBindVertexArray(drawer->vao);
glGenBuffers(1, &drawer->instances);
glyphas_drawer_setup_vao_(drawer->instances);
return drawer;
}
void glyphas_drawer_delete(glyphas_drawer_t* drawer) {
if (drawer == NULL) return;
glDeleteBuffers(1, &drawer->instances);
glDeleteVertexArrays(1, &drawer->vao);
memory_delete(drawer);
}
void glyphas_drawer_clear(
glyphas_drawer_t* drawer, gleasy_texture_2d_t tex, size_t reserve) {
assert(drawer != NULL);
assert(tex != 0);
assert(reserve > 0);
drawer->tex = tex;
drawer->instances_length = 0;
if (drawer->instances_reserved < reserve) {
glBindBuffer(GL_ARRAY_BUFFER, drawer->instances);
glBufferData(GL_ARRAY_BUFFER,
reserve * sizeof(glyphas_drawer_instance_t),
NULL, GL_DYNAMIC_DRAW);
drawer->instances_reserved = reserve;
}
}
void glyphas_drawer_add_block(
glyphas_drawer_t* drawer, const glyphas_block_t* block) {
assert(drawer != NULL);
assert(block != NULL);
size_t len;
const glyphas_block_item_t* items = glyphas_block_get_items(block, &len);
for (size_t i = 0; i < len; ++i) {
glyphas_drawer_add_block_item(drawer, &items[i]);
}
}
void glyphas_drawer_add_block_item(
glyphas_drawer_t* drawer, const glyphas_block_item_t* item) {
assert(drawer != NULL);
assert(item != NULL);
const glyphas_drawer_instance_t insta = {
.pos = item->pos,
.size = item->size,
.uv_pos = vec2(item->uv.left, item->uv.top),
.uv_size = vec2(item->uv.right-item->uv.left, item->uv.top-item->uv.bottom),
.color = item->color,
};
const size_t offset = drawer->instances_length * sizeof(insta);
glBindBuffer(GL_ARRAY_BUFFER, drawer->instances);
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(insta), &insta);
++drawer->instances_length;
}
void glyphas_drawer_draw(const glyphas_drawer_t* drawer) {
assert(drawer != NULL);
if (drawer->instances_length == 0) return;
glBindVertexArray(drawer->vao);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, drawer->tex);
glUniform1i(GLYPHAS_DRAWER_UNIFORM_TEX, 0);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, drawer->instances_length);
}

View File

@@ -0,0 +1,14 @@
#version 330
#extension GL_ARB_explicit_uniform_location : enable
layout (location = 0) uniform sampler2D u_tex;
in vec2 v_uv;
in vec4 v_color;
out vec4 o_color;
void main(void) {
float a = texture(u_tex, v_uv).r;
o_color = vec4(v_color.rgb, v_color.a*a);
}

51
util/glyphas/drawer.h Normal file
View File

@@ -0,0 +1,51 @@
#pragma once
#include "util/gleasy/texture.h"
#include "util/gleasy/program.h"
#include "./block.h"
struct glyphas_drawer_t;
typedef struct glyphas_drawer_t glyphas_drawer_t;
gleasy_program_t
glyphas_drawer_create_default_program(
void
);
glyphas_drawer_t*
glyphas_drawer_new(
void
);
void
glyphas_drawer_delete(
glyphas_drawer_t* drawer
);
void
glyphas_drawer_clear(
glyphas_drawer_t* drawer,
gleasy_texture_2d_t tex,
size_t reserve
);
void
glyphas_drawer_add_block(
glyphas_drawer_t* drawer,
const glyphas_block_t* block
/* The drawer doesn't hold the reference. */
);
void
glyphas_drawer_add_block_item(
glyphas_drawer_t* drawer,
const glyphas_block_item_t* item
/* The drawer doesn't hold the reference. */
);
/* bind the program before drawing */
void
glyphas_drawer_draw(
const glyphas_drawer_t* drawer
);

View File

@@ -0,0 +1,25 @@
#version 330
layout (location = 0) in vec2 i_pos;
layout (location = 1) in vec2 i_size;
layout (location = 2) in vec2 i_uv_pos;
layout (location = 3) in vec2 i_uv_size;
layout (location = 4) in vec4 i_color;
out vec2 v_uv;
out vec4 v_color;
void main(void) {
vec2 p =
gl_VertexID == 0? vec2(0., 0.):
gl_VertexID == 1? vec2(0., -1.):
gl_VertexID == 2? vec2(1., -1.):
gl_VertexID == 3? vec2(0., 0.):
gl_VertexID == 4? vec2(1., -1.):
gl_VertexID == 5? vec2(1., 0.):
vec2(0.);
gl_Position = vec4(p*i_size+i_pos, 0, 1);
v_uv = p*i_uv_size + i_uv_pos;
v_color = i_color;
}

78
util/glyphas/face.c Normal file
View File

@@ -0,0 +1,78 @@
#include "./face.h"
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <ft2build.h>
#include FT_ERRORS_H
#include FT_FREETYPE_H
#include "./context.h"
void glyphas_face_initialize_from_file(
glyphas_face_t* face,
const glyphas_context_t* ctx,
const char* path,
size_t index) {
assert(face != NULL);
assert(ctx != NULL);
*face = (typeof(*face)) {0};
const FT_Error err = FT_New_Face(ctx->ft, path, index, &face->ft);
if (err != FT_Err_Ok) {
fprintf(stderr,
"failed to load font file '%s': %s\n", path, FT_Error_String(err));
abort();
}
}
void glyphas_face_initialize_from_buffer(
glyphas_face_t* face,
const glyphas_context_t* ctx,
const void* data,
size_t length,
size_t index) {
assert(face != NULL);
assert(ctx != NULL);
*face = (typeof(*face)) {0};
const FT_Error err =
FT_New_Memory_Face(ctx->ft, data, length, index, &face->ft);
if (err != FT_Err_Ok) {
fprintf(stderr,
"failed to load font on memory: %s\n", FT_Error_String(err));
abort();
}
}
void glyphas_face_deinitialize(glyphas_face_t* face) {
assert(face != NULL);
FT_Done_Face(face->ft);
}
bool glyphas_face_set_pixel_size(
glyphas_face_t* face, int32_t width, int32_t height) {
assert(face != NULL);
assert(width > 0);
assert(height > 0);
const FT_Error err = FT_Set_Pixel_Sizes(face->ft, width, height);
return err == FT_Err_Ok;
}
bool glyphas_face_render_glyph(glyphas_face_t* face, uint64_t unicode) {
assert(face != NULL);
const FT_UInt index = FT_Get_Char_Index(face->ft, unicode);
if (index == 0) return false;
const FT_Error err = FT_Load_Glyph(face->ft, index, FT_LOAD_RENDER);
if (err != FT_Err_Ok) return false;
return glyphas_glyph_reset_from_ft_glyph_slot(&face->glyph, face->ft->glyph);
}

52
util/glyphas/face.h Normal file
View File

@@ -0,0 +1,52 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "./context.h"
#include "./glyph.h"
typedef struct {
FT_Face ft;
glyphas_glyph_t glyph;
} glyphas_face_t;
void
glyphas_face_initialize_from_file(
glyphas_face_t* face,
const glyphas_context_t* ctx,
const char* path,
size_t index
);
void
glyphas_face_initialize_from_buffer(
glyphas_face_t* face,
const glyphas_context_t* ctx,
const void* data,
size_t length,
size_t index
);
void
glyphas_face_deinitialize(
glyphas_face_t* face
);
bool
glyphas_face_set_pixel_size(
glyphas_face_t* face,
int32_t width,
int32_t height
);
/* Set sizes before rendering glyphs. */
bool
glyphas_face_render_glyph(
glyphas_face_t* face,
uint64_t unicode
);

36
util/glyphas/glyph.c Normal file
View File

@@ -0,0 +1,36 @@
#include "./glyph.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <ft2build.h>
#include FT_FREETYPE_H
bool glyphas_glyph_reset_from_ft_glyph_slot(
glyphas_glyph_t* glyph, FT_GlyphSlot slot) {
assert(glyph != NULL);
assert(slot != NULL);
if (slot->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) return false;
*glyph = (typeof(*glyph)) {
.bitmap = {
.width = slot->bitmap.width,
.height = slot->bitmap.rows,
.buffer = slot->bitmap.buffer,
},
.hmetrics = {
.bear_x = slot->metrics.horiBearingX/64,
.bear_y = slot->metrics.horiBearingY/64,
.advance = slot->metrics.horiAdvance /64,
},
.vmetrics = {
.bear_x = slot->metrics.vertBearingX/64,
.bear_y = slot->metrics.vertBearingY/64,
.advance = slot->metrics.vertAdvance /64,
},
};
return true;
}

32
util/glyphas/glyph.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <ft2build.h>
#include FT_FREETYPE_H
typedef struct {
int32_t width;
int32_t height;
uint8_t* buffer; /* gray-scale bitmap */
} glyphas_glyph_bitmap_t;
typedef struct {
int32_t bear_x;
int32_t bear_y;
int32_t advance;
} glyphas_glyph_metrics_t;
typedef struct {
glyphas_glyph_bitmap_t bitmap;
glyphas_glyph_metrics_t vmetrics;
glyphas_glyph_metrics_t hmetrics;
} glyphas_glyph_t;
/* The glyph's lifetime must be shorter than the slot. */
bool
glyphas_glyph_reset_from_ft_glyph_slot(
glyphas_glyph_t* glyph,
FT_GlyphSlot slot
);

179
util/glyphas/test.c Normal file
View File

@@ -0,0 +1,179 @@
/* An expression in assert() can be expected to be executed absolutely. */
#undef NDEBUG
#define SDL_MAIN_HANDLED
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include "util/conv/charcode.h"
#include "util/gleasy/buffer.h"
#include "util/gleasy/program.h"
#include "util/math/vector.h"
#include "./cache.h"
#include "./context.h"
#include "./drawer.h"
#include "./face.h"
#include "./glyph.h"
typedef struct {
glyphas_context_t glyphas;
glyphas_face_t face;
gleasy_atlas_t* atlas;
glyphas_cache_t* cache;
gleasy_program_t prog;
glyphas_drawer_t* drawer;
} context_t;
#define SCALE 0.01f
static void align_text_(context_t* ctx, const char* str) {
assert(ctx != NULL);
const size_t len = strlen(str);
glyphas_block_t* block = glyphas_block_new(
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL, -20, 400, len);
static vec4_t color = vec4(0, 0, 0, 1);
glyphas_block_add_characters(block, ctx->cache, &color, str, len);
vec2_t offset, size;
glyphas_block_calculate_geometry(block, &offset, &size);
offset = vec2(-offset.x, 0);
glyphas_block_translate(block, &offset);
size = vec2(1.f/200, 1.f/200);
glyphas_block_scale(block, &size);
static const vec2_t origin = vec2(.5f, -.5f);
glyphas_block_set_origin(block, &origin);
glyphas_drawer_clear(ctx->drawer, gleasy_atlas_get_texture(ctx->atlas), len);
glyphas_drawer_add_block(ctx->drawer, block);
glyphas_block_delete(block);
}
static void initialize_(context_t* ctx, const char* path, const char* str) {
assert(ctx != NULL);
*ctx = (typeof(*ctx)) {0};
glyphas_context_initialize(&ctx->glyphas);
glyphas_face_initialize_from_file(&ctx->face, &ctx->glyphas, path, 0);
ctx->atlas = gleasy_atlas_new(GL_RED, 256, 256, false /* anti-alias */);
ctx->cache = glyphas_cache_new(ctx->atlas, &ctx->face, 16, 16);
ctx->prog = glyphas_drawer_create_default_program();
ctx->drawer = glyphas_drawer_new();
assert(glGetError() == GL_NO_ERROR);
align_text_(ctx, str);
assert(glGetError() == GL_NO_ERROR);
}
static void draw_(const context_t* ctx) {
assert(ctx != NULL);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(ctx->prog);
glyphas_drawer_draw(ctx->drawer);
}
static void deinitialize_(context_t* ctx) {
assert(ctx != NULL);
glyphas_drawer_delete(ctx->drawer);
glDeleteProgram(ctx->prog);
glyphas_cache_delete(ctx->cache);
gleasy_atlas_delete(ctx->atlas);
glyphas_face_deinitialize(&ctx->face);
glyphas_context_deinitialize(&ctx->glyphas);
}
typedef struct {
SDL_Window* win;
SDL_GLContext gl;
} libs_t;
static void initialize_libraries_(libs_t* libs) {
assert(libs != NULL);
SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0");
assert(SDL_Init(SDL_INIT_VIDEO) == 0);
SDL_GL_SetAttribute(
SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
SDL_GL_SetAttribute(
SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
static const uint32_t win_flags =
SDL_WINDOW_ALLOW_HIGHDPI |
SDL_WINDOW_OPENGL |
SDL_WINDOW_SHOWN;
libs->win = SDL_CreateWindow(
"glyphas-test", /* title */
SDL_WINDOWPOS_CENTERED, /* X position */
SDL_WINDOWPOS_CENTERED, /* Y position */
400,
400,
win_flags);
assert(libs->win != NULL);
libs->gl = SDL_GL_CreateContext(libs->win);
glewExperimental = 1;
assert(glewInit() == GLEW_OK);
}
static void deinitialize_libraries_(libs_t* libs) {
assert(libs != NULL);
SDL_GL_DeleteContext(libs->gl);
SDL_DestroyWindow(libs->win);
SDL_Quit();
}
int main(int argc, char** argv) {
assert(argc == 3);
libs_t libs;
initialize_libraries_(&libs);
context_t ctx;
initialize_(&ctx, argv[1], argv[2]);
bool alive = true;
while (alive) {
SDL_Delay(30);
SDL_Event e;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) alive = false;
}
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
draw_(&ctx);
SDL_GL_SwapWindow(libs.win);
}
deinitialize_(&ctx);
deinitialize_libraries_(&libs);
return EXIT_SUCCESS;
}