/** * @param typefaceName[in] Name of the typeface. * @param pointSize[in] Size of the font in points. * * Checks the global font cache for the named font and returns a reference to * it. If the font is not in the cache, it gets loaded first. */ NoDice::Font& NoDice:: getFont(const std::string& typefaceName, unsigned int pointSize) { typedef std::map<std::string, Font> FontCache; static FontCache s_fontCache; std::ostringstream ostr; ostr << typefaceName << '_' << pointSize; std::string fontKey = ostr.str(); FontCache::iterator it = s_fontCache.find(fontKey); if (it != s_fontCache.end()) { return it->second; } // allow in-build-directory to take precedence std::string filename = "./assets/" + typefaceName + ".ttf"; if (0 != access(filename.c_str(), R_OK)) { filename = DATA_DIR + std::string("/") + typefaceName + ".ttf"; if (0 != access(filename.c_str(), R_OK)) { filename = typefaceName; } } std::pair<FontCache::iterator,bool> p = s_fontCache.insert( std::make_pair(fontKey, Font(filename, pointSize))); return p.first->second; }
/** * Free everything allocated w.r.t. fonts. */ void UninitFreeType() { for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) { FontCache *fc = FontCache::Get(fs); if (fc->HasParent()) delete fc; } #ifdef WITH_FREETYPE FT_Done_FreeType(_library); _library = NULL; #endif /* WITH_FREETYPE */ }
void InitializeSystemFontCache(FontCache& fc, const string& fong_file, const string& font_dir) { puts("Loading font files..."); try { size_t nFileLoaded(fc.LoadTypefaces(fong_file) != 0); if(!font_dir.empty()) //读取字体文件目录并载入目录下指定后缀名的字体文件。 try { HDirectory dir{font_dir.c_str()}; IO::PathNorm nm; std::for_each(FileIterator(&dir), FileIterator(), [&](const std::string& name){ if(!nm.is_self(name) && !dir.IsDirectory() /*&& IsExtensionOf(ext, dir.GetName())*/) { FontPath path(font_dir + dir.GetName()); if(path != fong_file) nFileLoaded += fc.LoadTypefaces(path) != 0; } }); } catch(FileOperationFailure&) {} fc.InitializeDefaultTypeface(); if(const auto nFaces = fc.GetFaces().size()) std::printf("%u face(s) in %u font file(s)" " are loaded\nsuccessfully.\n", nFaces, nFileLoaded); else throw LoggedEvent("No fonts found."); puts("Setting default font face..."); if(const auto* const pf = fc.GetDefaultTypefacePtr()) std::printf("\"%s\":\"%s\",\nsuccessfully.\n", pf->GetFamilyName().c_str(), pf->GetStyleName().c_str()); else throw LoggedEvent("Setting default font face failed."); return; } // TODO: Use %std::nested_exception. catch(std::exception& e) { puts(e.what()); } throw FatalError(" Font Caching Failure ", " Please make sure the fonts are\n" " stored in correct path.\n"); }
/** * (Re)initialize the freetype related things, i.e. load the non-sprite fonts. * @param monospace Whether to initialise the monospace or regular fonts. */ void InitFreeType(bool monospace) { for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) { if (monospace != (fs == FS_MONO)) continue; FontCache *fc = FontCache::Get(fs); if (fc->HasParent()) delete fc; #ifdef WITH_FREETYPE LoadFreeTypeFont(fs); #endif } }
PassRefPtr<SimpleFontData> FontFallbackIterator::uniqueSystemFontForHint( UChar32 hint) { // When we're asked for a fallback for the same characters again, we give up // because the shaper must have previously tried shaping with the font // already. if (!hint || m_previouslyAskedForHint.contains(hint)) return nullptr; FontCache* fontCache = FontCache::fontCache(); m_previouslyAskedForHint.add(hint); return fontCache->fallbackFontForCharacter( m_fontDescription, hint, m_fontFallbackList->primarySimpleFontData(m_fontDescription)); }
const PassRefPtr<SimpleFontData> FontFallbackIterator::uniqueSystemFontForHint(UChar32 hint) { FontCache* fontCache = FontCache::fontCache(); // When we're asked for a fallback for the same characters again, we give up // because the shaper must have previously tried shaping with the font // already. if (m_visitedSystemFonts.find(hint) != m_visitedSystemFonts.end()) { return nullptr; } RefPtr<SimpleFontData> fallbackFont = fontCache->fallbackFontForCharacter(m_fontDescription, hint, m_fontFallbackList->primarySimpleFontData(m_fontDescription)); return m_visitedSystemFonts.add(hint, fallbackFont).storedValue->value; }
TEST(FontCacheAndroid, fallbackFontForCharacter) { // A Latin character in the common locale system font, but not in the // Chinese locale-preferred font. const UChar32 testChar = 228; FontDescription fontDescription; fontDescription.setScript(USCRIPT_SIMPLIFIED_HAN); fontDescription.setGenericFamily(FontDescription::StandardFamily); FontCache* fontCache = FontCache::fontCache(); ASSERT_TRUE(fontCache); RefPtr<SimpleFontData> fontData = fontCache->fallbackFontForCharacter(fontDescription, testChar, 0); EXPECT_TRUE(fontData); }
TEST(FontCache, getLastResortFallbackFont) { FontCache* fontCache = FontCache::fontCache(); ASSERT_TRUE(fontCache); EmptyPlatform platform; FontDescription fontDescription; fontDescription.setGenericFamily(FontDescription::StandardFamily); RefPtr<SimpleFontData> fontData = fontCache->getLastResortFallbackFont(fontDescription, Retain); EXPECT_TRUE(fontData); fontDescription.setGenericFamily(FontDescription::SansSerifFamily); fontData = fontCache->getLastResortFallbackFont(fontDescription, Retain); EXPECT_TRUE(fontData); }
TEST(FontCache, getLastResortFallbackFont) { FontCache* fontCache = FontCache::fontCache(); ASSERT_TRUE(fontCache); Platform* oldPlatform = Platform::current(); OwnPtr<EmptyPlatform> platform = adoptPtr(new EmptyPlatform); Platform::initialize(platform.get()); FontDescription fontDescription; fontDescription.setGenericFamily(FontDescription::StandardFamily); RefPtr<SimpleFontData> fontData = fontCache->getLastResortFallbackFont(fontDescription, Retain); EXPECT_TRUE(fontData); fontDescription.setGenericFamily(FontDescription::SansSerifFamily); fontData = fontCache->getLastResortFallbackFont(fontDescription, Retain); EXPECT_TRUE(fontData); Platform::initialize(oldPlatform); }
namespace mui { class FontCache { struct Entry{ WCHAR * name; float size; FontStyle style; Font * font; bool operator==(Entry& other){ return size == other.size && style == other.style && str::Eq(name, other.name); } }; ScopedGdiPlus scope; Vec<Entry> cache; public: FontCache() { } ~FontCache() { for (Entry *e = cache.IterStart(); e; e = cache.IterNext()) { free(e->name); ::delete e->font; } } Font *GetFont(const WCHAR *name, float size, FontStyle style) { Entry f = { (WCHAR *)name, size, style, NULL }; for (Entry *e = cache.IterStart(); e; e = cache.IterNext()) { if (f == *e) return e->font; } f.font = ::new Font(name, size, style); if (!f.font) { // fall back to the default font, if a desired font can't be created f.font = ::new Font(L"Times New Roman", size, style); if (!f.font) { if (cache.Count() > 0) return cache.At(0).font; return NULL; } } f.name = str::Dup(f.name); cache.Append(f); return f.font; } }; static FontCache gFontCache; Font *GetCachedFont(const WCHAR *name, float size, FontStyle style) { return gFontCache.GetFont(name, size, style); } static void InitGraphicsMode(Graphics *g) { g->SetCompositingQuality(CompositingQualityHighQuality); g->SetSmoothingMode(SmoothingModeAntiAlias); //g.SetSmoothingMode(SmoothingModeHighQuality); g->SetTextRenderingHint(TextRenderingHintClearTypeGridFit); g->SetPageUnit(UnitPixel); } class GlobalGraphicsHack { ScopedGdiPlus scope; Bitmap bmp; public: Graphics gfx; GlobalGraphicsHack() : bmp(1, 1, PixelFormat32bppARGB), gfx(&bmp) { // cf. EbookEngine::RenderPage gfx.SetCompositingQuality(CompositingQualityHighQuality); gfx.SetSmoothingMode(SmoothingModeAntiAlias); gfx.SetTextRenderingHint(TextRenderingHintClearTypeGridFit); gfx.SetPageUnit(UnitPixel); } }; static GlobalGraphicsHack gGH; Graphics *AllocGraphicsForMeasureText() { return &gGH.gfx; } void FreeGraphicsForMeasureText(Graphics *g) { } }
Font *GetCachedFont(const WCHAR *name, float size, FontStyle style) { return gFontCache.GetFont(name, size, style); }
/** * @param FontName Name of the font to load */ void Font::Load(string FontName) { VFile* fontfile; Uint16 wpos; Uint16 hpos; Uint16 cdata; Uint16 nchars; Uint8 fnheight; Uint8 fnmaxw; Uint32 fntotalw; Uint32 ypos; Uint32 pos; Uint32 curchar; Uint8 data; SDL_Surface* OrgFonImg = 0; this->fontname = FontName; #ifdef USE_FONTCACHE if (fontCache.Get (this->fontname, &this->fontimg, this->chrdest)){ return; } #endif //USE_FONTCACHE //logger->debug("%s line %i: Load the font: %s\n\n", __FILE__, __LINE__, FontName.c_str()); fontfile = VFSUtils::VFS_Open(fontname.c_str()); if (0 == fontfile) { string s("Unable to load font "); s += fontname; throw(runtime_error(s)); } fontfile->seekSet(8); fontfile->readWord(&wpos, 1); fontfile->readWord(&cdata, 1); fontfile->readWord(&hpos, 1); fontfile->seekCur(2); fontfile->readWord(&nchars, 1); nchars = SDL_Swap16(nchars); // Yes, even on LE systems. fontfile->readByte(&fnheight, 1); fontfile->readByte(&fnmaxw, 1); nchars++; vector<Uint8> wchar(nchars); vector<Uint8> hchar(nchars<<1); vector<Uint16> dataoffsets(nchars); fontfile->readWord(&dataoffsets[0], nchars); fontfile->seekSet(wpos); fontfile->readByte(&wchar[0], nchars); fontfile->seekSet(hpos); fontfile->readByte(&hchar[0], nchars<<1); chrdest.resize(nchars); fntotalw = 0; for(unsigned int i = 0 ; i < nchars; i++) { chrdest[i].x = fntotalw; chrdest[i].y = 0; chrdest[i].h = fnheight; chrdest[i].w = wchar[i]; fntotalw += wchar[i]; } vector<Uint8> chardata(fnheight*fntotalw); for( curchar = 0; curchar < nchars; curchar++ ) { fontfile->seekSet(dataoffsets[curchar]); for( ypos = hchar[curchar<<1]; ypos < (Uint32)(hchar[curchar<<1]+hchar[(curchar<<1)+1]); ypos++ ) { pos = chrdest[curchar].x+ypos*fntotalw; for(unsigned int i = 0; i < wchar[curchar]; i+=2 ) { fontfile->readByte( &data, 1 ); #if 1 /* Each 4 bits contain a index to the pallete */ chardata[pos+i] = data&0xf; if( i+1<wchar[curchar] ) chardata[pos+i+1] = (data>>4); #else /* Each 4 bits contain a index to the pallete, convert them to 0 or 1 */ chardata[pos+i] = (data&0xb)!=0?1:0; if( i+1<wchar[curchar] ) chardata[pos+i+1] = (data>>4)!=0?1:0; //printf ("Data = %i\n", chardata[pos+i+1]); #endif } } } SDL_FreeSurface(fontimg); fontimg = NULL; OrgFonImg = SDL_CreateRGBSurfaceFrom(&chardata[0], fntotalw, fnheight, 8, fntotalw, 0, 0, 0, 0); if (this->fontname == "6point.fnt" || this->fontname == "8point.fnt" || this->fontname == "3point.fnt") SDL_SetColors(OrgFonImg, font_pal2, 0, 16); else if ( this->fontname == "12metfnt.fnt") SDL_SetColors(OrgFonImg, font_pal3, 0, 16); else SDL_SetColors(OrgFonImg, font_pal1, 0, 16); // SDL_SetColorKey(OrgFonImg, SDL_SRCCOLORKEY, 0); fontimg = SDL_DisplayFormat(OrgFonImg); SDL_FreeSurface(OrgFonImg); OrgFonImg = NULL; #ifdef USE_FONTCACHE fontCache.Add (this->fontname, this->fontimg, chrdest); #endif VFSUtils::VFS_Close(fontfile); }
const FontDataForRangeSet FontFallbackIterator::next(const Vector<UChar32>& hintList) { if (m_fallbackStage == OutOfLuck) return FontDataForRangeSet(); if (m_fallbackStage == FallbackPriorityFonts) { // Only try one fallback priority font, // then proceed to regular system fallback. m_fallbackStage = SystemFonts; FontDataForRangeSet fallbackPriorityFontRange(fallbackPriorityFont(hintList[0])); if (fallbackPriorityFontRange.hasFontData()) return fallbackPriorityFontRange; return next(hintList); } if (m_fallbackStage == SystemFonts) { // We've reached pref + system fallback. ASSERT(hintList.size()); RefPtr<SimpleFontData> systemFont = uniqueSystemFontForHint(hintList[0]); if (systemFont) return FontDataForRangeSet(systemFont); // If we don't have options from the system fallback anymore or had // previously returned them, we only have the last resort font left. // TODO: crbug.com/42217 Improve this by doing the last run with a last // resort font that has glyphs for everything, for example the Unicode // LastResort font, not just Times or Arial. FontCache* fontCache = FontCache::fontCache(); m_fallbackStage = OutOfLuck; RefPtr<SimpleFontData> lastResort = fontCache->getLastResortFallbackFont(m_fontDescription).get(); RELEASE_ASSERT(lastResort); return FontDataForRangeSet(lastResort); } ASSERT(m_fallbackStage == FontGroupFonts || m_fallbackStage == SegmentedFace); const FontData* fontData = m_fontFallbackList->fontDataAt( m_fontDescription, m_currentFontDataIndex); if (!fontData) { // If there is no fontData coming from the fallback list, it means // we are now looking at system fonts, either for prioritized symbol // or emoji fonts or by calling system fallback API. m_fallbackStage = isNonTextFallbackPriority(m_fontFallbackPriority) ? FallbackPriorityFonts : SystemFonts; return next(hintList); } // Otherwise we've received a fontData from the font-family: set of fonts, // and a non-segmented one in this case. if (!fontData->isSegmented()) { // Skip forward to the next font family for the next call to next(). m_currentFontDataIndex++; if (!fontData->isLoading()) { RefPtr<SimpleFontData> nonSegmented = const_cast<SimpleFontData*>(toSimpleFontData(fontData)); return FontDataForRangeSet(nonSegmented); } return next(hintList); } // Iterate over ranges of a segmented font below. const SegmentedFontData* segmented = toSegmentedFontData(fontData); if (m_fallbackStage != SegmentedFace) { m_segmentedFaceIndex = 0; m_fallbackStage = SegmentedFace; } ASSERT(m_segmentedFaceIndex < segmented->numFaces()); FontDataForRangeSet currentSegmentedFace = segmented->faceAt(m_segmentedFaceIndex); m_segmentedFaceIndex++; if (m_segmentedFaceIndex == segmented->numFaces()) { // Switch from iterating over a segmented face to the next family from // the font-family: group of fonts. m_fallbackStage = FontGroupFonts; m_currentFontDataIndex++; } if (rangeSetContributesForHint(hintList, currentSegmentedFace)) { if (currentSegmentedFace.fontData()->customFontData()) currentSegmentedFace.fontData()->customFontData()->beginLoadIfNeeded(); if (!currentSegmentedFace.fontData()->isLoading()) return currentSegmentedFace; m_trackedLoadingRangeSets.append(currentSegmentedFace); } return next(hintList); }
void CEngineSurface :: drawPrintText( const char* text, int textLen ) { static bool hasColor = 0; static int numColor = 7; if( !text || !_hCurrentFont || _drawTextColor[3] >= 255 ) return; int x = _drawTextPos[0] + _translateX; int y = _drawTextPos[1] + _translateY; int iTall = _hCurrentFont->getTall(); int j, iTotalWidth = 0; int curTextColor[4]; // HACKHACK: allow color strings in VGUI if( numColor != 7 && vgui_colorstrings->integer ) { for( j = 0; j < 3; j++ ) // grab predefined color curTextColor[j] = g_color_table[numColor][j]; } else { for( j = 0; j < 3; j++ ) // revert default color curTextColor[j] = _drawTextColor[j]; } curTextColor[3] = _drawTextColor[3]; // copy alpha if( textLen == 1 && vgui_colorstrings->integer ) { if( *text == '^' ) { hasColor = true; return; // skip '^' } else if( hasColor && isdigit( *text )) { numColor = ColorIndex( *text ); hasColor = false; // handled return; // skip colornum } else hasColor = false; } for( int i = 0; i < textLen; i++ ) { char ch = text[i]; int abcA,abcB,abcC; _hCurrentFont->getCharABCwide( ch, abcA, abcB, abcC ); iTotalWidth += abcA; int iWide = abcB; if( !iswspace( ch )) { // get the character texture from the cache int iTexId = 0; float *texCoords = NULL; if( !g_FontCache.GetTextureForChar( _hCurrentFont, ch, &iTexId, &texCoords )) continue; Assert( texCoords != NULL ); vpoint_t ul, lr; ul.point[0] = x + iTotalWidth; ul.point[1] = y; lr.point[0] = ul.point[0] + iWide; lr.point[1] = ul.point[1] + iTall; // gets at the texture coords for this character in its texture page ul.coord[0] = texCoords[0]; ul.coord[1] = texCoords[1]; lr.coord[0] = texCoords[2]; lr.coord[1] = texCoords[3]; vpoint_t clippedRect[2]; if( !ClipRect( ul, lr, &clippedRect[0], &clippedRect[1] )) continue; drawSetTexture( iTexId ); VGUI_SetupDrawingText( curTextColor ); VGUI_DrawQuad( &clippedRect[0], &clippedRect[1] ); // draw the letter } iTotalWidth += iWide + abcC; } _drawTextPos[0] += iTotalWidth; }
namespace mui { class FontCache { struct Entry { WCHAR * name; float size; FontStyle style; Font * font; Entry(WCHAR *name=NULL, float size=0.0f, FontStyle style=FontStyleRegular, Font *font=NULL) : name(name), size(size), style(style), font(font) { } bool operator==(const Entry& other) const { return size == other.size && style == other.style && str::Eq(name, other.name); } }; ScopedGdiPlus scope; Vec<Entry> cache; public: FontCache() { } ~FontCache() { for (Entry *e = cache.IterStart(); e; e = cache.IterNext()) { free(e->name); ::delete e->font; } } Font *GetFont(const WCHAR *name, float size, FontStyle style) { int idx = cache.Find(Entry((WCHAR *)name, size, style)); if (idx != -1) return cache.At(idx).font; Font *font = ::new Font(name, size, style); if (!font) { // fall back to the default font, if a desired font can't be created font = ::new Font(L"Times New Roman", size, style); if (!font) { return cache.Count() > 0 ? cache.At(0).font : NULL; } } cache.Append(Entry(str::Dup(name), size, style, font)); return font; } }; static FontCache gFontCache; Font *GetCachedFont(const WCHAR *name, float size, FontStyle style) { return gFontCache.GetFont(name, size, style); } class GlobalGraphicsHack { ScopedGdiPlus scope; Bitmap bmp; public: Graphics gfx; GlobalGraphicsHack() : bmp(1, 1, PixelFormat32bppARGB), gfx(&bmp) { // cf. EbookEngine::RenderPage gfx.SetCompositingQuality(CompositingQualityHighQuality); gfx.SetSmoothingMode(SmoothingModeAntiAlias); gfx.SetTextRenderingHint(TextRenderingHintClearTypeGridFit); gfx.SetPageUnit(UnitPixel); } }; static GlobalGraphicsHack gGH; Graphics *AllocGraphicsForMeasureText() { return &gGH.gfx; } void FreeGraphicsForMeasureText(Graphics *g) { } }