summaryrefslogtreecommitdiffstats
path: root/src/graphics/engine/text.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics/engine/text.cpp')
-rw-r--r--src/graphics/engine/text.cpp781
1 files changed, 780 insertions, 1 deletions
diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp
index 2a9543c..82abd62 100644
--- a/src/graphics/engine/text.cpp
+++ b/src/graphics/engine/text.cpp
@@ -19,5 +19,784 @@
#include "graphics/engine/text.h"
+#include "app/app.h"
+#include "common/image.h"
+#include "common/iman.h"
+#include "common/logger.h"
+#include "common/stringutils.h"
+#include "math/func.h"
-// TODO implementation
+#include <SDL/SDL.h>
+#include <SDL/SDL_ttf.h>
+
+
+namespace Gfx
+{
+
+/**
+ \struct CachedFont
+ \brief Base TTF font with UTF-8 char cache */
+struct CachedFont
+{
+ TTF_Font* font;
+ std::map<Gfx::UTF8Char, Gfx::CharTexture> cache;
+
+ CachedFont() : font(nullptr) {}
+};
+
+};
+
+
+
+Gfx::CText::CText(CInstanceManager *iMan, Gfx::CEngine* engine)
+{
+ m_iMan = iMan;
+ m_iMan->AddInstance(CLASS_TEXT, this);
+
+ m_device = nullptr;
+ m_engine = engine;
+
+ m_defaultSize = 12.0f;
+ m_fontPath = "fonts";
+
+ m_lastFontType = Gfx::FONT_COLOBOT;
+ m_lastFontSize = 0;
+ m_lastCachedFont = nullptr;
+}
+
+Gfx::CText::~CText()
+{
+ m_iMan->DeleteInstance(CLASS_TEXT, this);
+
+ m_iMan = nullptr;
+ m_device = nullptr;
+ m_engine = nullptr;
+}
+
+bool Gfx::CText::Create()
+{
+ if (TTF_Init() != 0)
+ {
+ m_error = std::string("TTF_Init error: ") + std::string(TTF_GetError());
+ return false;
+ }
+
+ m_fonts[Gfx::FONT_COLOBOT] = new MultisizeFont("dvu_sans.ttf");
+ m_fonts[Gfx::FONT_COLOBOT_BOLD] = new MultisizeFont("dvu_sans_bold.ttf");
+ m_fonts[Gfx::FONT_COLOBOT_ITALIC] = new MultisizeFont("dvu_sans_italic.ttf");
+
+ m_fonts[Gfx::FONT_COURIER] = new MultisizeFont("dvu_sans_mono.ttf");
+ m_fonts[Gfx::FONT_COURIER_BOLD] = new MultisizeFont("dvu_sans_mono_bold.ttf");
+
+ for (auto it = m_fonts.begin(); it != m_fonts.end(); ++it)
+ {
+ Gfx::FontType type = (*it).first;
+ CachedFont* cf = GetOrOpenFont(type, m_defaultSize);
+ if (cf == nullptr || cf->font == nullptr)
+ return false;
+ }
+
+ return true;
+}
+
+void Gfx::CText::Destroy()
+{
+ for (auto it = m_fonts.begin(); it != m_fonts.end(); ++it)
+ {
+ MultisizeFont* mf = (*it).second;
+
+ for (auto jt = mf->fonts.begin(); jt != mf->fonts.end(); ++jt)
+ {
+ CachedFont* cf = (*jt).second;
+
+ TTF_CloseFont(cf->font);
+
+ cf->font = nullptr;
+ delete cf;
+ }
+
+ mf->fonts.clear();
+ delete mf;
+ }
+
+ m_fonts.clear();
+
+ m_lastCachedFont = nullptr;
+
+ TTF_Quit();
+}
+
+void Gfx::CText::SetDevice(Gfx::CDevice* device)
+{
+ m_device = device;
+}
+
+std::string Gfx::CText::GetError()
+{
+ return m_error;
+}
+
+void Gfx::CText::FlushCache()
+{
+ for (auto it = m_fonts.begin(); it != m_fonts.end(); ++it)
+ {
+ MultisizeFont *mf = (*it).second;
+ for (auto jt = mf->fonts.begin(); jt != mf->fonts.end(); ++jt)
+ {
+ CachedFont *f = (*jt).second;
+ f->cache.clear();
+ }
+ }
+}
+
+void Gfx::CText::DrawText(const std::string &text, const std::vector<FontMetaChar> &format,
+ float size, Math::Point pos, float width, Gfx::TextAlign align,
+ int eol)
+{
+ float sw = 0.0f;
+
+ if (align == Gfx::TEXT_ALIGN_CENTER)
+ {
+ sw = GetStringWidth(text, format, size);
+ if (sw > width) sw = width;
+ pos.x -= sw / 2.0f;
+ }
+ else if (align == Gfx::TEXT_ALIGN_RIGHT)
+ {
+ sw = GetStringWidth(text, format, size);
+ if (sw > width) sw = width;
+ pos.x -= sw;
+ }
+
+ DrawString(text, format, size, pos, width, eol);
+}
+
+void Gfx::CText::DrawText(const std::string &text, Gfx::FontType font,
+ float size, Math::Point pos, float width, Gfx::TextAlign align,
+ int eol)
+{
+ float sw = 0.0f;
+
+ if (align == Gfx::TEXT_ALIGN_CENTER)
+ {
+ sw = GetStringWidth(text, font, size);
+ if (sw > width) sw = width;
+ pos.x -= sw / 2.0f;
+ }
+ else if (align == Gfx::TEXT_ALIGN_RIGHT)
+ {
+ sw = GetStringWidth(text, font, size);
+ if (sw > width) sw = width;
+ pos.x -= sw;
+ }
+
+ DrawString(text, font, size, pos, width, eol);
+}
+
+void Gfx::CText::SizeText(const std::string &text, const std::vector<FontMetaChar> &format,
+ float size, Math::Point pos, Gfx::TextAlign align,
+ Math::Point &start, Math::Point &end)
+{
+ start = end = pos;
+
+ float sw = GetStringWidth(text, format, size);
+ end.x += sw;
+ if (align == Gfx::TEXT_ALIGN_CENTER)
+ {
+ start.x -= sw/2.0f;
+ end.x -= sw/2.0f;
+ }
+ else if (align == Gfx::TEXT_ALIGN_RIGHT)
+ {
+ start.x -= sw;
+ end.x -= sw;
+ }
+
+ start.y -= GetDescent(Gfx::FONT_COLOBOT, size);
+ end.y += GetAscent(Gfx::FONT_COLOBOT, size);
+}
+
+void Gfx::CText::SizeText(const std::string &text, Gfx::FontType font,
+ float size, Math::Point pos, Gfx::TextAlign align,
+ Math::Point &start, Math::Point &end)
+{
+ start = end = pos;
+
+ float sw = GetStringWidth(text, font, size);
+ end.x += sw;
+ if (align == Gfx::TEXT_ALIGN_CENTER)
+ {
+ start.x -= sw/2.0f;
+ end.x -= sw/2.0f;
+ }
+ else if (align == Gfx::TEXT_ALIGN_RIGHT)
+ {
+ start.x -= sw;
+ end.x -= sw;
+ }
+
+ start.y -= GetDescent(font, size);
+ end.y += GetAscent(font, size);
+}
+
+float Gfx::CText::GetAscent(Gfx::FontType font, float size)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ Gfx::CachedFont* cf = GetOrOpenFont(font, size);
+ assert(cf != nullptr);
+ Math::IntPoint wndSize;
+ wndSize.y = TTF_FontAscent(cf->font);
+ Math::Point ifSize = m_engine->WindowToInterfaceSize(wndSize);
+ return ifSize.y;
+}
+
+float Gfx::CText::GetDescent(Gfx::FontType font, float size)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ Gfx::CachedFont* cf = GetOrOpenFont(font, size);
+ assert(cf != nullptr);
+ Math::IntPoint wndSize;
+ wndSize.y = TTF_FontDescent(cf->font);
+ Math::Point ifSize = m_engine->WindowToInterfaceSize(wndSize);
+ return ifSize.y;
+}
+
+float Gfx::CText::GetHeight(Gfx::FontType font, float size)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ Gfx::CachedFont* cf = GetOrOpenFont(font, size);
+ assert(cf != nullptr);
+ Math::IntPoint wndSize;
+ wndSize.y = TTF_FontHeight(cf->font);
+ Math::Point ifSize = m_engine->WindowToInterfaceSize(wndSize);
+ return ifSize.y;
+}
+
+
+float Gfx::CText::GetStringWidth(const std::string &text,
+ const std::vector<FontMetaChar> &format, float size)
+{
+ assert(StrUtils::Utf8StringLength(text) == format.size());
+
+ float width = 0.0f;
+ unsigned int index = 0;
+ unsigned int fmtIndex = 0;
+ while (index < text.length())
+ {
+ Gfx::FontType font = static_cast<Gfx::FontType>(format[fmtIndex] & Gfx::FONT_MASK_FONT);
+
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ width += GetCharWidth(ch, font, size, width);
+
+ index += len;
+ fmtIndex++;
+ }
+
+ return width;
+}
+
+float Gfx::CText::GetStringWidth(const std::string &text, Gfx::FontType font, float size)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ // TODO: special chars?
+
+ Gfx::CachedFont* cf = GetOrOpenFont(font, size);
+ assert(cf != nullptr);
+ Math::IntPoint wndSize;
+ TTF_SizeUTF8(cf->font, text.c_str(), &wndSize.x, &wndSize.y);
+ Math::Point ifSize = m_engine->WindowToInterfaceSize(wndSize);
+ return ifSize.x;
+}
+
+float Gfx::CText::GetCharWidth(Gfx::UTF8Char ch, Gfx::FontType font, float size, float offset)
+{
+ // TODO: if (font == Gfx::FONT_BUTTON)
+ if (font == Gfx::FONT_BUTTON) return 0.0f;
+
+ // TODO: special chars?
+ // TODO: tab sizing
+
+ Gfx::CachedFont* cf = GetOrOpenFont(font, size);
+ assert(cf != nullptr);
+
+ Gfx::CharTexture tex;
+ auto it = cf->cache.find(ch);
+ if (it != cf->cache.end())
+ tex = (*it).second;
+ else
+ tex = CreateCharTexture(ch, cf);
+
+ return tex.charSize.x;
+}
+
+
+int Gfx::CText::Justify(const std::string &text, const std::vector<FontMetaChar> &format,
+ float size, float width)
+{
+ assert(StrUtils::Utf8StringLength(text) == format.size());
+
+ float pos = 0.0f;
+ int cut = 0;
+ unsigned int index = 0;
+ unsigned int fmtIndex = 0;
+ while (index < text.length())
+ {
+ Gfx::FontType font = static_cast<Gfx::FontType>(format[fmtIndex] & Gfx::FONT_MASK_FONT);
+
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ if (font != Gfx::FONT_BUTTON)
+ {
+ if (ch.c1 == '\n')
+ return index+1;
+ if (ch.c1 == ' ')
+ cut = index+1;
+ }
+
+ pos += GetCharWidth(ch, font, size, pos);
+ if (pos > width)
+ {
+ if (cut == 0) return index;
+ else return cut;
+ }
+
+ index += len;
+ fmtIndex++;
+ }
+
+ return index;
+}
+
+int Gfx::CText::Justify(const std::string &text, Gfx::FontType font, float size, float width)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ float pos = 0.0f;
+ int cut = 0;
+ unsigned int index = 0;
+ while (index < text.length())
+ {
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ index += len;
+
+ if (ch.c1 == '\n')
+ return index+1;
+
+ if (ch.c1 == ' ' )
+ cut = index+1;
+
+ pos += GetCharWidth(ch, font, size, pos);
+ if (pos > width)
+ {
+ if (cut == 0) return index;
+ else return cut;
+ }
+ }
+
+ return index;
+}
+
+int Gfx::CText::Detect(const std::string &text, const std::vector<FontMetaChar> &format,
+ float size, float offset)
+{
+ assert(StrUtils::Utf8StringLength(text) == format.size());
+
+ float pos = 0.0f;
+ unsigned int index = 0;
+ unsigned int fmtIndex = 0;
+ while (index < text.length())
+ {
+ Gfx::FontType font = static_cast<Gfx::FontType>(format[fmtIndex] & Gfx::FONT_MASK_FONT);
+
+ // TODO: if (font == Gfx::FONT_BUTTON)
+ if (font == Gfx::FONT_BUTTON) continue;
+
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ if (ch.c1 == '\n')
+ return index;
+
+ float width = GetCharWidth(ch, font, size, pos);
+ if (offset <= pos + width/2.0f)
+ return index;
+
+ pos += width;
+ index += len;
+ fmtIndex++;
+ }
+
+ return index;
+}
+
+int Gfx::CText::Detect(const std::string &text, Gfx::FontType font, float size, float offset)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ float pos = 0.0f;
+ unsigned int index = 0;
+ while (index < text.length())
+ {
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ index += len;
+
+ if (ch.c1 == '\n')
+ return index;
+
+ float width = GetCharWidth(ch, font, size, pos);
+ if (offset <= pos + width/2.0f)
+ return index;
+
+ pos += width;
+ }
+
+ return index;
+}
+
+void Gfx::CText::DrawString(const std::string &text, const std::vector<FontMetaChar> &format,
+ float size, Math::Point pos, float width, int eol)
+{
+ assert(StrUtils::Utf8StringLength(text) == format.size());
+
+ m_engine->SetState(Gfx::ENG_RSTATE_TEXT);
+
+ Gfx::FontType font = Gfx::FONT_COLOBOT;
+ float start = pos.x;
+
+ unsigned int index = 0;
+ unsigned int fmtIndex = 0;
+ while (index < text.length())
+ {
+ font = static_cast<Gfx::FontType>(format[fmtIndex] & Gfx::FONT_MASK_FONT);
+
+ // TODO: if (font == Gfx::FONT_BUTTON)
+ if (font == Gfx::FONT_BUTTON) continue;
+
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ float offset = pos.x - start;
+ float cw = GetCharWidth(ch, font, size, offset);
+ if (offset + cw > width) // exceeds the maximum width?
+ {
+ // TODO: special end-of-line char
+ break;
+ }
+
+ Gfx::FontHighlight hl = static_cast<Gfx::FontHighlight>(format[fmtIndex] & Gfx::FONT_MASK_HIGHLIGHT);
+ if (hl != Gfx::FONT_HIGHLIGHT_NONE)
+ {
+ Math::Point charSize;
+ charSize.x = GetCharWidth(ch, font, size, offset);
+ charSize.y = GetHeight(font, size);
+ DrawHighlight(hl, pos, charSize);
+ }
+
+ DrawChar(ch, font, size, pos);
+
+ index += len;
+ fmtIndex++;
+ }
+
+ // TODO: eol
+}
+
+void Gfx::CText::DrawString(const std::string &text, Gfx::FontType font,
+ float size, Math::Point pos, float width, int eol)
+{
+ assert(font != Gfx::FONT_BUTTON);
+
+ m_engine->SetState(Gfx::ENG_RSTATE_TEXT);
+
+ unsigned int index = 0;
+ while (index < text.length())
+ {
+ Gfx::UTF8Char ch;
+
+ int len = StrUtils::Utf8CharSizeAt(text, index);
+ if (len >= 1)
+ ch.c1 = text[index];
+ if (len >= 2)
+ ch.c2 = text[index+1];
+ if (len >= 3)
+ ch.c3 = text[index+2];
+
+ index += len;
+
+ DrawChar(ch, font, size, pos);
+ }
+}
+
+void Gfx::CText::DrawHighlight(Gfx::FontHighlight hl, Math::Point pos, Math::Point size)
+{
+ // Gradient colors
+ Gfx::Color grad[4];
+
+ // TODO: switch to alpha factors
+
+ switch (hl)
+ {
+ case Gfx::FONT_HIGHLIGHT_LINK:
+ grad[0] = grad[1] = grad[2] = grad[3] = Gfx::Color(0.0f, 0.0f, 1.0f, 0.5f);
+ break;
+
+ case Gfx::FONT_HIGHLIGHT_TOKEN:
+ grad[0] = grad[1] = Gfx::Color(248.0f / 256.0f, 248.0f / 256.0f, 248.0f / 256.0f, 0.5f);
+ grad[2] = grad[3] = Gfx::Color(248.0f / 256.0f, 220.0f / 256.0f, 188.0f / 256.0f, 0.5f);
+ break;
+
+ case Gfx::FONT_HIGHLIGHT_TYPE:
+ grad[0] = grad[1] = Gfx::Color(248.0f / 256.0f, 248.0f / 256.0f, 248.0f / 256.0f, 0.5f);
+ grad[2] = grad[3] = Gfx::Color(169.0f / 256.0f, 234.0f / 256.0f, 169.0f / 256.0f, 0.5f);
+ break;
+
+ case Gfx::FONT_HIGHLIGHT_CONST:
+ grad[0] = grad[1] = Gfx::Color(248.0f / 256.0f, 248.0f / 256.0f, 248.0f / 256.0f, 0.5f);
+ grad[2] = grad[3] = Gfx::Color(248.0f / 256.0f, 176.0f / 256.0f, 169.0f / 256.0f, 0.5f);
+ break;
+
+ case Gfx::FONT_HIGHLIGHT_REM:
+ grad[0] = grad[1] = Gfx::Color(248.0f / 256.0f, 248.0f / 256.0f, 248.0f / 256.0f, 0.5f);
+ grad[2] = grad[3] = Gfx::Color(248.0f / 256.0f, 169.0f / 256.0f, 248.0f / 256.0f, 0.5f);
+ break;
+
+ case Gfx::FONT_HIGHLIGHT_KEY:
+ grad[0] = grad[1] = grad[2] = grad[3] =
+ Gfx::Color(192.0f / 256.0f, 192.0f / 256.0f, 192.0f / 256.0f, 0.5f);
+ break;
+
+ default:
+ return;
+ }
+
+ Math::IntPoint vsize = m_engine->GetWindowSize();
+ float h = 0.0f;
+ if (vsize.y <= 768.0f) // 1024x768 or less?
+ h = 1.01f / vsize.y; // 1 pixel
+ else // more than 1024x768?
+ h = 2.0f / vsize.y; // 2 pixels
+
+ Math::Point p1, p2;
+ p1.x = pos.x;
+ p2.x = pos.x + size.x;
+
+ if (hl == Gfx::FONT_HIGHLIGHT_LINK)
+ {
+ p1.y = pos.y;
+ p2.y = pos.y + h; // just emphasized
+ }
+ else
+ {
+ p1.y = pos.y;
+ p2.y = pos.y + size.y;
+ }
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, false);
+
+ Gfx::VertexCol quad[] =
+ {
+ Gfx::VertexCol(Math::Vector(p1.x, p1.y, 0.0f), grad[3]),
+ Gfx::VertexCol(Math::Vector(p1.x, p2.y, 0.0f), grad[0]),
+ Gfx::VertexCol(Math::Vector(p2.x, p1.y, 0.0f), grad[2]),
+ Gfx::VertexCol(Math::Vector(p2.x, p2.y, 0.0f), grad[1])
+ };
+
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4);
+ m_engine->AddStatisticTriangle(2);
+
+ m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+}
+
+void Gfx::CText::DrawChar(Gfx::UTF8Char ch, Gfx::FontType font, float size, Math::Point &pos)
+{
+ // TODO: if (font == Gfx::FONT_BUTTON)
+ if (font == Gfx::FONT_BUTTON) return;
+
+ // TODO: special chars?
+
+ CachedFont* cf = GetOrOpenFont(font, size);
+
+ if (cf == nullptr)
+ return;
+
+ auto it = cf->cache.find(ch);
+ CharTexture tex;
+ if (it != cf->cache.end())
+ {
+ tex = (*it).second;
+ }
+ else
+ {
+ tex = CreateCharTexture(ch, cf);
+
+ if (tex.id == 0) // invalid
+ return;
+
+ cf->cache[ch] = tex;
+ }
+
+ Math::Point p1(pos.x, pos.y + tex.charSize.y - tex.texSize.y);
+ Math::Point p2(pos.x + tex.texSize.x, pos.y + tex.charSize.y);
+
+ Math::Vector n(0.0f, 0.0f, -1.0f); // normal
+
+ Gfx::Vertex quad[4] =
+ {
+ Gfx::Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(0.0f, 1.0f)),
+ Gfx::Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(0.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(1.0f, 1.0f)),
+ Gfx::Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(1.0f, 0.0f))
+ };
+
+ m_device->SetTexture(0, tex.id);
+ m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4);
+ m_engine->AddStatisticTriangle(2);
+
+ pos.x += tex.charSize.x;
+}
+
+Gfx::CachedFont* Gfx::CText::GetOrOpenFont(Gfx::FontType font, float size)
+{
+ // TODO: sizing
+ int pointSize = static_cast<int>(size);
+
+ if (m_lastCachedFont != nullptr)
+ {
+ if (m_lastFontType == font && m_lastFontSize == pointSize)
+ return m_lastCachedFont;
+ }
+
+ auto it = m_fonts.find(font);
+ if (it == m_fonts.end())
+ {
+ m_error = std::string("Invalid font type ") + StrUtils::ToString<int>(static_cast<int>(font));
+ return nullptr;
+ }
+
+ MultisizeFont* mf = (*it).second;
+
+ auto jt = mf->fonts.find(pointSize);
+ if (jt != mf->fonts.end())
+ {
+ m_lastCachedFont = (*jt).second;
+ m_lastFontType = font;
+ m_lastFontSize = pointSize;
+ return m_lastCachedFont;
+ }
+
+ std::string path = CApplication::GetInstance().GetDataFilePath(m_fontPath, mf->fileName);
+
+ m_lastCachedFont = new CachedFont();
+ m_lastCachedFont->font = TTF_OpenFont(path.c_str(), pointSize);
+ if (m_lastCachedFont->font == nullptr)
+ m_error = std::string("TTF_OpenFont error ") + std::string(TTF_GetError());
+
+ mf->fonts[pointSize] = m_lastCachedFont;
+
+ return m_lastCachedFont;
+}
+
+Gfx::CharTexture Gfx::CText::CreateCharTexture(Gfx::UTF8Char ch, Gfx::CachedFont* font)
+{
+ CharTexture texture;
+
+ SDL_Surface* textSurface = nullptr;
+ SDL_Color white = {255, 255, 255, 0};
+ char str[] = { ch.c1, ch.c2, ch.c3, '\0' };
+ textSurface = TTF_RenderUTF8_Blended(font->font, str, white);
+
+ if (textSurface == nullptr)
+ {
+ m_error = "TTF_Render error";
+ return texture;
+ }
+
+ int w = Math::NextPowerOfTwo(textSurface->w);
+ int h = Math::NextPowerOfTwo(textSurface->h);
+
+ textSurface->flags = textSurface->flags & (~SDL_SRCALPHA);
+ SDL_Surface* textureSurface = SDL_CreateRGBSurface(0, w, h, 32, 0x00ff0000, 0x0000ff00,
+ 0x000000ff, 0xff000000);
+ SDL_BlitSurface(textSurface, NULL, textureSurface, NULL);
+
+ ImageData data;
+ data.surface = textureSurface;
+
+ Gfx::TextureCreateParams createParams;
+ createParams.format = Gfx::TEX_IMG_RGBA;
+ createParams.minFilter = Gfx::TEX_MIN_FILTER_NEAREST;
+ createParams.magFilter = Gfx::TEX_MAG_FILTER_NEAREST;
+ createParams.mipmap = false;
+
+ Gfx::Texture tex = m_device->CreateTexture(&data, createParams);
+
+ data.surface = nullptr;
+
+ SDL_FreeSurface(textSurface);
+ SDL_FreeSurface(textureSurface);
+
+ if (! tex.valid)
+ {
+ m_error = "Texture create error";
+ return texture;
+ }
+
+ texture.id = tex.id;
+ texture.texSize = m_engine->WindowToInterfaceSize(Math::IntPoint(textureSurface->w, textureSurface->h));
+ texture.charSize = m_engine->WindowToInterfaceSize(Math::IntPoint(textSurface->w, textSurface->h));
+
+ return texture;
+}