void FallbackFontRenderer::Draw(uint32_t unicode, spades::Vector2 offset, float size, spades::Vector4 color) { renderer->SetColorAlphaPremultiplied(color); float x = offset.x; float y = offset.y; auto glyph = FindGlyph(unicode); if(glyph.img == nullptr) { // no glyph found! draw box in the last resort IImage *img = whiteImage; renderer->DrawImage(img, AABB2(x, y, size, 1.f)); renderer->DrawImage(img, AABB2(x, y + size - 1.f, size, 1.f)); renderer->DrawImage(img, AABB2(x, y + 1.f, 1.f, size - 2.f)); renderer->DrawImage(img, AABB2(x + size - 1.f, y + 1.f, 1.f, size - 2.f)); return; } if(glyph.w == 0) { // null glyph. return; } float scale = size * glyph.sizeInverse; if(roundSize || scale < 1.f) { float newScale = std::max(1.f, floorf(scale)); // vertical-align: baseline offset.y += (scale - newScale) * glyph.size; scale = newScale; } AABB2 inRect(glyph.x, glyph.y, glyph.w, glyph.h); AABB2 outRect(glyph.offX, glyph.offY, glyph.w, glyph.h); // margin to make the border completely transparent // (considering the OpenGL's linear interpolation) if(!roundSize){ inRect.min.x -= 0.5f; inRect.min.y -= 0.5f; outRect.min.x -= 0.5f; outRect.min.y -= 0.5f; inRect.max.x += 0.5f; inRect.max.y += 0.5f; outRect.max.x += 0.5f; outRect.max.y += 0.5f; } outRect.min *= scale; outRect.max *= scale; outRect.min += offset; outRect.max += offset; renderer->DrawImage(glyph.img, outRect, inRect); }
float FallbackFontRenderer::Measure(uint32_t unicode, float size) { auto glyph = FindGlyph(unicode); if(glyph.img == nullptr) { // no glyph found! draw box in the last resort return size + 1.f; } float scale = size * glyph.sizeInverse; if(roundSize || scale < 1.f) { float newScale = std::max(1.f, floorf(scale)); scale = newScale; } return glyph.advance * scale; }
CTextRenderer::CGlyph* CTextRenderer::GetGlyph(CGlyphCache* pCache, CGlyphId GlyphId) { CTextRenderer::CGlyph* pGlyph = FindGlyph(pCache, GlyphId); //Load Glyph if(!pGlyph) pGlyph = LoadGlyph(pCache, GlyphId); //Update timer if(pGlyph) pGlyph->m_RenderTick = m_RenderTick; return pGlyph; }
/** Returns the width of a string. Only a single-line string is handled at * this moment. The returned value is scaled. This routine ignores any * rotation, so the returned value is the width of the bounding box when the * text is drawn at zero degrees (horizontally, left to right). */ float CXFFont::GetTextExtent(const wchar_t* text) const { assert(text); float total = 0; for (unsigned idx = 0; text[idx]; idx++) { unsigned short code = text[idx]; if (idx > 0) total += m_LetterSpacing; if (code == ' ') { total += m_WordSpacing; } else { const CXFGlyph* glyph = FindGlyph(code); if (glyph) total += glyph->GetWidth(); } } return total * m_ScaleX; }
/** Internal function that returns the height & depth of the character. These * sizes are unscaled. * * height = character height above the base line * depth = descender height (negative value, or zero) */ void CXFFont::GetGlyphVSize(unsigned short code, float *height, float* depth) { assert(height); assert(depth); *height = *depth = 0; const CXFGlyph* glyph = FindGlyph(code); if (glyph) { const CXFPolyLine* stroke; for (size_t sidx = 0; (stroke = glyph->GetPolyLine(sidx)) != 0; sidx++) { const CXFPoint* pt; for (size_t pidx = 0; (pt = stroke->GetPoint(pidx)) != 0; pidx++) { if (*depth > pt->m_y) *depth = pt->m_y; else if (*height < pt->m_y) *height = pt->m_y; } } } }
void CXFFont::DrawText(const wchar_t* text, std::vector<CXFPolyLine>* strokes) const { assert(text); assert(strokes); strokes->clear(); float scalex = m_ScaleX, scaley = m_ScaleY; if (m_Rotation > 135 && m_Rotation <= 315) { scalex = -m_ScaleX; scaley = -m_ScaleY; } bool vertmode = (m_Rotation > 45 && m_Rotation <= 135) || (m_Rotation > 225 && m_Rotation <= 315); float xpos = 0, ypos = 0; switch (m_AlignHor) { case CXF_ALIGNLEFT: xpos = 0; break; case CXF_ALIGNRIGHT: xpos = - GetTextExtent(text); break; case CXF_ALIGNCENTRE: xpos = - GetTextExtent(text) / 2; break; } switch (m_AlignVer) { case CXF_ALIGNBASE: ypos = 0; break; case CXF_ALIGNTOP: ypos = m_Ascender * m_ScaleY * 1.15; /* assume 15% internal leading above the ascender */ break; case CXF_ALIGNBOTTOM: ypos = m_Descender * m_ScaleY * 1.1; /* assume 10% internal leading below the descender */ break; case CXF_ALIGNCENTRE: ypos = (m_Ascender + m_Descender) * m_ScaleY; break; } if (!vertmode) ypos = -ypos; float xbase = xpos; /* save, for the overbar */ for (unsigned idx = 0; text[idx]; idx++) { unsigned short code = text[idx]; if (idx > 0) xpos += m_LetterSpacing * scalex; if (code == ' ') { xpos += m_WordSpacing * scalex; } else { const CXFGlyph* glyph = FindGlyph(code); if (glyph) { const CXFPolyLine *stroke; CXFPolyLine newstroke; for (size_t sidx = 0; (stroke = glyph->GetPolyLine(sidx)) != 0; sidx++) { newstroke.Clear(); const CXFPoint* pt; CXFPoint newpt; for (size_t pidx = 0; (pt = stroke->GetPoint(pidx)) != 0; pidx++) { if (vertmode) { newpt.m_x = ypos - (pt->m_y * scaley); newpt.m_y = xpos + pt->m_x * scalex; } else { newpt.m_x = xpos + pt->m_x * scalex; newpt.m_y = ypos + pt->m_y * scaley; } newstroke.AddPoint(newpt); } strokes->push_back(newstroke); } xpos += glyph->GetWidth() * scalex; } } } /* optionally draw the overbar */ if (m_Overbar) { CXFPolyLine newstroke; float y = m_CapsHeight * scaley * 1.2; /* overbar 12% above caps height */ if (vertmode) { newstroke.AddPoint(CXFPoint(ypos - y, xbase)); newstroke.AddPoint(CXFPoint(ypos - y, xpos)); } else { newstroke.AddPoint(CXFPoint(xbase, ypos + y)); newstroke.AddPoint(CXFPoint(xpos, ypos + y)); } strokes->push_back(newstroke); } }