diff options
Diffstat (limited to 'indra/llrender')
| -rw-r--r-- | indra/llrender/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/llrender/llfontbitmapcache.cpp | 163 | ||||
| -rw-r--r-- | indra/llrender/llfontbitmapcache.h | 42 | ||||
| -rw-r--r-- | indra/llrender/llfontfreetype.cpp | 281 | ||||
| -rw-r--r-- | indra/llrender/llfontfreetype.h | 31 | ||||
| -rw-r--r-- | indra/llrender/llfontfreetypesvg.cpp | 205 | ||||
| -rw-r--r-- | indra/llrender/llfontfreetypesvg.h | 54 | ||||
| -rw-r--r-- | indra/llrender/llfontgl.cpp | 96 | ||||
| -rw-r--r-- | indra/llrender/llfontgl.h | 23 | ||||
| -rw-r--r-- | indra/llrender/llfontregistry.cpp | 165 | ||||
| -rw-r--r-- | indra/llrender/llfontregistry.h | 43 | ||||
| -rw-r--r-- | indra/llrender/llgl.cpp | 2 | 
12 files changed, 814 insertions, 293 deletions
| diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index f66aee28de..d67ec34105 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -14,6 +14,7 @@ set(llrender_SOURCE_FILES      llcubemap.cpp      llfontbitmapcache.cpp      llfontfreetype.cpp +    llfontfreetypesvg.cpp      llfontgl.cpp      llfontregistry.cpp      llgl.cpp @@ -42,6 +43,7 @@ set(llrender_HEADER_FILES      llcubemap.h      llfontgl.h      llfontfreetype.h +    llfontfreetypesvg.h      llfontbitmapcache.h      llfontregistry.h      llgl.h diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp index c71e24c83a..42b0045cf3 100644 --- a/indra/llrender/llfontbitmapcache.cpp +++ b/indra/llrender/llfontbitmapcache.cpp @@ -30,14 +30,7 @@  #include "llfontbitmapcache.h"  LLFontBitmapCache::LLFontBitmapCache() -:	mNumComponents(0), -	mBitmapWidth(0), -	mBitmapHeight(0), -	mBitmapNum(-1), -	mMaxCharWidth(0), -	mMaxCharHeight(0), -	mCurrentOffsetX(1), -	mCurrentOffsetY(1) +  {  } @@ -45,121 +38,149 @@ LLFontBitmapCache::~LLFontBitmapCache()  {  } -void LLFontBitmapCache::init(S32 num_components, -							 S32 max_char_width, +void LLFontBitmapCache::init(S32 max_char_width,  							 S32 max_char_height)  {  	reset(); -	mNumComponents = num_components;  	mMaxCharWidth = max_char_width;  	mMaxCharHeight = max_char_height; + +	S32 image_width = mMaxCharWidth * 20; +	S32 pow_iw = 2; +	while (pow_iw < image_width) +	{ +		pow_iw <<= 1; +	} +	image_width = pow_iw; +	image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever. + +	mBitmapWidth = image_width; +	mBitmapHeight = image_width;  } -LLImageRaw *LLFontBitmapCache::getImageRaw(U32 bitmap_num) const +LLImageRaw *LLFontBitmapCache::getImageRaw(EFontGlyphType bitmap_type, U32 bitmap_num) const  { -	if (bitmap_num >= mImageRawVec.size()) -		return NULL; +	const U32 bitmap_idx = static_cast<U32>(bitmap_type); +	if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageRawVec[bitmap_idx].size()) +		return nullptr; -	return mImageRawVec[bitmap_num]; +	return mImageRawVec[bitmap_idx][bitmap_num];  } -LLImageGL *LLFontBitmapCache::getImageGL(U32 bitmap_num) const +LLImageGL *LLFontBitmapCache::getImageGL(EFontGlyphType bitmap_type, U32 bitmap_num) const  { -	if (bitmap_num >= mImageGLVec.size()) -		return NULL; +	const U32 bitmap_idx = static_cast<U32>(bitmap_type); +	if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageGLVec[bitmap_idx].size()) +		return nullptr; -	return mImageGLVec[bitmap_num]; +	return mImageGLVec[bitmap_idx][bitmap_num];  } -BOOL LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitmap_num) +BOOL LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyphType bitmap_type, U32& bitmap_num)  { -	if ((mBitmapNum<0) || (mCurrentOffsetX + width + 1) > mBitmapWidth) +	if (bitmap_type >= EFontGlyphType::Count) +	{ +		return FALSE; +	} + +	const U32 bitmap_idx = static_cast<U32>(bitmap_type); +	if (mImageRawVec[bitmap_idx].empty() || (mCurrentOffsetX[bitmap_idx] + width + 1) > mBitmapWidth)  	{ -		if ((mBitmapNum<0) || (mCurrentOffsetY + 2*mMaxCharHeight + 2) > mBitmapHeight) +		if ((mImageRawVec[bitmap_idx].empty()) || (mCurrentOffsetY[bitmap_idx] + 2*mMaxCharHeight + 2) > mBitmapHeight)  		{  			// We're out of space in the current image, or no image  			// has been allocated yet.  Make a new one. -			 -			mImageRawVec.push_back(new LLImageRaw); -			mBitmapNum = mImageRawVec.size()-1; -			LLImageRaw *image_raw = getImageRaw(mBitmapNum); - -			// Make corresponding GL image. -			mImageGLVec.push_back(new LLImageGL(FALSE)); -			LLImageGL *image_gl = getImageGL(mBitmapNum); -			 -			S32 image_width = mMaxCharWidth * 20; -			S32 pow_iw = 2; -			while (pow_iw < image_width) +             +            S32 image_width = mMaxCharWidth * 20; +            S32 pow_iw = 2; +            while (pow_iw < image_width) +            { +                pow_iw *= 2; +            } +            image_width = pow_iw; +            image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever. +            S32 image_height = image_width; +             +            mBitmapWidth = image_width; +            mBitmapHeight = image_height; +             +			S32 num_components = getNumComponents(bitmap_type); +			mImageRawVec[bitmap_idx].push_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components)); +			bitmap_num = mImageRawVec[bitmap_idx].size() - 1; + +			LLImageRaw* image_raw = getImageRaw(bitmap_type, bitmap_num); +			if (EFontGlyphType::Grayscale == bitmap_type)  			{ -				pow_iw *= 2; +				image_raw->clear(255, 0);  			} -			image_width = pow_iw; -			image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever. -			S32 image_height = image_width; - -			image_raw->resize(image_width, image_height, mNumComponents); - -			mBitmapWidth = image_width; -			mBitmapHeight = image_height; -			switch (mNumComponents) -			{ -				case 1: -					image_raw->clear(); -				break; -				case 2: -					image_raw->clear(255, 0); -				break; -			} +			// Make corresponding GL image. +			mImageGLVec[bitmap_idx].push_back(new LLImageGL(image_raw, false)); +			LLImageGL* image_gl = getImageGL(bitmap_type, bitmap_num);  			// Start at beginning of the new image. -			mCurrentOffsetX = 1; -			mCurrentOffsetY = 1; +			mCurrentOffsetX[bitmap_idx] = 1; +			mCurrentOffsetY[bitmap_idx] = 1; -			// Attach corresponding GL texture. -			image_gl->createGLTexture(0, image_raw); +			// Attach corresponding GL texture. (*TODO: is this needed?)  			gGL.getTexUnit(0)->bind(image_gl);  			image_gl->setFilteringOption(LLTexUnit::TFO_POINT); // was setMipFilterNearest(TRUE, TRUE);  		}  		else  		{  			// Move to next row in current image. -			mCurrentOffsetX = 1; -			mCurrentOffsetY += mMaxCharHeight + 1; +			mCurrentOffsetX[bitmap_idx] = 1; +			mCurrentOffsetY[bitmap_idx] += mMaxCharHeight + 1;  		}  	} -	pos_x = mCurrentOffsetX; -	pos_y = mCurrentOffsetY; -	bitmap_num = mBitmapNum; +	pos_x = mCurrentOffsetX[bitmap_idx]; +	pos_y = mCurrentOffsetY[bitmap_idx]; +	bitmap_num = getNumBitmaps(bitmap_type) - 1; -	mCurrentOffsetX += width + 1; +	mCurrentOffsetX[bitmap_idx] += width + 1;  	return TRUE;  }  void LLFontBitmapCache::destroyGL()  { -	for (std::vector<LLPointer<LLImageGL> >::iterator it = mImageGLVec.begin(); -		 it != mImageGLVec.end(); ++it) +	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)  	{ -		(*it)->destroyGLTexture(); +		for (LLImageGL* image_gl : mImageGLVec[idx]) +		{ +			image_gl->destroyGLTexture(); +		}  	}  }  void LLFontBitmapCache::reset()  { -	mImageRawVec.clear(); - -	mImageGLVec.clear(); +	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++) +	{ +		mImageRawVec[idx].clear(); +		mImageGLVec[idx].clear(); +		mCurrentOffsetX[idx] = 1; +		mCurrentOffsetY[idx] = 1; +	}  	mBitmapWidth = 0;  	mBitmapHeight = 0; -	mBitmapNum = -1; -	mCurrentOffsetX = 1; -	mCurrentOffsetY = 1;  } +//static +U32 LLFontBitmapCache::getNumComponents(EFontGlyphType bitmap_type) +{ +	switch (bitmap_type) +	{ +		case EFontGlyphType::Grayscale: +			return 2; +		case EFontGlyphType::Color: +			return 4; +		default: +			llassert(false); +			return 2; +	} +} diff --git a/indra/llrender/llfontbitmapcache.h b/indra/llrender/llfontbitmapcache.h index 7de3a6b56f..c63281ab70 100644 --- a/indra/llrender/llfontbitmapcache.h +++ b/indra/llrender/llfontbitmapcache.h @@ -30,6 +30,14 @@  #include <vector>  #include "lltrace.h" +enum class EFontGlyphType : U32 +{ +	Grayscale = 0, +	Color, +	Count, +	Unspecified, +}; +  // Maintain a collection of bitmaps containing rendered glyphs.  // Generalizes the single-bitmap logic from LLFontFreetype and LLFontGL.  class LLFontBitmapCache @@ -39,35 +47,35 @@ public:  	~LLFontBitmapCache();  	// Need to call this once, before caching any glyphs. - 	void init(S32 num_components, -			  S32 max_char_width, + 	void init(S32 max_char_width,  			  S32 max_char_height);  	void reset(); -	BOOL nextOpenPos(S32 width, S32 &posX, S32 &posY, S32 &bitmapNum); +	BOOL nextOpenPos(S32 width, S32& posX, S32& posY, EFontGlyphType bitmapType, U32& bitmapNum);  	void destroyGL(); - 	LLImageRaw *getImageRaw(U32 bitmapNum = 0) const; - 	LLImageGL *getImageGL(U32 bitmapNum = 0) const; -	 + 	LLImageRaw* getImageRaw(EFontGlyphType bitmapType, U32 bitmapNum) const; + 	LLImageGL* getImageGL(EFontGlyphType bitmapType, U32 bitmapNum) const; +  	S32 getMaxCharWidth() const { return mMaxCharWidth; } -	S32 getNumComponents() const { return mNumComponents; } +	U32 getNumBitmaps(EFontGlyphType bitmapType) const { return (bitmapType < EFontGlyphType::Count) ? mImageRawVec[static_cast<U32>(bitmapType)].size() : 0; }  	S32 getBitmapWidth() const { return mBitmapWidth; }  	S32 getBitmapHeight() const { return mBitmapHeight; } +protected: +	static U32 getNumComponents(EFontGlyphType bitmap_type); +  private: -	S32 mNumComponents; -	S32 mBitmapWidth; -	S32 mBitmapHeight; -	S32 mBitmapNum; -	S32 mMaxCharWidth; -	S32 mMaxCharHeight; -	S32 mCurrentOffsetX; -	S32 mCurrentOffsetY; -	std::vector<LLPointer<LLImageRaw> >	mImageRawVec; -	std::vector<LLPointer<LLImageGL> > mImageGLVec; +	S32 mBitmapWidth = 0; +	S32 mBitmapHeight = 0; +	S32 mCurrentOffsetX[static_cast<U32>(EFontGlyphType::Count)] = { 1 }; +	S32 mCurrentOffsetY[static_cast<U32>(EFontGlyphType::Count)] = { 1 }; +	S32 mMaxCharWidth = 0; +	S32 mMaxCharHeight = 0; +	std::vector<LLPointer<LLImageRaw>> mImageRawVec[static_cast<U32>(EFontGlyphType::Count)]; +	std::vector<LLPointer<LLImageGL>> mImageGLVec[static_cast<U32>(EFontGlyphType::Count)];  };  #endif //LL_LLFONTBITMAPCACHE_H diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index e964d1586f..ca9f2ed778 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -34,14 +34,17 @@  #ifdef LL_WINDOWS  #include <freetype2\freetype\ftsystem.h>  #endif +#include "llfontfreetypesvg.h"  // For some reason, this won't work if it's not wrapped in the ifdef  #ifdef FT_FREETYPE_H  #include FT_FREETYPE_H  #endif +#include "lldir.h"  #include "llerror.h"  #include "llimage.h" +#include "llimagepng.h"  //#include "llimagej2c.h"  #include "llmath.h"	// Linden math  #include "llstring.h" @@ -49,6 +52,8 @@  #include "llfontbitmapcache.h"  #include "llgl.h" +#define ENABLE_OT_SVG_SUPPORT +  FT_Render_Mode gFontRenderMode = FT_RENDER_MODE_NORMAL;  LLFontManager *gFontManagerp = NULL; @@ -81,6 +86,16 @@ LLFontManager::LLFontManager()  		LL_ERRS() << "Freetype initialization failure!" << LL_ENDL;  		FT_Done_FreeType(gFTLibrary);  	} + +#ifdef ENABLE_OT_SVG_SUPPORT +	SVG_RendererHooks hooks = { +		LLFontFreeTypeSvgRenderer::OnInit, +		LLFontFreeTypeSvgRenderer::OnFree, +		LLFontFreeTypeSvgRenderer::OnRender, +		LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot, +	}; +	FT_Property_Set(gFTLibrary, "ot-svg", "svg-hooks", &hooks); +#endif  }  LLFontManager::~LLFontManager() @@ -89,8 +104,9 @@ LLFontManager::~LLFontManager()  } -LLFontGlyphInfo::LLFontGlyphInfo(U32 index) +LLFontGlyphInfo::LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type)  :	mGlyphIndex(index), +	mGlyphType(glyph_type),  	mWidth(0),			// In pixels  	mHeight(0),			// In pixels  	mXAdvance(0.f),		// In pixels @@ -99,10 +115,25 @@ LLFontGlyphInfo::LLFontGlyphInfo(U32 index)  	mYBitmapOffset(0), 	// Offset to the origin in the bitmap  	mXBearing(0),		// Distance from baseline to left in pixels  	mYBearing(0),		// Distance from baseline to top in pixels -	mBitmapNum(0) // Which bitmap in the bitmap cache contains this glyph +	mBitmapEntry(std::make_pair(EFontGlyphType::Unspecified, -1)) // Which bitmap in the bitmap cache contains this glyph  {  } +LLFontGlyphInfo::LLFontGlyphInfo(const LLFontGlyphInfo& fgi) +	: mGlyphIndex(fgi.mGlyphIndex) +	, mGlyphType(fgi.mGlyphType) +	, mWidth(fgi.mWidth) +	, mHeight(fgi.mHeight) +	, mXAdvance(fgi.mXAdvance) +	, mYAdvance(fgi.mYAdvance) +	, mXBitmapOffset(fgi.mXBitmapOffset) +	, mYBitmapOffset(fgi.mYBitmapOffset) +	, mXBearing(fgi.mXBearing) +	, mYBearing(fgi.mYBearing) +{ +	mBitmapEntry = fgi.mBitmapEntry; +} +  LLFontFreetype::LLFontFreetype()  :	mFontBitmapCachep(new LLFontBitmapCache),  	mAscender(0.f), @@ -156,7 +187,7 @@ void ft_close_cb(FT_Stream stream) {  }  #endif -BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n) +BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, bool is_fallback, S32 face_n)  {  	// Don't leak face objects.  This is also needed to deal with  	// changed font file names. @@ -220,7 +251,7 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v  	S32 max_char_width = ll_round(0.5f + (x_max - x_min));  	S32 max_char_height = ll_round(0.5f + (y_max - y_min)); -	mFontBitmapCachep->init(components, max_char_width, max_char_height); +	mFontBitmapCachep->init(max_char_width, max_char_height);  	if (!mFTFace->charmap)  	{ @@ -231,7 +262,7 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v  	if (!mIsFallback)  	{  		// Add the default glyph -		addGlyphFromFont(this, 0, 0); +		addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);  	}  	mName = filename; @@ -323,14 +354,11 @@ void LLFontFreetype::clearFontStreams()  }  #endif -void LLFontFreetype::setFallbackFonts(const font_vector_t &font) -{ -	mFallbackFonts = font; -} - -const LLFontFreetype::font_vector_t &LLFontFreetype::getFallbackFonts() const +void LLFontFreetype::addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor)  { -	return mFallbackFonts; +	// Insert functor fallbacks before generic fallbacks +	mFallbackFonts.insert((functor) ? std::find_if(mFallbackFonts.begin(), mFallbackFonts.end(), [](const fallback_font_t& fe) { return !fe.second; }) : mFallbackFonts.end(), +	                      std::make_pair(fallback_font, functor));  }  F32 LLFontFreetype::getLineHeight() const @@ -354,7 +382,7 @@ F32 LLFontFreetype::getXAdvance(llwchar wch) const  		return 0.0;  	// Return existing info only if it is current -	LLFontGlyphInfo* gi = getGlyphInfo(wch); +	LLFontGlyphInfo* gi = getGlyphInfo(wch, EFontGlyphType::Unspecified);  	if (gi)  	{  		return gi->mXAdvance; @@ -386,10 +414,10 @@ F32 LLFontFreetype::getXKerning(llwchar char_left, llwchar char_right) const  		return 0.0;  	//llassert(!mIsFallback); -	LLFontGlyphInfo* left_glyph_info = getGlyphInfo(char_left);; +	LLFontGlyphInfo* left_glyph_info = getGlyphInfo(char_left, EFontGlyphType::Unspecified);;  	U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0;  	// Kern this puppy. -	LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right); +	LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right, EFontGlyphType::Unspecified);  	U32 right_glyph = right_glyph_info ? right_glyph_info->mGlyphIndex : 0;  	FT_Vector  delta; @@ -420,60 +448,94 @@ BOOL LLFontFreetype::hasGlyph(llwchar wch) const  	return(mCharGlyphInfoMap.find(wch) != mCharGlyphInfoMap.end());  } -LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch) const +LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch, EFontGlyphType glyph_type) const  {  	if (mFTFace == NULL)  		return FALSE;  	llassert(!mIsFallback); +	llassert(glyph_type < EFontGlyphType::Count);  	//LL_DEBUGS() << "Adding new glyph for " << wch << " to font" << LL_ENDL;  	FT_UInt glyph_index; +	// Fallback fonts with a functor have precedence over everything else +	fallback_font_vector_t::const_iterator it_fallback = mFallbackFonts.cbegin(); +	/* This leads to a bug SL-19831 "Check marks in the menu are less visible." +	** Also, LLFontRegistry::createFont() says: "Fallback fonts don't render" +	for (; it_fallback != mFallbackFonts.cend() && it_fallback->second; ++it_fallback) +	{ +		if (it_fallback->second(wch)) +		{ +			glyph_index = FT_Get_Char_Index(it_fallback->first->mFTFace, wch); +			if (glyph_index) +			{ +				return addGlyphFromFont(it_fallback->first, wch, glyph_index, glyph_type); +			} +		} +	} +	*/ +  	// Initialize char to glyph map  	glyph_index = FT_Get_Char_Index(mFTFace, wch);  	if (glyph_index == 0)  	{  		//LL_INFOS() << "Trying to add glyph from fallback font!" << LL_ENDL; -		font_vector_t::const_iterator iter; -		for(iter = mFallbackFonts.begin(); iter != mFallbackFonts.end(); iter++) +		for (; it_fallback != mFallbackFonts.cend(); ++it_fallback)  		{ -			glyph_index = FT_Get_Char_Index((*iter)->mFTFace, wch); +			glyph_index = FT_Get_Char_Index(it_fallback->first->mFTFace, wch);  			if (glyph_index)  			{ -				return addGlyphFromFont(*iter, wch, glyph_index); +				return addGlyphFromFont(it_fallback->first, wch, glyph_index, glyph_type);  			}  		}  	} -	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); -	if (iter == mCharGlyphInfoMap.end()) +	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch); +	char_glyph_info_map_t::iterator iter =  +		std::find_if(range_it.first, range_it.second, [&glyph_type](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == glyph_type; }); +	if (iter == range_it.second)  	{ -		return addGlyphFromFont(this, wch, glyph_index); +		return addGlyphFromFont(this, wch, glyph_index, glyph_type);  	}  	return NULL;  } -LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const +LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType requested_glyph_type) const  {      LL_PROFILE_ZONE_SCOPED;  	if (mFTFace == NULL)  		return NULL;  	llassert(!mIsFallback); -	fontp->renderGlyph(glyph_index); +	fontp->renderGlyph(requested_glyph_type, glyph_index); + +	EFontGlyphType bitmap_glyph_type = EFontGlyphType::Unspecified; +	switch (fontp->mFTFace->glyph->bitmap.pixel_mode) +	{ +		case FT_PIXEL_MODE_MONO: +		case FT_PIXEL_MODE_GRAY: +			bitmap_glyph_type = EFontGlyphType::Grayscale; +			break; +		case FT_PIXEL_MODE_BGRA: +			bitmap_glyph_type = EFontGlyphType::Color; +			break; +		default: +			llassert_always(true); +			break; +	}  	S32 width = fontp->mFTFace->glyph->bitmap.width;  	S32 height = fontp->mFTFace->glyph->bitmap.rows;  	S32 pos_x, pos_y; -	S32 bitmap_num; -	mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_num); +	U32 bitmap_num; +	mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_glyph_type, bitmap_num);  	mAddGlyphCount++; -	LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index); +	LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index, requested_glyph_type);  	gi->mXBitmapOffset = pos_x;  	gi->mYBitmapOffset = pos_y; -	gi->mBitmapNum = bitmap_num; +	gi->mBitmapEntry = std::make_pair(bitmap_glyph_type, bitmap_num);  	gi->mWidth = width;  	gi->mHeight = height;  	gi->mXBearing = fontp->mFTFace->glyph->bitmap_left; @@ -484,8 +546,12 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l  	insertGlyphInfo(wch, gi); -	llassert(fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO -	    || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY); +	if (requested_glyph_type != bitmap_glyph_type) +	{ +		LLFontGlyphInfo* gi_temp = new LLFontGlyphInfo(*gi); +		gi_temp->mGlyphType = bitmap_glyph_type; +		insertGlyphInfo(wch, gi_temp); +	}  	if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO  	    || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) @@ -520,78 +586,86 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l  			buffer_row_stride = width;  		} -		switch (mFontBitmapCachep->getNumComponents()) -		{ -		case 1: -			mFontBitmapCachep->getImageRaw(bitmap_num)->setSubImage(pos_x, -																	pos_y, -																	width, -																	height, -																	buffer_data, -																	buffer_row_stride, -																	TRUE); -			break; -		case 2: -			setSubImageLuminanceAlpha(pos_x,	 -									  pos_y, -									  bitmap_num, -									  width, -									  height, -									  buffer_data, -									  buffer_row_stride); -			break; -		default: -			break; -		} +		setSubImageLuminanceAlpha(pos_x, +									pos_y, +									bitmap_num, +									width, +									height, +									buffer_data, +									buffer_row_stride);  		if (tmp_graydata)  			delete[] tmp_graydata; +	} +	else if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) +	{ +		setSubImageBGRA(pos_x, +		                pos_y, +		                bitmap_num, +		                fontp->mFTFace->glyph->bitmap.width, +		                fontp->mFTFace->glyph->bitmap.rows, +		                fontp->mFTFace->glyph->bitmap.buffer, +		                llabs(fontp->mFTFace->glyph->bitmap.pitch));  	} else { -		// we don't know how to handle this pixel format from FreeType; -		// omit it from the font-image. +		llassert(false);  	} -	LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_num); -	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num); +	LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_glyph_type, bitmap_num); +	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_glyph_type, bitmap_num);  	image_gl->setSubImage(image_raw, 0, 0, image_gl->getWidth(), image_gl->getHeight());  	return gi;  } -LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch) const +LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch, EFontGlyphType glyph_type) const  { -	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); -	if (iter != mCharGlyphInfoMap.end()) +	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch); + +	char_glyph_info_map_t::iterator iter = (EFontGlyphType::Unspecified != glyph_type) +		? std::find_if(range_it.first, range_it.second, [&glyph_type](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == glyph_type; }) +		: range_it.first; +	if (iter != range_it.second)  	{  		return iter->second;  	}  	else  	{  		// this glyph doesn't yet exist, so render it and return the result -		return addGlyph(wch); +		return addGlyph(wch, (EFontGlyphType::Unspecified != glyph_type) ? glyph_type : EFontGlyphType::Grayscale);  	}  }  void LLFontFreetype::insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const  { -	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); -	if (iter != mCharGlyphInfoMap.end()) +	llassert(gi->mGlyphType < EFontGlyphType::Count); +	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch); + +	char_glyph_info_map_t::iterator iter = +		std::find_if(range_it.first, range_it.second, [&gi](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == gi->mGlyphType; }); +	if (iter != range_it.second)  	{  		delete iter->second;  		iter->second = gi;  	}  	else  	{ -		mCharGlyphInfoMap[wch] = gi; +		mCharGlyphInfoMap.insert(std::make_pair(wch, gi));  	}  } -void LLFontFreetype::renderGlyph(U32 glyph_index) const +void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) const  {  	if (mFTFace == NULL)  		return; -	llassert_always(! FT_Load_Glyph(mFTFace, glyph_index, FT_LOAD_FORCE_AUTOHINT) ); +	FT_Int32 load_flags = FT_LOAD_FORCE_AUTOHINT; +	if (EFontGlyphType::Color == bitmap_type) +	{ +		// We may not actually get a color render so our caller should always examine mFTFace->glyph->bitmap.pixel_mode +		load_flags |= FT_LOAD_COLOR; +	} + +	llassert_always(! FT_Load_Glyph(mFTFace, glyph_index, load_flags) );  	llassert_always(! FT_Render_Glyph(mFTFace->glyph, gFontRenderMode) ); @@ -601,7 +675,7 @@ void LLFontFreetype::renderGlyph(U32 glyph_index) const  void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)  {  	resetBitmapCache();  -	loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mFontBitmapCachep->getNumComponents(), mIsFallback); +	loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mIsFallback, 0);  	if (!mIsFallback)  	{  		// This is the head of the list - need to rebuild ourself and all fallbacks. @@ -611,11 +685,9 @@ void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)  		}  		else  		{ -			for(font_vector_t::iterator it = mFallbackFonts.begin(); -				it != mFallbackFonts.end(); -				++it) +			for (fallback_font_vector_t::iterator it = mFallbackFonts.begin(); it != mFallbackFonts.end(); ++it)  			{ -				(*it)->reset(vert_dpi, horz_dpi); +				it->first->reset(vert_dpi, horz_dpi);  			}  		}  	} @@ -637,7 +709,7 @@ void LLFontFreetype::resetBitmapCache()  	if(!mIsFallback)  	{  		// Add the empty glyph -		addGlyphFromFont(this, 0, 0); +		addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);  	}  } @@ -651,6 +723,34 @@ const std::string &LLFontFreetype::getName() const  	return mName;  } +static void dumpFontBitmap(const LLImageRaw* image_raw, const std::string& file_name) +{ +	LLPointer<LLImagePNG> tmpImage = new LLImagePNG(); +	if ( (tmpImage->encode(image_raw, 0.0f)) && (tmpImage->save(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name))) ) +	{ +		LL_INFOS("Font") << "Successfully saved " << file_name << LL_ENDL; +	} +	else +	{ +		LL_WARNS("Font") << "Failed to save " << file_name << LL_ENDL; +	} +} + +void LLFontFreetype::dumpFontBitmaps() const +{ +	// Dump all the regular bitmaps (if any) +	for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Grayscale); idx < cnt; idx++) +	{ +		dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, idx), llformat("%s_%d_%d_%d.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx)); +	} + +	// Dump all the color bitmaps (if any) +	for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Color); idx < cnt; idx++) +	{ +		dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, idx), llformat("%s_%d_%d_%d_clr.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx)); +	} +} +  const LLFontBitmapCache* LLFontFreetype::getFontBitmapCache() const  {  	return mFontBitmapCachep; @@ -666,17 +766,46 @@ U8 LLFontFreetype::getStyle() const  	return mStyle;  } +bool LLFontFreetype::setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const +{ +	LLImageRaw* image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, bitmap_num); +	llassert(!mIsFallback); +	llassert(image_raw && (image_raw->getComponents() == 4)); + +	// NOTE: inspired by LLImageRaw::setSubImage() +	U32* image_data = (U32*)image_raw->getData(); +	if (!image_data) +	{ +		return false; +	} + +	for (U32 idxRow = 0; idxRow < height; idxRow++) +	{ +		const U32 nSrcRow = height - 1 - idxRow; +		const U32 nSrcOffset = nSrcRow * width * image_raw->getComponents(); +		const U32 nDstOffset = (y + idxRow) * image_raw->getWidth() + x; + +		for (U32 idxCol = 0; idxCol < width; idxCol++) +		{ +			U32 nTemp = nSrcOffset + idxCol * 4; +			image_data[nDstOffset + idxCol] = data[nTemp + 3] << 24 | data[nTemp] << 16 | data[nTemp + 1] << 8 | data[nTemp + 2]; +		} +	} + +	return true; +} +  void LLFontFreetype::setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride) const  { -	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num); +	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, bitmap_num);  	llassert(!mIsFallback);  	llassert(image_raw && (image_raw->getComponents() == 2)); -  	U8 *target = image_raw->getData(); +    llassert(target); -	if (!data) +	if (!data || !target)  	{  		return;  	} diff --git a/indra/llrender/llfontfreetype.h b/indra/llrender/llfontfreetype.h index f61f169987..b036d337ba 100644 --- a/indra/llrender/llfontfreetype.h +++ b/indra/llrender/llfontfreetype.h @@ -56,9 +56,11 @@ private:  struct LLFontGlyphInfo  { -	LLFontGlyphInfo(U32 index); +	LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type); +	LLFontGlyphInfo(const LLFontGlyphInfo& fgi);  	U32 mGlyphIndex; +	EFontGlyphType mGlyphType;  	// Metrics  	S32 mWidth;			// In pixels @@ -71,7 +73,7 @@ struct LLFontGlyphInfo  	S32 mYBitmapOffset; // Offset to the origin in the bitmap  	S32 mXBearing;	// Distance from baseline to left in pixels  	S32 mYBearing;	// Distance from baseline to top in pixels -	S32 mBitmapNum; // Which bitmap in the bitmap cache contains this glyph +	std::pair<EFontGlyphType, S32> mBitmapEntry; // Which bitmap in the bitmap cache contains this glyph  };  extern LLFontManager *gFontManagerp; @@ -84,7 +86,7 @@ public:  	// is_fallback should be true for fallback fonts that aren't used  	// to render directly (Unicode backup, primarily) -	BOOL loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n = 0); +	BOOL loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, bool is_fallback, S32 face_n);  	S32 getNumFaces(const std::string& filename); @@ -93,10 +95,8 @@ public:  	void clearFontStreams();  #endif -	typedef std::vector<LLPointer<LLFontFreetype> > font_vector_t; - -	void setFallbackFonts(const font_vector_t &font); -	const font_vector_t &getFallbackFonts() const; +	typedef std::function<bool(llwchar)> char_functor_t; +	void addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor = nullptr);  	// Global font metrics - in units of pixels  	F32 getLineHeight() const; @@ -135,7 +135,7 @@ public:  	F32 getXKerning(llwchar char_left, llwchar char_right) const; // Get the kerning between the two characters  	F32 getXKerning(const LLFontGlyphInfo* left_glyph_info, const LLFontGlyphInfo* right_glyph_info) const; // Get the kerning between the two characters -	LLFontGlyphInfo* getGlyphInfo(llwchar wch) const; +	LLFontGlyphInfo* getGlyphInfo(llwchar wch, EFontGlyphType glyph_type) const;  	void reset(F32 vert_dpi, F32 horz_dpi); @@ -143,6 +143,7 @@ public:  	const std::string& getName() const; +	void       dumpFontBitmaps() const;  	const LLFontBitmapCache* getFontBitmapCache() const;  	void setStyle(U8 style); @@ -151,10 +152,11 @@ public:  private:  	void resetBitmapCache();  	void setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride = 0) const; +	bool setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const;  	BOOL hasGlyph(llwchar wch) const;		// Has a glyph for this character -	LLFontGlyphInfo* addGlyph(llwchar wch) const;		// Add a new character to the font if necessary -	LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const;	// Add a glyph from this font to the other (returns the glyph_index, 0 if not found) -	void renderGlyph(U32 glyph_index) const; +	LLFontGlyphInfo* addGlyph(llwchar wch, EFontGlyphType glyph_type) const;		// Add a new character to the font if necessary +	LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType bitmap_type) const;	// Add a glyph from this font to the other (returns the glyph_index, 0 if not found) +	void renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) const;  	void insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const;  	std::string mName; @@ -174,9 +176,12 @@ private:  #endif  	BOOL mIsFallback; -	font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars) +	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) -	typedef boost::unordered_map<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t; +	// *NOTE: the same glyph can be present with multiple representations (but the pointer is always unique) +	typedef boost::unordered_multimap<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t;  	mutable char_glyph_info_map_t mCharGlyphInfoMap; // Information about glyph location in bitmap  	mutable LLFontBitmapCache* mFontBitmapCachep; diff --git a/indra/llrender/llfontfreetypesvg.cpp b/indra/llrender/llfontfreetypesvg.cpp new file mode 100644 index 0000000000..19d327a4c9 --- /dev/null +++ b/indra/llrender/llfontfreetypesvg.cpp @@ -0,0 +1,205 @@ +/** + * @file llfontfreetypesvg.cpp + * @brief Freetype font library SVG glyph rendering + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llfontfreetypesvg.h" + +#if LL_WINDOWS +#pragma warning (push) +#pragma warning (disable : 4702) +#endif + +#define NANOSVG_IMPLEMENTATION +#include <nanosvg/nanosvg.h> +#define NANOSVGRAST_IMPLEMENTATION +#include <nanosvg/nanosvgrast.h> + +#if LL_WINDOWS +#pragma warning (pop) +#endif + +struct LLSvgRenderData +{ +	FT_UInt    GlyphIndex = 0; +	FT_Error   Error = FT_Err_Ok; // FreeType currently (@2.12.1) ignores the error value returned by the preset glyph slot callback so we return it at render time +	// (See https://github.com/freetype/freetype/blob/5faa1df8b93ebecf0f8fd5fe8fda7b9082eddced/src/base/ftobjs.c#L1170) +	NSVGimage* pNSvgImage = nullptr; +	float      Scale = 0.f; +}; + +// static +FT_Error LLFontFreeTypeSvgRenderer::OnInit(FT_Pointer* state) +{ +	// The SVG driver hook state is shared across all callback invocations; since our state is lightweight +	// we store it in the glyph instead. +	*state = nullptr; + +	return FT_Err_Ok; +} + +// static +void LLFontFreeTypeSvgRenderer::OnFree(FT_Pointer* state) +{ +} + +// static +void LLFontFreeTypeSvgRenderer::OnDataFinalizer(void* objectp) +{ +	FT_GlyphSlot glyph_slot = static_cast<FT_GlyphSlot>(objectp); + +	LLSvgRenderData* pData = static_cast<LLSvgRenderData*>(glyph_slot->generic.data); +	glyph_slot->generic.data = nullptr; +	glyph_slot->generic.finalizer = nullptr; +	delete(pData); +} + +//static +FT_Error LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer*) +{ +	FT_SVG_Document document = static_cast<FT_SVG_Document>(glyph_slot->other); + +	llassert(!glyph_slot->generic.data || !cache || glyph_slot->glyph_index == ((LLSvgRenderData*)glyph_slot->generic.data)->GlyphIndex); +	if (!glyph_slot->generic.data) +	{ +		glyph_slot->generic.data = new LLSvgRenderData(); +		glyph_slot->generic.finalizer = LLFontFreeTypeSvgRenderer::OnDataFinalizer; +	} +	LLSvgRenderData* datap = static_cast<LLSvgRenderData*>(glyph_slot->generic.data); +	if (!cache) +	{ +		datap->GlyphIndex = glyph_slot->glyph_index; +		datap->Error = FT_Err_Ok; +	} + +	// NOTE: nsvgParse modifies the input string so we need a temporary copy +	llassert(!datap->pNSvgImage || cache); +	if (!datap->pNSvgImage) +	{ +		char* document_buffer = new char[document->svg_document_length + 1]; +		memcpy(document_buffer, document->svg_document, document->svg_document_length); +		document_buffer[document->svg_document_length] = '\0'; + +		datap->pNSvgImage = nsvgParse(document_buffer, "px", 0.); + +		delete[] document_buffer; +	} + +	if (!datap->pNSvgImage) +	{ +		datap->Error = FT_Err_Invalid_SVG_Document; +		return FT_Err_Invalid_SVG_Document; +	} + +	// We don't (currently) support transformations so test for an identity rotation matrix + zero translation +	if (document->transform.xx != 1 << 16 || document->transform.yx != 0 || +		document->transform.xy != 0 || document->transform.yy != 1 << 16 || +		document->delta.x > 0 || document->delta.y > 0) +	{ +		datap->Error = FT_Err_Unimplemented_Feature; +		return FT_Err_Unimplemented_Feature; +	} + +	float svg_width = datap->pNSvgImage->width; +	float svg_height = datap->pNSvgImage->height; +	if (svg_width == 0.f || svg_height == 0.f) +	{ +		svg_width = document->units_per_EM; +		svg_height = document->units_per_EM; +	} + +	float svg_x_scale = (float)document->metrics.x_ppem / floorf(svg_width); +	float svg_y_scale = (float)document->metrics.y_ppem / floorf(svg_height); +	float svg_scale = llmin(svg_x_scale, svg_y_scale); +	datap->Scale = svg_scale; + +	glyph_slot->bitmap.width = floorf(svg_width) * svg_scale; +	glyph_slot->bitmap.rows = floorf(svg_height) * svg_scale; +	glyph_slot->bitmap_left = (document->metrics.x_ppem - glyph_slot->bitmap.width) / 2; +	glyph_slot->bitmap_top = glyph_slot->face->size->metrics.ascender / 64.f; +	glyph_slot->bitmap.pitch = glyph_slot->bitmap.width * 4; +	glyph_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; + +	/* Copied as-is from fcft (MIT license) */ + +	// Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box. +	float horiBearingX = 0.; +	float horiBearingY = -glyph_slot->bitmap_top; + +	// XXX parentheses correct? +	float vertBearingX = glyph_slot->metrics.horiBearingX / 64.0f - glyph_slot->metrics.horiAdvance / 64.0f / 2; +	float vertBearingY = (glyph_slot->metrics.vertAdvance / 64.0f - glyph_slot->metrics.height / 64.0f) / 2; + +	// Do conversion in two steps to avoid 'bad function cast' warning +	glyph_slot->metrics.width = glyph_slot->bitmap.width * 64; +	glyph_slot->metrics.height = glyph_slot->bitmap.rows * 64; +	glyph_slot->metrics.horiBearingX = horiBearingX * 64; +	glyph_slot->metrics.horiBearingY = horiBearingY * 64; +	glyph_slot->metrics.vertBearingX = vertBearingX * 64; +	glyph_slot->metrics.vertBearingY = vertBearingY * 64; +	if (glyph_slot->metrics.vertAdvance == 0) +	{ +		glyph_slot->metrics.vertAdvance = glyph_slot->bitmap.rows * 1.2f * 64; +	} + +	return FT_Err_Ok; +} + +// static +FT_Error LLFontFreeTypeSvgRenderer::OnRender(FT_GlyphSlot glyph_slot, FT_Pointer*) +{ +	LLSvgRenderData* datap = static_cast<LLSvgRenderData*>(glyph_slot->generic.data); +	llassert(FT_Err_Ok == datap->Error); +	if (FT_Err_Ok != datap->Error) +	{ +		return datap->Error; +	} + +	// Render to glyph bitmap +	NSVGrasterizer* nsvgRasterizer = nsvgCreateRasterizer(); +	nsvgRasterize(nsvgRasterizer, datap->pNSvgImage, 0, 0, datap->Scale, glyph_slot->bitmap.buffer, glyph_slot->bitmap.width, glyph_slot->bitmap.rows, glyph_slot->bitmap.pitch); +	nsvgDeleteRasterizer(nsvgRasterizer); +	nsvgDelete(datap->pNSvgImage); +	datap->pNSvgImage = nullptr; + +	// Convert from RGBA to BGRA +	U32* pixel_buffer = (U32*)glyph_slot->bitmap.buffer; U8* byte_buffer = glyph_slot->bitmap.buffer; +	for (size_t y = 0, h = glyph_slot->bitmap.rows; y < h; y++) +	{ +		for (size_t x = 0, w = glyph_slot->bitmap.pitch / 4; x < w; x++) +		{ +			size_t pixel_idx = y * w + x; +			size_t byte_idx = pixel_idx * 4; +			U8 alpha = byte_buffer[byte_idx + 3]; +			// Store as ARGB (*TODO - do we still have to care about endianness?) +			pixel_buffer[y * w + x] = alpha << 24 | (byte_buffer[byte_idx] * alpha / 0xFF) << 16 | (byte_buffer[byte_idx + 1] * alpha / 0xFF) << 8 | (byte_buffer[byte_idx + 2] * alpha / 0xFF); +		} +	} + +	glyph_slot->format = FT_GLYPH_FORMAT_BITMAP; +	glyph_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; +	return FT_Err_Ok; +} diff --git a/indra/llrender/llfontfreetypesvg.h b/indra/llrender/llfontfreetypesvg.h new file mode 100644 index 0000000000..b5f541991a --- /dev/null +++ b/indra/llrender/llfontfreetypesvg.h @@ -0,0 +1,54 @@ +/** + * @file llfontfreetypesvg.h + * @brief Freetype font library SVG glyph rendering + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#pragma once + +#include <ft2build.h> +#include FT_TYPES_H +#include FT_MODULE_H +#include FT_OTSVG_H + + // See https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html +class LLFontFreeTypeSvgRenderer +{ +public: +	// Called when the very first OT-SVG glyph is rendered (across the entire lifetime of our FT_Library object) +	static FT_Error OnInit(FT_Pointer* state); + +	// Called when the ot-svg module is being freed (but only called if the init hook was called previously) +	static void     OnFree(FT_Pointer* state); + +	// Called to preset the glyph slot, twice per glyph: +	//   - when FT_Load_Glyph needs to preset the glyph slot (with cache == false) +	//   - right before the svg module calls the render callback hook. (with cache == true) +	static FT_Error OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer* state); + +	// Called to render an OT-SVG glyph (right after the preset hook OnPresetGlypthSlot was called with cache set to TRUE) +	static FT_Error OnRender(FT_GlyphSlot glyph_slot, FT_Pointer* state); + +	// Called to deallocate our per glyph slot data +	static void OnDataFinalizer(void* objectp); +}; diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 15fddbc99f..f0b89f9a1d 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -89,14 +89,14 @@ void LLFontGL::destroyGL()  	mFontFreetype->destroyGL();  } -BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback, S32 face_n) +BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n)  {  	if(mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL))  	{  		mFontFreetype = new LLFontFreetype;  	} -	return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback, face_n); +	return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, is_fallback, face_n);  }  S32 LLFontGL::getNumFaces(const std::string& filename) @@ -110,14 +110,14 @@ S32 LLFontGL::getNumFaces(const std::string& filename)  }  S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRect& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, -    ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses) const +    ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses, BOOL use_color) const  {      LLRectf rect_float(rect.mLeft, rect.mTop, rect.mRight, rect.mBottom); -    return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses); +    return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses, use_color);  }  S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,  -					 ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses) const +					 ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses, BOOL use_color) const  {  	F32 x = rect.mLeft;  	F32 y = 0.f; @@ -138,12 +138,12 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rec  		y = rect.mBottom;  		break;  	} -	return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses); +	return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses, use_color);  }  S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,  -					 ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const +					 ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses, BOOL use_color) const  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; @@ -193,7 +193,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  	if (-1 == max_chars)  	{ -		length = (S32)wstr.length() - begin_offset; +		max_chars = length = (S32)wstr.length() - begin_offset;  	}  	else  	{ @@ -254,7 +254,6 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  	const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; -  	BOOL draw_ellipses = FALSE;  	if (use_ellipses)  	{ @@ -278,7 +277,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  	LLColor4U text_color(color); -	S32 bitmap_num = -1; +	std::pair<EFontGlyphType, S32> bitmap_entry = std::make_pair(EFontGlyphType::Grayscale, -1);  	S32 glyph_count = 0;  	for (i = begin_offset; i < begin_offset + length; i++)  	{ @@ -288,7 +287,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  		next_glyph = NULL;  		if(!fgi)  		{ -			fgi = mFontFreetype->getGlyphInfo(wch); +			fgi = mFontFreetype->getGlyphInfo(wch, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);  		}  		if (!fgi)  		{ @@ -296,8 +295,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  			break;  		}  		// Per-glyph bitmap texture. -		S32 next_bitmap_num = fgi->mBitmapNum; -		if (next_bitmap_num != bitmap_num) +		std::pair<EFontGlyphType, S32> next_bitmap_entry = fgi->mBitmapEntry; +		if (next_bitmap_entry != bitmap_entry)  		{  			// Actually draw the queued glyphs before switching their texture;  			// otherwise the queued glyphs will be taken from wrong textures. @@ -311,8 +310,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  				glyph_count = 0;  			} -			bitmap_num = next_bitmap_num; -			LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num); +			bitmap_entry = next_bitmap_entry; +			LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second);  			gGL.getTexUnit(0)->bind(font_image);  		} @@ -345,7 +344,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  			glyph_count = 0;  		} -		drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength); +		drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, (bitmap_entry.first == EFontGlyphType::Grayscale) ? text_color : LLColor4U::white, style_to_add, shadow, drop_shadow_strength);  		chars_drawn++;  		cur_x += fgi->mXAdvance; @@ -355,7 +354,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  		if (next_char && (next_char < LAST_CHARACTER))  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(next_char); +			next_glyph = mFontFreetype->getGlyphInfo(next_char, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);  			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);  		} @@ -409,7 +408,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  				shadow,  				S32_MAX, max_pixels,  				right_x, -				FALSE);  +				FALSE, +				use_color);   		gGL.popUIMatrix();  	} @@ -423,19 +423,19 @@ S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, cons  	return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);  } -S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, BOOL use_ellipses) const +S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses, BOOL use_color) const  { -	return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses); +	return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color);  }  S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const  { -	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); +	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE, FALSE);  }  S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow) const  { -	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, FALSE); +	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, FALSE, FALSE);  }  // font metrics - override for LLFontFreetype that returns units of virtual pixels @@ -488,7 +488,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars) const  	return getWidthF32(wchars, 0, S32_MAX);  } -F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max_chars ) const +F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max_chars) const  {  	LLWString wtext = utf8str_to_wstring(utf8text);  	return getWidthF32(wtext.c_str(), begin_offset, max_chars); @@ -512,7 +512,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars  		next_glyph = NULL;  		if(!fgi)  		{ -			fgi = mFontFreetype->getGlyphInfo(wch); +			fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  		}  		F32 advance = mFontFreetype->getXAdvance(fgi); @@ -535,7 +535,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars  			&& (next_char < LAST_CHARACTER))  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(next_char); +			next_glyph = mFontFreetype->getGlyphInfo(next_char, EFontGlyphType::Unspecified);  			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);  		}  		// Round after kerning. @@ -556,7 +556,7 @@ void LLFontGL::generateASCIIglyphs()      LL_PROFILE_ZONE_SCOPED_CATEGORY_UI      for (U32 i = 32; (i < 127); i++)      { -        mFontFreetype->getGlyphInfo(i); +        mFontFreetype->getGlyphInfo(i, EFontGlyphType::Grayscale);      }  } @@ -630,7 +630,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch  		next_glyph = NULL;  		if(!fgi)  		{ -			fgi = mFontFreetype->getGlyphInfo(wch); +			fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  			if (NULL == fgi)  			{ @@ -655,7 +655,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch  		if (((i+1) < max_chars) && wchars[i+1])  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1]); +			next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1], EFontGlyphType::Unspecified);  			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);  		} @@ -702,7 +702,7 @@ S32	LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_  	{  		llwchar wch = wchars[i]; -		const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch); +		const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  		// last character uses character width, since the whole character needs to be visible  		// other characters just use advance @@ -777,7 +777,7 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 t  		next_glyph = NULL;  		if(!glyph)  		{ -			glyph = mFontFreetype->getGlyphInfo(wch); +			glyph = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  		}  		F32 char_width = mFontFreetype->getXAdvance(glyph); @@ -807,7 +807,7 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 t  			&& (wchars[(pos + 1)]))  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1]); +			next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1], EFontGlyphType::Unspecified);  			cur_x += mFontFreetype->getXKerning(glyph, next_glyph);  		} @@ -847,6 +847,26 @@ void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::st  	LLFontGL::loadDefaultFonts();  } +void LLFontGL::dumpTextures() +{ +	if (mFontFreetype.notNull()) +	{ +		mFontFreetype->dumpFontBitmaps(); +	} +} + +// static +void LLFontGL::dumpFonts() +{ +	sFontRegistry->dump(); +} + +// static +void LLFontGL::dumpFontTextures() +{ +	sFontRegistry->dumpTextures(); +} +  // Force standard fonts to get generated up front.  // This is primarily for error detection purposes.  // Don't do this during initClass because it can be slow and we want to get @@ -1010,6 +1030,20 @@ LLFontGL::VAlign LLFontGL::vAlignFromName(const std::string& name)  }  //static +LLFontGL* LLFontGL::getFontEmoji() +{ +	static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Large", 0)); +	return fontp;; +} + +//static +LLFontGL* LLFontGL::getFontEmojiHuge() +{ +	static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Huge", 0)); +	return fontp;; +} + +//static  LLFontGL* LLFontGL::getFontMonospace()  {  	static LLFontGL* fontp = getFont(LLFontDescriptor("Monospace","Monospace",0)); diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 9bbf9ce0c4..a9d512f7ce 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -87,7 +87,7 @@ public:  	void destroyGL(); -	BOOL loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, BOOL is_fallback, S32 face_n = 0); +	BOOL loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n);  	S32 getNumFaces(const std::string& filename); @@ -98,7 +98,8 @@ public:  				U8 style = NORMAL, ShadowType shadow = NO_SHADOW,   				S32 max_chars = S32_MAX,  				F32* right_x=NULL,  -				BOOL use_ellipses = FALSE) const; +				BOOL use_ellipses = FALSE, +				BOOL use_color = FALSE) const;  	S32 render(const LLWString &text, S32 begin_offset,   				const LLRectf& rect,  @@ -107,7 +108,8 @@ public:  				U8 style = NORMAL, ShadowType shadow = NO_SHADOW,   				S32 max_chars = S32_MAX,  				F32* right_x=NULL,  -				BOOL use_ellipses = FALSE) const; +				BOOL use_ellipses = FALSE, +				BOOL use_color = FALSE) const;  	S32 render(const LLWString &text, S32 begin_offset,   				F32 x, F32 y,  @@ -116,12 +118,13 @@ public:  				U8 style = NORMAL, ShadowType shadow = NO_SHADOW,   				S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX,   				F32* right_x=NULL,  -				BOOL use_ellipses = FALSE) const; +				BOOL use_ellipses = FALSE, +				BOOL use_color = FALSE) const;  	S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const;  	// renderUTF8 does a conversion, so is slower! -	S32 renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, BOOL use_ellipses) const; +	S32 renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, BOOL use_ellipses, BOOL use_color) const;  	S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const;  	S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style = NORMAL, ShadowType shadow = NO_SHADOW) const; @@ -132,12 +135,12 @@ public:  	S32 getWidth(const std::string& utf8text) const;  	S32 getWidth(const llwchar* wchars) const; -	S32 getWidth(const std::string& utf8text, S32 offset, S32 max_chars ) const; +	S32 getWidth(const std::string& utf8text, S32 offset, S32 max_chars) const;  	S32 getWidth(const llwchar* wchars, S32 offset, S32 max_chars) const;  	F32 getWidthF32(const std::string& utf8text) const;  	F32 getWidthF32(const llwchar* wchars) const; -	F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars ) const; +	F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars) const;  	F32 getWidthF32(const llwchar* wchars, S32 offset, S32 max_chars, bool no_padding = false) const;  	// The following are called often, frequently with large buffers, so do not use a string interface @@ -165,6 +168,10 @@ public:  	static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures = true); +	       void dumpTextures(); +	static void dumpFonts(); +	static void dumpFontTextures(); +  	// Load sans-serif, sans-serif-small, etc.  	// Slow, requires multiple seconds to load fonts.  	static bool loadDefaultFonts(); @@ -187,6 +194,8 @@ public:  	static void setFontDisplay(BOOL flag) { sDisplayFont = flag; } +	static LLFontGL* getFontEmoji(); +	static LLFontGL* getFontEmojiHuge();  	static LLFontGL* getFontMonospace();  	static LLFontGL* getFontSansSerifSmall();      static LLFontGL* getFontSansSerifSmallBold(); diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index 9750bd4fa1..d2c7e466e6 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -47,6 +47,10 @@ bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node);  const std::string MACOSX_FONT_PATH_LIBRARY = "/Library/Fonts/";  const std::string MACOSX_FONT_SUPPLEMENTAL = "Supplemental/"; +LLFontDescriptor::char_functor_map_t LLFontDescriptor::mCharFunctors({ +	{ "is_emoji", LLStringOps::isEmoji } +}); +  LLFontDescriptor::LLFontDescriptor():  	mStyle(0)  { @@ -55,22 +59,22 @@ LLFontDescriptor::LLFontDescriptor():  LLFontDescriptor::LLFontDescriptor(const std::string& name,  								   const std::string& size,   								   const U8 style, -								   const string_vec_t& file_names): +								   const font_file_info_vec_t& font_files):  	mName(name),  	mSize(size),  	mStyle(style), -	mFileNames(file_names) +	mFontFiles(font_files)  {  }  LLFontDescriptor::LLFontDescriptor(const std::string& name,  	const std::string& size,  	const U8 style, -	const string_vec_t& file_names, -	const string_vec_t& ft_collection_listections) : -	LLFontDescriptor(name, size, style, file_names) +	const font_file_info_vec_t& font_list, +	const font_file_info_vec_t& font_collection_files) : +	LLFontDescriptor(name, size, style, font_list)  { -	mFontCollectionsList = ft_collection_listections; +	mFontCollectionFiles = font_collection_files;  }  LLFontDescriptor::LLFontDescriptor(const std::string& name, @@ -82,7 +86,6 @@ LLFontDescriptor::LLFontDescriptor(const std::string& name,  {  } -  bool LLFontDescriptor::operator<(const LLFontDescriptor& b) const  {  	if (mName < b.mName) @@ -175,7 +178,19 @@ LLFontDescriptor LLFontDescriptor::normalize() const  	if (removeSubString(new_name,"Italic"))  		new_style |= LLFontGL::ITALIC; -	return LLFontDescriptor(new_name,new_size,new_style,getFileNames(),getFontCollectionsList()); +	return LLFontDescriptor(new_name,new_size,new_style, getFontFiles(), getFontCollectionFiles()); +} + +void LLFontDescriptor::addFontFile(const std::string& file_name, const std::string& char_functor) +{ +	char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor); +	mFontFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr)); +} + +void LLFontDescriptor::addFontCollectionFile(const std::string& file_name, const std::string& char_functor) +{ +	char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor); +	mFontCollectionFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr));  }  LLFontRegistry::LLFontRegistry(bool create_gl_textures) @@ -273,17 +288,24 @@ bool font_desc_init_from_xml(LLXMLNodePtr node, LLFontDescriptor& desc)  		if (child->hasName("file"))  		{  			std::string font_file_name = child->getTextContents(); -			desc.getFileNames().push_back(font_file_name); -			 +			std::string char_functor; + +			if (child->hasAttribute("functor")) +			{ +				child->getAttributeString("functor", char_functor); +			} +  			if (child->hasAttribute("load_collection"))  			{  				BOOL col = FALSE;  				child->getAttributeBOOL("load_collection", col);  				if (col)  				{ -					desc.getFontCollectionsList().push_back(font_file_name); +					desc.addFontCollectionFile(font_file_name, char_functor);  				}  			} + +			desc.addFontFile(font_file_name, char_functor);  		}  		else if (child->hasName("os"))  		{ @@ -326,19 +348,19 @@ bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node)  					// A little roundabout because the map key is const,  					// so we have to fetch it, make a new map key, and  					// replace the old entry. -					string_vec_t match_file_names = match_desc->getFileNames(); -					match_file_names.insert(match_file_names.begin(), -											desc.getFileNames().begin(), -											desc.getFileNames().end()); +					font_file_info_vec_t font_files = match_desc->getFontFiles(); +					font_files.insert(font_files.begin(), +									  desc.getFontFiles().begin(), +									  desc.getFontFiles().end()); -					string_vec_t collections_list = match_desc->getFontCollectionsList(); -					collections_list.insert(collections_list.begin(), -						desc.getFontCollectionsList().begin(), -						desc.getFontCollectionsList().end()); +					font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles(); +					font_collection_files.insert(font_collection_files.begin(), +						desc.getFontCollectionFiles().begin(), +						desc.getFontCollectionFiles().end());  					LLFontDescriptor new_desc = *match_desc; -					new_desc.getFileNames() = match_file_names; -					new_desc.getFontCollectionsList() = collections_list; +					new_desc.setFontFiles(font_files); +					new_desc.setFontCollectionFiles(font_collection_files);  					registry->mFontMap.erase(*match_desc);  					registry->mFontMap[new_desc] = NULL;  				} @@ -423,82 +445,80 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)  	// Build list of font names to look for.  	// Files specified for this font come first, followed by those from the default descriptor. -	string_vec_t file_names = match_desc->getFileNames(); -	string_vec_t ft_collection_list = match_desc->getFontCollectionsList(); -	string_vec_t default_file_names; +	font_file_info_vec_t font_files = match_desc->getFontFiles(); +	font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles();  	LLFontDescriptor default_desc("default",s_template_string,0);  	const LLFontDescriptor *match_default_desc = getMatchingFontDesc(default_desc);  	if (match_default_desc)  	{ -		file_names.insert(file_names.end(), -						  match_default_desc->getFileNames().begin(), -						  match_default_desc->getFileNames().end()); -		ft_collection_list.insert(ft_collection_list.end(), -			match_default_desc->getFontCollectionsList().begin(), -			match_default_desc->getFontCollectionsList().end()); +		font_files.insert(font_files.end(), +						  match_default_desc->getFontFiles().begin(), +						  match_default_desc->getFontFiles().end()); +		font_collection_files.insert(font_collection_files.end(), +			match_default_desc->getFontCollectionFiles().begin(), +			match_default_desc->getFontCollectionFiles().end());  	}  	// Add ultimate fallback list - generated dynamically on linux,  	// null elsewhere. -	file_names.insert(file_names.end(), -					  getUltimateFallbackList().begin(), -					  getUltimateFallbackList().end()); +	std::transform(getUltimateFallbackList().begin(), getUltimateFallbackList().end(), std::back_inserter(font_files), +	               [](const std::string& file_name) { return LLFontFileInfo(file_name); });  	// Load fonts based on names. -	if (file_names.empty()) +	if (font_files.empty())  	{  		LL_WARNS() << "createFont failed, no file names specified" << LL_ENDL;  		return NULL;  	} -	LLFontFreetype::font_vector_t fontlist;  	LLFontGL *result = NULL; -	// Snarf all fonts we can into fontlist.  First will get pulled -	// off the list and become the "head" font, set to non-fallback. +	// The first font will get pulled will be the "head" font, set to non-fallback.  	// Rest will consitute the fallback list.  	BOOL is_first_found = TRUE; -	std::string local_path = LLFontGL::getFontPathLocal(); -	std::string sys_path = LLFontGL::getFontPathSystem(); -	 +	string_vec_t font_search_paths; +	font_search_paths.push_back(LLFontGL::getFontPathLocal()); +	font_search_paths.push_back(LLFontGL::getFontPathSystem()); +#if LL_DARWIN +	font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY); +	font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL); +	font_search_paths.push_back(LLFontGL::getFontPathSystem() + MACOSX_FONT_SUPPLEMENTAL); +#endif +  	// The fontname string may contain multiple font file names separated by semicolons.  	// Break it apart and try loading each one, in order. -	for(string_vec_t::iterator file_name_it = file_names.begin(); -		file_name_it != file_names.end();  -		++file_name_it) +	for(font_file_info_vec_t::iterator font_file_it = font_files.begin(); +		font_file_it != font_files.end(); +		++font_file_it)  	{  		LLFontGL *fontp = NULL; -		string_vec_t font_paths; -		font_paths.push_back(local_path + *file_name_it); -		font_paths.push_back(sys_path + *file_name_it); -#if LL_DARWIN -		font_paths.push_back(MACOSX_FONT_PATH_LIBRARY + *file_name_it); -		font_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL + *file_name_it); -		font_paths.push_back(sys_path +  MACOSX_FONT_SUPPLEMENTAL + *file_name_it); -#endif -		 -		bool is_ft_collection = (std::find(ft_collection_list.begin(), ft_collection_list.end(), *file_name_it) != ft_collection_list.end()); + +		bool is_ft_collection = (std::find_if(font_collection_files.begin(), font_collection_files.end(), +		                                      [&font_file_it](const LLFontFileInfo& ffi) { return font_file_it->FileName == ffi.FileName; }) != font_collection_files.end()); +  		// *HACK: Fallback fonts don't render, so we can use that to suppress  		// creation of OpenGL textures for test apps. JC  		BOOL is_fallback = !is_first_found || !mCreateGLTextures;  		F32 extra_scale = (is_fallback)?fallback_scale:1.0;  		F32 point_size_scale = extra_scale * point_size;  		bool is_font_loaded = false; -		for(string_vec_t::iterator font_paths_it = font_paths.begin(); -			font_paths_it != font_paths.end(); -			++font_paths_it) +		for(string_vec_t::iterator font_search_path_it = font_search_paths.begin(); +			font_search_path_it != font_search_paths.end(); +			++font_search_path_it)  		{ +			const std::string font_path = *font_search_path_it + font_file_it->FileName; +  			fontp = new LLFontGL; -			S32 num_faces = is_ft_collection ? fontp->getNumFaces(*font_paths_it) : 1; +			S32 num_faces = is_ft_collection ? fontp->getNumFaces(font_path) : 1;  			for (S32 i = 0; i < num_faces; i++)  			{  				if (fontp == NULL)  				{  					fontp = new LLFontGL;  				} -				if (fontp->loadFace(*font_paths_it, point_size_scale, -								 LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback, i)) +				if (fontp->loadFace(font_path, point_size_scale, +								 LLFontGL::sVertDPI, LLFontGL::sHorizDPI, is_fallback, i))  				{  					is_font_loaded = true;  					if (is_first_found) @@ -508,7 +528,8 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)  					}  					else  					{ -						fontlist.push_back(fontp->mFontFreetype); +						result->mFontFreetype->addFallbackFont(fontp->mFontFreetype, font_file_it->CharFunctor); +  						delete fontp;  						fontp = NULL;  					} @@ -523,17 +544,12 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)  		}  		if(!is_font_loaded)  		{ -			LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << *file_name_it <<  LL_ENDL; +			LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << font_file_it->FileName <<  LL_ENDL;  			delete fontp;  			fontp = NULL;  		}  	} -	if (result && !fontlist.empty()) -	{ -		result->mFontFreetype->setFallbackFonts(fontlist); -	} -  	if (result)  	{  		result->mFontDescriptor = desc; @@ -720,11 +736,22 @@ void LLFontRegistry::dump()  				<< " size=[" << desc.getSize() << "]"  				<< " fileNames="  				<< LL_ENDL; -		for (string_vec_t::const_iterator file_it=desc.getFileNames().begin(); -			 file_it != desc.getFileNames().end(); +		for (font_file_info_vec_t::const_iterator file_it=desc.getFontFiles().begin(); +			 file_it != desc.getFontFiles().end();  			 ++file_it)  		{ -			LL_INFOS() << "  file: " << *file_it <<LL_ENDL; +			LL_INFOS() << "  file: " << file_it->FileName << LL_ENDL; +		} +	} +} + +void LLFontRegistry::dumpTextures() +{ +	for (const auto& fontEntry : mFontMap) +	{ +		if (fontEntry.second) +		{ +			fontEntry.second->dumpTextures();  		}  	}  } diff --git a/indra/llrender/llfontregistry.h b/indra/llrender/llfontregistry.h index e30c81c630..b0ef72c5de 100644 --- a/indra/llrender/llfontregistry.h +++ b/indra/llrender/llfontregistry.h @@ -34,13 +34,32 @@ class LLFontGL;  typedef std::vector<std::string> string_vec_t; +struct LLFontFileInfo +{ +	LLFontFileInfo(const std::string& file_name, const std::function<bool(llwchar)>& char_functor = nullptr) +		: FileName(file_name) +		, CharFunctor(char_functor) +	{ +	} + +	LLFontFileInfo(const LLFontFileInfo& ffi) +		: FileName(ffi.FileName) +		, CharFunctor(ffi.CharFunctor) +	{ +	} + +	std::string FileName; +	std::function<bool(llwchar)> CharFunctor; +}; +typedef std::vector<LLFontFileInfo> font_file_info_vec_t; +  class LLFontDescriptor  {  public:  	LLFontDescriptor();  	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style); -	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const string_vec_t& file_names); -	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const string_vec_t& file_names, const string_vec_t& font_collections); +	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const font_file_info_vec_t& font_list); +	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const font_file_info_vec_t& font_list, const font_file_info_vec_t& font_collection_list);  	LLFontDescriptor normalize() const;  	bool operator<(const LLFontDescriptor& b) const; @@ -51,19 +70,26 @@ public:  	void setName(const std::string& name) { mName = name; }  	const std::string& getSize() const { return mSize; }  	void setSize(const std::string& size) { mSize = size; } -	const std::vector<std::string>& getFileNames() const { return mFileNames; } -	std::vector<std::string>& getFileNames() { return mFileNames; } -	const std::vector<std::string>& getFontCollectionsList() const { return mFontCollectionsList; } -	std::vector<std::string>& getFontCollectionsList() { return mFontCollectionsList; } + +	void addFontFile(const std::string& file_name, const std::string& char_functor = LLStringUtil::null); +	const font_file_info_vec_t & getFontFiles() const { return mFontFiles; } +	void setFontFiles(const font_file_info_vec_t& font_files) { mFontFiles = font_files; } +	void addFontCollectionFile(const std::string& file_name, const std::string& char_functor = LLStringUtil::null); +	const font_file_info_vec_t& getFontCollectionFiles() const { return mFontCollectionFiles; } +	void setFontCollectionFiles(const font_file_info_vec_t& font_collection_files) { mFontCollectionFiles = font_collection_files; } +  	const U8 getStyle() const { return mStyle; }  	void setStyle(U8 style) { mStyle = style; }  private:  	std::string mName;  	std::string mSize; -	string_vec_t mFileNames; -	string_vec_t mFontCollectionsList; +	font_file_info_vec_t mFontFiles; +	font_file_info_vec_t mFontCollectionFiles;  	U8 mStyle; + +	typedef std::map<std::string, std::function<bool(llwchar)>> char_functor_map_t; +	static char_functor_map_t mCharFunctors;  };  class LLFontRegistry @@ -94,6 +120,7 @@ public:  	bool nameToSize(const std::string& size_name, F32& size);  	void dump(); +	void dumpTextures();  	const string_vec_t& getUltimateFallbackList() const; diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index cfc9ce735d..88402730c9 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -2329,7 +2329,7 @@ void LLGLSyncFence::wait()  	if (mSync)  	{  		while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED) -		{ +		{ //track the number of times we've waited here  		}  	}  #endif | 
