Render outlined text with dft.

This commit is contained in:
Josh Holtrop 2021-01-07 20:04:38 -05:00
parent b396fa8419
commit 072abd788c
5 changed files with 216 additions and 7 deletions

3
.gitmodules vendored
View File

@ -13,3 +13,6 @@
[submodule "modules/glamour"]
path = modules/glamour
url = git://github.com/Dav1dde/glamour
[submodule "modules/dft"]
path = modules/dft
url = ../../dft.git

View File

@ -1,5 +1,6 @@
configure do
check_d_compiler
check_cfg package: "freetype2"
end
build do
@ -12,8 +13,9 @@ build do
"modules/DerelictGL3/source",
"modules/gl3n/gl3n",
"modules/glamour/glamour",
"modules/dft/src",
].each do |dir|
sources += glob("#{dir}/**/*.d")
sources += glob("#{dir}/**/*.{d,c}")
end
env["D_IMPORT_PATH"] += [
"modules/DerelictUtil/source",
@ -21,6 +23,7 @@ build do
"modules/DerelictGL3/source",
"modules/gl3n",
"modules/glamour",
"modules/dft/src",
]
env["DFLAGS"] += ["-fversion=Derelict3", "-fversion=gl3n", "-fversion=SDLImage2"]
env["LDFLAGS"] += ["-static-libgcc"]

BIN
assets/FreeSans.ttf Normal file

Binary file not shown.

1
modules/dft Submodule

@ -0,0 +1 @@
Subproject commit 4a0727b535bb3164e48c6bb57194f737dfeb0aab

214
src/app.d
View File

@ -1,24 +1,142 @@
import std.stdio;
import core.stdc.string;
import derelict.sdl2.sdl;
import derelict.opengl3.gl3;
import glamour.vao;
import glamour.shader;
import glamour.vbo;
import glamour.texture;
import gl3n.linalg;
static import dft;
enum int WIDTH = 800;
enum int HEIGHT = 600;
Shader spinny_program;
GLint position_idx;
GLint color_idx;
GLint view_idx;
Shader font_program;
GLint font_coords_idx;
GLint font_viewport_size_idx;
GLint font_position_idx;
GLint font_texture_idx;
GLint font_color_idx;
mat4 view_matrix;
private uint next_power_of_2(uint n)
{
n--;
n |= n >> 1u;
n |= n >> 2u;
n |= n >> 4u;
n |= n >> 8u;
n |= n >> 16u;
n++;
return n;
}
class Glyph
{
Texture2D m_texture;
VAO m_vao;
Buffer m_vbo;
int m_advance;
this(dft.Font font, uint char_code, int outline_size)
{
dft.Font.Glyph dft_glyph = font.get_glyph(char_code, outline_size);
m_advance = dft_glyph.advance;
m_texture = new Texture2D();
uint rwidth = next_power_of_2(dft_glyph.width);
uint rheight = next_power_of_2(dft_glyph.height);
ubyte[] texture_data = new ubyte[rwidth * rheight];
for (uint row = 0u; row < dft_glyph.height; row++)
{
memcpy(&texture_data[row * rwidth],
&dft_glyph.bitmap[(dft_glyph.height - row - 1) * dft_glyph.width],
dft_glyph.width);
}
m_texture.bind();
m_texture.set_parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
m_texture.set_parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
m_texture.set_parameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
m_texture.set_parameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
m_texture.set_data(texture_data, GL_ALPHA, rwidth, rheight, GL_ALPHA, GL_UNSIGNED_BYTE, false);
m_vao = new VAO();
m_vao.bind();
int left = dft_glyph.left;
int top = dft_glyph.top;
float s_max = dft_glyph.width / cast(float)rwidth;
float t_max = dft_glyph.height / cast(float)rheight;
GLfloat[] data = [
cast(GLfloat)left, cast(GLfloat)(top - dft_glyph.height), 0.0, 0.0,
cast(GLfloat)(left + dft_glyph.width), cast(GLfloat)(top - dft_glyph.height), s_max, 0.0,
cast(GLfloat)left, cast(GLfloat)top, 0.0, t_max,
cast(GLfloat)(left + dft_glyph.width), cast(GLfloat)top, s_max, t_max,
];
m_vbo = new Buffer(data);
m_vbo.bind();
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, null);
}
void render()
{
m_vao.bind();
m_texture.bind();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
@property int advance()
{
return m_advance;
}
}
class OutlinedGlyph
{
Glyph m_outline_glyph;
Glyph m_inner_glyph;
this(dft.Font font, uint char_code, int outline_size)
{
m_outline_glyph = new Glyph(font, char_code, outline_size);
m_inner_glyph = new Glyph(font, char_code, 0);
}
void render()
{
glUniform4f(font_color_idx, 0.0, 0.0, 0.0, 1.0);
m_outline_glyph.render();
glUniform4f(font_color_idx, 1.0, 1.0, 1.0, 1.0);
m_inner_glyph.render();
}
@property int advance()
{
return m_inner_glyph.advance;
}
}
dft.Font font;
VAO spinny_vao;
void init()
{
glActiveTexture(GL_TEXTURE0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glClearColor (1.0, 0.7, 0.0, 0.0);
glViewport(0, 0, WIDTH, HEIGHT);
immutable string shader_src = `
#version 130
vertex:
uniform mat4 view;
in vec2 position;
@ -36,11 +154,12 @@ fragment:
gl_FragColor = vec4(color_i, 1.0);
}
`;
VAO vao = new VAO();
Shader program = new Shader("program", shader_src);
program.bind();
position_idx = program.get_attrib_location("position");
color_idx = program.get_attrib_location("color");
spinny_vao = new VAO();
spinny_vao.bind();
spinny_program = new Shader("spinny_program", shader_src);
spinny_program.bind();
position_idx = spinny_program.get_attrib_location("position");
color_idx = spinny_program.get_attrib_location("color");
float[] vertices = [0.4, 0.4, -0.4, 0.4, -0.4, -0.4, 0.4, -0.4];
float[] colors = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0];
ushort[] indices = [0, 1, 2, 3];
@ -54,19 +173,102 @@ fragment:
glVertexAttribPointer(color_idx, 3, GL_FLOAT, GL_FALSE, 0, null);
ElementBuffer ibo = new ElementBuffer(indices);
ibo.bind();
view_idx = program.get_uniform_location("view");
view_idx = spinny_program.get_uniform_location("view");
font = new dft.Font("assets/FreeSans.ttf", 60);
immutable string font_shader_src = `
#version 130
vertex:
/* Viewport width and height */
uniform ivec2 viewport_size;
/* Position of lower left corner of glyph */
uniform ivec2 position;
/* Vertex coordinates: x, y, s, t */
in vec4 coords;
/* Output texture coordinate: s, t */
out vec2 texture_coord_v;
/**
* Map coordinates such that:
* (0 .. viewport_size.[xy]) => (-1.0 .. 1.0)
*/
vec2 map_to_screen(vec2 position)
{
return 2.0 * position / viewport_size - 1.0;
}
void main(void)
{
gl_Position = vec4(map_to_screen(vec2(position) + coords.xy), 0, 1);
texture_coord_v = coords.zw;
}
fragment:
/* Texture coordinate: s, t */
in vec2 texture_coord_v;
/* Texture unit */
uniform sampler2D texture;
uniform vec4 color;
out vec4 frag_color;
void main(void)
{
frag_color = vec4(1, 1, 1, texture2D(texture, texture_coord_v).a) * color;
}
`;
font_program = new Shader("fontshader", font_shader_src);
font_program.bind();
font_coords_idx = font_program.get_attrib_location("coords");
font_viewport_size_idx = font_program.get_uniform_location("viewport_size");
font_position_idx = font_program.get_uniform_location("position");
font_texture_idx = font_program.get_uniform_location("texture");
font_color_idx = font_program.get_uniform_location("color");
glUniform2i(font_viewport_size_idx, WIDTH, HEIGHT);
glUniform1i(font_texture_idx, 0);
}
OutlinedGlyph[uint] outlined_glyphs;
OutlinedGlyph get_glyph(uint char_code)
{
if (char_code in outlined_glyphs)
{
return outlined_glyphs[char_code];
}
return outlined_glyphs[char_code] = new OutlinedGlyph(font, char_code, 80);
}
void draw_string(int x, int y, string s)
{
foreach (c; s)
{
glUniform2i(font_position_idx, x, y);
OutlinedGlyph outlined_glyph = get_glyph(c);
outlined_glyph.render();
x += outlined_glyph.advance;
}
}
void display(SDL_Window * window)
{
glClear(GL_COLOR_BUFFER_BIT);
spinny_program.bind();
spinny_vao.bind();
view_matrix.make_identity();
view_matrix.rotatez(SDL_GetTicks() / 500.0);
view_matrix.scale(HEIGHT / cast(float)WIDTH, 1.0, 1.0);
glUniformMatrix4fv(view_idx, 1, GL_TRUE, view_matrix.value_ptr);
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_SHORT, null);
font_program.bind();
draw_string(40, 40, "Hello world!");
SDL_GL_SwapWindow(window);
}