-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from tsoding/freetype
FreeType2 Support
- Loading branch information
Showing
14 changed files
with
2,465 additions
and
250 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#version 330 core | ||
|
||
uniform sampler2D font; | ||
uniform float time; | ||
uniform vec2 resolution; | ||
|
||
in vec2 uv; | ||
in vec2 glyph_uv_pos; | ||
in vec2 glyph_uv_size; | ||
in vec4 glyph_fg_color; | ||
in vec4 glyph_bg_color; | ||
|
||
vec3 hsl2rgb(vec3 c) { | ||
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0); | ||
return c.z + c.y * (rgb-0.5)*(1.0-abs(2.0*c.z-1.0)); | ||
} | ||
|
||
void main() { | ||
vec2 t = glyph_uv_pos + glyph_uv_size * uv; | ||
vec4 tc = texture(font, t); | ||
vec2 frag_uv = gl_FragCoord.xy / resolution; | ||
vec4 rainbow = vec4(hsl2rgb(vec3((time + frag_uv.x + frag_uv.y), 0.5, 0.5)), 1.0); | ||
gl_FragColor = glyph_bg_color * (1.0 - tc.x) + tc.x * glyph_fg_color * rainbow; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#version 330 core | ||
|
||
uniform vec2 resolution; | ||
uniform vec2 camera; | ||
|
||
layout(location = 0) in vec2 pos; | ||
layout(location = 1) in vec2 size; | ||
layout(location = 2) in vec2 uv_pos; | ||
layout(location = 3) in vec2 uv_size; | ||
layout(location = 4) in vec4 fg_color; | ||
layout(location = 5) in vec4 bg_color; | ||
|
||
out vec2 uv; | ||
out vec2 glyph_uv_pos; | ||
out vec2 glyph_uv_size; | ||
out vec4 glyph_fg_color; | ||
out vec4 glyph_bg_color; | ||
|
||
vec2 camera_project(vec2 point) | ||
{ | ||
return 2.0 * (point - camera) / resolution; | ||
} | ||
|
||
void main() { | ||
uv = vec2(float(gl_VertexID & 1), float((gl_VertexID >> 1) & 1)); | ||
gl_Position = vec4(camera_project(uv * size + pos), 0.0, 1.0); | ||
glyph_uv_pos = uv_pos; | ||
glyph_uv_size = uv_size; | ||
glyph_fg_color = fg_color; | ||
glyph_bg_color = bg_color; | ||
} |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
#include <assert.h> | ||
#include <stdbool.h> | ||
#include "./free_glyph.h" | ||
#include "./gl_extra.h" | ||
|
||
#include "./stb_image_write.h" | ||
#include "./stb_image.h" | ||
|
||
typedef struct { | ||
size_t offset; | ||
GLint comps; | ||
GLenum type; | ||
} Attr_Def; | ||
|
||
static const Attr_Def glyph_attr_defs[COUNT_FREE_GLYPH_ATTRS] = { | ||
[FREE_GLYPH_ATTR_POS] = { | ||
.offset = offsetof(Free_Glyph, pos), | ||
.comps = 2, | ||
.type = GL_FLOAT | ||
}, | ||
[FREE_GLYPH_ATTR_SIZE] = { | ||
.offset = offsetof(Free_Glyph, size), | ||
.comps = 2, | ||
.type = GL_FLOAT | ||
}, | ||
[FREE_GLYPH_ATTR_UV_POS] = { | ||
.offset = offsetof(Free_Glyph, uv_pos), | ||
.comps = 2, | ||
.type = GL_FLOAT | ||
}, | ||
[FREE_GLYPH_ATTR_UV_SIZE] = { | ||
.offset = offsetof(Free_Glyph, uv_size), | ||
.comps = 2, | ||
.type = GL_FLOAT | ||
}, | ||
[FREE_GLYPH_ATTR_FG_COLOR] = { | ||
.offset = offsetof(Free_Glyph, fg_color), | ||
.comps = 4, | ||
.type = GL_FLOAT | ||
}, | ||
[FREE_GLYPH_ATTR_BG_COLOR] = { | ||
.offset = offsetof(Free_Glyph, bg_color), | ||
.comps = 4, | ||
.type = GL_FLOAT | ||
}, | ||
}; | ||
static_assert(COUNT_FREE_GLYPH_ATTRS == 6, "The amount of glyph vertex attributes have changed"); | ||
|
||
|
||
void free_glyph_buffer_init(Free_Glyph_Buffer *fgb, | ||
FT_Face face, | ||
const char *vert_file_path, | ||
const char *frag_file_path) | ||
{ | ||
// Init Vertex Attributes | ||
{ | ||
glGenVertexArrays(1, &fgb->vao); | ||
glBindVertexArray(fgb->vao); | ||
|
||
glGenBuffers(1, &fgb->vbo); | ||
glBindBuffer(GL_ARRAY_BUFFER, fgb->vbo); | ||
glBufferData(GL_ARRAY_BUFFER, sizeof(fgb->glyphs), fgb->glyphs, GL_DYNAMIC_DRAW); | ||
|
||
for (Free_Glyph_Attr attr = 0; attr < COUNT_FREE_GLYPH_ATTRS; ++attr) { | ||
glEnableVertexAttribArray(attr); | ||
switch (glyph_attr_defs[attr].type) { | ||
case GL_FLOAT: | ||
glVertexAttribPointer( | ||
attr, | ||
glyph_attr_defs[attr].comps, | ||
glyph_attr_defs[attr].type, | ||
GL_FALSE, | ||
sizeof(Free_Glyph), | ||
(void*) glyph_attr_defs[attr].offset); | ||
break; | ||
|
||
case GL_INT: | ||
glVertexAttribIPointer( | ||
attr, | ||
glyph_attr_defs[attr].comps, | ||
glyph_attr_defs[attr].type, | ||
sizeof(Free_Glyph), | ||
(void*) glyph_attr_defs[attr].offset); | ||
break; | ||
|
||
default: | ||
assert(false && "unreachable"); | ||
exit(1); | ||
} | ||
glVertexAttribDivisor(attr, 1); | ||
} | ||
} | ||
|
||
// Init Shaders | ||
{ | ||
GLuint vert_shader = 0; | ||
if (!compile_shader_file(vert_file_path, GL_VERTEX_SHADER, &vert_shader)) { | ||
exit(1); | ||
} | ||
GLuint frag_shader = 0; | ||
if (!compile_shader_file(frag_file_path, GL_FRAGMENT_SHADER, &frag_shader)) { | ||
exit(1); | ||
} | ||
|
||
GLuint program = 0; | ||
if (!link_program(vert_shader, frag_shader, &program)) { | ||
exit(1); | ||
} | ||
|
||
glUseProgram(program); | ||
|
||
fgb->time_uniform = glGetUniformLocation(program, "time"); | ||
fgb->resolution_uniform = glGetUniformLocation(program, "resolution"); | ||
fgb->camera_uniform = glGetUniformLocation(program, "camera"); | ||
} | ||
|
||
// Glyph Texture Atlas | ||
{ | ||
for (int i = 32; i < 128; ++i) { | ||
if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { | ||
fprintf(stderr, "ERROR: could not load glyph of a character with code %d\n", i); | ||
exit(1); | ||
} | ||
|
||
fgb->atlas_width += face->glyph->bitmap.width; | ||
if (fgb->atlas_height < face->glyph->bitmap.rows) { | ||
fgb->atlas_height = face->glyph->bitmap.rows; | ||
} | ||
} | ||
|
||
glActiveTexture(GL_TEXTURE0); | ||
glGenTextures(1, &fgb->glyphs_texture); | ||
glBindTexture(GL_TEXTURE_2D, fgb->glyphs_texture); | ||
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
|
||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | ||
glTexImage2D( | ||
GL_TEXTURE_2D, | ||
0, | ||
GL_RED, | ||
(GLsizei) fgb->atlas_width, | ||
(GLsizei) fgb->atlas_height, | ||
0, | ||
GL_RED, | ||
GL_UNSIGNED_BYTE, | ||
NULL); | ||
|
||
int x = 0; | ||
for (int i = 32; i < 128; ++i) { | ||
if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { | ||
fprintf(stderr, "ERROR: could not load glyph of a character with code %d\n", i); | ||
exit(1); | ||
} | ||
|
||
if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL)) { | ||
fprintf(stderr, "ERROR: could not render glyph of a character with code %d\n", i); | ||
exit(1); | ||
} | ||
|
||
fgb->metrics[i].ax = face->glyph->advance.x >> 6; | ||
fgb->metrics[i].ay = face->glyph->advance.y >> 6; | ||
fgb->metrics[i].bw = face->glyph->bitmap.width; | ||
fgb->metrics[i].bh = face->glyph->bitmap.rows; | ||
fgb->metrics[i].bl = face->glyph->bitmap_left; | ||
fgb->metrics[i].bt = face->glyph->bitmap_top; | ||
fgb->metrics[i].tx = (float) x / (float) fgb->atlas_width; | ||
|
||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | ||
glTexSubImage2D( | ||
GL_TEXTURE_2D, | ||
0, | ||
x, | ||
0, | ||
face->glyph->bitmap.width, | ||
face->glyph->bitmap.rows, | ||
GL_RED, | ||
GL_UNSIGNED_BYTE, | ||
face->glyph->bitmap.buffer); | ||
x += face->glyph->bitmap.width; | ||
} | ||
} | ||
} | ||
|
||
void free_glyph_buffer_clear(Free_Glyph_Buffer *fgb) | ||
{ | ||
fgb->glyphs_count = 0; | ||
} | ||
|
||
void free_glyph_buffer_push(Free_Glyph_Buffer *fgb, Free_Glyph glyph) | ||
{ | ||
assert(fgb->glyphs_count < FREE_GLYPH_BUFFER_CAP); | ||
fgb->glyphs[fgb->glyphs_count++] = glyph; | ||
} | ||
|
||
void free_glyph_buffer_sync(Free_Glyph_Buffer *fgb) | ||
{ | ||
glBufferSubData(GL_ARRAY_BUFFER, | ||
0, | ||
fgb->glyphs_count * sizeof(Free_Glyph), | ||
fgb->glyphs); | ||
} | ||
|
||
void free_glyph_buffer_draw(Free_Glyph_Buffer *fgb) | ||
{ | ||
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, (GLsizei) fgb->glyphs_count); | ||
} | ||
|
||
void free_glyph_buffer_render_line_sized(Free_Glyph_Buffer *fgb, const char *text, size_t text_size, Vec2f pos, Vec4f fg_color, Vec4f bg_color) | ||
{ | ||
for (size_t i = 0; i < text_size; ++i) { | ||
Glyph_Metric metric = fgb->metrics[(int) text[i]]; | ||
float x2 = pos.x + metric.bl; | ||
float y2 = -pos.y - metric.bt; | ||
float w = metric.bw; | ||
float h = metric.bh; | ||
|
||
pos.x += metric.ax; | ||
pos.y += metric.ay; | ||
|
||
Free_Glyph glyph = {0}; | ||
glyph.pos = vec2f(x2, -y2); | ||
glyph.size = vec2f(w, -h); | ||
glyph.uv_pos = vec2f(metric.tx, 0.0f); | ||
glyph.uv_size = vec2f(metric.bw / (float) fgb->atlas_width, metric.bh / (float) fgb->atlas_height); | ||
glyph.fg_color = fg_color; | ||
glyph.bg_color = bg_color; | ||
free_glyph_buffer_push(fgb, glyph); | ||
} | ||
} | ||
|
||
void free_glyph_buffer_render_line(Free_Glyph_Buffer *fgb, const char *text, Vec2f pos, Vec4f fg_color, Vec4f bg_color) | ||
{ | ||
free_glyph_buffer_render_line_sized(fgb, text, strlen(text), pos, fg_color, bg_color); | ||
} |
Oops, something went wrong.