void TextureFont::MeasureCharacterPos(const std::string &str, int charIndex, float &charX, float &charY) { assert(charIndex >= 0); float x = 0.0f, y = GetHeight(); int i = 0; Uint32 chr; int len = utf8_decode_char(&chr, &str[i]); while (str[i] && (i < charIndex)) { Uint32 nextChar; i += len; len = utf8_decode_char(&nextChar, &str[i]); assert(!str[i] || len); // assert valid encoding if (chr == '\n') { x = 0.0f; y += GetHeight(); } else { const Glyph &glyph = GetGlyph(chr); float advance = glyph.advX; if (nextChar != '\n' && nextChar != '\0') advance += GetKern(glyph, GetGlyph(nextChar)); x += advance; } chr = nextChar; } charX = x; charY = y; }
int TextureFont::PickCharacter(const char *str, float mouseX, float mouseY) const { assert(str && mouseX >= 0.0f && mouseY >= 0.0f); // at the point of the mouse in-box test, the vars have the following values: // i1: the index of the character being tested // i2: the index of the next character // charBytes: the number of bytes used to encode the next character // right: the right edge of the box being tested // bottom: the bottom of the box being tested // x: the x-coordinate of the next character // chr1: the Unicode value of the character being tested // chr2: the Unicode value of the next character Uint32 chr2 = '\n'; // pretend we've just had a new line float bottom = 0.0f, x = 0.0f; int i2 = 0, charBytes = 0; do { int i1 = i2; Uint32 chr1 = chr2; // read the next character i2 += charBytes; charBytes = utf8_decode_char(&chr2, &str[i2]); assert(!str[i2] || charBytes); // assert valid encoding float right; if (chr1 == '\n') { right = std::numeric_limits<float>::max(); x = 0.0f; } else { const glfglyph_t &glyph = GetGlyph(chr1); float advance = glyph.advx; if (chr2 != '\n' && chr2 != '\0') { FT_Vector kern; FT_Get_Kerning(m_face, glyph.ftIndex, GetGlyph(chr2).ftIndex, FT_KERNING_UNFITTED, &kern); advance += float(kern.x) / 64.0f; } right = x + (advance / 2.0f); x += advance; } if ((mouseY < bottom) && (mouseX < right)) return i1; if (chr1 == '\n') bottom += GetHeight(); } while (charBytes); return i2; }
void FontInstance::DrawFinal( QuadTexture* Surface,const int Px, const int Py,Utf32String& Str,const unsigned long Color,bool Gen) { ScopedLock Sl(Lock); FT_Error Error; ScanLineUserInfo Info; FT_Glyph AGlyphs[512]; /* glyph image */ FT_Vector APos [512]; /* glyph position */ //get all glyph and compute rendering pos int PosX=0; int PosY=0; //Py+(Face->height>>6)/3; unsigned int GlyphArrIdx=0; for (unsigned int i=0;i<Str.size();i++) { //retrieve the glyph with the UTF-32 code AGlyphs[GlyphArrIdx]=GetGlyph(Str[i]); if (AGlyphs[GlyphArrIdx]!=0) { /* store current pen position */ APos[GlyphArrIdx].x = PosX; APos[GlyphArrIdx].y = PosY; //advance is in 16.16 fixed format PosX+=AGlyphs[GlyphArrIdx]->advance.x>>16; PosY+=AGlyphs[GlyphArrIdx]->advance.y>>16; GlyphArrIdx++; } }
int Font::GetLineWidthInSourcePixels( const wstring &szLine ) const { int iLineWidth = 0; for( unsigned i=0; i<szLine.size(); i++ ) iLineWidth += GetGlyph(szLine[i]).m_iHadvance; if( szLine.size() > 0 ) { /* Add overdraw. */ iLineWidth += GetGlyph(szLine[0]).m_pPage->m_iDrawExtraPixelsLeft; iLineWidth += GetGlyph(szLine[szLine.size()-1]).m_pPage->m_iDrawExtraPixelsRight; } return iLineWidth; }
void OpenGLText::DrawString(const std::string &text, int x, int y) { for (char curChar : text) { OpenGLTextGlyph const& glyph = GetGlyph(curChar); glyph.Draw(x, y); x += glyph.w; } }
void OpenGLText::GetExtent(wxString const& text, int &w, int &h) { lineHeight = 0; int dx=0, dy=0; w = 0; h = 0; // Simulate drawing of string for (int curChar : text) { // Handle carriage returns if (curChar == '\n') { if (dx > w) w = dx; dx = 0; dy += lineHeight; lineHeight = 0; } // Handle normal glyphs else { OpenGLTextGlyph const& glyph = GetGlyph(curChar); dx += glyph.w; if (glyph.h > lineHeight) lineHeight = glyph.h; } } // Return results if (dx > w) w = dx; h = dy+lineHeight; }
void OpenGLText::GetExtent(std::string const& text, int &w, int &h) { w = h = 0; for (char curChar : text) { OpenGLTextGlyph const& glyph = GetGlyph(curChar); w += glyph.w; h = std::max(h, glyph.h); } }
TBFontGlyph *TBFontGlyphCache::CreateAndCacheGlyph(const TBID &hash_id, UCS4 cp) { assert(!GetGlyph(hash_id, cp)); TBFontGlyph *glyph = new TBFontGlyph(hash_id, cp); if (glyph && m_glyphs.Add(glyph->hash_id, glyph)) return glyph; delete glyph; return nullptr; }
int Font::GetLineWidthInSourcePixels( const wstring &szLine ) const { int iLineWidth = 0; for( unsigned i=0; i<szLine.size(); i++ ) iLineWidth += GetGlyph(szLine[i]).m_iHadvance; return iLineWidth; }
int Font::GetLineHeightInSourcePixels( const wstring &szLine ) const { int iLineHeight = 0; /* The height of a line is the height of its tallest used font page. */ for( unsigned i=0; i<szLine.size(); i++ ) iLineHeight = max( iLineHeight, GetGlyph(szLine[i]).m_pPage->m_iHeight ); return iLineHeight; }
float CglFont::GetTextWidth_(const std::u8string& text) { if (text.empty()) return 0.0f; float w = 0.0f; float maxw = 0.0f; const GlyphInfo* prv_g=NULL; const GlyphInfo* cur_g; int pos = 0; while (pos < text.length()) { const char32_t u = Utf8GetNextChar(text, pos); switch (u) { // inlined colorcode case ColorCodeIndicator: pos = SkipColorCodes(text, pos - 1); if (pos<0) { pos = text.length(); } break; // reset color case ColorResetIndicator: break; // newline case 0x0d: // CR+LF if (pos < text.length() && text[pos] == 0x0a) pos++; case 0x0a: // LF if (prv_g) w += prv_g->advance; if (w > maxw) maxw = w; w = 0.0f; prv_g = NULL; break; // printable char default: cur_g = &GetGlyph(u); if (prv_g) w += GetKerning(*prv_g, *cur_g); prv_g = cur_g; } } if (prv_g) w += prv_g->advance; if (w > maxw) maxw = w; return maxw; }
void TextureFont::RenderString(const char *str, float x, float y, const Color &color) { PROFILE_SCOPED() m_vertices.Clear(); float alpha_f = color.a / 255.0f; const Color premult_color = Color(color.r * alpha_f, color.g * alpha_f, color.b * alpha_f, color.a); float px = x; float py = y; int i = 0; while (str[i]) { if (str[i] == '\n') { px = x; py += GetHeight(); i++; } else { Uint32 chr; int n = utf8_decode_char(&chr, &str[i]); assert(n); i += n; const Glyph &glyph = GetGlyph(chr); AddGlyphGeometry(&m_vertices, glyph, roundf(px), py, premult_color); if (str[i]) { Uint32 chr2; n = utf8_decode_char(&chr2, &str[i]); assert(n); px += GetKern(glyph, GetGlyph(chr2)); } px += glyph.advX; } } m_renderer->DrawTriangles(&m_vertices, m_renderState, m_mat.get()); }
void TextureFont::RenderString(const char *str, float x, float y, const Color &color) { m_renderer->SetBlendMode(Graphics::BLEND_ALPHA); m_vertices.Clear(); float px = x; float py = y; int i = 0; while (str[i]) { if (str[i] == '\n') { px = x; py += GetHeight(); i++; } else { Uint32 chr; int n = utf8_decode_char(&chr, &str[i]); assert(n); i += n; const glfglyph_t &glyph = GetGlyph(chr); AddGlyphGeometry(&m_vertices, glyph, roundf(px), py, color); if (str[i]) { Uint32 chr2; n = utf8_decode_char(&chr2, &str[i]); assert(n); FT_Vector kern; FT_Get_Kerning(m_face, glyph.ftIndex, GetGlyph(chr2).ftIndex, FT_KERNING_UNFITTED, &kern); px += float(kern.x) / 64.0; } px += glyph.advx; } } m_renderer->DrawTriangles(&m_vertices, m_mat.Get()); }
PRUint32 gfxFT2FontBase::GetGlyph(PRUint32 unicode, PRUint32 variation_selector) { if (variation_selector) { PRUint32 id = gfxFT2LockedFace(this).GetUVSGlyph(unicode, variation_selector); if (id) return id; } return GetGlyph(unicode); }
int FontInstance::GetLen(std::wstring& Text) { ScopedLock Sl(Lock); float Length=0.0f; for (unsigned int i=0;i<Text.size();i++) { //retrieve the glyph with the UTF-32 code FT_Glyph Glyph=GetGlyph(Text[i]); Length+=(float)(Glyph->advance.x>>16); FT_Done_Glyph(Glyph); } return (int)Length; };
bool FontRenderer::CreateGlyphTextures() { FT_Set_Pixel_Sizes(_face, _fontWidth, _fontHeight); for (auto& glyph : _glyphs) { glDeleteTextures(1, &glyph.texture); glGenTextures(1, &glyph.texture); } CHECK_GL_ERROR; for (int character = -std::numeric_limits<char>::max(); character <= std::numeric_limits<char>::max(); ++character) { Glyph* glyph = GetGlyph(character); glBindTexture(GL_TEXTURE_2D, glyph->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Uploading glyph bitmaps requires 1 byte alignment. glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Try to load the character. if (FT_Load_Char(_face, character, FT_LOAD_RENDER) != FT_Err_Ok) { return false; } const FT_GlyphSlot glyphSlot = _face->glyph; // Upload the bitmap, which contains an 8bit grayscale image. glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, glyphSlot->bitmap.width, glyphSlot->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, glyphSlot->bitmap.buffer); glyph->left = glyphSlot->bitmap_left; glyph->top = glyphSlot->bitmap_top; glyph->width = glyphSlot->bitmap.width; glyph->rows = glyphSlot->bitmap.rows; glyph->advance_x = glyphSlot->advance.x; glyph->advance_y = glyphSlot->advance.y; } _glyphTexturesDirty = (GetGLErrorVerbose() != GL_NO_ERROR); return !_glyphTexturesDirty; }
float CglFont::GetTextHeight_(const std::u8string& text, float* descender, int* numLines) { if (text.empty()) { if (descender) *descender = 0.0f; if (numLines) *numLines = 0; return 0.0f; } float h = 0.0f, d = GetLineHeight() + GetDescender(); unsigned int multiLine = 1; int pos = 0; while (pos < text.length()) { const char32_t u = Utf8GetNextChar(text, pos); switch(u) { // inlined colorcode case ColorCodeIndicator: pos = SkipColorCodes(text, pos - 1); if (pos<0) { pos = text.length(); } break; // reset color case ColorResetIndicator: break; // newline case 0x0d: // CR+LF if (pos < text.length() && text[pos] == 0x0a) ++pos; case 0x0a: // LF multiLine++; d = GetLineHeight() + GetDescender(); break; // printable char default: const GlyphInfo& g = GetGlyph(u); if (g.descender < d) d = g.descender; if (multiLine < 2 && g.height > h) h = g.height; // only calc height for the first line } } if (multiLine>1) d -= (multiLine-1) * GetLineHeight(); if (descender) *descender = d; if (numLines) *numLines = multiLine; return h; }
void TextureFont::MeasureCharacterPos(const char *str, int charIndex, float &charX, float &charY) const { assert(str && (charIndex >= 0)); float x = 0.0f, y = GetHeight(); int i = 0; Uint32 chr; int len = utf8_decode_char(&chr, &str[i]); while (str[i] && (i < charIndex)) { Uint32 nextChar; i += len; len = utf8_decode_char(&nextChar, &str[i]); assert(!str[i] || len); // assert valid encoding if (chr == '\n') { x = 0.0f; y += GetHeight(); } else { const glfglyph_t &glyph = GetGlyph(chr); float advance = glyph.advx; if (nextChar != '\n' && nextChar != '\0') { FT_Vector kern; FT_Get_Kerning(m_face, glyph.ftIndex, GetGlyph(nextChar).ftIndex, FT_KERNING_UNFITTED, &kern); advance += float(kern.x) / 64.0f; } x += advance; } chr = nextChar; } charX = x; charY = y; }
bool Font::FontCompleteForString( const wstring &str ) const { map<wchar_t,glyph*>::const_iterator m_pDefault = m_iCharToGlyph.find( FONT_DEFAULT_GLYPH ); if( m_pDefault == m_iCharToGlyph.end() ) RageException::Throw( "The default glyph is missing from the font \"%s\".", path.c_str() ); for( unsigned i = 0; i < str.size(); ++i ) { /* If the glyph for this character is the default glyph, we're incomplete. */ const glyph &g = GetGlyph( str[i] ); if( &g == m_pDefault->second ) return false; } return true; }
int TBFontFace::GetStringWidth(const char *str, int len) { int width = 0; int i = 0; while (str[i] && i < len) { UCS4 cp = utf8::decode_next(str, &i, len); if (cp == 0xFFFF) continue; if (!m_font_renderer) // This is the test font. Use same glyph width as height. width += m_metrics.height / 3 + 1; else if (TBFontGlyph *glyph = GetGlyph(cp, false)) width += glyph->metrics.advance; } return width; }
TBBitmapFragment *TBFontGlyphCache::CreateFragment(TBFontGlyph *glyph, int w, int h, int stride, uint32 *data) { assert(GetGlyph(glyph->hash_id, glyph->cp)); // Don't bother if the requested glyph is too large. if (w > TB_GLYPH_CACHE_WIDTH || h > TB_GLYPH_CACHE_HEIGHT) return nullptr; bool try_drop_largest = true; bool dropped_large_enough_glyph = false; do { // Attempt creating a fragment for the rendered glyph data if (TBBitmapFragment *frag = m_frag_manager.CreateNewFragment(glyph->hash_id, false, w, h, stride, data)) { glyph->frag = frag; m_all_rendered_glyphs.AddLast(glyph); return frag; } // Drop the oldest glyph that's large enough to free up the space we need. if (try_drop_largest) { const int check_limit = 20; int check_count = 0; for (TBFontGlyph *oldest = m_all_rendered_glyphs.GetFirst(); oldest && check_count < check_limit; oldest = oldest->GetNext()) { if (oldest->frag->Width() >= w && oldest->frag->GetAllocatedHeight() >= h) { DropGlyphFragment(oldest); dropped_large_enough_glyph = true; break; } check_count++; } try_drop_largest = false; } // We had no large enough glyph so just drop the oldest one. We will likely // spin around the loop, fail and drop again a few times before we succeed. if (!dropped_large_enough_glyph) { if (TBFontGlyph *oldest = m_all_rendered_glyphs.GetFirst()) DropGlyphFragment(oldest); else break; } } while (true); return nullptr; }
bool TBFontFace::RenderGlyphs(const char *glyph_str, int glyph_str_len) { if (!m_font_renderer) return true; // This is the test font if (glyph_str_len == TB_ALL_TO_TERMINATION) glyph_str_len = strlen(glyph_str); bool has_all_glyphs = true; int i = 0; while (glyph_str[i] && i < glyph_str_len) { UCS4 cp = utf8::decode_next(glyph_str, &i, glyph_str_len); if (!GetGlyph(cp, true)) has_all_glyphs = false; } return has_all_glyphs; }
void CRasterFont::SinglePassDrawString(const CDrawStringOptions& opts, int x, int y, int& xout, int& yout, CTextRenderBuffer* renderBuf, const wchar_t* str, s32 length) const { if (!x0_) return; const wchar_t* chr = str; const CGlyph* prevGlyph = nullptr; while (*chr == '\0') { const CGlyph* glyph = GetGlyph(*chr); if (glyph) { if (opts.x0_ == 0) { x += glyph->GetA(); if (prevGlyph != 0) x += KernLookup(x1c_kerning, prevGlyph->GetKernStart(), *chr); int left = 0; int top = 0; if (renderBuf) { left += x; top += glyph->GetBaseline() - y; renderBuf->AddCharacter(zeus::CVector2i(left, top), *chr, opts.x4_vec[0]); } x += glyph->GetC() + glyph->GetB(); } } prevGlyph = glyph; chr++; if (length == -1) continue; if ((str - chr) >= length) break; } xout = x; yout = y; }
uint GetGlyphWidth(FontSize size, WChar key) { FT_Face face = GetFontFace(size); GlyphEntry *glyph; if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) { SpriteID sprite = GetUnicodeGlyph(size, key); if (sprite == 0) sprite = GetUnicodeGlyph(size, '?'); return SpriteExists(sprite) ? GetSprite(sprite, ST_FONT)->width + (size != FS_NORMAL && size != FS_MONO) : 0; } glyph = GetGlyphPtr(size, key); if (glyph == NULL || glyph->sprite == NULL) { GetGlyph(size, key); glyph = GetGlyphPtr(size, key); } return glyph->width; }
void OpenGLText::DrawString(const wxString &text, int x, int y) { lineHeight = 0; int dx=x, dy=y; for (int curChar : text) { // Handle carriage returns if (curChar == '\n') { dx = x; dy += lineHeight; } // Handle normal glyphs else { OpenGLTextGlyph const& glyph = GetGlyph(curChar); glyph.Draw(dx, dy); dx += glyph.w; if (glyph.h > lineHeight) lineHeight = glyph.h; } } }
void CRasterFont::GetSize(const CDrawStringOptions& opts, int& width, int& height, const wchar_t* str, int len) const { width = 0; height = 0; const wchar_t* chr = str; const CGlyph* prevGlyph = nullptr; int prevWidth = 0; while (*chr != L'\0') { const CGlyph* glyph = GetGlyph(*chr); if (glyph) { if (opts.x0_ == 0) { int advance = 0; if (prevGlyph) advance = KernLookup(x1c_kerning, prevGlyph->GetKernStart(), *chr); s16 curWidth = prevWidth - (glyph->GetA() + glyph->GetB() + glyph->GetC() + advance); s16 curHeight = glyph->GetBaseline() - (x8_monoHeight + glyph->GetCellHeight()); width = curWidth; prevWidth = curWidth; if (curHeight > height) height = curHeight; } } prevGlyph = glyph; chr++; if (len == -1) continue; if ((str - chr) >= len) break; } }
void TBFontFace::DrawString(int x, int y, const TBColor &color, const char *str, int len) { if (m_bgFont) m_bgFont->DrawString(x+m_bgX, y+m_bgY, m_bgColor, str, len); if (m_font_renderer) g_renderer->BeginBatchHint(TBRenderer::BATCH_HINT_DRAW_BITMAP_FRAGMENT); int i = 0; while (str[i] && i < len) { UCS4 cp = utf8::decode_next(str, &i, len); if (cp == 0xFFFF) continue; if (TBFontGlyph *glyph = GetGlyph(cp, true)) { if (glyph->frag) { TBRect dst_rect(x + glyph->metrics.x, y + glyph->metrics.y + GetAscent(), glyph->frag->Width(), glyph->frag->Height()); TBRect src_rect(0, 0, glyph->frag->Width(), glyph->frag->Height()); if (glyph->has_rgb) g_renderer->DrawBitmap(dst_rect, src_rect, glyph->frag); else g_renderer->DrawBitmapColored(dst_rect, src_rect, color, glyph->frag); } x += glyph->metrics.advance; } else if (!m_font_renderer) // This is the test font. Use same glyph width as height and draw square. { g_renderer->DrawRect(TBRect(x, y, m_metrics.height / 3, m_metrics.height), color); x += m_metrics.height / 3 + 1; } } if (m_font_renderer) g_renderer->EndBatchHint(); }
void ezioFont::SetGlyph(Uint8 glyph, const Uint8 *data) { if (!data || !length || !bg || !pixel_on || !pixel_off) throw ezioException(EZIOEX_EMUL_FONT_SET_GLYPH); SDL_Surface *surface = GetGlyph(glyph); if (surface != NULL) { SDL_FreeSurface(surface); keymap[glyph] = NULL; } surface = SDL_CreateRGBSurface(SDL_SWSURFACE, bg->w, bg->h, 32, rmask, gmask, bmask, amask); SDL_SetAlpha(surface, 0, 0); SDL_BlitSurface(bg, NULL, surface, NULL); SDL_Rect rect; rect.x = rect.y = 0; rect.w = pixel_on->w; rect.h = pixel_on->h; for (Sint32 y = 0; y < height; y++) { rect.x = 0; rect.w = pixel_on->w; rect.h = pixel_on->h; Uint8 bits = (Uint32)data[y]; for (Sint32 x = 0; x < width; x++) { if ((bits & 0x80 ) != 0) SDL_BlitSurface(pixel_on, NULL, surface, &rect); bits = bits << 1; rect.x += pixel_on->w + 1; } rect.y += pixel_on->h + 1; } keymap[glyph] = surface; }
void CglFont::RenderStringOutlined(float x, float y, const float& scaleX, const float& scaleY, const std::string& str) { const float shiftX = (scaleX/fontSize) * GetOutlineWidth(), shiftY = (scaleY/fontSize) * GetOutlineWidth(); const float startx = x; const float lineHeight_ = scaleY * GetLineHeight(); const std::u8string& ustr = toustring(str); const size_t length = str.length(); va.EnlargeArrays(length * 4, 0, VA_SIZE_2DT); va2.EnlargeArrays(length * 4, 0, VA_SIZE_2DT); int skippedLines; bool colorChanged; const GlyphInfo* g = NULL; float4 newColor = textColor; char32_t c; int i = 0; do { const bool endOfString = SkipColorCodesAndNewLines(ustr, &i, &newColor, &colorChanged, &skippedLines, &baseTextColor); if (endOfString) return; c = Utf8GetNextChar(str,i); if (colorChanged) { if (autoOutlineColor) { SetColors(&newColor,NULL); } else { SetTextColor(&newColor); } } const GlyphInfo* c_g = &GetGlyph(c); if (skippedLines>0) { x = startx; y -= skippedLines * lineHeight_; } else if (g) { x += scaleX * GetKerning(*g, *c_g); } g = c_g; const auto& tc = g->texCord; const auto& stc = g->shadowTexCord; const float dx0 = (scaleX * g->size.x0()) + x, dy0 = (scaleY * g->size.y0()) + y; const float dx1 = (scaleX * g->size.x1()) + x, dy1 = (scaleY * g->size.y1()) + y; // draw outline va2.AddVertexQ2dT(dx0-shiftX, dy1-shiftY, stc.x0(), stc.y1()); va2.AddVertexQ2dT(dx0-shiftX, dy0+shiftY, stc.x0(), stc.y0()); va2.AddVertexQ2dT(dx1+shiftX, dy0+shiftY, stc.x1(), stc.y0()); va2.AddVertexQ2dT(dx1+shiftX, dy1-shiftY, stc.x1(), stc.y1()); // draw the actual character va.AddVertexQ2dT(dx0, dy1, tc.x0(), tc.y1()); va.AddVertexQ2dT(dx0, dy0, tc.x0(), tc.y0()); va.AddVertexQ2dT(dx1, dy0, tc.x1(), tc.y0()); va.AddVertexQ2dT(dx1, dy1, tc.x1(), tc.y1()); } while(true); }
void CglFont::RenderString(float x, float y, const float& scaleX, const float& scaleY, const std::string& str) { /** * NOTE: * Font rendering does not use display lists, but VAs. It's actually faster * (450% faster with a 7600GT!) for these reasons: * * 1. When using DLs, we can not group multiple glyphs into one glBegin/End pair * because glTranslatef can not go between such a pair. * 2. We can now eliminate all glPushMatrix/PopMatrix pairs related to font rendering * because the transformations are calculated on the fly. These are just a couple of * floating point multiplications and shouldn't be too expensive. */ const float startx = x; const float lineHeight_ = scaleY * GetLineHeight(); unsigned int length = (unsigned int)str.length(); const std::u8string& ustr = toustring(str); va.EnlargeArrays(length * 4, 0, VA_SIZE_2DT); int skippedLines; bool colorChanged; const GlyphInfo* g = NULL; float4 newColor = textColor; char32_t c; int i = 0; do { const bool endOfString = SkipColorCodesAndNewLines(ustr, &i, &newColor, &colorChanged, &skippedLines, &baseTextColor); if (endOfString) return; c = Utf8GetNextChar(str,i); if (colorChanged) { if (autoOutlineColor) { SetColors(&newColor,NULL); } else { SetTextColor(&newColor); } } const GlyphInfo* c_g = &GetGlyph(c); if (skippedLines>0) { x = startx; y -= skippedLines * lineHeight_; } else if (g) { x += scaleX * GetKerning(*g, *c_g); } g = c_g; const auto& tc = g->texCord; const float dx0 = (scaleX * g->size.x0()) + x, dy0 = (scaleY * g->size.y0()) + y; const float dx1 = (scaleX * g->size.x1()) + x, dy1 = (scaleY * g->size.y1()) + y; va.AddVertexQ2dT(dx0, dy1, tc.x0(), tc.y1()); va.AddVertexQ2dT(dx0, dy0, tc.x0(), tc.y0()); va.AddVertexQ2dT(dx1, dy0, tc.x1(), tc.y0()); va.AddVertexQ2dT(dx1, dy1, tc.x1(), tc.y1()); } while(true); }