summaryrefslogtreecommitdiff
path: root/indra/llrender/llfontfreetype.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llrender/llfontfreetype.cpp')
-rw-r--r--indra/llrender/llfontfreetype.cpp140
1 files changed, 133 insertions, 7 deletions
diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp
index 357dfc8ea8..73540200c9 100644
--- a/indra/llrender/llfontfreetype.cpp
+++ b/indra/llrender/llfontfreetype.cpp
@@ -52,6 +52,7 @@
//#include "imdebug.h"
#include "llfontbitmapcache.h"
#include "llgl.h"
+#include "llwindow.h"
#if !defined(LL_NO_OTSVG)
#define ENABLE_OT_SVG_SUPPORT
@@ -111,6 +112,7 @@ LLFontManager::~LLFontManager()
LLFontGlyphInfo::LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type)
: mGlyphIndex(index),
mGlyphType(glyph_type),
+ mChar(0),
mWidth(0), // In pixels
mHeight(0), // In pixels
mXAdvance(0.f), // In pixels
@@ -128,6 +130,7 @@ LLFontGlyphInfo::LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type)
LLFontGlyphInfo::LLFontGlyphInfo(const LLFontGlyphInfo& fgi)
: mGlyphIndex(fgi.mGlyphIndex)
, mGlyphType(fgi.mGlyphType)
+ , mChar(fgi.mChar)
, mWidth(fgi.mWidth)
, mHeight(fgi.mHeight)
, mXAdvance(fgi.mXAdvance)
@@ -152,7 +155,8 @@ LLFontFreetype::LLFontFreetype()
mFTFace(nullptr),
mRenderGlyphCount(0),
mStyle(0),
- mPointSize(0)
+ mPointSize(0),
+ mMaxDigitWidth(0.0f)
{
}
@@ -190,7 +194,7 @@ bool LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v
return false;
openArgs.flags = FT_OPEN_MEMORY;
- int error = FT_Open_Face( gFTLibrary, &openArgs, 0, &mFTFace );
+ int error = FT_Open_Face( gFTLibrary, &openArgs, face_n, &mFTFace );
if (error)
return false;
@@ -199,6 +203,9 @@ bool LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v
mHinting = hinting;
mFontFlags = flags;
mWeight = weight;
+ mFaceIndex = face_n;
+ mVertDPI = vert_dpi;
+ mHorzDPI = horz_dpi;
bool variable_font = false;
if (weight >= 0)
@@ -316,7 +323,7 @@ S32 LLFontFreetype::getNumFaces(const std::string& filename)
}
void LLFontFreetype::addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font,
- const char_functor_t& functor)
+ const char_functor_t& functor) const
{
mFallbackFonts.emplace_back(fallback_font, functor);
}
@@ -345,6 +352,10 @@ F32 LLFontFreetype::getXAdvance(llwchar wch) const
LLFontGlyphInfo* gi = getGlyphInfo(wch, EFontGlyphType::Unspecified);
if (gi)
{
+ if (wch >= '0' && wch <= '9' && mMaxDigitWidth > 0.0f)
+ {
+ return mMaxDigitWidth;
+ }
return gi->mXAdvance;
}
else
@@ -365,6 +376,12 @@ F32 LLFontFreetype::getXAdvance(const LLFontGlyphInfo* glyph) const
if (mFTFace == nullptr)
return 0.0;
+ // Use max digit width for tabular numbers
+ if (mWeight > 0 && glyph->mChar >= '0' && glyph->mChar <= '9' && mMaxDigitWidth > 0.0f)
+ {
+ return mMaxDigitWidth;
+ }
+
return glyph->mXAdvance;
}
@@ -386,8 +403,27 @@ F32 LLFontFreetype::getXKerning(const LLFontGlyphInfo* left_glyph_info, const LL
if (mFTFace == nullptr)
return 0.0;
- U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0;
- U32 right_glyph = right_glyph_info ? right_glyph_info->mGlyphIndex : 0;
+ U32 left_glyph = 0;
+ U32 right_glyph = 0;
+
+ if (left_glyph_info)
+ {
+ if (mWeight > 0 && left_glyph_info->mChar >= '0' && left_glyph_info->mChar <= '9')
+ {
+ // Disable kerning for digits when using tabular numbers
+ return 0.0;
+ }
+ left_glyph = left_glyph_info->mGlyphIndex;
+ }
+ if (right_glyph_info)
+ {
+ if (mWeight > 0 && right_glyph_info->mChar >= '0' && right_glyph_info->mChar <= '9')
+ {
+ // Disable kerning for digits when using tabular numbers
+ return 0.0;
+ }
+ right_glyph = right_glyph_info->mGlyphIndex;
+ }
FT_Vector delta;
@@ -421,6 +457,18 @@ bool LLFontFreetype::hasGlyph(llwchar wch) const
return(mCharGlyphInfoMap.find(wch) != mCharGlyphInfoMap.end());
}
+bool LLFontFreetype::hasFallbackPath(const std::string& path) const
+{
+ for (const fallback_font_t& pair : mFallbackFonts)
+ {
+ if (pair.first->getName() == path)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch, EFontGlyphType glyph_type) const
{
if (!mFTFace)
@@ -503,6 +551,31 @@ LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch, EFontGlyphType glyph_type
glyph_type);
}
}
+
+ // Nothing above covers this char: ask the OS for a font that does,
+ // load it and attach it.
+ if (mAttemptedFallbackChars.insert(wch).second)
+ {
+ LLFontFallbackMatch match = LLWindow::findFallbackFontForChar(wch);
+ if (!match.mPath.empty() && !hasFallbackPath(match.mPath))
+ {
+ LLPointer<LLFontFreetype> fallback = new LLFontFreetype;
+ if (fallback->loadFace(match.mPath, mPointSize, mVertDPI, mHorzDPI,
+ /*weight*/ -1, /*is_fallback*/ true,
+ match.mFaceIndex, mHinting, mFontFlags))
+ {
+ glyph_index = FT_Get_Char_Index(fallback->mFTFace, wch);
+ if (glyph_index)
+ {
+ LL_DEBUGS("Font") << "Lazy OS fallback for U+" << std::hex << (U32)wch << std::dec
+ << ": " << match.mPath << " (face " << match.mFaceIndex << ")" << LL_ENDL;
+ addFallbackFont(fallback, nullptr);
+ return addGlyphFromFont(fallback, wch, glyph_index, glyph_type);
+ }
+ // Matched font doesn't actually cover wch: discard it.
+ }
+ }
+ }
}
auto range_it = mCharGlyphInfoMap.equal_range(wch);
@@ -550,6 +623,7 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l
mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_glyph_type, bitmap_num);
LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index, requested_glyph_type);
+ gi->mChar = wch;
gi->mXBitmapOffset = pos_x;
gi->mYBitmapOffset = pos_y;
gi->mBitmapEntry = std::make_pair(bitmap_glyph_type, bitmap_num);
@@ -566,6 +640,14 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l
gi->mXAdvance = fontp->mFTFace->glyph->advance.x / 64.f;
gi->mYAdvance = fontp->mFTFace->glyph->advance.y / 64.f;
+ if (mWeight > 0 && wch >= '0' && wch <= '9')
+ {
+ // Digits are supposed to be preloaded, and buffers
+ // refresh when new chars get added, so this lazy load
+ // should not cause any issues.
+ mMaxDigitWidth = llmax(mMaxDigitWidth, gi->mXAdvance);
+ }
+
insertGlyphInfo(wch, gi);
if (requested_glyph_type != bitmap_glyph_type)
@@ -722,7 +804,50 @@ void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index, ll
llassert_always_msg(FT_Err_Ok == error, message.c_str());
}
- llassert_always(! FT_Render_Glyph(mFTFace->glyph, gFontRenderMode) );
+ // TODO: Make this more sturdy, make asserts/ll_errs conditional
+ // to non-critical characters.
+ // Temporarily leaving them for data gathering, but unicode chars
+ // like emojis should not cause the app to crash and should either
+ // fallback to some predetermined bitmap or simply return.
+
+ // Verify glyph slot is valid
+ if (!mFTFace->glyph)
+ {
+ LL_ERRS() << "FT_Load_Glyph succeeded but glyph slot is null for wchar " << llformat("U+%xu", U32(wch)) << LL_ENDL;
+ return;
+ }
+
+ // Check if bitmap buffer is already allocated
+ // It can potentially be preallocated for:
+ // 1. SVG/color glyphs rendered by FreeType's SVG_RendererHooks
+ // 2. Embedded bitmap fonts
+ // 3. Some Color emoji that use FT_LOAD_COLOR
+ if (!mFTFace->glyph->bitmap.buffer)
+ {
+ error = FT_Render_Glyph(mFTFace->glyph, gFontRenderMode);
+ if (error != FT_Err_Ok)
+ {
+ std::string render_message = llformat(
+ "Error %d (%s) rendering wchar %u glyph %u: format=%lu, pixel_mode=%d, render_mode=%d",
+ error, FT_Error_String(error), wch, glyph_index,
+ (unsigned long)mFTFace->glyph->format, mFTFace->glyph->bitmap.pixel_mode, gFontRenderMode);
+
+ // Try with FT_RENDER_MODE_NORMAL as fallback
+ if (gFontRenderMode != FT_RENDER_MODE_NORMAL)
+ {
+ LL_WARNS_ONCE() << render_message << LL_ENDL;
+ error = FT_Render_Glyph(mFTFace->glyph, FT_RENDER_MODE_NORMAL);
+ if (error != FT_Err_Ok)
+ {
+ LL_ERRS() << "Fallback to FT_RENDER_MODE_NORMAL failed. " << render_message << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_ERRS() << render_message << LL_ENDL;
+ }
+ }
+ }
mRenderGlyphCount++;
}
@@ -730,7 +855,7 @@ void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index, ll
void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)
{
resetBitmapCache();
- loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mWeight, mIsFallback, 0, mHinting, mFontFlags);
+ loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mWeight, mIsFallback, mFaceIndex, mHinting, mFontFlags);
if (!mIsFallback)
{
// This is the head of the list - need to rebuild ourself and all fallbacks.
@@ -758,6 +883,7 @@ void LLFontFreetype::resetBitmapCache()
}
mCharGlyphInfoMap.clear();
mFontBitmapCachep->reset();
+ mMaxDigitWidth = 0.0f;
// Adding default glyph is skipped for fallback fonts here as well as in loadFace().
// This if was added as fix for EXT-4971.