Clean up BitmapFont code (#1129)

This commit is contained in:
vfjpl 2021-03-30 17:03:02 +02:00 committed by GitHub
parent 4edb1eb3ad
commit 4d055913dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 76 additions and 70 deletions

View File

@ -27,6 +27,14 @@
#include <framework/otml/otml.h> #include <framework/otml/otml.h>
namespace
{
// for performance reasons we use statics vectors that are allocated on demand
CoordsBuffer s_coordsBuffer;
std::vector<Point> s_glyphsPositions(1);
std::vector<int> s_lineWidths(1);
}
void BitmapFont::load(const OTMLNodePtr& fontNode) void BitmapFont::load(const OTMLNodePtr& fontNode)
{ {
OTMLNodePtr textureNode = fontNode->at("texture"); OTMLNodePtr textureNode = fontNode->at("texture");
@ -35,11 +43,12 @@ void BitmapFont::load(const OTMLNodePtr& fontNode)
m_glyphHeight = fontNode->valueAt<int>("height"); m_glyphHeight = fontNode->valueAt<int>("height");
m_yOffset = fontNode->valueAt("y-offset", 0); m_yOffset = fontNode->valueAt("y-offset", 0);
m_firstGlyph = fontNode->valueAt("first-glyph", 32); m_firstGlyph = fontNode->valueAt("first-glyph", 32);
m_glyphSpacing = fontNode->valueAt("spacing", Size(0,0)); m_glyphSpacing = fontNode->valueAt("spacing", Size(0, 0));
int spaceWidth = fontNode->valueAt("space-width", glyphSize.width()); int spaceWidth = fontNode->valueAt("space-width", glyphSize.width());
// load font texture // load font texture
m_texture = g_textures.getTexture(textureFile); m_texture = g_textures.getTexture(textureFile);
Size textureSize = m_texture->getSize();
if(OTMLNodePtr node = fontNode->get("fixed-glyph-width")) { if(OTMLNodePtr node = fontNode->get("fixed-glyph-width")) {
for(int glyph = m_firstGlyph; glyph < 256; ++glyph) for(int glyph = m_firstGlyph; glyph < 256; ++glyph)
@ -68,7 +77,7 @@ void BitmapFont::load(const OTMLNodePtr& fontNode)
// calculate glyphs texture coords // calculate glyphs texture coords
int numHorizontalGlyphs = m_texture->getSize().width() / glyphSize.width(); int numHorizontalGlyphs = textureSize.width() / glyphSize.width();
for(int glyph = m_firstGlyph; glyph < 256; ++glyph) { for(int glyph = m_firstGlyph; glyph < 256; ++glyph) {
m_glyphsTextureCoords[glyph].setRect(((glyph - m_firstGlyph) % numHorizontalGlyphs) * glyphSize.width(), m_glyphsTextureCoords[glyph].setRect(((glyph - m_firstGlyph) % numHorizontalGlyphs) * glyphSize.width(),
((glyph - m_firstGlyph) / numHorizontalGlyphs) * glyphSize.height(), ((glyph - m_firstGlyph) / numHorizontalGlyphs) * glyphSize.height(),
@ -86,11 +95,9 @@ void BitmapFont::drawText(const std::string& text, const Point& startPos)
void BitmapFont::drawText(const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align) void BitmapFont::drawText(const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align)
{ {
static CoordsBuffer coordsBuffer; s_coordsBuffer.clear();
coordsBuffer.clear(); calculateDrawTextCoords(s_coordsBuffer, text, screenCoords, align);
g_painter->drawTextureCoords(s_coordsBuffer, m_texture);
calculateDrawTextCoords(coordsBuffer, text, screenCoords, align);
g_painter->drawTextureCoords(coordsBuffer, m_texture);
} }
void BitmapFont::calculateDrawTextCoords(CoordsBuffer& coordsBuffer, const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align) void BitmapFont::calculateDrawTextCoords(CoordsBuffer& coordsBuffer, const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align)
@ -103,7 +110,7 @@ void BitmapFont::calculateDrawTextCoords(CoordsBuffer& coordsBuffer, const std::
// map glyphs positions // map glyphs positions
Size textBoxSize; Size textBoxSize;
const std::vector<Point>& glyphsPositions = calculateGlyphsPositions(text, align, &textBoxSize); const auto& glyphsPositions = calculateGlyphsPositions(text, align, &textBoxSize);
for(int i = 0; i < textLenght; ++i) { for(int i = 0; i < textLenght; ++i) {
int glyph = (uchar)text[i]; int glyph = (uchar)text[i];
@ -171,52 +178,49 @@ void BitmapFont::calculateDrawTextCoords(CoordsBuffer& coordsBuffer, const std::
const std::vector<Point>& BitmapFont::calculateGlyphsPositions(const std::string& text, const std::vector<Point>& BitmapFont::calculateGlyphsPositions(const std::string& text,
Fw::AlignmentFlag align, Fw::AlignmentFlag align,
Size *textBoxSize) Size* textBoxSize)
{ {
// for performance reasons we use statics vectors that are allocated on demand
static std::vector<Point> glyphsPositions(1);
static std::vector<int> lineWidths(1);
int textLength = text.length(); int textLength = text.length();
int maxLineWidth = 0; int maxLineWidth = 0;
int lines = 0; int lines = 0;
int glyph; int glyph;
int i;
// return if there is no text // return if there is no text
if(textLength == 0) { if(textLength == 0) {
if(textBoxSize) if(textBoxSize)
textBoxSize->resize(0,m_glyphHeight); textBoxSize->resize(0, m_glyphHeight);
return glyphsPositions; return s_glyphsPositions;
} }
// resize glyphsPositions vector when needed // resize s_glyphsPositions vector when needed
if(textLength > (int)glyphsPositions.size()) if(textLength > (int)s_glyphsPositions.size())
glyphsPositions.resize(textLength); s_glyphsPositions.resize(textLength);
// calculate lines width // calculate lines width
if((align & Fw::AlignRight || align & Fw::AlignHorizontalCenter) || textBoxSize) { if((align & Fw::AlignRight || align & Fw::AlignHorizontalCenter) || textBoxSize) {
lineWidths[0] = 0; s_lineWidths[0] = 0;
for(i = 0; i< textLength; ++i) { for(int i = 0; i < textLength; ++i) {
glyph = (uchar)text[i]; glyph = (uchar)text[i];
if(glyph == (uchar)'\n') { if(glyph == (uchar)'\n') {
lines++; lines++;
if(lines+1 > (int)lineWidths.size()) // resize s_lineWidths vector when needed
lineWidths.resize(lines+1); if(lines+1 > (int)s_lineWidths.size())
lineWidths[lines] = 0; s_lineWidths.resize(lines+1);
s_lineWidths[lines] = 0;
} else if(glyph >= 32) { } else if(glyph >= 32) {
lineWidths[lines] += m_glyphsSize[glyph].width() ; s_lineWidths[lines] += m_glyphsSize[glyph].width();
if((i+1 != textLength && text[i+1] != '\n')) // only add space if letter is not the last or before a \n. // only add space if letter is not the last or before a \n
lineWidths[lines] += m_glyphSpacing.width(); if((i+1 != textLength && text[i+1] != '\n'))
maxLineWidth = std::max<int>(maxLineWidth, lineWidths[lines]); s_lineWidths[lines] += m_glyphSpacing.width();
maxLineWidth = std::max<int>(maxLineWidth, s_lineWidths[lines]);
} }
} }
} }
Point virtualPos(0, m_yOffset); Point virtualPos(0, m_yOffset);
lines = 0; lines = 0;
for(i = 0; i < textLength; ++i) { for(int i = 0; i < textLength; ++i) {
glyph = (uchar)text[i]; glyph = (uchar)text[i];
// new line or first glyph // new line or first glyph
@ -228,16 +232,16 @@ const std::vector<Point>& BitmapFont::calculateGlyphsPositions(const std::string
// calculate start x pos // calculate start x pos
if(align & Fw::AlignRight) { if(align & Fw::AlignRight) {
virtualPos.x = (maxLineWidth - lineWidths[lines]); virtualPos.x = (maxLineWidth - s_lineWidths[lines]);
} else if(align & Fw::AlignHorizontalCenter) { } else if(align & Fw::AlignHorizontalCenter) {
virtualPos.x = (maxLineWidth - lineWidths[lines]) / 2; virtualPos.x = (maxLineWidth - s_lineWidths[lines]) / 2;
} else { // AlignLeft } else { // AlignLeft
virtualPos.x = 0; virtualPos.x = 0;
} }
} }
// store current glyph topLeft // store current glyph topLeft
glyphsPositions[i] = virtualPos; s_glyphsPositions[i] = virtualPos;
// render only if the glyph is valid // render only if the glyph is valid
if(glyph >= 32 && glyph != (uchar)'\n') { if(glyph >= 32 && glyph != (uchar)'\n') {
@ -250,7 +254,7 @@ const std::vector<Point>& BitmapFont::calculateGlyphsPositions(const std::string
textBoxSize->setHeight(virtualPos.y + m_glyphHeight); textBoxSize->setHeight(virtualPos.y + m_glyphHeight);
} }
return glyphsPositions; return s_glyphsPositions;
} }
Size BitmapFont::calculateTextRectSize(const std::string& text) Size BitmapFont::calculateTextRectSize(const std::string& text)
@ -265,11 +269,12 @@ void BitmapFont::calculateGlyphsWidthsAutomatically(const ImagePtr& image, const
if(!image) if(!image)
return; return;
int numHorizontalGlyphs = image->getSize().width() / glyphSize.width(); Size imageSize = image->getSize();
auto texturePixels = image->getPixels(); int numHorizontalGlyphs = imageSize.width() / glyphSize.width();
const auto& texturePixels = image->getPixels();
// small AI to auto calculate pixels widths // small AI to auto calculate pixels widths
for(int glyph = m_firstGlyph; glyph< 256; ++glyph) { for(int glyph = m_firstGlyph; glyph < 256; ++glyph) {
Rect glyphCoords(((glyph - m_firstGlyph) % numHorizontalGlyphs) * glyphSize.width(), Rect glyphCoords(((glyph - m_firstGlyph) % numHorizontalGlyphs) * glyphSize.width(),
((glyph - m_firstGlyph) / numHorizontalGlyphs) * glyphSize.height(), ((glyph - m_firstGlyph) / numHorizontalGlyphs) * glyphSize.height(),
glyphSize.width(), glyphSize.width(),
@ -279,7 +284,7 @@ void BitmapFont::calculateGlyphsWidthsAutomatically(const ImagePtr& image, const
int filledPixels = 0; int filledPixels = 0;
// check if all vertical pixels are alpha // check if all vertical pixels are alpha
for(int y = glyphCoords.top(); y <= glyphCoords.bottom(); ++y) { for(int y = glyphCoords.top(); y <= glyphCoords.bottom(); ++y) {
if(texturePixels[(y * image->getSize().width() * 4) + (x*4) + 3] != 0) if(texturePixels[(y * imageSize.width() * 4) + (x*4) + 3] != 0)
filledPixels++; filledPixels++;
} }
if(filledPixels > 0) if(filledPixels > 0)
@ -293,51 +298,54 @@ void BitmapFont::calculateGlyphsWidthsAutomatically(const ImagePtr& image, const
std::string BitmapFont::wrapText(const std::string& text, int maxWidth) std::string BitmapFont::wrapText(const std::string& text, int maxWidth)
{ {
std::string outText; std::string outText;
std::string line;
std::vector<std::string> words; std::vector<std::string> words;
std::vector<std::string> wordsSplit = stdext::split(text); std::vector<std::string> wordsSplit = stdext::split(text);
// break huge words into small ones // break huge words into small ones
for(const auto &word: wordsSplit) { for(const auto& word: wordsSplit) {
int wordWidth = calculateTextRectSize(word).width(); int wordWidth = calculateTextRectSize(word).width();
if(wordWidth > maxWidth) { if(wordWidth > maxWidth) {
std::string newWord; std::string newWord;
for(uint j=0;j<word.length();++j) { for(uint j = 0; j < word.length(); ++j) {
std::string candidate = newWord + word[j]; std::string candidate = newWord + word[j];
if(j != word.length() - 1) if(j != word.length() - 1)
candidate += "-"; candidate += '-';
int candidateWidth = calculateTextRectSize(candidate).width(); int candidateWidth = calculateTextRectSize(candidate).width();
if(candidateWidth > maxWidth) { if(candidateWidth > maxWidth) {
newWord += "-"; newWord += '-';
words.push_back(newWord); words.push_back(newWord);
newWord = ""; newWord.clear();
} }
newWord += word[j]; newWord += word[j];
} }
words.push_back(newWord); words.push_back(newWord);
} else } else {
words.push_back(word); words.push_back(word);
}
} }
// compose lines // compose lines
for(const auto &word: words) { std::string line(words[0]);
std::string candidate = line + word; for(ulong i = 1; i < words.size(); ++i)
int candidateWidth = calculateTextRectSize(candidate).width(); {
const auto& word = words[i];
if(candidateWidth > maxWidth) { line.push_back(' ');
if(!line.empty()) ulong lineSize = line.size();
outText += line.substr(0, line.length()-1) + "\n"; line.append(word);
line = "";
if(calculateTextRectSize(line).width() > maxWidth)
{
line.resize(lineSize);
line.back() = '\n';
outText.append(line);
line.assign(word);
} }
line += word + " ";
} }
outText.append(line);
outText += line;
outText = outText.substr(0, outText.length()-1);
return outText; return outText;
} }

View File

@ -55,7 +55,7 @@ public:
std::string wrapText(const std::string& text, int maxWidth); std::string wrapText(const std::string& text, int maxWidth);
std::string getName() { return m_name; } const std::string& getName() { return m_name; }
int getGlyphHeight() { return m_glyphHeight; } int getGlyphHeight() { return m_glyphHeight; }
const Rect* getGlyphsTextureCoords() { return m_glyphsTextureCoords; } const Rect* getGlyphsTextureCoords() { return m_glyphsTextureCoords; }
const Size* getGlyphsSize() { return m_glyphsSize; } const Size* getGlyphsSize() { return m_glyphsSize; }
@ -79,4 +79,3 @@ private:
#endif #endif

View File

@ -50,18 +50,16 @@ std::string date_time_string(const char* format/* = "%b %d %Y %H:%M:%S"*/)
char date[100]; char date[100];
std::time_t tnow; std::time_t tnow;
std::time(&tnow); std::time(&tnow);
std::tm *ts = std::localtime(&tnow); std::tm* ts = std::localtime(&tnow);
std::strftime(date, 100, format, ts); std::strftime(date, 100, format, ts);
return std::string(date); return std::string(date);
} }
std::string dec_to_hex(uint64_t num) std::string dec_to_hex(uint64_t num)
{ {
std::string str;
std::ostringstream o; std::ostringstream o;
o << std::hex << num; o << std::hex << num;
str = o.str(); return o.str();
return str;
} }
uint64_t hex_to_dec(const std::string& str) uint64_t hex_to_dec(const std::string& str)
@ -74,7 +72,7 @@ uint64_t hex_to_dec(const std::string& str)
bool is_valid_utf8(const std::string& src) bool is_valid_utf8(const std::string& src)
{ {
const unsigned char *bytes = (const unsigned char *)src.c_str(); const unsigned char* bytes = (const unsigned char*)src.c_str();
while(*bytes) { while(*bytes) {
if( (// ASCII if( (// ASCII
// use bytes[0] <= 0x7F to allow ASCII control characters // use bytes[0] <= 0x7F to allow ASCII control characters
@ -146,7 +144,7 @@ bool is_valid_utf8(const std::string& src)
std::string utf8_to_latin1(const std::string& src) std::string utf8_to_latin1(const std::string& src)
{ {
std::string out; std::string out;
for(uint i=0;i<src.length();) { for(uint i = 0; i < src.length();) {
uchar c = src[i++]; uchar c = src[i++];
if((c >= 32 && c < 128) || c == 0x0d || c == 0x0a || c == 0x09) if((c >= 32 && c < 128) || c == 0x0d || c == 0x0a || c == 0x09)
out += c; out += c;
@ -273,7 +271,7 @@ void replace_all(std::string& str, const std::string& search, const std::string&
std::vector<std::string> split(const std::string& str, const std::string& separators) std::vector<std::string> split(const std::string& str, const std::string& separators)
{ {
std::vector<std::string> splitted; std::vector<std::string> splitted;
boost::split(splitted, str, boost::is_any_of(std::string(separators))); boost::split(splitted, str, boost::is_any_of(separators));
return splitted; return splitted;
} }

View File

@ -63,11 +63,12 @@ std::string utf16_to_latin1(const std::wstring& src);
std::wstring latin1_to_utf16(const std::string& src); std::wstring latin1_to_utf16(const std::string& src);
#endif #endif
// always returns at least one element in vector
std::vector<std::string> split(const std::string& str, const std::string& separators = " "); std::vector<std::string> split(const std::string& str, const std::string& separators = " ");
template<typename T> std::vector<T> split(const std::string& str, const std::string& separators = " ") { template<typename T> std::vector<T> split(const std::string& str, const std::string& separators = " ") {
std::vector<std::string> splitted = split(str, separators); std::vector<std::string> splitted = split(str, separators);
std::vector<T> results(splitted.size()); std::vector<T> results(splitted.size());
for(uint i=0;i<splitted.size();++i) for(uint i = 0; i < splitted.size(); ++i)
results[i] = safe_cast<T>(splitted[i]); results[i] = safe_cast<T>(splitted[i]);
return results; return results;
} }

View File

@ -142,9 +142,9 @@ void UITextEdit::update(bool focusCursor)
// map glyphs positions // map glyphs positions
Size textBoxSize; Size textBoxSize;
const std::vector<Point>& glyphsPositions = m_font->calculateGlyphsPositions(text, m_textAlign, &textBoxSize); const auto& glyphsPositions = m_font->calculateGlyphsPositions(text, m_textAlign, &textBoxSize);
const Rect *glyphsTextureCoords = m_font->getGlyphsTextureCoords(); const Rect* glyphsTextureCoords = m_font->getGlyphsTextureCoords();
const Size *glyphsSize = m_font->getGlyphsSize(); const Size* glyphsSize = m_font->getGlyphsSize();
int glyph; int glyph;
// update rect size // update rect size

View File

@ -42,7 +42,7 @@ public:
void setCursorVisible(bool enable) { m_cursorVisible = enable; } void setCursorVisible(bool enable) { m_cursorVisible = enable; }
void setChangeCursorImage(bool enable) { m_changeCursorImage = enable; } void setChangeCursorImage(bool enable) { m_changeCursorImage = enable; }
void setTextHidden(bool hidden); void setTextHidden(bool hidden);
void setValidCharacters(const std::string validCharacters) { m_validCharacters = validCharacters; } void setValidCharacters(const std::string& validCharacters) { m_validCharacters = validCharacters; }
void setShiftNavigation(bool enable) { m_shiftNavigation = enable; } void setShiftNavigation(bool enable) { m_shiftNavigation = enable; }
void setMultiline(bool enable) { m_multiline = enable; } void setMultiline(bool enable) { m_multiline = enable; }
void setMaxLength(uint maxLength) { m_maxLength = maxLength; } void setMaxLength(uint maxLength) { m_maxLength = maxLength; }