Skip to content

Commit 4f0ab34

Browse files
committed
Overlay: Font - add merge_fonts property
1 parent a968b2b commit 4f0ab34

File tree

7 files changed

+189
-83
lines changed

7 files changed

+189
-83
lines changed

Components/Overlay/include/OgreFont.h

+22-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ THE SOFTWARE
2828
#define _Font_H__
2929

3030
#include "OgreOverlayPrerequisites.h"
31+
#include "OgrePrerequisites.h"
3132
#include "OgreResource.h"
3233
#include "OgreCommon.h"
3334
#include "OgreSharedPtr.h"
@@ -44,6 +45,8 @@ namespace Ogre
4445
/** \addtogroup Overlays
4546
* @{
4647
*/
48+
class Font;
49+
typedef SharedPtr<Font> FontPtr;
4750

4851
/// decode UTF8 encoded bytestream to uint32 codepoints
4952
_OgreOverlayExport std::vector<uint32> utftoc32(String str);
@@ -124,12 +127,21 @@ namespace Ogre
124127
/// Range of code points to generate glyphs for (truetype only)
125128
CodePointRangeList mCodePointRangeList;
126129

130+
std::vector<FontPtr> mMergeFonts;
131+
127132
/// Internal method for loading from ttf
128133
void createTextureFromFont(void);
129134

130135
void loadImpl() override;
131136
void unloadImpl() override;
132137
size_t calculateSize(void) const override { return 0; } // permanent resource is in the texture
138+
139+
friend class ImGuiOverlay;
140+
DataStreamPtr _getTTFData();
141+
142+
void* _prepareFont(void* context, uint32& glyphCount, int32& max_height, int32& max_width);
143+
void _loadGlyphs(void* font, int32 max_height, Image& img, uint32& l, uint32& m);
144+
133145
public:
134146

135147
/** Constructor.
@@ -287,6 +299,16 @@ namespace Ogre
287299
{
288300
return mCodePointRangeList;
289301
}
302+
303+
/** Add a font to merge with this one.
304+
305+
This is useful when you want to use a font for most of the characters, but
306+
fall back to another font for characters not present in the current font. e.g. icons.
307+
*/
308+
void addMergeFont(const FontPtr& font) { mMergeFonts.push_back(font); }
309+
310+
const std::vector<FontPtr>& getMergeFontList() const { return mMergeFonts; }
311+
290312
/** Gets the material generated for this font, as a weak reference.
291313
292314
This will only be valid after the Font has been loaded.
@@ -343,8 +365,6 @@ namespace Ogre
343365
*/
344366
void _setMaterial(const MaterialPtr& mat);
345367
};
346-
347-
typedef SharedPtr<Font> FontPtr;
348368
/** @} */
349369
/** @} */
350370
}

Components/Overlay/src/OgreFont.cpp

+110-74
Original file line numberDiff line numberDiff line change
@@ -328,114 +328,85 @@ namespace Ogre
328328
mTexture->load();
329329
}
330330
//---------------------------------------------------------------------
331-
void Font::loadResource(Resource* res)
331+
DataStreamPtr Font::_getTTFData()
332332
{
333-
// Locate ttf file, load it pre-buffered into memory by wrapping the
334-
// original DataStream in a MemoryDataStream
335-
DataStreamPtr dataStreamPtr =
336-
ResourceGroupManager::getSingleton().openResource(
337-
mSource, mGroup, this);
338-
MemoryDataStream ttfchunk(dataStreamPtr);
333+
// Locate ttf file, load it pre-buffered into memory by wrapping
334+
// the original DataStream in a MemoryDataStream
335+
return ResourceGroupManager::getSingleton().openResource(mSource, mGroup, this);
336+
}
339337

340-
// If codepoints not supplied, assume ASCII
341-
if (mCodePointRangeList.empty())
342-
{
343-
mCodePointRangeList.push_back(CodePointRange(32, 126));
344-
}
338+
void* Font::_prepareFont(void* context, uint32& glyphCount, int32& max_height, int32& max_width)
339+
{
345340
float vpScale = OverlayManager::getSingleton().getPixelRatio();
341+
MemoryDataStream ttfchunk(_getTTFData(), false);
346342
#ifdef HAVE_FREETYPE
347-
// ManualResourceLoader implementation - load the texture
348-
FT_Library ftLibrary;
349-
// Init freetype
350-
if( FT_Init_FreeType( &ftLibrary ) )
351-
OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "Could not init FreeType library!",
352-
"Font::Font");
353-
343+
FT_Library ftLibrary = static_cast<FT_Library>(context);
354344
FT_Face face;
355345

356346
// Load font
357-
if( FT_New_Memory_Face( ftLibrary, ttfchunk.getPtr(), (FT_Long)ttfchunk.size() , 0, &face ) )
358-
OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR,
359-
"Could not open font face!", "Font::createTextureFromFont" );
360-
347+
if (FT_New_Memory_Face(ftLibrary, ttfchunk.getPtr(), (FT_Long)ttfchunk.size(), 0, &face))
348+
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Could not open font face");
361349

362350
// Convert our point size to freetype 26.6 fixed point format
363351
FT_F26Dot6 ftSize = (FT_F26Dot6)(mTtfSize * (1 << 6));
364352
if (FT_Set_Char_Size(face, ftSize, 0, mTtfResolution * vpScale, mTtfResolution * vpScale))
365-
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Could not set char size!");
366-
367-
//FILE *fo_def = stdout;
368-
369-
FT_Pos max_height = 0, max_width = 0;
353+
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Could not set char size");
370354

371355
// Calculate maximum width, height and bearing
372-
size_t glyphCount = 0;
373356
for (const CodePointRange& range : mCodePointRangeList)
374357
{
375-
for(CodePoint cp = range.first; cp <= range.second; ++cp, ++glyphCount)
358+
glyphCount += range.second - range.first + 1;
359+
for(CodePoint cp = range.first; cp <= range.second; ++cp)
376360
{
377361
FT_Load_Char( face, cp, FT_LOAD_RENDER );
378362

379-
max_height = std::max<FT_Pos>(2 * face->glyph->bitmap.rows - (face->glyph->metrics.horiBearingY >> 6), max_height);
363+
max_height = std::max<int32>(2 * face->glyph->bitmap.rows - (face->glyph->metrics.horiBearingY >> 6), max_height);
380364
mTtfMaxBearingY = std::max(int(face->glyph->metrics.horiBearingY >> 6), mTtfMaxBearingY);
381-
max_width = std::max<FT_Pos>(face->glyph->bitmap.width, max_width);
365+
max_width = std::max<int32>(face->glyph->bitmap.width, max_width);
382366
}
383-
384367
}
368+
369+
return face;
385370
#else
386-
stbtt_fontinfo font;
387-
stbtt_InitFont(&font, ttfchunk.getPtr(), 0);
388-
// 64 gives the same texture resolution as freetype.
389-
float scale = stbtt_ScaleForPixelHeight(&font, vpScale * mTtfSize * mTtfResolution / 64);
371+
stbtt_fontinfo* font = static_cast<stbtt_fontinfo*>(context);
372+
stbtt_InitFont(font, ttfchunk.getPtr(), 0);
390373

391-
int max_width = 0, max_height = 0;
392-
// Calculate maximum width, height and bearing
393-
size_t glyphCount = 0;
374+
// 64 gives the same texture resolution as freetype.
375+
float scale = stbtt_ScaleForPixelHeight(font, vpScale * mTtfSize * mTtfResolution / 64);
394376
for (const CodePointRange& range : mCodePointRangeList)
395377
{
396-
for(CodePoint cp = range.first; cp <= range.second; ++cp, ++glyphCount)
378+
glyphCount += range.second - range.first + 1;
379+
for(CodePoint cp = range.first; cp <= range.second; ++cp)
397380
{
398-
int idx = stbtt_FindGlyphIndex(&font, cp);
381+
int idx = stbtt_FindGlyphIndex(font, cp);
399382
if (!idx) // It is actually in the font?
400383
continue;
401384
TRect<int> r;
402-
stbtt_GetGlyphBitmapBox(&font, idx, scale, scale, &r.left, &r.top, &r.right, &r.bottom);
385+
stbtt_GetGlyphBitmapBox(font, idx, scale, scale, &r.left, &r.top, &r.right, &r.bottom);
403386
max_height = std::max(r.height(), max_height);
404387
mTtfMaxBearingY = std::max(-r.top, mTtfMaxBearingY);
405388
max_width = std::max(r.width(), max_width);
406389
}
407390
}
408391

409392
max_height *= 1.125;
393+
return font;
410394
#endif
411-
uint char_spacer = 1;
412-
413-
// Now work out how big our texture needs to be
414-
size_t rawSize = (max_width + char_spacer) * (max_height + char_spacer) * glyphCount;
415-
416-
uint32 tex_side = static_cast<uint32>(Math::Sqrt((Real)rawSize));
417-
// Now round up to nearest power of two
418-
uint32 roundUpSize = Bitwise::firstPO2From(tex_side);
419-
420-
// Would we benefit from using a non-square texture (2X width)
421-
uint32 finalWidth, finalHeight;
422-
if (roundUpSize * roundUpSize * 0.5 >= rawSize)
423-
{
424-
finalHeight = static_cast<uint32>(roundUpSize * 0.5);
425-
}
426-
else
427-
{
428-
finalHeight = roundUpSize;
429-
}
430-
finalWidth = roundUpSize;
395+
}
431396

432-
Real textureAspect = (Real)finalWidth / (Real)finalHeight;
397+
void Font::_loadGlyphs(void* _face, int32 max_height, Image& img, uint32& l, uint32& m)
398+
{
399+
uint char_spacer = 1;
400+
float finalWidth = img.getWidth();
401+
float finalHeight = img.getHeight();
402+
float textureAspect = finalWidth / finalHeight;
433403

434-
Image img(PF_BYTE_LA, finalWidth, finalHeight);
435-
// Reset content (transparent)
436-
img.setTo(ColourValue::ZERO);
404+
#ifdef HAVE_FREETYPE
405+
FT_Face face = static_cast<FT_Face>(_face);
406+
#else
407+
stbtt_fontinfo* font = static_cast<stbtt_fontinfo*>(_face);
408+
#endif
437409

438-
uint32 l = 0, m = 0;
439410
for (const CodePointRange& range : mCodePointRangeList)
440411
{
441412
for(CodePoint cp = range.first; cp <= range.second; ++cp )
@@ -464,7 +435,7 @@ namespace Ogre
464435
FT_Pos y_bearing = mTtfMaxBearingY - (face->glyph->metrics.horiBearingY >> 6);
465436
FT_Pos x_bearing = face->glyph->metrics.horiBearingX >> 6;
466437
#else
467-
int idx = stbtt_FindGlyphIndex(&font, cp);
438+
int idx = stbtt_FindGlyphIndex(font, cp);
468439
if (!idx)
469440
{
470441
LogManager::getSingleton().logWarning(
@@ -473,20 +444,20 @@ namespace Ogre
473444
}
474445

475446
if(cp == ' ') // should figure out how advance works for stbtt..
476-
idx = stbtt_FindGlyphIndex(&font, '0');
447+
idx = stbtt_FindGlyphIndex(font, '0');
477448

478449
TRect<int> r;
479-
stbtt_GetGlyphBitmapBox(&font, idx, scale, scale, &r.left, &r.top, &r.right, &r.bottom);
450+
stbtt_GetGlyphBitmapBox(font, idx, scale, scale, &r.left, &r.top, &r.right, &r.bottom);
480451

481452
uint width = r.width();
482453

483454
int y_bearing = mTtfMaxBearingY + r.top;
484455
int xoff = 0, yoff = 0;
485-
buffer = stbtt_GetCodepointBitmap(&font, scale, scale, cp, &buffer_pitch, &buffer_h, &xoff, &yoff);
456+
buffer = stbtt_GetCodepointBitmap(font, scale, scale, cp, &buffer_pitch, &buffer_h, &xoff, &yoff);
486457

487458
int advance = xoff + width, x_bearing = xoff;
488459
// should be multiplied with scale, but still does not seem to do the right thing
489-
// stbtt_GetGlyphHMetrics(&font, cp, &advance, &x_bearing);
460+
// stbtt_GetGlyphHMetrics(font, cp, &advance, &x_bearing);
490461
#endif
491462
// If at end of row
492463
if( finalWidth - 1 < l + width )
@@ -532,11 +503,76 @@ namespace Ogre
532503
#ifndef HAVE_FREETYPE
533504
if (buffer != NULL)
534505
{
535-
STBTT_free(buffer, font.userdata);
506+
STBTT_free(buffer, font->userdata);
536507
}
537508
#endif
538509
}
539510
}
511+
}
512+
513+
void Font::loadResource(Resource* res)
514+
{
515+
// If codepoints not supplied, assume ASCII
516+
if (mCodePointRangeList.empty())
517+
{
518+
mCodePointRangeList.push_back(CodePointRange(32, 126));
519+
}
520+
521+
int32 max_height = 0, max_width = 0;
522+
uint32 glyphCount = 0;
523+
#ifdef HAVE_FREETYPE
524+
// ManualResourceLoader implementation - load the texture
525+
FT_Library ftLibrary;
526+
// Init freetype
527+
if( FT_Init_FreeType( &ftLibrary ) )
528+
OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "Could not init FreeType library");
529+
530+
std::vector<void*> faces;
531+
faces.push_back(_prepareFont(ftLibrary, glyphCount, max_height, max_width));
532+
533+
for (const auto& font : mMergeFonts)
534+
{
535+
faces.push_back(font->_prepareFont(ftLibrary, glyphCount, max_height, max_width));
536+
font->mTtfMaxBearingY = mTtfMaxBearingY = std::max(font->mTtfMaxBearingY, mTtfMaxBearingY);
537+
}
538+
#else
539+
stbtt_fontinfo font;
540+
auto face = _prepareFont(&font, glyphCount, max_height, max_width);
541+
#endif
542+
uint char_spacer = 1;
543+
544+
// Now work out how big our texture needs to be
545+
size_t rawSize = (max_width + char_spacer) * (max_height + char_spacer) * glyphCount;
546+
547+
uint32 tex_side = static_cast<uint32>(Math::Sqrt((Real)rawSize));
548+
// Now round up to nearest power of two
549+
uint32 roundUpSize = Bitwise::firstPO2From(tex_side);
550+
551+
// Would we benefit from using a non-square texture (2X width)
552+
uint32 finalWidth, finalHeight;
553+
if (roundUpSize * roundUpSize * 0.5 >= rawSize)
554+
{
555+
finalHeight = static_cast<uint32>(roundUpSize * 0.5);
556+
}
557+
else
558+
{
559+
finalHeight = roundUpSize;
560+
}
561+
finalWidth = roundUpSize;
562+
563+
Image img(PF_BYTE_LA, finalWidth, finalHeight);
564+
// Reset content (transparent)
565+
img.setTo(ColourValue::ZERO);
566+
567+
uint32 l = 0, m = 0;
568+
_loadGlyphs(faces[0], max_height, img, l, m);
569+
int j = 1;
570+
for (const auto& font : mMergeFonts)
571+
{
572+
font->_loadGlyphs(faces[j++], max_height, img, l, m);
573+
mCodePointMap.insert(font->mCodePointMap.begin(), font->mCodePointMap.end());
574+
}
575+
540576
#ifdef HAVE_FREETYPE
541577
FT_Done_FreeType(ftLibrary);
542578
#endif

Components/Overlay/src/OgreImGuiOverlay.cpp

+28-5
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,7 @@ ImFont* ImGuiOverlay::addFont(const String& name, const String& group)
123123
StringUtil::format("Font '%s' not found in group '%s'", name.c_str(), group.c_str()));
124124

125125
OgreAssert(font->getType() == FT_TRUETYPE, "font must be of FT_TRUETYPE");
126-
DataStreamPtr dataStreamPtr =
127-
ResourceGroupManager::getSingleton().openResource(font->getSource(), font->getGroup());
128-
MemoryDataStream ttfchunk(dataStreamPtr, false); // transfer ownership to imgui
126+
MemoryDataStream ttfchunk(font->_getTTFData(), false); // transfer ownership to imgui
129127

130128
// convert codepoint ranges for imgui
131129
CodePointRange cprange;
@@ -149,8 +147,33 @@ ImFont* ImGuiOverlay::addFont(const String& name, const String& group)
149147

150148
ImFontConfig cfg;
151149
strncpy(cfg.Name, name.c_str(), IM_ARRAYSIZE(cfg.Name) - 1);
152-
return io.Fonts->AddFontFromMemoryTTF(ttfchunk.getPtr(), ttfchunk.size(), font->getTrueTypeSize() * vpScale, &cfg,
153-
cprangePtr);
150+
auto* ret = io.Fonts->AddFontFromMemoryTTF(ttfchunk.getPtr(), ttfchunk.size(), font->getTrueTypeSize() * vpScale,
151+
&cfg, cprangePtr);
152+
153+
cfg.MergeMode = true;
154+
155+
for(const auto& mergeFont : font->getMergeFontList())
156+
{
157+
MemoryDataStream mergeTtfchunk(mergeFont->_getTTFData(), false); // transfer ownership to imgui
158+
159+
CodePointRange mergeCprange;
160+
for (const auto& r : mergeFont->getCodePointRangeList())
161+
{
162+
mergeCprange.push_back(r.first);
163+
mergeCprange.push_back(r.second);
164+
}
165+
166+
OgreAssert(!mergeCprange.empty(), "merge font must have codepoint ranges");
167+
mergeCprange.push_back(0); // terminate
168+
mCodePointRanges.push_back(mergeCprange);
169+
cprangePtr = mCodePointRanges.back().data();
170+
171+
cfg.MergeMode = true;
172+
ret = io.Fonts->AddFontFromMemoryTTF(mergeTtfchunk.getPtr(), mergeTtfchunk.size(),
173+
mergeFont->getTrueTypeSize() * vpScale, &cfg, cprangePtr);
174+
}
175+
176+
return ret;
154177
}
155178

156179
void ImGuiOverlay::ImGUIRenderable::createFontTexture()

0 commit comments

Comments
 (0)