| 704 | } |
| 705 | |
| 706 | const SGlyph *GetGlyph(int Chr, int FontSize) |
| 707 | { |
| 708 | FontSize = std::clamp(FontSize, MIN_FONT_SIZE, MAX_FONT_SIZE); |
| 709 | |
| 710 | // Find glyph index and most appropriate font face. |
| 711 | FT_Face Face; |
| 712 | FT_UInt GlyphIndex = GetCharGlyph(Chr, &Face, false); |
| 713 | if(GlyphIndex == 0) |
| 714 | { |
| 715 | // Use replacement character if glyph could not be found, |
| 716 | // also retrieve replacement character from the atlas. |
| 717 | return Chr == REPLACEMENT_CHARACTER ? nullptr : GetGlyph(REPLACEMENT_CHARACTER, FontSize); |
| 718 | } |
| 719 | |
| 720 | // Check if glyph for this (font face, character, font size)-combination was already rendered. |
| 721 | SGlyph &Glyph = m_Glyphs[std::make_tuple(Face, Chr, FontSize)]; |
| 722 | if(Glyph.m_State == SGlyph::EState::RENDERED) |
| 723 | return &Glyph; |
| 724 | else if(Glyph.m_State == SGlyph::EState::ERROR) |
| 725 | return nullptr; |
| 726 | |
| 727 | // Else, render it. |
| 728 | Glyph.m_FontSize = FontSize; |
| 729 | Glyph.m_Face = Face; |
| 730 | Glyph.m_Chr = Chr; |
| 731 | Glyph.m_GlyphIndex = GlyphIndex; |
| 732 | if(RenderGlyph(Glyph)) |
| 733 | return &Glyph; |
| 734 | |
| 735 | // Use replacement character if the glyph could not be rendered, |
| 736 | // also retrieve replacement character from the atlas. |
| 737 | const SGlyph *pReplacementCharacter = Chr == REPLACEMENT_CHARACTER ? nullptr : GetGlyph(REPLACEMENT_CHARACTER, FontSize); |
| 738 | if(pReplacementCharacter) |
| 739 | { |
| 740 | Glyph = *pReplacementCharacter; |
| 741 | return &Glyph; |
| 742 | } |
| 743 | |
| 744 | // Keep failed glyph in the cache so we don't attempt to render it again, |
| 745 | // but set its state to ERROR so we don't return it to the text render. |
| 746 | Glyph.m_State = SGlyph::EState::ERROR; |
| 747 | return nullptr; |
| 748 | } |
| 749 | |
| 750 | vec2 Kerning(const SGlyph *pLeft, const SGlyph *pRight) const |
| 751 | { |