mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 16:20:06 +00:00
Create a text shader and font utility
This commit is contained in:
parent
817a59f741
commit
82b265b6d4
11 changed files with 473 additions and 5 deletions
179
scwx-qt/source/scwx/qt/util/font.cpp
Normal file
179
scwx-qt/source/scwx/qt/util/font.cpp
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
#include <scwx/qt/util/font.hpp>
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <ft2build.h>
|
||||
#include <QFile>
|
||||
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
namespace scwx
|
||||
{
|
||||
namespace qt
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
static const std::string logPrefix_ = "[scwx::qt::util::font] ";
|
||||
|
||||
static std::unordered_map<std::string, std::shared_ptr<Font>> fontMap_;
|
||||
|
||||
static FT_Library ft_ {nullptr};
|
||||
static std::mutex ftMutex_;
|
||||
|
||||
static bool InitializeFreeType();
|
||||
|
||||
class FontImpl
|
||||
{
|
||||
public:
|
||||
explicit FontImpl(const std::string& resource) :
|
||||
resource_(resource), fontData_(), face_ {nullptr}
|
||||
{
|
||||
}
|
||||
|
||||
~FontImpl() {}
|
||||
|
||||
const std::string resource_;
|
||||
|
||||
QByteArray fontData_;
|
||||
FT_Face face_;
|
||||
};
|
||||
|
||||
Font::Font(const std::string& resource) :
|
||||
p(std::make_unique<FontImpl>(resource))
|
||||
{
|
||||
}
|
||||
Font::~Font()
|
||||
{
|
||||
FT_Done_Face(p->face_);
|
||||
}
|
||||
|
||||
void Font::GenerateGlyphs(OpenGLFunctions& gl,
|
||||
std::unordered_map<char, Glyph>& glyphs,
|
||||
unsigned int height)
|
||||
{
|
||||
FT_Error error;
|
||||
FT_Face& face = p->face_;
|
||||
|
||||
// Allow single-byte texture colors
|
||||
gl.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
FT_Set_Pixel_Sizes(p->face_, 0, 48);
|
||||
|
||||
for (unsigned char c = 0; c < 128; c++)
|
||||
{
|
||||
if (glyphs.find(c) != glyphs.end())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning) << logPrefix_ << "Found glyph "
|
||||
<< static_cast<uint16_t>(c) << ", skipping";
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((error = FT_Load_Char(face, c, FT_LOAD_RENDER)) != 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << logPrefix_ << "Failed to load glyph "
|
||||
<< static_cast<uint16_t>(c) << ": " << error;
|
||||
continue;
|
||||
}
|
||||
|
||||
GLuint texture;
|
||||
gl.glGenTextures(1, &texture);
|
||||
gl.glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
gl.glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RED,
|
||||
face->glyph->bitmap.width,
|
||||
face->glyph->bitmap.rows,
|
||||
0,
|
||||
GL_RED,
|
||||
GL_UNSIGNED_BYTE,
|
||||
face->glyph->bitmap.buffer);
|
||||
|
||||
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
glyphs.insert(
|
||||
{c,
|
||||
Glyph {
|
||||
texture,
|
||||
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
|
||||
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
|
||||
face->glyph->advance.x}});
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Font> Font::Create(const std::string& resource)
|
||||
{
|
||||
std::shared_ptr<Font> font = nullptr;
|
||||
FT_Error error;
|
||||
|
||||
if (!InitializeFreeType())
|
||||
{
|
||||
return font;
|
||||
}
|
||||
|
||||
auto it = fontMap_.find(resource);
|
||||
if (it != fontMap_.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
QFile fontFile(resource.c_str());
|
||||
fontFile.open(QIODevice::ReadOnly);
|
||||
if (!fontFile.isOpen())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< logPrefix_ << "Could not read font file: " << resource;
|
||||
return font;
|
||||
}
|
||||
|
||||
font = std::make_shared<Font>(resource);
|
||||
font->p->fontData_ = fontFile.readAll();
|
||||
|
||||
{
|
||||
std::scoped_lock(ftMutex_);
|
||||
if ((error = FT_New_Memory_Face(
|
||||
ft_,
|
||||
reinterpret_cast<const FT_Byte*>(font->p->fontData_.data()),
|
||||
font->p->fontData_.size(),
|
||||
0,
|
||||
&font->p->face_)) != 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< logPrefix_ << "Failed to load font: " << error;
|
||||
font.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (font != nullptr)
|
||||
{
|
||||
fontMap_.insert({resource, font});
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
static bool InitializeFreeType()
|
||||
{
|
||||
std::scoped_lock(ftMutex_);
|
||||
|
||||
FT_Error error;
|
||||
|
||||
if (ft_ == nullptr && (error = FT_Init_FreeType(&ft_)) != 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< logPrefix_ << "Could not init FreeType library: " << error;
|
||||
ft_ = nullptr;
|
||||
}
|
||||
|
||||
return (ft_ != nullptr);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace qt
|
||||
} // namespace scwx
|
||||
Loading…
Add table
Add a link
Reference in a new issue