Font rendering v1

This commit is contained in:
Kevin Trogant 2022-10-24 13:14:45 +02:00
parent 7236465623
commit 5d555fd5a0
13 changed files with 5262 additions and 7 deletions

Binary file not shown.

View File

@ -4,6 +4,9 @@
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
AssetManager* AssetManager::ptr = nullptr;
bool AssetManager::loadTexture(const char* path, Texture* p_texture)
@ -24,3 +27,63 @@ bool AssetManager::loadTexture(const char* path, Texture* p_texture)
return true;
}
bool AssetManager::loadFontData(const char* path, float char_height, FontData* p_font)
{
FileBuffer ttf_data = {};
if (!loadFile(path, &ttf_data))
return false;
static unsigned char bitmap_memory[512 * 512];;
FontData font_data = {};
auto* data = reinterpret_cast<const unsigned char*>(ttf_data.data);
stbtt_BakeFontBitmap(data,
0,
char_height,
bitmap_memory,
512, 512,
32, 225, // latin-1 printable characters range
font_data.char_data);
font_data.char_height = char_height;
int off = stbtt_GetFontOffsetForIndex(data,0);
if (off < 0) {
releaseFileBuffer(ttf_data);
return false;
}
stbtt_fontinfo info;
if (!stbtt_InitFont(&info, data, off)) {
releaseFileBuffer(ttf_data);
return false;
}
font_data.scale_factor = stbtt_ScaleForPixelHeight(&info, char_height);
stbtt_GetFontVMetrics(&info, &font_data.ascent, &font_data.descent, &font_data.line_gap);
int x0, y0, x1, y1;
stbtt_GetFontBoundingBox(&info, &x0, &y0, &x1, &y1);
font_data.baseline_adjust = font_data.scale_factor * static_cast<float>(-y0);
*p_font = font_data;
releaseFileBuffer(ttf_data);
return true;
}
bool AssetManager::loadFontBitmap(const char *path, float char_height, Texture* p_texture)
{
FileBuffer ttf_data = {};
if (!loadFile(path, &ttf_data))
return false;
static unsigned char bitmap_memory[512 * 512];
stbtt_bakedchar char_data[225];
stbtt_BakeFontBitmap(reinterpret_cast<const unsigned char*>(ttf_data.data),
0,
char_height,
bitmap_memory,
512, 512,
32, 225, // latin-1 printable characters range
char_data);
*p_texture = Texture(512, 512, bitmap_memory, GL_R8);
releaseFileBuffer(ttf_data);
return true;
}

View File

@ -6,6 +6,7 @@
#include "Texture.h"
#include "Renderer.h"
#include "Font.h"
// Predefine, because that saves us from including android headers here
struct AAssetManager;
@ -51,6 +52,10 @@ public:
/// \return @c true if the texture was loaded successfully, @c false otherwise
bool loadTexture(const char* path, Texture* p_texture);
bool loadFontData(const char* path, float char_height, FontData* p_font);
bool loadFontBitmap(const char* path, float char_height, Texture* p_texture);
protected:
AssetManager() = default;
virtual ~AssetManager() = default;

View File

@ -13,7 +13,9 @@ set(kde_SRCS
StringRepository.cpp
Hash.h
Hash.cpp
GameState.h)
GameState.h
Font.cpp
Font.h)
if (ANDROID)
message(STATUS "Building for Android")

View File

@ -0,0 +1 @@
#include "Font.h"

25
app/src/main/cpp/Font.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef KRIMI_DINNER_ENGINE_FONT_H
#define KRIMI_DINNER_ENGINE_FONT_H
#include "stb_truetype.h"
#ifdef _WIN32
#include "glad.h"
#else
#include <GLES3/gl3.h>
#endif
/// Data that defines a font
struct FontData
{
stbtt_bakedchar char_data[225];
float char_height;
float scale_factor;
int ascent;
int descent;
int line_gap;
float baseline_adjust;
};
#endif

View File

@ -51,6 +51,7 @@ NativeEngine::NativeEngine(struct android_app *app)
// BEISPIELCODE
m_smiley = StringRepository::global->internString("smiley_PNG42.png");
m_smiley_pos = { 100, 100 };
AssetManager::ptr->loadFontData("Milky Honey.ttf", 24.f, &m_font);
// ENDE VOM BEISPIELCODE
}
@ -174,12 +175,12 @@ void NativeEngine::gameLoop()
// BEISPIELCODE
static float x = 1.f;
static float t = 1.f;
static float d = -0.01f;
x += d;
if ( x <= 0.f)
t += d;
if (t <= 0.f)
d *= -1.f;
else if (x >= 1.f)
else if (t >= 1.f)
d *= -1.f;
if (input_event_count > 0) {
@ -189,7 +190,35 @@ void NativeEngine::gameLoop()
}
Renderer::ptr->addRect(100, 100, 500, 500, 0.3f, 0.3f, 0.3f, 1.f);
Renderer::ptr->addRect(m_smiley_pos.x, m_smiley_pos.y, 500, 500, 0.f, x*x, 1.f - x*x, 1.f, m_smiley);
Renderer::ptr->addRect(m_smiley_pos.x, m_smiley_pos.y, 500, 500, 0.f, t*t, 1.f - t*t, 1.f, m_smiley);
float x = 50;
float y = 50;
float start_x = x;
float start_y = y;
y += m_font.baseline_adjust;
const char* text = "Hallo Welt";
for (const char* c = text; *c != '\0'; ++c) {
if (*c >= 32) {
stbtt_aligned_quad quad;
stbtt_GetBakedQuad(m_font.char_data, 512, 512, (int)*c - 32, &x, &y, &quad, 1);
float w = quad.x1 - quad.x0;
float h = quad.y1 - quad.y0;
Renderer::ptr->addFontRect(
x, y,
w, h,
1.f, 1.f, 1.f, 1.f,
24.f,
StringRepository::global->internString("Milky Honey.ttf"),
quad.s0, quad.t0,
quad.s1, quad.t1);
ALOGI("%f %f %f %f", quad.x0, quad.y0, quad.x1, quad.y1);
}
}
// ENDE VOM BEISPIELCODE
}

View File

@ -8,6 +8,8 @@
#include "StringRepository.h"
#include "GameState.h"
#include "Font.h"
#include <game-activity/native_app_glue/android_native_app_glue.h>
#include <EGL/egl.h>
@ -61,6 +63,9 @@ private:
StringHandle m_smiley;
Position m_smiley_pos;
StringHandle m_font_bitmap;
FontData m_font;
Position m_last_down;
bool m_in_motion;

View File

@ -14,8 +14,10 @@ static const char* g_vert_src = "#version 300 es
"layout (location = 2) in vec2 v_src_p0; \n"
"layout (location = 3) in vec2 v_src_p1; \n"
"layout (location = 4) in vec4 v_color; // color \n"
"layout (location = 5) in int v_expand_r; // \n"
"out vec4 f_color; \n"
"out vec2 f_uv; \n"
"flat out int f_expand_r; \n"
"const vec2 vertices[6] = vec2[6]( \n"
" vec2(-1,-1), \n"
" vec2(+1, -1), \n"
@ -39,17 +41,21 @@ static const char* g_vert_src = "#version 300 es
" 1.0); \n"
" f_color = v_color; \n"
" f_uv = src; \n"
" f_expand_r = v_expand_r; \n"
"} \n";
static const char* g_frag_src = "#version 300 es \n"
"precision mediump float; \n"
"in vec2 f_uv; \n"
"in vec4 f_color; \n"
"flat in int f_expand_r; \n"
"layout (location = 0) out vec4 frag_color; \n"
"uniform sampler2D s_texture; \n"
"void main() \n"
"{ \n"
" vec4 tex_color = texture(s_texture, f_uv); \n"
" if (f_expand_r == 1) \n"
" tex_color = tex_color.rrrr; \n"
" frag_color = tex_color * f_color; \n"
"} \n";
@ -112,16 +118,19 @@ Renderer::Renderer()
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), (GLvoid*)(4 * sizeof(float)));
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), (GLvoid*)(6 * sizeof(float)));
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(Rect), (GLvoid*)(8 * sizeof(float)));
glVertexAttribPointer(5, 1, GL_INT, GL_FALSE, sizeof(Rect), (GLvoid*)(12 * sizeof(float)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glVertexAttribDivisor(0, 1);
glVertexAttribDivisor(1, 1);
glVertexAttribDivisor(2, 1);
glVertexAttribDivisor(3, 1);
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);
glBindVertexArray(0);
// Dummy texture (1x1px white)
@ -203,16 +212,59 @@ void Renderer::addRect(float x,
rect.dst_p1_y = y + h;
rect.src_p0_x = src_x;
rect.src_p0_y = src_y;
rect.src_p1_x = src_w;
rect.src_p1_x = src_x + src_w;
rect.src_p1_y = src_y + src_h;
rect.r = r;
rect.g = g;
rect.b = b;
rect.a = a;
rect.expand_r = 0;
m_rects.push_back(rect);
m_draw_textures.push_back(texture);
}
void Renderer::addFontRect(float x,
float y,
float w,
float h,
float r,
float g,
float b,
float a,
float char_height,
StringHandle font,
float src_x0,
float src_y0,
float src_x1,
float src_y1)
{
if (m_textures.find(font) == m_textures.end()) {
Texture tex;
if (!AssetManager::ptr->loadFontBitmap(StringRepository::global->getString(font), char_height, &tex)) {
ALOGE("Failed to load texture");
return;
}
m_textures.insert(std::make_pair(font, tex));
}
Rect rect = {};
rect.dst_p0_x = x;
rect.dst_p0_y = y;
rect.dst_p1_x = x + w;
rect.dst_p1_y = y + h;
rect.src_p0_x = src_x0;
rect.src_p0_y = src_y0;
rect.src_p1_x = src_x1;
rect.src_p1_y = src_y1;
rect.r = r;
rect.g = g;
rect.b = b;
rect.a = a;
rect.expand_r = 1;
m_rects.push_back(rect);
m_draw_textures.push_back(font);
}
void Renderer::renderFrame(float width, float height)
{
assert(m_rects.size() == m_draw_textures.size());

View File

@ -114,6 +114,36 @@ public:
float src_w,
float src_h);
/// @brief Adds a tinted font rectangle to the screen
/// \param x X-coordinate of the top-left corner
/// \param y Y-coordinate of the top-left corner
/// \param w width in pixels
/// \param h height in pixels
/// \param r red [0,1]
/// \param g green [0,1]
/// \param b blue [0,1]
/// \param a alpha [0,1]
/// \param char_height Height of characters in pixels
/// \param font String handle in the global string repository of the ttf file name
/// \param src_x top left corner of the source rectangle inside the texture [0, 1]
/// \param src_y top left corner of the source rectangle inside the texture [0, 1]
/// \param src_w width of the source rectangle inside the texture [0, 1]
/// \param src_h height of the source rectangle inside the texture [0, 1]
void addFontRect(float x,
float y,
float w,
float h,
float r,
float g,
float b,
float a,
float char_height,
StringHandle font,
float src_x,
float src_y,
float src_w,
float src_h);
/// @brief Renders the frame
///
/// You don't need to call this.
@ -135,6 +165,7 @@ private:
float src_p0_x, src_p0_y;
float src_p1_x, src_p1_y;
float r, g, b, a;
int expand_r;
};
GLuint m_shader;

View File

@ -23,6 +23,29 @@ Texture::Texture(unsigned int width, unsigned int height, const void *data)
glBindTexture(GL_TEXTURE_2D, 0);
}
Texture::Texture(unsigned int width, unsigned int height, const void* data, GLint format)
: m_texture(0)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_R8,
static_cast<GLsizei>(width),
static_cast<GLsizei>(height),
0,
GL_RED,
GL_UNSIGNED_BYTE,
data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture::destroy()
{
glDeleteTextures(1, &m_texture);

View File

@ -18,6 +18,8 @@ class Texture
public:
Texture() : m_texture(0) {}
Texture(unsigned int width, unsigned int height, const void* data);
Texture(unsigned int width, unsigned int height, const void* data, GLint format);
Texture(const Texture&) = default;
Texture(Texture&&) = default;
Texture& operator=(const Texture&) = default;

File diff suppressed because it is too large Load Diff