mirror of
https://github.com/luanti-org/luanti.git
synced 2025-11-27 19:35:24 +01:00
Refactor parts of CGUITTFont
This commit is contained in:
@@ -270,6 +270,8 @@ u32 CNullDriver::getTextureCount() const
|
||||
ITexture *CNullDriver::addTexture(const core::dimension2d<u32> &size, const io::path &name, ECOLOR_FORMAT format)
|
||||
{
|
||||
IImage *image = new CImage(format, size);
|
||||
// the image data will be uploaded, so zero it
|
||||
memset(image->getData(), 0, image->getImageDataSizeInBytes());
|
||||
ITexture *t = addTexture(name, image);
|
||||
image->drop();
|
||||
return t;
|
||||
|
||||
@@ -185,7 +185,8 @@ void FontEngine::updateCache()
|
||||
getFont(FONT_SIZE_UNSPECIFIED, FM_Unspecified);
|
||||
}
|
||||
|
||||
void FontEngine::refresh() {
|
||||
void FontEngine::refresh()
|
||||
{
|
||||
clearCache();
|
||||
updateCache();
|
||||
updateSkin();
|
||||
@@ -193,7 +194,7 @@ void FontEngine::refresh() {
|
||||
|
||||
void FontEngine::setMediaFont(const std::string &name, const std::string &data)
|
||||
{
|
||||
static std::unordered_set<std::string> valid_names {
|
||||
const static std::unordered_set<std::string> valid_names{
|
||||
"regular", "bold", "italic", "bold_italic",
|
||||
"mono", "mono_bold", "mono_italic", "mono_bold_italic",
|
||||
};
|
||||
@@ -222,6 +223,20 @@ void FontEngine::clearMediaFonts()
|
||||
refresh();
|
||||
}
|
||||
|
||||
gui::SGUITTFace *FontEngine::getOrLoadFace(const std::string &filename)
|
||||
{
|
||||
auto it = m_local_faces.find(filename);
|
||||
if (it != m_local_faces.end())
|
||||
return it->second.get();
|
||||
|
||||
irr_ptr<gui::SGUITTFace> face(gui::SGUITTFace::loadFace(filename));
|
||||
if (!face)
|
||||
return nullptr;
|
||||
auto *ret = face.get();
|
||||
m_local_faces.emplace(filename, std::move(face));
|
||||
return ret;
|
||||
}
|
||||
|
||||
gui::IGUIFont *FontEngine::initFont(FontSpec spec)
|
||||
{
|
||||
assert(spec.mode != FM_Unspecified);
|
||||
@@ -261,7 +276,7 @@ gui::IGUIFont *FontEngine::initFont(FontSpec spec)
|
||||
|
||||
auto createFont = [&](gui::SGUITTFace *face) -> gui::CGUITTFont* {
|
||||
auto *font = gui::CGUITTFont::createTTFont(m_env,
|
||||
face, size, true, true, font_shadow,
|
||||
face, size, true, spec.mode != _FM_Fallback, font_shadow,
|
||||
font_shadow_alpha);
|
||||
|
||||
if (!font)
|
||||
@@ -309,11 +324,8 @@ gui::IGUIFont *FontEngine::initFont(FontSpec spec)
|
||||
infostream << "Creating new font: " << font_path.c_str()
|
||||
<< " " << size << "pt" << std::endl;
|
||||
|
||||
// Grab the face.
|
||||
if (auto *face = gui::SGUITTFace::loadFace(font_path)) {
|
||||
auto *font = createFont(face);
|
||||
face->drop();
|
||||
return font;
|
||||
if (auto *face = getOrLoadFace(font_path)) {
|
||||
return createFont(face);
|
||||
}
|
||||
|
||||
errorstream << "FontEngine: Cannot load '" << font_path <<
|
||||
|
||||
@@ -162,6 +162,8 @@ private:
|
||||
/** refresh after fonts have been changed */
|
||||
void refresh();
|
||||
|
||||
gui::SGUITTFace *getOrLoadFace(const std::string &filename);
|
||||
|
||||
/** callback to be used on change of font size setting */
|
||||
static void fontSettingChanged(const std::string &name, void *userdata);
|
||||
|
||||
@@ -174,6 +176,9 @@ private:
|
||||
/** internal storage for caching fonts of different size */
|
||||
std::map<unsigned int, gui::IGUIFont*> m_font_cache[FontSpec::MAX_VARIANTS];
|
||||
|
||||
/** local faces, indexed by file path */
|
||||
std::unordered_map<std::string, irr_ptr<gui::SGUITTFace>> m_local_faces;
|
||||
|
||||
/** media-provided faces, indexed by filename (without extension) */
|
||||
std::unordered_map<std::string, irr_ptr<gui::SGUITTFace>> m_media_faces;
|
||||
|
||||
|
||||
@@ -156,11 +156,7 @@ bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefini
|
||||
// (could be solved by more refactoring in Irrlicht, but not needed for now)
|
||||
sanity_check(definition.msaa < 1);
|
||||
|
||||
video::IImage *image = m_driver->createImage(definition.format, size);
|
||||
// Cannot use image->fill because it's not implemented for all formats.
|
||||
std::memset(image->getData(), 0, image->getDataSizeFromFormat(definition.format, size.Width, size.Height));
|
||||
*texture = m_driver->addTexture(definition.name.c_str(), image);
|
||||
image->drop();
|
||||
*texture = m_driver->addTexture(size, definition.name.c_str(), definition.format);
|
||||
} else if (definition.msaa > 0) {
|
||||
*texture = m_driver->addRenderTargetTextureMs(size, definition.msaa, definition.name.c_str(), definition.format);
|
||||
} else {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "irrlicht_changes/CGUITTFont.h"
|
||||
#include "util/string.h"
|
||||
#include "guiScrollBar.h"
|
||||
#include <IOSOperator.h>
|
||||
#include <string>
|
||||
|
||||
inline u32 clamp_u8(s32 value)
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
|
||||
#include "irr_ptr.h"
|
||||
#include "log.h"
|
||||
#include "filesys.h"
|
||||
#include "debug.h"
|
||||
#include "IGUIEnvironment.h"
|
||||
|
||||
@@ -46,9 +45,8 @@
|
||||
namespace gui
|
||||
{
|
||||
|
||||
std::map<io::path, SGUITTFace*> SGUITTFace::faces;
|
||||
FT_Library SGUITTFace::freetype_library = nullptr;
|
||||
std::size_t SGUITTFace::n_faces = 0;
|
||||
size_t SGUITTFace::n_faces = 0;
|
||||
|
||||
FT_Library SGUITTFace::getFreeTypeLibrary()
|
||||
{
|
||||
@@ -85,54 +83,28 @@ SGUITTFace* SGUITTFace::createFace(std::string &&buffer)
|
||||
auto ft = getFreeTypeLibrary();
|
||||
if (!ft)
|
||||
return nullptr;
|
||||
return (FT_New_Memory_Face(ft,
|
||||
bool ok = FT_New_Memory_Face(ft,
|
||||
reinterpret_cast<const FT_Byte*>(face->face_buffer.data()),
|
||||
face->face_buffer.size(), 0, &face->face))
|
||||
? nullptr : face.release();
|
||||
face->face_buffer.size(), 0, &face->face) == 0;
|
||||
return ok ? face.release() : nullptr;
|
||||
}
|
||||
|
||||
SGUITTFace* SGUITTFace::loadFace(const io::path &filename)
|
||||
{
|
||||
auto it = faces.find(filename);
|
||||
if (it != faces.end()) {
|
||||
it->second->grab();
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::string buffer;
|
||||
if (!fs::ReadFile(filename.c_str(), buffer, true)) {
|
||||
errorstream << "CGUITTFont: Reading file " << filename.c_str() << " failed." << std::endl;
|
||||
irr_ptr<SGUITTFace> face(new SGUITTFace(""));
|
||||
auto ft = getFreeTypeLibrary();
|
||||
if (!ft)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto *face = SGUITTFace::createFace(std::move(buffer));
|
||||
if (!face) {
|
||||
errorstream << "CGUITTFont: FT_New_Memory_Face failed." << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
faces.emplace(filename, face);
|
||||
return face;
|
||||
}
|
||||
|
||||
void SGUITTFace::dropFilename()
|
||||
{
|
||||
if (!filename.has_value())
|
||||
return;
|
||||
|
||||
auto it = faces.find(*filename);
|
||||
if (it == faces.end())
|
||||
return;
|
||||
|
||||
SGUITTFace* f = it->second;
|
||||
// Drop our face. If this was the last face, the destructor will clean up.
|
||||
if (f->drop())
|
||||
faces.erase(*filename);
|
||||
// Prefer FT_New_Face because it doesn't require loading everything
|
||||
// to memory.
|
||||
bool ok = FT_New_Face(ft, filename.c_str(), 0, &face->face) == 0;
|
||||
return ok ? face.release() : nullptr;
|
||||
}
|
||||
|
||||
video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const
|
||||
{
|
||||
// Make sure our casts to s32 in the loops below will not cause problems
|
||||
if ((s32)bits.rows < 0 || (s32)bits.width < 0)
|
||||
if (bits.rows > INT32_MAX || bits.width > INT32_MAX)
|
||||
FATAL_ERROR("Insane font glyph size");
|
||||
|
||||
// Determine what our texture size should be.
|
||||
@@ -140,8 +112,8 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide
|
||||
core::dimension2du d(bits.width + 1, bits.rows + 1);
|
||||
core::dimension2du texture_size;
|
||||
|
||||
// Create and load our image now.
|
||||
video::IImage* image = 0;
|
||||
// Turn bitmap into an image
|
||||
video::IImage *image = nullptr;
|
||||
switch (bits.pixel_mode)
|
||||
{
|
||||
case FT_PIXEL_MODE_MONO:
|
||||
@@ -233,6 +205,7 @@ void SGUITTGlyph::preload(u32 char_index, FT_Face face, CGUITTFont *parent, u32
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate slot from page
|
||||
glyph_page = parent->getLastGlyphPageIndex();
|
||||
u32 texture_side_length = page->texture->getOriginalSize().Width;
|
||||
core::vector2di page_position(
|
||||
@@ -242,35 +215,79 @@ void SGUITTGlyph::preload(u32 char_index, FT_Face face, CGUITTFont *parent, u32
|
||||
source_rect.UpperLeftCorner = page_position;
|
||||
source_rect.LowerRightCorner = core::vector2di(page_position.X + bits.width, page_position.Y + bits.rows);
|
||||
|
||||
page->dirty = true;
|
||||
++page->used_slots;
|
||||
--page->available_slots;
|
||||
|
||||
// We grab the glyph bitmap here so the data won't be removed when the next glyph is loaded.
|
||||
surface = createGlyphImage(bits, parent->getDriver());
|
||||
// createGlyphImage can now be called, the next preload() call will however
|
||||
// invalidate the data in `bits`.
|
||||
}
|
||||
|
||||
void SGUITTGlyph::unload()
|
||||
{
|
||||
if (surface)
|
||||
{
|
||||
surface->drop();
|
||||
surface = 0;
|
||||
}
|
||||
// reset isLoaded to false
|
||||
source_rect = core::recti();
|
||||
}
|
||||
|
||||
bool CGUITTGlyphPage::createPageTexture(const u8 pixel_mode,
|
||||
const core::dimension2du texture_size)
|
||||
{
|
||||
if (texture)
|
||||
return false;
|
||||
|
||||
bool flgmip = driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
|
||||
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
|
||||
bool flgcpy = driver->getTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY);
|
||||
driver->setTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY, true);
|
||||
|
||||
// Create texture
|
||||
switch (pixel_mode) {
|
||||
case FT_PIXEL_MODE_MONO:
|
||||
texture = driver->addTexture(texture_size, name, video::ECF_A1R5G5B5);
|
||||
break;
|
||||
case FT_PIXEL_MODE_GRAY:
|
||||
default:
|
||||
texture = driver->addTexture(texture_size, name, video::ECF_A8R8G8B8);
|
||||
break;
|
||||
}
|
||||
|
||||
// Restore texture creation flags
|
||||
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, flgmip);
|
||||
driver->setTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY, flgcpy);
|
||||
|
||||
return texture ? true : false;
|
||||
}
|
||||
|
||||
//! Updates the texture atlas with new glyphs.
|
||||
void CGUITTGlyphPage::updateTexture()
|
||||
{
|
||||
if (!isDirty())
|
||||
return;
|
||||
|
||||
void *ptr = texture->lock();
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
video::ECOLOR_FORMAT format = texture->getColorFormat();
|
||||
core::dimension2du size = texture->getOriginalSize();
|
||||
video::IImage* pageholder = driver->createImageFromData(format, size, ptr, true, false);
|
||||
|
||||
for (auto &it : glyph_to_be_paged)
|
||||
it.surface->copyTo(pageholder, it.glyph->source_rect.UpperLeftCorner);
|
||||
|
||||
pageholder->drop();
|
||||
texture->unlock();
|
||||
glyph_to_be_paged.clear();
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
|
||||
CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env,
|
||||
CGUITTFont *CGUITTFont::createTTFont(IGUIEnvironment *env,
|
||||
SGUITTFace *face, u32 size, bool antialias,
|
||||
bool transparency, u32 shadow, u32 shadow_alpha)
|
||||
bool preload, u32 shadow, u32 shadow_alpha)
|
||||
{
|
||||
CGUITTFont* font = new CGUITTFont(env);
|
||||
bool ret = font->load(face, size, antialias, transparency);
|
||||
if (!ret)
|
||||
{
|
||||
CGUITTFont *font = new CGUITTFont(env);
|
||||
bool ret = font->load(face, size, antialias, true, preload);
|
||||
if (!ret) {
|
||||
font->drop();
|
||||
return 0;
|
||||
}
|
||||
@@ -284,9 +301,9 @@ CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env,
|
||||
//////////////////////
|
||||
|
||||
//! Constructor.
|
||||
CGUITTFont::CGUITTFont(IGUIEnvironment *env)
|
||||
: use_monochrome(false), use_transparency(true), use_hinting(true), use_auto_hinting(true),
|
||||
batch_load_size(1)
|
||||
CGUITTFont::CGUITTFont(IGUIEnvironment *env) :
|
||||
use_monochrome(false), use_hinting(true), use_auto_hinting(true),
|
||||
batch_load_size(1)
|
||||
{
|
||||
|
||||
if (env) {
|
||||
@@ -300,16 +317,16 @@ batch_load_size(1)
|
||||
setInvisibleCharacters(L" ");
|
||||
}
|
||||
|
||||
bool CGUITTFont::load(SGUITTFace *face, const u32 size, const bool antialias, const bool transparency)
|
||||
bool CGUITTFont::load(SGUITTFace *face, const u32 size, const bool antialias,
|
||||
const bool transparency, const bool preload)
|
||||
{
|
||||
if (!Driver || size == 0 || !face)
|
||||
return false;
|
||||
|
||||
this->size = size;
|
||||
|
||||
// Update the font loading flags when the font is first loaded.
|
||||
// Update the font loading flags when the font is first loaded
|
||||
this->use_monochrome = !antialias;
|
||||
this->use_transparency = transparency;
|
||||
update_load_flags();
|
||||
|
||||
// Store our face.
|
||||
@@ -327,11 +344,13 @@ bool CGUITTFont::load(SGUITTFace *face, const u32 size, const bool antialias, co
|
||||
Glyphs.clear();
|
||||
Glyphs.set_used(tt_face->num_glyphs);
|
||||
|
||||
// Cache the first 127 ascii characters.
|
||||
// Cache the first 127 ASCII characters
|
||||
if (preload) {
|
||||
u32 old_size = batch_load_size;
|
||||
batch_load_size = 127;
|
||||
getGlyphIndexByChar((char32_t)0);
|
||||
getGlyphIndexByChar(U' '); // char needs to exist, so pick space
|
||||
batch_load_size = old_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -366,31 +385,27 @@ void CGUITTFont::update_glyph_pages() const
|
||||
{
|
||||
for (u32 i = 0; i != Glyph_Pages.size(); ++i)
|
||||
{
|
||||
if (Glyph_Pages[i]->dirty)
|
||||
if (Glyph_Pages[i]->isDirty())
|
||||
Glyph_Pages[i]->updateTexture();
|
||||
}
|
||||
}
|
||||
|
||||
CGUITTGlyphPage* CGUITTFont::getLastGlyphPage() const
|
||||
{
|
||||
CGUITTGlyphPage* page = 0;
|
||||
if (Glyph_Pages.empty())
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
page = Glyph_Pages[getLastGlyphPageIndex()];
|
||||
return nullptr;
|
||||
CGUITTGlyphPage *page = Glyph_Pages[getLastGlyphPageIndex()];
|
||||
if (page->available_slots == 0)
|
||||
page = 0;
|
||||
}
|
||||
return nullptr;
|
||||
return page;
|
||||
}
|
||||
|
||||
CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8 pixel_mode)
|
||||
{
|
||||
CGUITTGlyphPage* page = 0;
|
||||
CGUITTGlyphPage *page = nullptr;
|
||||
|
||||
// Name of our page.
|
||||
io::path name("TTFontGlyphPage_");
|
||||
io::path name("glyph_");
|
||||
name += tt_face->family_name;
|
||||
name += ".";
|
||||
name += tt_face->style_name;
|
||||
@@ -403,19 +418,15 @@ CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8 pixel_mode)
|
||||
page = new CGUITTGlyphPage(Driver, name);
|
||||
|
||||
// Determine our maximum texture size.
|
||||
// If we keep getting 0, set it to 1024x1024, as that number is pretty safe.
|
||||
core::dimension2du max_texture_size = max_page_texture_size;
|
||||
if (max_texture_size.Width == 0 || max_texture_size.Height == 0)
|
||||
max_texture_size = Driver->getMaxTextureSize();
|
||||
if (max_texture_size.Width == 0 || max_texture_size.Height == 0)
|
||||
max_texture_size = core::dimension2du(1024, 1024);
|
||||
core::dimension2du max_texture_size = Driver->getMaxTextureSize();
|
||||
|
||||
// We want to try to put at least 144 glyphs on a single texture.
|
||||
// We want to try to put at least 180 glyphs on a single texture.
|
||||
// magic number = floor(texture_size / sqrt(180))
|
||||
core::dimension2du page_texture_size;
|
||||
if (size <= 21) page_texture_size = core::dimension2du(256, 256);
|
||||
else if (size <= 42) page_texture_size = core::dimension2du(512, 512);
|
||||
else if (size <= 84) page_texture_size = core::dimension2du(1024, 1024);
|
||||
else if (size <= 168) page_texture_size = core::dimension2du(2048, 2048);
|
||||
if (size <= 19) page_texture_size = core::dimension2du(256, 256);
|
||||
else if (size <= 38) page_texture_size = core::dimension2du(512, 512);
|
||||
else if (size <= 76) page_texture_size = core::dimension2du(1024, 1024);
|
||||
else if (size <= 152) page_texture_size = core::dimension2du(2048, 2048);
|
||||
else page_texture_size = core::dimension2du(4096, 4096);
|
||||
|
||||
if (page_texture_size.Width > max_texture_size.Width || page_texture_size.Height > max_texture_size.Height)
|
||||
@@ -428,21 +439,12 @@ CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8 pixel_mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (page)
|
||||
{
|
||||
// Determine the number of glyph slots on the page and add it to the list of pages.
|
||||
// Determine the number of glyph slots on the page and add it to the list of pages
|
||||
page->available_slots = (page_texture_size.Width / size) * (page_texture_size.Height / size);
|
||||
Glyph_Pages.push_back(page);
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
void CGUITTFont::setTransparency(const bool flag)
|
||||
{
|
||||
use_transparency = flag;
|
||||
reset_images();
|
||||
}
|
||||
|
||||
void CGUITTFont::setMonochrome(const bool flag)
|
||||
{
|
||||
use_monochrome = flag;
|
||||
@@ -467,6 +469,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position
|
||||
void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& position, bool hcenter, bool vcenter, const core::rect<s32>* clip)
|
||||
{
|
||||
const auto &colors = text.getColors();
|
||||
constexpr video::SColor fallback_color(255, 255, 255, 255); // if colors is too short
|
||||
|
||||
if (!Driver)
|
||||
return;
|
||||
@@ -499,9 +502,6 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
|
||||
const std::u32string utext = convertWCharToU32String(text.c_str());
|
||||
const u32 lineHeight = getLineHeight();
|
||||
|
||||
// Key: Glyph page index. Value: Arrays relevant for rendering
|
||||
std::map<u32, CGUITTGlyphPage*> Render_Map;
|
||||
|
||||
// Start parsing characters.
|
||||
// The same logic is applied to `CGUITTFont::getDimension`
|
||||
char32_t previousChar = 0;
|
||||
@@ -538,7 +538,7 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
|
||||
const u32 width = getWidthFromCharacter(currentChar);
|
||||
|
||||
// Skip whitespace characters
|
||||
if (InvisibleChars.find_first_of(currentChar) != std::u32string::npos)
|
||||
if (InvisibleChars.find(currentChar) != std::u32string::npos)
|
||||
goto skip_invisible;
|
||||
|
||||
if (clip) {
|
||||
@@ -568,14 +568,10 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
|
||||
offset += getKerning(currentChar, previousChar);
|
||||
|
||||
// Determine rendering information.
|
||||
CGUITTGlyphPage* const page = Glyph_Pages[glyph->glyph_page];
|
||||
page->render_positions.push_back(core::position2di(offset.X + offx, offset.Y + offy));
|
||||
CGUITTGlyphPage *const page = Glyph_Pages[glyph->glyph_page];
|
||||
page->render_positions.emplace_back(offset.X + offx, offset.Y + offy);
|
||||
page->render_source_rects.push_back(glyph->source_rect);
|
||||
if (i < colors.size())
|
||||
page->render_colors.push_back(colors[i]);
|
||||
else
|
||||
page->render_colors.push_back(video::SColor(255,255,255,255));
|
||||
Render_Map[glyph->glyph_page] = page;
|
||||
page->render_colors.push_back(i < colors.size() ? colors[i] : fallback_color);
|
||||
}
|
||||
else if (fallback)
|
||||
{
|
||||
@@ -587,7 +583,7 @@ void CGUITTFont::draw(const EnrichedString &text, const core::rect<s32>& positio
|
||||
|
||||
fallback->draw(core::stringw(l1),
|
||||
core::rect<s32>({offset.X-1, offset.Y-1}, position.LowerRightCorner), // ???
|
||||
i < colors.size() ? colors[i] : video::SColor(255, 255, 255, 255),
|
||||
i < colors.size() ? colors[i] : fallback_color,
|
||||
false, false, clip);
|
||||
}
|
||||
|
||||
@@ -600,9 +596,14 @@ skip_invisible:
|
||||
update_glyph_pages();
|
||||
core::array<core::vector2di> tmp_positions;
|
||||
core::array<core::recti> tmp_source_rects;
|
||||
for (const auto &it : Render_Map)
|
||||
{
|
||||
CGUITTGlyphPage *page = it.second;
|
||||
for (u32 page_i = 0; page_i < Glyph_Pages.size(); ++page_i) {
|
||||
CGUITTGlyphPage *page = Glyph_Pages[page_i];
|
||||
|
||||
if (page->render_positions.empty())
|
||||
continue;
|
||||
|
||||
assert(page->render_positions.size() == page->render_colors.size());
|
||||
assert(page->render_positions.size() == page->render_source_rects.size());
|
||||
|
||||
// render runs of matching color in batch
|
||||
video::SColor colprev;
|
||||
@@ -616,9 +617,6 @@ skip_invisible:
|
||||
tmp_source_rects.set_data(&page->render_source_rects[ibegin], i - ibegin);
|
||||
--i;
|
||||
|
||||
if (!use_transparency)
|
||||
colprev.color |= 0xff000000;
|
||||
|
||||
if (shadow_offset) {
|
||||
for (size_t i = 0; i < tmp_positions.size(); ++i)
|
||||
tmp_positions[i] += core::vector2di(shadow_offset, shadow_offset);
|
||||
@@ -746,13 +744,14 @@ u32 CGUITTFont::getGlyphIndexByChar(char32_t c) const
|
||||
return 0;
|
||||
|
||||
// If our glyph is already loaded, don't bother doing any batch loading code.
|
||||
if (glyph != 0 && Glyphs[glyph - 1].isLoaded())
|
||||
if (Glyphs[glyph - 1].isLoaded())
|
||||
return glyph;
|
||||
|
||||
// Determine our batch loading positions.
|
||||
u32 half_size = (batch_load_size / 2);
|
||||
u32 start_pos = 0;
|
||||
if (c > half_size) start_pos = c - half_size;
|
||||
if (c > half_size)
|
||||
start_pos = c - half_size;
|
||||
u32 end_pos = start_pos + batch_load_size;
|
||||
|
||||
// Load all our characters.
|
||||
@@ -769,7 +768,8 @@ u32 CGUITTFont::getGlyphIndexByChar(char32_t c) const
|
||||
{
|
||||
auto *this2 = const_cast<CGUITTFont*>(this); // oh well
|
||||
glyph.preload(char_index, tt_face, this2, size, load_flags);
|
||||
Glyph_Pages[glyph.glyph_page]->pushGlyphToBePaged(&glyph);
|
||||
auto *surface = glyph.createGlyphImage(tt_face->glyph->bitmap, Driver);
|
||||
Glyph_Pages[glyph.glyph_page]->pushGlyphToBePaged(&glyph, surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -876,51 +876,6 @@ void CGUITTFont::setInvisibleCharacters(const wchar_t *s)
|
||||
InvisibleChars = convertWCharToU32String(s);
|
||||
}
|
||||
|
||||
video::IImage* CGUITTFont::createTextureFromChar(const char32_t& ch)
|
||||
{
|
||||
// This character allows us to print something to the screen for unknown, unrecognizable, or
|
||||
// unrepresentable characters. See Unicode spec.
|
||||
const char32_t UTF_REPLACEMENT_CHARACTER = 0xFFFD;
|
||||
|
||||
u32 n = getGlyphIndexByChar(ch);
|
||||
if (n == 0)
|
||||
n = getGlyphIndexByChar(UTF_REPLACEMENT_CHARACTER);
|
||||
|
||||
const SGUITTGlyph& glyph = Glyphs[n-1];
|
||||
CGUITTGlyphPage* page = Glyph_Pages[glyph.glyph_page];
|
||||
|
||||
if (page->dirty)
|
||||
page->updateTexture();
|
||||
|
||||
video::ITexture* tex = page->texture;
|
||||
|
||||
// Acquire a read-only lock of the corresponding page texture.
|
||||
void* ptr = tex->lock(video::ETLM_READ_ONLY);
|
||||
if (!ptr)
|
||||
return nullptr;
|
||||
|
||||
video::ECOLOR_FORMAT format = tex->getColorFormat();
|
||||
core::dimension2du tex_size = tex->getOriginalSize();
|
||||
video::IImage* pageholder = Driver->createImageFromData(format, tex_size, ptr, true, false);
|
||||
|
||||
// Copy the image data out of the page texture.
|
||||
core::dimension2du glyph_size(glyph.source_rect.getSize());
|
||||
video::IImage* image = Driver->createImage(format, glyph_size);
|
||||
pageholder->copyTo(image, core::position2di(0, 0), glyph.source_rect);
|
||||
|
||||
tex->unlock();
|
||||
pageholder->drop();
|
||||
return image;
|
||||
}
|
||||
|
||||
video::ITexture* CGUITTFont::getPageTextureByIndex(const u32& page_index) const
|
||||
{
|
||||
if (page_index < Glyph_Pages.size())
|
||||
return Glyph_Pages[page_index]->texture;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::u32string CGUITTFont::convertWCharToU32String(const wchar_t* const charArray) const
|
||||
{
|
||||
static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4, "unexpected wchar size");
|
||||
|
||||
@@ -39,72 +39,49 @@
|
||||
#include "IGUIEnvironment.h"
|
||||
#include "IGUIFont.h"
|
||||
#include "IVideoDriver.h"
|
||||
#include "IrrlichtDevice.h"
|
||||
#include "util/enriched_string.h"
|
||||
#include "util/basic_macros.h"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class CGUITTFont;
|
||||
|
||||
// Manages the FT_Face cache.
|
||||
struct SGUITTFace : public IReferenceCounted
|
||||
{
|
||||
private:
|
||||
|
||||
static std::map<io::path, SGUITTFace*> faces;
|
||||
static FT_Library freetype_library;
|
||||
static std::size_t n_faces;
|
||||
static size_t n_faces;
|
||||
|
||||
static FT_Library getFreeTypeLibrary();
|
||||
|
||||
// This holds the font file data for this face.
|
||||
// Must not be deallocated until we are done with the face!
|
||||
std::string face_buffer;
|
||||
|
||||
public:
|
||||
|
||||
SGUITTFace(std::string &&buffer);
|
||||
|
||||
~SGUITTFace();
|
||||
|
||||
std::optional<std::string> filename;
|
||||
|
||||
FT_Face face;
|
||||
/// Must not be deallocated until we are done with the face!
|
||||
std::string face_buffer;
|
||||
|
||||
static SGUITTFace* createFace(std::string &&buffer);
|
||||
|
||||
static SGUITTFace* loadFace(const io::path &filename);
|
||||
|
||||
void dropFilename();
|
||||
};
|
||||
class CGUITTFont;
|
||||
|
||||
//! Structure representing a single TrueType glyph.
|
||||
struct SGUITTGlyph
|
||||
{
|
||||
//! Constructor.
|
||||
SGUITTGlyph() :
|
||||
glyph_page(0),
|
||||
source_rect(),
|
||||
offset(),
|
||||
advance(),
|
||||
surface(0)
|
||||
advance()
|
||||
{}
|
||||
|
||||
DISABLE_CLASS_COPY(SGUITTGlyph);
|
||||
|
||||
//! This class would be trivially copyable except for the reference count on `surface`.
|
||||
SGUITTGlyph(SGUITTGlyph &&other) noexcept :
|
||||
glyph_page(other.glyph_page),
|
||||
source_rect(other.source_rect),
|
||||
offset(other.offset),
|
||||
advance(other.advance),
|
||||
surface(other.surface)
|
||||
{
|
||||
other.surface = 0;
|
||||
}
|
||||
|
||||
//! Destructor.
|
||||
~SGUITTGlyph() { unload(); }
|
||||
|
||||
//! If true, the glyph has been loaded.
|
||||
@@ -113,17 +90,13 @@ namespace gui
|
||||
}
|
||||
|
||||
//! Preload the glyph.
|
||||
//! The preload process occurs when the program tries to cache the glyph from FT_Library.
|
||||
//! However, it simply defines the SGUITTGlyph's properties and will only create the page
|
||||
//! textures if necessary. The actual creation of the textures should only occur right
|
||||
//! before the batch draw call.
|
||||
void preload(u32 char_index, FT_Face face, CGUITTFont *parent, u32 font_size, const FT_Int32 loadFlags);
|
||||
|
||||
//! Unloads the glyph.
|
||||
void unload();
|
||||
|
||||
//! Creates the IImage object from the FT_Bitmap.
|
||||
video::IImage* createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const;
|
||||
video::IImage* createGlyphImage(const FT_Bitmap &bits, video::IVideoDriver *driver) const;
|
||||
|
||||
//! The page the glyph is on.
|
||||
u32 glyph_page;
|
||||
@@ -136,107 +109,74 @@ namespace gui
|
||||
|
||||
//! Glyph advance information.
|
||||
core::vector2di advance;
|
||||
};
|
||||
|
||||
//! This is just the temporary image holder. After this glyph is paged,
|
||||
//! it will be dropped.
|
||||
mutable video::IImage* surface;
|
||||
//! Wrapper struct for a preloaded glyph
|
||||
struct SGUITTGlyphPending {
|
||||
SGUITTGlyphPending(const SGUITTGlyph *glyph, video::IImage *surface) noexcept :
|
||||
glyph(glyph), surface(surface)
|
||||
{}
|
||||
~SGUITTGlyphPending() {
|
||||
if (surface)
|
||||
surface->drop();
|
||||
}
|
||||
|
||||
DISABLE_CLASS_COPY(SGUITTGlyphPending)
|
||||
|
||||
SGUITTGlyphPending(SGUITTGlyphPending &&other) noexcept :
|
||||
glyph(other.glyph), surface(other.surface)
|
||||
{
|
||||
other.surface = nullptr;
|
||||
}
|
||||
|
||||
const SGUITTGlyph *glyph;
|
||||
video::IImage *surface;
|
||||
};
|
||||
|
||||
//! Holds a sheet of glyphs.
|
||||
class CGUITTGlyphPage
|
||||
{
|
||||
public:
|
||||
CGUITTGlyphPage(video::IVideoDriver* Driver, const io::path& texture_name) :texture(0), available_slots(0), used_slots(0), dirty(false), driver(Driver), name(texture_name) {}
|
||||
CGUITTGlyphPage(video::IVideoDriver *Driver, const io::path &texture_name) :
|
||||
texture(0), available_slots(0), used_slots(0),
|
||||
driver(Driver), name(texture_name)
|
||||
{}
|
||||
~CGUITTGlyphPage()
|
||||
{
|
||||
if (texture)
|
||||
{
|
||||
if (driver)
|
||||
driver->removeTexture(texture);
|
||||
else
|
||||
texture->drop();
|
||||
}
|
||||
}
|
||||
|
||||
//! Create the actual page texture,
|
||||
bool createPageTexture(const u8& pixel_mode, const core::dimension2du& texture_size)
|
||||
{
|
||||
if( texture )
|
||||
return false;
|
||||
|
||||
bool flgmip = driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
|
||||
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
|
||||
bool flgcpy = driver->getTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY);
|
||||
driver->setTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY, true);
|
||||
|
||||
// Set the texture color format.
|
||||
switch (pixel_mode)
|
||||
{
|
||||
case FT_PIXEL_MODE_MONO:
|
||||
texture = driver->addTexture(texture_size, name, video::ECF_A1R5G5B5);
|
||||
break;
|
||||
case FT_PIXEL_MODE_GRAY:
|
||||
default:
|
||||
texture = driver->addTexture(texture_size, name, video::ECF_A8R8G8B8);
|
||||
break;
|
||||
}
|
||||
|
||||
// Restore our texture creation flags.
|
||||
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, flgmip);
|
||||
driver->setTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY, flgcpy);
|
||||
|
||||
return texture ? true : false;
|
||||
}
|
||||
bool createPageTexture(u8 pixel_mode, core::dimension2du texture_size);
|
||||
|
||||
//! Add the glyph to a list of glyphs to be paged.
|
||||
//! This collection will be cleared after updateTexture is called.
|
||||
void pushGlyphToBePaged(const SGUITTGlyph* glyph)
|
||||
void pushGlyphToBePaged(const SGUITTGlyph *glyph, video::IImage *surface)
|
||||
{
|
||||
glyph_to_be_paged.push_back(glyph);
|
||||
if (!glyph || !surface)
|
||||
return;
|
||||
glyph_to_be_paged.emplace_back(glyph, surface);
|
||||
}
|
||||
|
||||
inline bool isDirty() const
|
||||
{
|
||||
return !glyph_to_be_paged.empty();
|
||||
}
|
||||
|
||||
//! Updates the texture atlas with new glyphs.
|
||||
void updateTexture()
|
||||
{
|
||||
if (!dirty)
|
||||
return;
|
||||
|
||||
void* ptr = texture->lock();
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
video::ECOLOR_FORMAT format = texture->getColorFormat();
|
||||
core::dimension2du size = texture->getOriginalSize();
|
||||
video::IImage* pageholder = driver->createImageFromData(format, size, ptr, true, false);
|
||||
|
||||
for (u32 i = 0; i < glyph_to_be_paged.size(); ++i)
|
||||
{
|
||||
const SGUITTGlyph* glyph = glyph_to_be_paged[i];
|
||||
if (glyph && glyph->surface)
|
||||
{
|
||||
glyph->surface->copyTo(pageholder, glyph->source_rect.UpperLeftCorner);
|
||||
glyph->surface->drop();
|
||||
glyph->surface = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pageholder->drop();
|
||||
texture->unlock();
|
||||
glyph_to_be_paged.clear();
|
||||
dirty = false;
|
||||
}
|
||||
void updateTexture();
|
||||
|
||||
video::ITexture* texture;
|
||||
u32 available_slots;
|
||||
u32 used_slots;
|
||||
bool dirty;
|
||||
|
||||
core::array<core::vector2di> render_positions;
|
||||
core::array<core::recti> render_source_rects;
|
||||
core::array<video::SColor> render_colors;
|
||||
std::vector<core::vector2di> render_positions;
|
||||
std::vector<core::recti> render_source_rects;
|
||||
std::vector<video::SColor> render_colors;
|
||||
|
||||
private:
|
||||
core::array<const SGUITTGlyph*> glyph_to_be_paged;
|
||||
std::vector<SGUITTGlyphPending> glyph_to_be_paged;
|
||||
video::IVideoDriver* driver;
|
||||
io::path name;
|
||||
};
|
||||
@@ -249,11 +189,11 @@ namespace gui
|
||||
//! \param env The IGUIEnvironment the font loads out of.
|
||||
//! \param size The size of the font glyphs in pixels. Since this is the size of the individual glyphs, the true height of the font may change depending on the characters used.
|
||||
//! \param antialias set the use_monochrome (opposite to antialias) flag
|
||||
//! \param transparency set the use_transparency flag
|
||||
//! \param preload create texture with important glyphs directly
|
||||
//! \return Returns a pointer to a CGUITTFont. Will return 0 if the font failed to load.
|
||||
static CGUITTFont* createTTFont(IGUIEnvironment *env,
|
||||
SGUITTFace *face, u32 size, bool antialias = true,
|
||||
bool transparency = true, u32 shadow = 0, u32 shadow_alpha = 255);
|
||||
bool preload = true, u32 shadow = 0, u32 shadow_alpha = 255);
|
||||
|
||||
//! Destructor
|
||||
virtual ~CGUITTFont();
|
||||
@@ -261,15 +201,9 @@ namespace gui
|
||||
//! Sets the amount of glyphs to batch load.
|
||||
void setBatchLoadSize(u32 batch_size) { batch_load_size = batch_size; }
|
||||
|
||||
//! Sets the maximum texture size for a page of glyphs.
|
||||
void setMaxPageTextureSize(const core::dimension2du& texture_size) { max_page_texture_size = texture_size; }
|
||||
|
||||
//! Get the font size.
|
||||
u32 getFontSize() const { return size; }
|
||||
|
||||
//! Check the font's transparency.
|
||||
bool isTransparent() const { return use_transparency; }
|
||||
|
||||
//! Check if the font auto-hinting is enabled.
|
||||
//! Auto-hinting is FreeType's built-in font hinting engine.
|
||||
bool useAutoHinting() const { return use_auto_hinting; }
|
||||
@@ -281,11 +215,6 @@ namespace gui
|
||||
//! The font can either be a 256 color grayscale font, or a 2 color monochrome font.
|
||||
bool useMonochrome() const { return use_monochrome; }
|
||||
|
||||
//! Tells the font to allow transparency when rendering.
|
||||
//! Default: true.
|
||||
//! \param flag If true, the font draws using transparency.
|
||||
void setTransparency(const bool flag);
|
||||
|
||||
//! Tells the font to use monochrome rendering.
|
||||
//! Default: false.
|
||||
//! \param flag If true, the font draws using a monochrome image. If false, the font uses a grayscale image.
|
||||
@@ -339,27 +268,14 @@ namespace gui
|
||||
//! Set font that should be used for glyphs not present in ours
|
||||
void setFallback(gui::IGUIFont* font) { fallback = font; }
|
||||
|
||||
//! Create corresponding character's software image copy from the font,
|
||||
//! so you can use this data just like any ordinary video::IImage.
|
||||
//! \param ch The character you need
|
||||
video::IImage* createTextureFromChar(const char32_t& ch);
|
||||
|
||||
//! This function is for debugging mostly. If the page doesn't exist it returns zero.
|
||||
//! \param page_index Simply return the texture handle of a given page index.
|
||||
video::ITexture* getPageTextureByIndex(const u32& page_index) const;
|
||||
|
||||
inline video::IVideoDriver *getDriver() const { return Driver; }
|
||||
|
||||
inline s32 getAscender() const { return font_metrics.ascender; }
|
||||
|
||||
protected:
|
||||
bool use_monochrome;
|
||||
bool use_transparency;
|
||||
bool use_hinting;
|
||||
bool use_auto_hinting;
|
||||
u32 size;
|
||||
u32 batch_load_size;
|
||||
core::dimension2du max_page_texture_size;
|
||||
|
||||
private:
|
||||
// Helper functions for the same-named public member functions above
|
||||
@@ -371,17 +287,23 @@ namespace gui
|
||||
std::u32string convertWCharToU32String(const wchar_t* const) const;
|
||||
|
||||
CGUITTFont(IGUIEnvironment *env);
|
||||
bool load(SGUITTFace *face, const u32 size, const bool antialias, const bool transparency);
|
||||
bool load(SGUITTFace *face, const u32 size, const bool antialias,
|
||||
const bool transparency, const bool preload);
|
||||
|
||||
void reset_images();
|
||||
void update_glyph_pages() const;
|
||||
void update_load_flags()
|
||||
{
|
||||
// Set up our loading flags.
|
||||
load_flags = FT_LOAD_DEFAULT | FT_LOAD_RENDER;
|
||||
if (!useHinting()) load_flags |= FT_LOAD_NO_HINTING;
|
||||
if (!useAutoHinting()) load_flags |= FT_LOAD_NO_AUTOHINT;
|
||||
if (useMonochrome()) load_flags |= FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO;
|
||||
else load_flags |= FT_LOAD_TARGET_NORMAL;
|
||||
if (!useHinting())
|
||||
load_flags |= FT_LOAD_NO_HINTING;
|
||||
if (!useAutoHinting())
|
||||
load_flags |= FT_LOAD_NO_AUTOHINT;
|
||||
if (useMonochrome())
|
||||
load_flags |= FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO;
|
||||
else
|
||||
load_flags |= FT_LOAD_TARGET_NORMAL;
|
||||
}
|
||||
|
||||
/// Gets the overall font height, including a line gap of 1 px
|
||||
@@ -394,7 +316,6 @@ namespace gui
|
||||
core::vector2di getKerning(const char32_t thisLetter, const char32_t previousLetter) const;
|
||||
|
||||
video::IVideoDriver* Driver = nullptr;
|
||||
std::optional<io::path> filename;
|
||||
FT_Face tt_face;
|
||||
FT_Size_Metrics font_metrics;
|
||||
FT_Int32 load_flags;
|
||||
|
||||
Reference in New Issue
Block a user