Implement StringUtil which has stolen some functions from Font and added unicode2Chars

This commit is contained in:
Tomas Jakobsson 2017-11-10 19:48:23 +01:00
parent 8da400aa84
commit 232f36f866
5 changed files with 160 additions and 154 deletions

136
es-core/src/StringUtil.h Normal file
View file

@ -0,0 +1,136 @@
#pragma once
#ifndef ES_CORE_STRING_UTIL_H
#define ES_CORE_STRING_UTIL_H
namespace StringUtil
{
inline unsigned int chars2Unicode(const std::string& _string, size_t& _cursor)
{
const char& c = _string[_cursor];
unsigned int result = '?';
if((c & 0x80) == 0) // 0xxxxxxx, one byte character
{
// 0xxxxxxx
result = ((_string[_cursor++] ) );
}
else if((c & 0xE0) == 0xC0) // 110xxxxx, two byte character
{
// 110xxxxx 10xxxxxx
result = ((_string[_cursor++] & 0x1F) << 6) |
((_string[_cursor++] & 0x3F) );
}
else if((c & 0xF0) == 0xE0) // 1110xxxx, three byte character
{
// 1110xxxx 10xxxxxx 10xxxxxx
result = ((_string[_cursor++] & 0x0F) << 12) |
((_string[_cursor++] & 0x3F) << 6) |
((_string[_cursor++] & 0x3F) );
}
else if((c & 0xF8) == 0xF0) // 11110xxx, four byte character
{
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
result = ((_string[_cursor++] & 0x07) << 18) |
((_string[_cursor++] & 0x3F) << 12) |
((_string[_cursor++] & 0x3F) << 6) |
((_string[_cursor++] & 0x3F) );
}
else
{
// error, invalid unicode
++_cursor;
}
return result;
} // chars2Unicode
inline std::string unicode2Chars(const unsigned int _unicode)
{
std::string result;
if(_unicode < 0x80) // one byte character
{
result += ((_unicode ) );
}
else if(_unicode < 0x800) // two byte character
{
result += ((_unicode >> 6) ) | 0xC0;
result += ((_unicode ) & 0x3F) | 0x80;
}
else if(_unicode < 0xFFFF) // three byte character
{
result += ((_unicode >> 12) ) | 0xE0;
result += ((_unicode >> 6) & 0x3F) | 0x80;
result += ((_unicode ) & 0x3F) | 0x80;
}
else if(_unicode <= 0x1fffff) // four byte character
{
result += ((_unicode >> 18) ) | 0xF0;
result += ((_unicode >> 12) & 0x3F) | 0x80;
result += ((_unicode >> 6) & 0x3F) | 0x80;
result += ((_unicode ) & 0x3F) | 0x80;
}
else
{
// error, invalid unicode
result += '?';
}
return result;
} // unicode2Chars
inline size_t nextCursor(const std::string& _string, const size_t _cursor)
{
size_t result = _cursor;
while(result < _string.length())
{
++result;
if((_string[result] & 0xC0) != 0x80) // break if current character is not 10xxxxxx
break;
}
return result;
} // nextCursor
inline size_t prevCursor(const std::string& _string, const size_t _cursor)
{
size_t result = _cursor;
while(result > 0)
{
--result;
if((_string[result] & 0xC0) != 0x80) // break if current character is not 10xxxxxx
break;
}
return result;
} // prevCursor
inline size_t moveCursor(const std::string& _string, const size_t _cursor, const int _amount)
{
size_t result = _cursor;
if(_amount > 0)
{
for(int i = 0; i < _amount; ++i)
result = nextCursor(_string, result);
}
else if(_amount < 0)
{
for(int i = _amount; i < 0; ++i)
result = prevCursor(_string, result);
}
return result;
} // moveCursor
}
#endif // ES_CORE_STRING_UTIL_H

View file

@ -3,6 +3,7 @@
#include "Log.h" #include "Log.h"
#include "Renderer.h" #include "Renderer.h"
#include "Settings.h" #include "Settings.h"
#include "StringUtil.h"
#include "Util.h" #include "Util.h"
TextComponent::TextComponent(Window* window) : GuiComponent(window), TextComponent::TextComponent(Window* window) : GuiComponent(window),
@ -197,7 +198,7 @@ void TextComponent::onTextChanged()
while(text.size() && size.x() + abbrevSize.x() > mSize.x()) while(text.size() && size.x() + abbrevSize.x() > mSize.x())
{ {
size_t newSize = Font::getPrevCursor(text, text.size()); size_t newSize = StringUtil::prevCursor(text, text.size());
text.erase(newSize, text.size() - newSize); text.erase(newSize, text.size() - newSize);
size = f->sizeText(text); size = f->sizeText(text);
} }

View file

@ -2,6 +2,7 @@
#include "resources/Font.h" #include "resources/Font.h"
#include "Renderer.h" #include "Renderer.h"
#include "StringUtil.h"
#define TEXT_PADDING_HORIZ 10 #define TEXT_PADDING_HORIZ 10
#define TEXT_PADDING_VERT 2 #define TEXT_PADDING_VERT 2
@ -59,7 +60,7 @@ void TextEditComponent::textInput(const char* text)
{ {
if(mCursor > 0) if(mCursor > 0)
{ {
size_t newCursor = Font::getPrevCursor(mText, mCursor); size_t newCursor = StringUtil::prevCursor(mText, mCursor);
mText.erase(mText.begin() + newCursor, mText.begin() + mCursor); mText.erase(mText.begin() + newCursor, mText.begin() + mCursor);
mCursor = newCursor; mCursor = newCursor;
} }
@ -190,7 +191,7 @@ void TextEditComponent::updateCursorRepeat(int deltaTime)
void TextEditComponent::moveCursor(int amt) void TextEditComponent::moveCursor(int amt)
{ {
mCursor = Font::moveCursor(mText, mCursor, amt); mCursor = StringUtil::moveCursor(mText, mCursor, amt);
onCursorChanged(); onCursorChanged();
} }

View file

@ -2,6 +2,7 @@
#include "Log.h" #include "Log.h"
#include "Renderer.h" #include "Renderer.h"
#include "StringUtil.h"
#include "Util.h" #include "Util.h"
FT_Library Font::sLibrary = NULL; FT_Library Font::sLibrary = NULL;
@ -10,130 +11,6 @@ int Font::getSize() const { return mSize; }
std::map< std::pair<std::string, int>, std::weak_ptr<Font> > Font::sFontMap; std::map< std::pair<std::string, int>, std::weak_ptr<Font> > Font::sFontMap;
// utf8 stuff
size_t Font::getNextCursor(const std::string& str, size_t cursor)
{
// compare to character at the cursor
const char& c = str[cursor];
size_t result = cursor;
if((c & 0x80) == 0) // 0xxxxxxx, one byte character
{
result += 1;
}
else if((c & 0xE0) == 0xC0) // 110xxxxx, two bytes left in character
{
result += 2;
}
else if((c & 0xF0) == 0xE0) // 1110xxxx, three bytes left in character
{
result += 3;
}
else if((c & 0xF8) == 0xF0) // 11110xxx, four bytes left in character
{
result += 4;
}
else
{
// error, invalid utf8 string
// if this assert is tripped, the cursor is in the middle of a utf8 code point
assert((c & 0xC0) != 0x80); // character is 10xxxxxx
// if that wasn't it, something crazy happened
assert(false);
}
if(str.length() < result || result < cursor) // don't go beyond the very end of the string, try and catch overflow
return cursor;
return result;
}
// note: will happily accept malformed utf8
size_t Font::getPrevCursor(const std::string& str, size_t cursor)
{
if(cursor == 0)
return 0;
do
{
cursor--;
} while(cursor > 0 &&
(str[cursor] & 0xC0) == 0x80); // character is 10xxxxxx
return cursor;
}
size_t Font::moveCursor(const std::string& str, size_t cursor, int amt)
{
if(amt > 0)
{
for(int i = 0; i < amt; i++)
cursor = Font::getNextCursor(str, cursor);
}
else if(amt < 0)
{
for(int i = amt; i < 0; i++)
cursor = Font::getPrevCursor(str, cursor);
}
return cursor;
}
UnicodeChar Font::readUnicodeChar(const std::string& str, size_t& cursor)
{
const char& c = str[cursor];
if((c & 0x80) == 0) // 0xxxxxxx, one byte character
{
// 0xxxxxxx
cursor++;
return (UnicodeChar)c;
}
else if((c & 0xE0) == 0xC0) // 110xxxxx, two bytes left in character
{
// 110xxxxx 10xxxxxx
UnicodeChar val = ((str[cursor] & 0x1F) << 6) |
(str[cursor + 1] & 0x3F);
cursor += 2;
return val;
}
else if((c & 0xF0) == 0xE0) // 1110xxxx, three bytes left in character
{
// 1110xxxx 10xxxxxx 10xxxxxx
UnicodeChar val = ((str[cursor] & 0x0F) << 12) |
((str[cursor + 1] & 0x3F) << 6) |
(str[cursor + 2] & 0x3F);
cursor += 3;
return val;
}
else if((c & 0xF8) == 0xF0) // 11110xxx, four bytes left in character
{
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UnicodeChar val = ((str[cursor] & 0x07) << 18) |
((str[cursor + 1] & 0x3F) << 12) |
((str[cursor + 2] & 0x3F) << 6) |
(str[cursor + 3] & 0x3F);
cursor += 4;
return val;
}
else
{
// error, invalid utf8 string
// if this assert is tripped, the cursor is in the middle of a utf8 code point
assert((c & 0xC0) != 0x80); // character is 10xxxxxx
// if that wasn't it, something crazy happened
assert(false);
}
// error
return 0;
}
Font::FontFace::FontFace(ResourceData&& d, int size) : data(d) Font::FontFace::FontFace(ResourceData&& d, int size) : data(d)
{ {
int err = FT_New_Memory_Face(sLibrary, data.ptr.get(), data.length, 0, &face); int err = FT_New_Memory_Face(sLibrary, data.ptr.get(), data.length, 0, &face);
@ -201,7 +78,7 @@ Font::Font(int size, const std::string& path) : mSize(size), mPath(path)
initLibrary(); initLibrary();
// always initialize ASCII characters // always initialize ASCII characters
for(UnicodeChar i = 32; i < 128; i++) for(unsigned int i = 32; i < 128; i++)
getGlyph(i); getGlyph(i);
clearFaceCache(); clearFaceCache();
@ -398,7 +275,7 @@ std::vector<std::string> getFallbackFontPaths()
#endif #endif
} }
FT_Face Font::getFaceForChar(UnicodeChar id) FT_Face Font::getFaceForChar(unsigned int id)
{ {
static const std::vector<std::string> fallbackFonts = getFallbackFontPaths(); static const std::vector<std::string> fallbackFonts = getFallbackFontPaths();
@ -430,7 +307,7 @@ void Font::clearFaceCache()
mFaceCache.clear(); mFaceCache.clear();
} }
Font::Glyph* Font::getGlyph(UnicodeChar id) Font::Glyph* Font::getGlyph(unsigned int id)
{ {
// is it already loaded? // is it already loaded?
auto it = mGlyphMap.find(id); auto it = mGlyphMap.find(id);
@ -571,9 +448,9 @@ Vector2f Font::sizeText(std::string text, float lineSpacing)
size_t i = 0; size_t i = 0;
while(i < text.length()) while(i < text.length())
{ {
UnicodeChar character = readUnicodeChar(text, i); // advances i unsigned int character = StringUtil::chars2Unicode(text, i); // advances i
if(character == (UnicodeChar)'\n') if(character == '\n')
{ {
if(lineWidth > highestWidth) if(lineWidth > highestWidth)
highestWidth = lineWidth; highestWidth = lineWidth;
@ -600,7 +477,7 @@ float Font::getHeight(float lineSpacing) const
float Font::getLetterHeight() float Font::getLetterHeight()
{ {
Glyph* glyph = getGlyph((UnicodeChar)'S'); Glyph* glyph = getGlyph('S');
assert(glyph); assert(glyph);
return glyph->texSize.y() * glyph->texture->textureSize.y(); return glyph->texSize.y() * glyph->texture->textureSize.y();
} }
@ -664,21 +541,21 @@ Vector2f Font::getWrappedTextCursorOffset(std::string text, float xLen, size_t s
size_t cursor = 0; size_t cursor = 0;
while(cursor < stop) while(cursor < stop)
{ {
UnicodeChar wrappedCharacter = readUnicodeChar(wrappedText, wrapCursor); unsigned int wrappedCharacter = StringUtil::chars2Unicode(wrappedText, wrapCursor);
UnicodeChar character = readUnicodeChar(text, cursor); unsigned int character = StringUtil::chars2Unicode(text, cursor);
if(wrappedCharacter == (UnicodeChar)'\n' && character != (UnicodeChar)'\n') if(wrappedCharacter == '\n' && character != '\n')
{ {
//this is where the wordwrap inserted a newline //this is where the wordwrap inserted a newline
//reset lineWidth and increment y, but don't consume a cursor character //reset lineWidth and increment y, but don't consume a cursor character
lineWidth = 0.0f; lineWidth = 0.0f;
y += getHeight(lineSpacing); y += getHeight(lineSpacing);
cursor = getPrevCursor(text, cursor); // unconsume cursor = StringUtil::prevCursor(text, cursor); // unconsume
continue; continue;
} }
if(character == (UnicodeChar)'\n') if(character == '\n')
{ {
lineWidth = 0.0f; lineWidth = 0.0f;
y += getHeight(lineSpacing); y += getHeight(lineSpacing);
@ -727,7 +604,7 @@ TextCache* Font::buildTextCache(const std::string& text, Vector2f offset, unsign
{ {
float x = offset[0] + (xLen != 0 ? getNewlineStartOffset(text, 0, xLen, alignment) : 0); float x = offset[0] + (xLen != 0 ? getNewlineStartOffset(text, 0, xLen, alignment) : 0);
float yTop = getGlyph((UnicodeChar)'S')->bearing.y(); float yTop = getGlyph('S')->bearing.y();
float yBot = getHeight(lineSpacing); float yBot = getHeight(lineSpacing);
float y = offset[1] + (yBot + yTop)/2.0f; float y = offset[1] + (yBot + yTop)/2.0f;
@ -735,17 +612,16 @@ TextCache* Font::buildTextCache(const std::string& text, Vector2f offset, unsign
std::map< FontTexture*, std::vector<TextCache::Vertex> > vertMap; std::map< FontTexture*, std::vector<TextCache::Vertex> > vertMap;
size_t cursor = 0; size_t cursor = 0;
UnicodeChar character;
Glyph* glyph;
while(cursor < text.length()) while(cursor < text.length())
{ {
character = readUnicodeChar(text, cursor); // also advances cursor unsigned int character = StringUtil::chars2Unicode(text, cursor); // also advances cursor
Glyph* glyph;
// invalid character // invalid character
if(character == 0) if(character == 0)
continue; continue;
if(character == (UnicodeChar)'\n') if(character == '\n')
{ {
y += getHeight(lineSpacing); y += getHeight(lineSpacing);
x = offset[0] + (xLen != 0 ? getNewlineStartOffset(text, cursor /* cursor is already advanced */, xLen, alignment) : 0); x = offset[0] + (xLen != 0 ? getNewlineStartOffset(text, cursor /* cursor is already advanced */, xLen, alignment) : 0);

View file

@ -21,8 +21,6 @@ class TextCache;
#define FONT_PATH_LIGHT ":/opensans_hebrew_condensed_light.ttf" #define FONT_PATH_LIGHT ":/opensans_hebrew_condensed_light.ttf"
#define FONT_PATH_REGULAR ":/opensans_hebrew_condensed_regular.ttf" #define FONT_PATH_REGULAR ":/opensans_hebrew_condensed_regular.ttf"
typedef unsigned long UnicodeChar;
enum Alignment enum Alignment
{ {
ALIGN_LEFT, ALIGN_LEFT,
@ -68,12 +66,6 @@ public:
size_t getMemUsage() const; // returns an approximation of VRAM used by this font's texture (in bytes) size_t getMemUsage() const; // returns an approximation of VRAM used by this font's texture (in bytes)
static size_t getTotalMemUsage(); // returns an approximation of total VRAM used by font textures (in bytes) static size_t getTotalMemUsage(); // returns an approximation of total VRAM used by font textures (in bytes)
// utf8 stuff
static size_t getNextCursor(const std::string& str, size_t cursor);
static size_t getPrevCursor(const std::string& str, size_t cursor);
static size_t moveCursor(const std::string& str, size_t cursor, int moveAmt); // negative moveAmt = move backwards, positive = move forwards
static UnicodeChar readUnicodeChar(const std::string& str, size_t& cursor); // reads unicode character at cursor AND moves cursor to the next valid unicode char
private: private:
static FT_Library sLibrary; static FT_Library sLibrary;
static std::map< std::pair<std::string, int>, std::weak_ptr<Font> > sFontMap; static std::map< std::pair<std::string, int>, std::weak_ptr<Font> > sFontMap;
@ -114,7 +106,7 @@ private:
void getTextureForNewGlyph(const Vector2i& glyphSize, FontTexture*& tex_out, Vector2i& cursor_out); void getTextureForNewGlyph(const Vector2i& glyphSize, FontTexture*& tex_out, Vector2i& cursor_out);
std::map< unsigned int, std::unique_ptr<FontFace> > mFaceCache; std::map< unsigned int, std::unique_ptr<FontFace> > mFaceCache;
FT_Face getFaceForChar(UnicodeChar id); FT_Face getFaceForChar(unsigned int id);
void clearFaceCache(); void clearFaceCache();
struct Glyph struct Glyph
@ -128,9 +120,9 @@ private:
Vector2f bearing; Vector2f bearing;
}; };
std::map<UnicodeChar, Glyph> mGlyphMap; std::map<unsigned int, Glyph> mGlyphMap;
Glyph* getGlyph(UnicodeChar id); Glyph* getGlyph(unsigned int id);
int mMaxGlyphHeight; int mMaxGlyphHeight;