Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions indra/llrender/llfontfreetype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
//#include "imdebug.h"
#include "llfontbitmapcache.h"
#include "llgl.h"
#include "llwindow.h"

#define ENABLE_OT_SVG_SUPPORT

Expand Down Expand Up @@ -188,7 +189,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;
Expand All @@ -197,6 +198,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)
Expand Down Expand Up @@ -314,7 +318,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);
}
Expand Down Expand Up @@ -419,6 +423,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)
Expand Down Expand Up @@ -501,6 +517,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);
Expand Down Expand Up @@ -728,7 +769,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.
Expand Down
12 changes: 10 additions & 2 deletions indra/llrender/llfontfreetype.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "llfontbitmapcache.h"

#include <unordered_map>
#include <unordered_set>

// Hack. FT_Face is just a typedef for a pointer to a struct,
// but there's no simple forward declarations file for FreeType,
Expand Down Expand Up @@ -108,7 +109,7 @@ class LLFontFreetype : public LLRefCount
S32 getNumFaces(const std::string& filename);

typedef std::function<bool(llwchar)> char_functor_t;
void addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor = nullptr);
void addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor = nullptr) const;

// Global font metrics - in units of pixels
F32 getLineHeight() const;
Expand Down Expand Up @@ -167,6 +168,7 @@ class LLFontFreetype : public LLRefCount
bool setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const;
bool setVariationAxis(const std::string& axis_tag, F32 value);
bool hasGlyph(llwchar wch) const; // Has a glyph for this character
bool hasFallbackPath(const std::string& path) const; // Is a fallback font with this file path already attached?
LLFontGlyphInfo* addGlyph(llwchar wch, EFontGlyphType glyph_type) const; // Add a new character to the font if necessary
LLFontGlyphInfo* addGlyphFromFont(
const LLFontFreetype *fontp,
Expand All @@ -191,9 +193,15 @@ class LLFontFreetype : public LLRefCount
EFontHinting mHinting;
S32 mFontFlags;
S32 mWeight = -1;
S32 mFaceIndex = 0; // Face index within the (possibly collection) font file
F32 mVertDPI = 0.f; // Kept so lazily-discovered fallback faces can be
F32 mHorzDPI = 0.f; // opened at this font's size (see addGlyph)
typedef std::pair<LLPointer<LLFontFreetype>, char_functor_t> fallback_font_t;
typedef std::vector<fallback_font_t> fallback_font_vector_t;
fallback_font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars)
// mutable: fallback fonts are also discovered lazily in addGlyph (const)
mutable fallback_font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars)
// Codepoints we've already asked the OS about, so we only query once each
mutable std::unordered_set<llwchar> mAttemptedFallbackChars;

// *NOTE: the same glyph can be present with multiple representations (but the pointer is always unique)
typedef std::unordered_multimap<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t;
Expand Down
14 changes: 14 additions & 0 deletions indra/llwindow/llwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,20 @@ std::vector<std::string> LLWindow::getDynamicFallbackFontList()
#endif
}

// static
LLFontFallbackMatch LLWindow::findFallbackFontForChar(llwchar wch)
{
#if LL_SDL_WINDOW && !LL_MESA_HEADLESS
return LLWindowSDL::findFallbackFontForChar(wch);
#elif LL_WINDOWS
return LLWindowWin32::findFallbackFontForChar(wch);
#elif LL_DARWIN
return LLWindowMacOSX::findFallbackFontForChar(wch);
#else
return LLFontFallbackMatch();
#endif
}

// static
std::vector<std::string> LLWindow::getDisplaysResolutionList()
{
Expand Down
10 changes: 10 additions & 0 deletions indra/llwindow/llwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ class LLSplashScreen;
class LLPreeditor;
class LLWindowCallbacks;

// Result of an OS font-fallback query; empty mPath means no font was found.
struct LLFontFallbackMatch
{
std::string mPath;
S32 mFaceIndex = 0;
};

// Refer to llwindow_test in test/common/llwindow for usage example

class LLWindow : public LLInstanceTracker<LLWindow>
Expand Down Expand Up @@ -183,6 +190,9 @@ class LLWindow : public LLInstanceTracker<LLWindow>

static std::vector<std::string> getDynamicFallbackFontList();

// Ask the OS for a font file covering the given codepoint (lazy fallback).
static LLFontFallbackMatch findFallbackFontForChar(llwchar wch);

// Provide native key event data
virtual LLSD getNativeKeyData() { return LLSD::emptyMap(); }

Expand Down
6 changes: 6 additions & 0 deletions indra/llwindow/llwindowmacosx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2633,6 +2633,12 @@ std::vector<std::string> LLWindowMacOSX::getDynamicFallbackFontList()
return std::vector<std::string>();
}

LLFontFallbackMatch LLWindowMacOSX::findFallbackFontForChar(llwchar wch)
{
// Not implemented on macOS; would use CoreText (CTFontCreateForString).
return LLFontFallbackMatch();
}

// static
MASK LLWindowMacOSX::modifiersToMask(S16 modifiers)
{
Expand Down
1 change: 1 addition & 0 deletions indra/llwindow/llwindowmacosx.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class LLWindowMacOSX : public LLWindow
static std::vector<std::string> getDisplaysResolutionList();

static std::vector<std::string> getDynamicFallbackFontList();
static LLFontFallbackMatch findFallbackFontForChar(llwchar wch);

// Provide native key event data
LLSD getNativeKeyData() override;
Expand Down
116 changes: 36 additions & 80 deletions indra/llwindow/llwindowsdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
#include <glib.h>
#endif

#include <algorithm>

extern "C" {
# include "fontconfig/fontconfig.h"
}
Expand Down Expand Up @@ -1879,100 +1881,54 @@ void LLWindowSDL::bringToFront()
//static
std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList()
{
std::vector<std::string> rtns;
#if LL_LINUX
// Use libfontconfig to find us a nice ordered list of fallback fonts
// specific to this system.
std::string final_fallback("/usr/share/fonts/truetype/kochi/kochi-gothic.ttf");
const int max_font_count_cutoff = 40; // fonts are expensive in the current system, don't enumerate an arbitrary number of them
// Our 'ideal' font properties which define the sorting results.
// slant=0 means Roman, index=0 means the first face in a font file
// (the one we actually use), weight=80 means medium weight,
// spacing=0 means proportional spacing.
std::string sort_order("slant=0:index=0:weight=80:spacing=0");
// elide_unicode_coverage removes fonts from the list whose unicode
// range is covered by fonts earlier in the list. This usually
// removes ~90% of the fonts as redundant (which is great because
// the font list can be huge), but might unnecessarily reduce the
// renderable range if for some reason our FreeType actually fails
// to use some of the fonts we want it to.
const bool elide_unicode_coverage = true;

FcFontSet *fs = nullptr;
FcPattern *sortpat = nullptr;

LL_INFOS() << "Getting system font list from FontConfig..." << LL_ENDL;

// If the user has a system-wide language preference, then favor
// fonts from that language group. This doesn't affect the types
// of languages that can be displayed, but ensures that their
// preferred language is rendered from a single consistent font where
// possible.
FL_Locale *locale = nullptr;
FL_Success success = FL_FindLocale(&locale, FL_MESSAGES);
if (success != 0)
{
if (success >= 2 && locale->lang) // confident!
{
LL_INFOS("AppInit") << "Language " << locale->lang << LL_ENDL;
LL_INFOS("AppInit") << "Location " << locale->country << LL_ENDL;
LL_INFOS("AppInit") << "Variant " << locale->variant << LL_ENDL;

LL_INFOS() << "Preferring fonts of language: "
<< locale->lang
<< LL_ENDL;
sort_order = "lang=" + std::string(locale->lang) + ":"
+ sort_order;
}
}
FL_FreeLocale(&locale);
// Lazy-loaded fonts, seeded with fonts.xml
return std::vector<std::string>();
}

LLFontFallbackMatch LLWindowSDL::findFallbackFontForChar(llwchar wch)
{
LLFontFallbackMatch result;
#if LL_LINUX
if (!FcInit())
{
LL_WARNS() << "FontConfig failed to initialize." << LL_ENDL;
rtns.push_back(final_fallback);
return rtns;
LL_WARNS_ONCE() << "FontConfig failed to initialize." << LL_ENDL;
return result;
}

sortpat = FcNameParse((FcChar8*) sort_order.c_str());
if (sortpat)
{
// Sort the list of system fonts from most-to-least-desirable.
FcResult result;
fs = FcFontSort(nullptr, sortpat, elide_unicode_coverage, nullptr, &result);
FcPatternDestroy(sortpat);
}
// Ask FontConfig for the best font covering this codepoint.
FcCharSet* charset = FcCharSetCreate();
FcCharSetAddChar(charset, (FcChar32)wch);

FcPattern* pat = FcPatternCreate();
FcPatternAddCharSet(pat, FC_CHARSET, charset);
FcPatternAddBool(pat, FC_SCALABLE, FcTrue);

FcConfigSubstitute(nullptr, pat, FcMatchPattern);
FcDefaultSubstitute(pat);

int found_font_count = 0;
if (fs)
FcResult fc_result;
FcPattern* match = FcFontMatch(nullptr, pat, &fc_result);
if (match)
{
// Get the full pathnames to the fonts, where available,
// which is what we really want.
found_font_count = fs->nfont;
for (int i=0; i<fs->nfont; ++i)
FcChar8* filename = nullptr;
if (FcResultMatch == FcPatternGetString(match, FC_FILE, 0, &filename) && filename)
{
FcChar8 *filename;
if (FcResultMatch == FcPatternGetString(fs->fonts[i], FC_FILE, 0, &filename) && filename)
result.mPath = (const char*)filename;

// .ttc/.otc collections carry several faces; get the right one.
int index = 0;
if (FcResultMatch == FcPatternGetInteger(match, FC_INDEX, 0, &index))
{
rtns.push_back(std::string((const char*)filename));
if (rtns.size() >= max_font_count_cutoff)
break; // hit limit
result.mFaceIndex = index;
}
}
FcFontSetDestroy (fs);
FcPatternDestroy(match);
}

LL_DEBUGS() << "Using font list: " << LL_ENDL;
for (auto it = rtns.begin(); it != rtns.end(); ++it)
{
LL_DEBUGS() << " file: " << *it << LL_ENDL;
}

LL_INFOS() << "Using " << rtns.size() << "/" << found_font_count << " system fonts." << LL_ENDL;

rtns.push_back(final_fallback);
FcPatternDestroy(pat);
FcCharSetDestroy(charset);
#endif
return rtns;
return result;
}

void LLWindowSDL::setLanguageTextInput(const LLCoordGL& position)
Expand Down
1 change: 1 addition & 0 deletions indra/llwindow/llwindowsdl.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ class LLWindowSDL final : public LLWindow
void setTitle(const std::string title) override;

static std::vector<std::string> getDynamicFallbackFontList();
static LLFontFallbackMatch findFallbackFontForChar(llwchar wch);

void *createSharedContext() override;
void makeContextCurrent(void *context) override;
Expand Down
6 changes: 6 additions & 0 deletions indra/llwindow/llwindowwin32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4736,6 +4736,12 @@ std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList()
// Fonts previously in getFontListSans() have moved to fonts.xml.
return std::vector<std::string>();
}

LLFontFallbackMatch LLWindowWin32::findFallbackFontForChar(llwchar wch)
{
// Not implemented on Windows; would use DirectWrite (IDWriteFontFallback::MapCharacters).
return LLFontFallbackMatch();
}
#endif // LL_WINDOWS

inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread()
Expand Down
1 change: 1 addition & 0 deletions indra/llwindow/llwindowwin32.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class LLWindowWin32 : public LLWindow
static PROC WINAPI getProcAddress(const char* func);
static std::vector<std::string> getDisplaysResolutionList();
static std::vector<std::string> getDynamicFallbackFontList();
static LLFontFallbackMatch findFallbackFontForChar(llwchar wch);
static void setDPIAwareness();

void* getDirectInput8() override;
Expand Down
Loading