Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d4e9d1c906 | ||
|
ca9a238d98 |
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <glad/glad.h>
|
||||
|
||||
@@ -11,33 +13,35 @@ namespace JGL {
|
||||
|
||||
class JGL::CachedGlyph {
|
||||
private:
|
||||
GLuint texture = 0;
|
||||
char character;
|
||||
std::array<GLfloat, 12> texcoords;
|
||||
public:
|
||||
int x2offset = 0, y2offset = 0, w = 0, h = 0;
|
||||
float advanceX = 0, advanceY = 0;
|
||||
|
||||
//CachedGlyph(GLuint texture_id, char c);
|
||||
CachedGlyph(GLuint texture_id, char c, float x2o, float y2o, float w, float h, float advX, float advY);
|
||||
CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2o, float y2o, float w, float h, float advX, float advY);
|
||||
char getCharacter();
|
||||
const GLuint* getTexture();
|
||||
const std::array<GLfloat, 12> getTexCoords() const;
|
||||
};
|
||||
|
||||
class JGL::CachedFont {
|
||||
private:
|
||||
std::vector<CachedGlyph*> glyphs{};
|
||||
std::map<char, CachedGlyph*> glyphs;
|
||||
GLuint texture = 0;
|
||||
GLsizei texture_width = 0, texture_height = 0;
|
||||
unsigned int font_size = 0;
|
||||
unsigned int font_index = 0;
|
||||
public:
|
||||
void appendGlyph(CachedGlyph* glyph);
|
||||
void eraseGlyph(CachedGlyph* glyph);
|
||||
void eraseGlyph(char c);
|
||||
void eraseGlyph(GLuint texture_id);
|
||||
unsigned int getFontSize();
|
||||
unsigned int getFontIndex();
|
||||
CachedGlyph* getGlyph(char c);
|
||||
std::vector<CachedGlyph*> getGlyphs();
|
||||
CachedFont(unsigned int font_size, unsigned int font_index);
|
||||
std::map<char, CachedGlyph*> getGlyphs();
|
||||
const GLuint* getTexture();
|
||||
const GLsizei getTextureWidth() const;
|
||||
const GLsizei getTextureHeight() const;
|
||||
CachedFont(GLuint texture_id, GLsizei texture_width, GLsizei texture_height, unsigned int font_size, unsigned int font_index);
|
||||
};
|
||||
|
||||
class JGL::FontCache {
|
||||
@@ -47,7 +51,7 @@ public:
|
||||
std::vector<CachedFont*> getFonts();
|
||||
CachedFont* getFont(unsigned int font_size, unsigned int font_index);
|
||||
void appendFont(CachedFont* font);
|
||||
void newFont(unsigned int font_size, unsigned int font_index);
|
||||
void newFont(GLuint texture_id, GLsizei texture_width, GLsizei texture_height, unsigned int font_size, unsigned int font_index);
|
||||
void eraseFont(CachedFont* font);
|
||||
void purgeCache();
|
||||
};
|
||||
};
|
||||
|
@@ -7,12 +7,11 @@ char CachedGlyph::getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
const GLuint* CachedGlyph::getTexture() {
|
||||
return &texture;
|
||||
const std::array<GLfloat, 12> CachedGlyph::getTexCoords() const {
|
||||
return texcoords;
|
||||
}
|
||||
|
||||
CachedGlyph::CachedGlyph(GLuint texture_id, char c, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY) {
|
||||
texture = texture_id;
|
||||
CachedGlyph::CachedGlyph(char c, std::array<GLfloat, 12> texcoords, float x2offset, float y2offset, float w, float h, float advanceX, float advanceY) {
|
||||
character = c;
|
||||
this->x2offset = x2offset;
|
||||
this->y2offset = y2offset;
|
||||
@@ -20,13 +19,14 @@ CachedGlyph::CachedGlyph(GLuint texture_id, char c, float x2offset, float y2offs
|
||||
this->h = h;
|
||||
this->advanceX = advanceX;
|
||||
this->advanceY = advanceY;
|
||||
this->texcoords = texcoords;
|
||||
}
|
||||
|
||||
//TODO
|
||||
//Because most things shown would be english characters. We can cut down on the iteration time significantly
|
||||
//by putting each english character at the beginning of the list in order of how often they usually occur in text.
|
||||
void JGL::CachedFont::appendGlyph(JGL::CachedGlyph* glyph) {
|
||||
glyphs.push_back(glyph);
|
||||
glyphs.emplace(glyph->getCharacter(), glyph);
|
||||
}
|
||||
|
||||
unsigned int JGL::CachedFont::getFontSize() {
|
||||
@@ -38,63 +38,48 @@ unsigned int JGL::CachedFont::getFontIndex() {
|
||||
}
|
||||
|
||||
CachedGlyph* JGL::CachedFont::getGlyph(char c) {
|
||||
for (const auto& g : glyphs)
|
||||
if (c == g->getCharacter())
|
||||
return g;
|
||||
auto it = glyphs.find(c);
|
||||
if (it != glyphs.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CachedFont::CachedFont(unsigned int font_size, unsigned int font_index) {
|
||||
CachedFont::CachedFont(GLuint texture_id, GLsizei texture_width, GLsizei texture_height, unsigned int font_size, unsigned int font_index) {
|
||||
this->texture = texture_id;
|
||||
this->texture_width = texture_width;
|
||||
this->texture_height = texture_height;
|
||||
this->font_size = font_size;
|
||||
this->font_index = font_index;
|
||||
}
|
||||
|
||||
void CachedFont::eraseGlyph(CachedGlyph* glyph) {
|
||||
if (glyph == nullptr)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < glyphs.size(); i++)
|
||||
if (glyphs[i] == glyph)
|
||||
glDeleteTextures(1, glyphs[i]->getTexture()),
|
||||
delete glyphs[i],
|
||||
glyphs.erase(glyphs.begin() + i);
|
||||
}
|
||||
|
||||
void CachedFont::eraseGlyph(char c) {
|
||||
for (int i = 0; i < glyphs.size(); i++)
|
||||
if (glyphs[i]->getCharacter() == c)
|
||||
glDeleteTextures(1, glyphs[i]->getTexture()),
|
||||
delete glyphs[i],
|
||||
glyphs.erase(glyphs.begin() + i);
|
||||
}
|
||||
|
||||
void CachedFont::eraseGlyph(GLuint texture_id) {
|
||||
for (int i = 0; i < glyphs.size(); i++)
|
||||
if (glyphs[i]->getTexture() == &texture_id)
|
||||
glDeleteTextures(1, glyphs[i]->getTexture()),
|
||||
delete glyphs[i],
|
||||
glyphs.erase(glyphs.begin() + i);
|
||||
}
|
||||
|
||||
std::vector<CachedGlyph*> CachedFont::getGlyphs() {
|
||||
std::map<char, CachedGlyph*> CachedFont::getGlyphs() {
|
||||
return glyphs;
|
||||
}
|
||||
|
||||
const GLuint* CachedFont::getTexture() {
|
||||
return &texture;
|
||||
}
|
||||
|
||||
const GLsizei CachedFont::getTextureWidth() const {
|
||||
return texture_width;
|
||||
}
|
||||
|
||||
const GLsizei CachedFont::getTextureHeight() const {
|
||||
return texture_height;
|
||||
}
|
||||
|
||||
void FontCache::appendFont(CachedFont* font) {
|
||||
cachedFonts.push_back(font);
|
||||
}
|
||||
|
||||
void FontCache::newFont(unsigned int font_size, unsigned int font_index) {
|
||||
auto* font = new CachedFont(font_size, font_index);
|
||||
void FontCache::newFont(GLuint texture_id, GLsizei texture_width, GLsizei texture_height, unsigned int font_size, unsigned int font_index) {
|
||||
auto* font = new CachedFont(texture_id, texture_width, texture_height, font_size, font_index);
|
||||
cachedFonts.push_back(font);
|
||||
}
|
||||
|
||||
void FontCache::eraseFont(CachedFont* font) {
|
||||
for (int i = 0; i < cachedFonts.size(); i++) {
|
||||
if (cachedFonts[i] == font) {
|
||||
for (auto& g: cachedFonts[i]->getGlyphs())
|
||||
cachedFonts[i]->eraseGlyph(g);
|
||||
|
||||
delete cachedFonts[i];
|
||||
cachedFonts.erase(cachedFonts.begin() + i);
|
||||
}
|
||||
|
@@ -63,14 +63,8 @@ namespace JGL {
|
||||
Font font{};
|
||||
CachedFont* cachedFont = fontCache.getFont(size, font_index);
|
||||
|
||||
//If the font doesn't exist in the cache yet.
|
||||
if (!cachedFont) {
|
||||
fontCache.newFont(size, font_index);
|
||||
cachedFont = fontCache.getFont(size, font_index);
|
||||
}
|
||||
|
||||
//Set up the regular font.
|
||||
for (const auto& f : faces)
|
||||
for (const auto &f : faces)
|
||||
if (f.index == font_index)
|
||||
font = f;
|
||||
if (font.face == nullptr)
|
||||
@@ -78,64 +72,114 @@ namespace JGL {
|
||||
|
||||
FT_Set_Pixel_Sizes(font.face, 0, size);
|
||||
|
||||
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
|
||||
//If the font doesn't exist in the cache yet.
|
||||
if (!cachedFont) {
|
||||
GLuint texture_id;
|
||||
glGenTextures(1, &texture_id);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||
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_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
|
||||
std::vector<GLuint> textures(text.length());
|
||||
GLsizei width = 0;
|
||||
GLsizei max_height = 0;
|
||||
|
||||
//For each character
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
float x2, y2, w, h;
|
||||
//If the font is in the cache already.
|
||||
if (cachedFont->getGlyph(text.c_str()[i])) {
|
||||
CachedGlyph* glyph = cachedFont->getGlyph(text.c_str()[i]);
|
||||
FT_ULong charcode;
|
||||
FT_UInt gindex;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, *glyph->getTexture());
|
||||
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_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
//We have to loop over the available glyphs twice as we need the
|
||||
//final width and height of the texture before we can construct it
|
||||
//and subsequently upload the glyph data.
|
||||
|
||||
x2 = x + glyph->x2offset * scale;
|
||||
y2 = y - glyph->y2offset * scale; // Adjust y-coordinate
|
||||
w = glyph->w * scale;
|
||||
h = glyph->h * scale;
|
||||
x += glyph->advanceX * scale;
|
||||
y += glyph->advanceY * scale;
|
||||
|
||||
} else {
|
||||
if (FT_Load_Char(font.face, text.c_str()[i], FT_LOAD_RENDER))
|
||||
continue;
|
||||
charcode = FT_Get_First_Char(font.face, &gindex);
|
||||
//Strings are char-based so we only handle charcodes within the extended ASCII range.
|
||||
while (gindex != 0 && charcode < 255) {
|
||||
if (FT_Load_Char(font.face, charcode, FT_LOAD_RENDER))
|
||||
std::cout << "Error::FREETYPE: Failed to load charcode: " << charcode << std::endl;
|
||||
|
||||
FT_GlyphSlot g = font.face->glyph;
|
||||
glGenTextures(1, &textures.at(i));
|
||||
glBindTexture(GL_TEXTURE_2D, textures[i]);
|
||||
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_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, g->bitmap.width, g->bitmap.rows, 0, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
|
||||
|
||||
x2 = x + g->bitmap_left * scale;
|
||||
y2 = -y - g->bitmap_top * scale; // Adjust y-coordinate
|
||||
w = g->bitmap.width * scale;
|
||||
h = g->bitmap.rows * scale;
|
||||
x += (g->advance.x >> 6) * scale;
|
||||
y += (g->advance.y >> 6) * scale;
|
||||
cachedFont->appendGlyph(new CachedGlyph(textures.at(i), text.c_str()[i], g->bitmap_left, g->bitmap_top, g->bitmap.width, g->bitmap.rows, (g->advance.x >> 6), (g->advance.y >> 6)));
|
||||
width += g->bitmap.width;
|
||||
max_height = std::max(max_height, (GLsizei)g->bitmap.rows);
|
||||
charcode = FT_Get_Next_Char(font.face, charcode, &gindex);
|
||||
}
|
||||
|
||||
GLfloat vertices[12] = {x2, y2, x2, y2 + h, x2 + w, y2 + h,x2, y2, x2 + w, y2 + h, x2 + w, y2};
|
||||
GLfloat textureCoordinates[12] = {0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0};
|
||||
fontCache.newFont(texture_id, width, max_height, size, font_index);
|
||||
cachedFont = fontCache.getFont(size, font_index);
|
||||
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(GL_FLOAT) * 2, &textureCoordinates);
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(GL_FLOAT) * 2, &vertices);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, max_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
|
||||
|
||||
GLsizei xoffset = 0;
|
||||
|
||||
charcode = FT_Get_First_Char(font.face, &gindex);
|
||||
while (gindex != 0 && charcode < 255) {
|
||||
if (FT_Load_Char(font.face, charcode, FT_LOAD_RENDER))
|
||||
std::cout << "Error::FREETYPE: Failed to load charcode: " << charcode << std::endl;
|
||||
|
||||
FT_GlyphSlot g = font.face->glyph;
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, 0, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
|
||||
|
||||
GLfloat u0 = (GLfloat)xoffset / cachedFont->getTextureWidth();
|
||||
GLfloat u1 = u0 + (GLfloat)g->bitmap.width / cachedFont->getTextureWidth();
|
||||
|
||||
GLfloat v0 = 0.0f;
|
||||
GLfloat v1 = (GLfloat)g->bitmap.rows / cachedFont->getTextureHeight();
|
||||
|
||||
std::array<GLfloat, 12> texcoords = {
|
||||
u0, v0,
|
||||
u0, v1,
|
||||
u1, v1,
|
||||
u0, v0,
|
||||
u1, v1,
|
||||
u1, v0
|
||||
};
|
||||
|
||||
cachedFont->appendGlyph(new CachedGlyph((char)charcode, texcoords, g->bitmap_left, g->bitmap_top, g->bitmap.width, g->bitmap.rows, (g->advance.x >> 6), (g->advance.y >> 6)));
|
||||
|
||||
xoffset += g->bitmap.width;
|
||||
charcode = FT_Get_Next_Char(font.face, charcode, &gindex);
|
||||
}
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture
|
||||
|
||||
glColor4f(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f);
|
||||
|
||||
//Texture parameters are restored when the texture is bound
|
||||
glBindTexture(GL_TEXTURE_2D, *cachedFont->getTexture());
|
||||
|
||||
std::vector<GLfloat> vertices;
|
||||
std::vector<GLfloat> texcoords;
|
||||
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
float x2, y2, w, h;
|
||||
CachedGlyph *glyph = cachedFont->getGlyph(text.c_str()[i]);
|
||||
if (glyph == nullptr) continue;
|
||||
|
||||
x2 = x + glyph->x2offset * scale;
|
||||
y2 = y - glyph->y2offset * scale; // Adjust y-coordinate
|
||||
w = glyph->w * scale;
|
||||
h = glyph->h * scale;
|
||||
x += glyph->advanceX * scale;
|
||||
y += glyph->advanceY * scale;
|
||||
|
||||
std::array<GLfloat, 12> glyph_vertices = {
|
||||
x2, y2,
|
||||
x2, y2 + h,
|
||||
x2 + w, y2 + h,
|
||||
x2, y2,
|
||||
x2 + w, y2 + h,
|
||||
x2 + w, y2
|
||||
};
|
||||
auto glyph_texcoords = glyph->getTexCoords();
|
||||
|
||||
vertices.insert(vertices.end(), glyph_vertices.begin(), glyph_vertices.end());
|
||||
texcoords.insert(texcoords.end(), glyph_texcoords.begin(), glyph_texcoords.end());
|
||||
}
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(GLfloat) * 2, vertices.data());
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(GLfloat) * 2, texcoords.data());
|
||||
glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 2);
|
||||
}
|
||||
|
||||
void J2D::DrawString(const Color3& color, const std::string& text, float x, float y, float scale, u32 size, unsigned int font_index) {
|
||||
|
Reference in New Issue
Block a user