Finally implemented TextCaches for TextComponent and TextEditComponent.

Huge boost in performance with rendering game descriptions.
(It's About Damn Time (TM))
This commit is contained in:
Aloshi 2013-08-21 20:08:36 -05:00
parent 11f774e019
commit df897c0b5a
6 changed files with 90 additions and 100 deletions

View file

@ -283,12 +283,6 @@ void Font::renderTextCache(TextCache* cache)
return; return;
} }
if(cache->sourceFont != this)
{
LOG(LogError) << "Attempted to draw TextCache with font other than its source!";
return;
}
glBindTexture(GL_TEXTURE_2D, textureID); glBindTexture(GL_TEXTURE_2D, textureID);
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND); glEnable(GL_BLEND);
@ -314,17 +308,34 @@ void Font::renderTextCache(TextCache* cache)
Eigen::Vector2f Font::sizeText(std::string text) const Eigen::Vector2f Font::sizeText(std::string text) const
{ {
float cwidth = 0.0f; float lineWidth = 0.0f;
float highestWidth = 0.0f;
float y = (float)getHeight();
for(unsigned int i = 0; i < text.length(); i++) for(unsigned int i = 0; i < text.length(); i++)
{ {
unsigned char letter = text[i]; unsigned char letter = text[i];
if(letter == '\n')
{
if(lineWidth > highestWidth)
highestWidth = lineWidth;
lineWidth = 0.0f;
y += getHeight();
}
if(letter < 32 || letter >= 128) if(letter < 32 || letter >= 128)
letter = 127; letter = 127;
cwidth += charData[letter].advX * fontScale; lineWidth += charData[letter].advX * fontScale;
} }
return Eigen::Vector2f(cwidth, getHeight()); if(lineWidth > highestWidth)
highestWidth = lineWidth;
return Eigen::Vector2f(highestWidth, y);
} }
int Font::getHeight() const int Font::getHeight() const
@ -333,8 +344,6 @@ int Font::getHeight() const
} }
void Font::drawCenteredText(std::string text, float xOffset, float y, unsigned int color) void Font::drawCenteredText(std::string text, float xOffset, float y, unsigned int color)
{ {
Eigen::Vector2f pos = sizeText(text); Eigen::Vector2f pos = sizeText(text);
@ -350,73 +359,21 @@ void Font::drawCenteredText(std::string text, float xOffset, float y, unsigned i
//draws text and ensures it's never longer than xLen //draws text and ensures it's never longer than xLen
void Font::drawWrappedText(std::string text, const Eigen::Vector2f& offset, float xLen, unsigned int color) void Font::drawWrappedText(std::string text, const Eigen::Vector2f& offset, float xLen, unsigned int color)
{ {
float y = offset.y(); text = wrapText(text, xLen);
drawText(text, offset, color);
std::string line, word, temp;
Eigen::Vector2f textSize;
size_t space, newline;
while(text.length() > 0 || !line.empty()) //while there's text or we still have text to render
{
space = text.find(' ', 0);
if(space == std::string::npos)
space = text.length() - 1;
word = text.substr(0, space + 1);
//check if the next word contains a newline
newline = word.find('\n', 0);
if(newline != std::string::npos)
{
word = word.substr(0, newline);
text.erase(0, newline + 1);
}else{
text.erase(0, space + 1);
}
temp = line + word;
textSize = sizeText(temp);
//if we're on the last word and it'll fit on the line, just add it to the line
if((textSize.x() <= xLen && text.length() == 0) || newline != std::string::npos)
{
line = temp;
word = "";
}
//if the next line will be too long or we're on the last of the text, render it
if(textSize.x() > xLen || text.length() == 0 || newline != std::string::npos)
{
//render line now
if(textSize.x() > 0) //make sure it's not blank
drawText(line, Eigen::Vector2f(offset.x(), y), color);
//increment y by height and some extra padding for the next line
y += textSize.y() + 4;
//move the word we skipped to the next line
line = word;
}else{
//there's still space, continue building the line
line = temp;
}
}
} }
Eigen::Vector2f Font::sizeWrappedText(std::string text, float xLen) const //the worst algorithm ever written
//breaks up a normal string with newlines to make it fit xLen
std::string Font::wrapText(std::string text, float xLen) const
{ {
Eigen::Vector2f out(0, 0); std::string out;
float y = 0;
std::string line, word, temp; std::string line, word, temp;
Eigen::Vector2f textSize;
size_t space, newline; size_t space, newline;
Eigen::Vector2f textSize;
while(text.length() > 0 || !line.empty()) //while there's text or we still have text to render while(text.length() > 0 || !line.empty()) //while there's text or we still have text to render
{ {
space = text.find(' ', 0); space = text.find(' ', 0);
@ -449,29 +406,29 @@ Eigen::Vector2f Font::sizeWrappedText(std::string text, float xLen) const
//if the next line will be too long or we're on the last of the text, render it //if the next line will be too long or we're on the last of the text, render it
if(textSize.x() > xLen || text.length() == 0 || newline != std::string::npos) if(textSize.x() > xLen || text.length() == 0 || newline != std::string::npos)
{ {
//increment y by height and some extra padding for the next line //output line now
y += textSize.y() + 4; if(textSize.x() > 0) //make sure it's not blank
out += line + '\n';
//move the word we skipped to the next line //move the word we skipped to the next line
line = word; line = word;
//update our maximum known line width
if(textSize.x() > out.x())
out[0] = textSize.x();
}else{ }else{
//there's still space, continue building the line //there's still space, continue building the line
line = temp; line = temp;
} }
} }
out[1] = y; if(!out.empty()) //chop off the last newline
out.erase(out.length() - 1, 1);
return out; return out;
} }
Eigen::Vector2f Font::sizeWrappedText(std::string text, float xLen) const
{
text = wrapText(text, xLen);
return sizeText(text);
}
//============================================================================================================= //=============================================================================================================
//TextCache //TextCache
@ -502,6 +459,13 @@ TextCache* Font::buildTextCache(const std::string& text, float offsetX, float of
{ {
unsigned char letter = text[charNum]; unsigned char letter = text[charNum];
if(letter == '\n')
{
y += (float)getHeight();
x = offsetX;
continue;
}
if(letter < 32 || letter >= 128) if(letter < 32 || letter >= 128)
letter = 127; //print [X] if character is not standard ASCII letter = 127; //print [X] if character is not standard ASCII
@ -533,14 +497,14 @@ TextCache* Font::buildTextCache(const std::string& text, float offsetX, float of
x += charData[letter].advX * fontScale; x += charData[letter].advX * fontScale;
} }
TextCache* cache = new TextCache(vertCount, vert, colors, this); TextCache* cache = new TextCache(vertCount, vert, colors);
if(color != 0x00000000) if(color != 0x00000000)
cache->setColor(color); cache->setColor(color);
return cache; return cache;
} }
TextCache::TextCache(int verts, Vertex* v, GLubyte* c, Font* f) : vertCount(verts), verts(v), colors(c), sourceFont(f) TextCache::TextCache(int verts, Vertex* v, GLubyte* c) : vertCount(verts), verts(v), colors(c)
{ {
} }

View file

@ -53,6 +53,8 @@ public:
void drawText(std::string text, const Eigen::Vector2f& offset, unsigned int color); void drawText(std::string text, const Eigen::Vector2f& offset, unsigned int color);
Eigen::Vector2f sizeText(std::string text) const; //Sets the width and height of a given string to supplied pointers. A dimension is skipped if its pointer is NULL. Eigen::Vector2f sizeText(std::string text) const; //Sets the width and height of a given string to supplied pointers. A dimension is skipped if its pointer is NULL.
std::string wrapText(std::string text, float xLen) const;
void drawWrappedText(std::string text, const Eigen::Vector2f& offset, float xLen, unsigned int color); void drawWrappedText(std::string text, const Eigen::Vector2f& offset, float xLen, unsigned int color);
Eigen::Vector2f sizeWrappedText(std::string text, float xLen) const; Eigen::Vector2f sizeWrappedText(std::string text, float xLen) const;
@ -102,13 +104,12 @@ public:
void setColor(unsigned int color); void setColor(unsigned int color);
TextCache(int verts, Vertex* v, GLubyte* c, Font* f); TextCache(int verts, Vertex* v, GLubyte* c);
~TextCache(); ~TextCache();
const int vertCount; int vertCount;
const Vertex* verts; Vertex* verts;
const GLubyte* colors; GLubyte* colors;
const Font* sourceFont;
}; };
#endif #endif

View file

@ -20,27 +20,26 @@ TextComponent::TextComponent(Window* window, const std::string& text, std::share
void TextComponent::onSizeChanged() void TextComponent::onSizeChanged()
{ {
mAutoCalcExtent << (getSize().x() == 0), (getSize().y() == 0); mAutoCalcExtent << (getSize().x() == 0), (getSize().y() == 0);
calculateExtent(); onTextChanged();
} }
void TextComponent::setFont(std::shared_ptr<Font> font) void TextComponent::setFont(std::shared_ptr<Font> font)
{ {
mFont = font; mFont = font;
onTextChanged();
calculateExtent();
} }
void TextComponent::setColor(unsigned int color) void TextComponent::setColor(unsigned int color)
{ {
mColor = color; mColor = color;
mOpacity = mColor & 0x000000FF; mOpacity = mColor & 0x000000FF;
onTextChanged();
} }
void TextComponent::setText(const std::string& text) void TextComponent::setText(const std::string& text)
{ {
mText = text; mText = text;
onTextChanged();
calculateExtent();
} }
void TextComponent::setCentered(bool center) void TextComponent::setCentered(bool center)
@ -70,9 +69,13 @@ void TextComponent::render(const Eigen::Affine3f& parentTrans)
{ {
Eigen::Vector2f textSize = font->sizeWrappedText(mText, getSize().x()); Eigen::Vector2f textSize = font->sizeWrappedText(mText, getSize().x());
Eigen::Vector2f pos((getSize().x() - textSize.x()) / 2, 0); Eigen::Vector2f pos((getSize().x() - textSize.x()) / 2, 0);
font->drawWrappedText(mText, pos, getSize().x(), (mColor >> 8 << 8) | getOpacity());
Eigen::Affine3f centeredTrans = trans;
centeredTrans = centeredTrans.translate(Eigen::Vector3f(pos.x(), pos.y(), 0));
Renderer::setMatrix(centeredTrans);
font->renderTextCache(mTextCache.get());
}else{ }else{
font->drawWrappedText(mText, Eigen::Vector2f(0, 0), getSize().x(), mColor >> 8 << 8 | getOpacity()); font->renderTextCache(mTextCache.get());
} }
} }
@ -94,6 +97,14 @@ void TextComponent::calculateExtent()
} }
} }
void TextComponent::onTextChanged()
{
calculateExtent();
std::shared_ptr<Font> f = getFont();
mTextCache = std::unique_ptr<TextCache>(f->buildTextCache(f->wrapText(mText, mSize.x()), 0, 0, (mColor >> 8 << 8) | mOpacity));
}
void TextComponent::setValue(const std::string& value) void TextComponent::setValue(const std::string& value)
{ {
setText(value); setText(value);

View file

@ -26,10 +26,13 @@ private:
void calculateExtent(); void calculateExtent();
void onTextChanged();
unsigned int mColor; unsigned int mColor;
std::shared_ptr<Font> mFont; std::shared_ptr<Font> mFont;
Eigen::Matrix<bool, 1, 2> mAutoCalcExtent; Eigen::Matrix<bool, 1, 2> mAutoCalcExtent;
std::string mText; std::string mText;
std::unique_ptr<TextCache> mTextCache;
bool mCentered; bool mCentered;
}; };

View file

@ -69,11 +69,16 @@ void TextEditComponent::textInput(const char* text)
void TextEditComponent::onTextChanged() void TextEditComponent::onTextChanged()
{ {
std::shared_ptr<Font> f = getFont();
std::string wrappedText = f->wrapText(mText, mSize.x());
mTextCache = std::unique_ptr<TextCache>(f->buildTextCache(wrappedText, 0, 0, 0x00000000 | getOpacity()));
if(mAllowResize) if(mAllowResize)
{ {
float y = getFont()->sizeWrappedText(mText, mSize.x()).y(); float y = f->sizeText(wrappedText).y();
if(y == 0) if(y == 0)
y = (float)getFont()->getHeight(); y = (float)f->getHeight();
setSize(mSize.x(), y); setSize(mSize.x(), y);
} }
@ -96,8 +101,11 @@ void TextEditComponent::render(const Eigen::Affine3f& parentTrans)
Renderer::setMatrix(trans); Renderer::setMatrix(trans);
std::shared_ptr<Font> f = getFont(); if(mTextCache != NULL)
f->drawWrappedText(mText, Eigen::Vector2f(0, 0), mSize.x(), 0x000000 | getOpacity()); {
std::shared_ptr<Font> f = getFont();
f->renderTextCache(mTextCache.get());
}
} }
std::shared_ptr<Font> TextEditComponent::getFont() std::shared_ptr<Font> TextEditComponent::getFont()

View file

@ -4,12 +4,13 @@
#include "GuiBox.h" #include "GuiBox.h"
class Font; class Font;
class TextCache;
class TextEditComponent : public GuiComponent class TextEditComponent : public GuiComponent
{ {
public: public:
TextEditComponent(Window* window); TextEditComponent(Window* window);
void textInput(const char* text) override; void textInput(const char* text) override;
void render(const Eigen::Affine3f& parentTrans) override; void render(const Eigen::Affine3f& parentTrans) override;
@ -33,4 +34,6 @@ private:
std::shared_ptr<Font> getFont(); std::shared_ptr<Font> getFont();
GuiBox mBox; GuiBox mBox;
std::unique_ptr<TextCache> mTextCache;
}; };