aboutsummaryrefslogtreecommitdiff
path: root/src/Text.cpp
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-09-23 23:45:21 +0200
committerdec05eba <dec05eba@protonmail.com>2020-09-23 23:45:25 +0200
commit4690ba0cc66338b1f00e08fb6054ee95c1c0dcc6 (patch)
tree17f609546b85cb6b1243e53f66c27fd89ead91ef /src/Text.cpp
parent23dd37254cdf7479b88a7f1d711ecb5de92440e8 (diff)
Fallback to cjk font, change font to system noto sans
Diffstat (limited to 'src/Text.cpp')
-rw-r--r--src/Text.cpp200
1 files changed, 125 insertions, 75 deletions
diff --git a/src/Text.cpp b/src/Text.cpp
index 0517c15..bdd41c3 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -21,31 +21,12 @@ namespace QuickMedia
return -1;
}
- Text::Text(const sf::Font *_font) :
- font(_font),
- characterSize(0),
- vertices(sf::PrimitiveType::Quads),
- maxWidth(0.0f),
- color(sf::Color::White),
- urlColor(URL_COLOR),
- dirty(false),
- dirtyText(false),
- dirtyCaret(false),
- plainText(false),
- editable(false),
- visible(true),
- caretMoveDirection(CaretMoveDirection::NONE),
- lineSpacing(0.0f),
- characterSpacing(0.0f),
- caretIndex(0)
- {
-
- }
+ Text::Text(const sf::Font *_font, const sf::Font *_cjk_font) : Text("", _font, _cjk_font, 0, 0.0f, false) {}
- Text::Text(const sf::String &_str, const sf::Font *_font, unsigned int _characterSize, float _maxWidth, bool _plainText) :
+ Text::Text(sf::String _str, const sf::Font *_font, const sf::Font *_cjk_font, unsigned int _characterSize, float _maxWidth, bool _plainText) :
font(_font),
+ cjk_font(_cjk_font),
characterSize(_characterSize),
- vertices(sf::PrimitiveType::Quads),
maxWidth(_maxWidth),
color(sf::Color::White),
urlColor(URL_COLOR),
@@ -60,19 +41,21 @@ namespace QuickMedia
characterSpacing(0.0f),
caretIndex(0)
{
- setString(_str);
+ vertices[0].setPrimitiveType(sf::PrimitiveType::Quads);
+ vertices[1].setPrimitiveType(sf::PrimitiveType::Quads);
+ setString(std::move(_str));
}
- void Text::setString(const sf::String &str)
+ void Text::setString(sf::String str)
{
if(str != this->str)
{
- this->str = str;
+ this->str = std::move(str);
dirty = true;
dirtyText = true;
- if((int)str.getSize() < caretIndex)
+ if((int)this->str.getSize() < caretIndex)
{
- caretIndex = str.getSize();
+ caretIndex = this->str.getSize();
dirtyCaret = true;
}
}
@@ -177,22 +160,68 @@ namespace QuickMedia
{
return boundingBox.height;
}
+
+ // TODO: Is there a more efficient way to do this? maybe japanese characters have a specific bit-pattern?
+ static bool is_japanese_codepoint(sf::Uint32 codepoint) {
+ return (codepoint >= 0x2E80 && codepoint <= 0x2FD5) // Kanji radicals
+ || (codepoint >= 0x3000 && codepoint <= 0x303F) // Punctuation
+ || (codepoint >= 0x3041 && codepoint <= 0x3096) // Hiragana
+ || (codepoint >= 0x30A0 && codepoint <= 0x30FF) // Katakana
+ || (codepoint >= 0x31F0 && codepoint <= 0x31FF) // Miscellaneous symbols and characters 1
+ || (codepoint >= 0x3220 && codepoint <= 0x3243) // Miscellaneous symbols and characters 2
+ || (codepoint >= 0x3280 && codepoint <= 0x337F) // Miscellaneous symbols and characters 3
+ || (codepoint >= 0x3400 && codepoint <= 0x4DB5) // Kanji 1
+ || (codepoint >= 0x4E00 && codepoint <= 0x9FCB) // Kanji 2
+ || (codepoint >= 0xF900 && codepoint <= 0xFA6A) // Kanji 3
+ || (codepoint >= 0xFF01 && codepoint <= 0xFF5E) // Alphanumeric and punctuation (full width)
+ || (codepoint >= 0xFF5F && codepoint <= 0xFF9F); // Katakana and punctuation (half width)
+ }
+
+ static size_t find_end_of_japanese(const sf::Uint32 *str, size_t size) {
+ for(size_t i = 0; i < size; ++i) {
+ if(!is_japanese_codepoint(str[i]))
+ return i;
+ }
+ return size;
+ }
+
+ static size_t find_end_of_non_japanese(const sf::Uint32 *str, size_t size) {
+ for(size_t i = 0; i < size; ++i) {
+ if(is_japanese_codepoint(str[i]))
+ return i;
+ }
+ return size;
+ }
+
+ void Text::splitTextByFont() {
+ textElements.clear();
+ size_t index = 0;
+ size_t size = str.getSize();
+ while(index < size) {
+ size_t offset;
+ bool is_japanese = is_japanese_codepoint(str[index]);
+ if(is_japanese)
+ offset = find_end_of_japanese(str.getData() + index + 1, size - index - 1);
+ else
+ offset = find_end_of_non_japanese(str.getData() + index + 1, size - index - 1);
+ textElements.push_back({ StringViewUtf32(str.getData() + index, offset + 1), TextElement::Type::TEXT });
+ textElements.back().is_japanese = is_japanese;
+ index += 1 + offset;
+ }
+ }
// Logic loosely based on https://github.com/SFML/SFML/wiki/Source:-CurvedText
- void Text::updateGeometry(bool update_even_if_not_dirty)
- {
- if(dirtyText)
- {
- textElements.clear();
- StringViewUtf32 wholeStr(this->str.getData(), this->str.getSize());
- textElements.push_back({ wholeStr, TextElement::Type::TEXT });
+ void Text::updateGeometry(bool update_even_if_not_dirty) {
+ if(dirtyText) {
dirtyText = false;
+ splitTextByFont();
}
if(!update_even_if_not_dirty && !dirty)
return;
- vertices.clear();
+ vertices[0].clear();
+ vertices[1].clear();
float hspace = font->getGlyph(' ', characterSize, false).advance + characterSpacing;
float vspace = font->getLineSpacing(characterSize);
@@ -205,14 +234,20 @@ namespace QuickMedia
for(usize textElementIndex = 0; textElementIndex < textElements.size(); ++textElementIndex)
{
TextElement &textElement = textElements[textElementIndex];
+ const sf::Font *ff = font;
+ size_t vertices_index = 0;
+ if(textElement.is_japanese) {
+ ff = cjk_font;
+ vertices_index = 1;
+ }
- usize vertexOffset = vertices.getVertexCount();
- vertices.resize(vertices.getVertexCount() + 4 * (textElement.text.size + 1));
+ usize vertexOffset = vertices[vertices_index].getVertexCount();
+ vertices[vertices_index].resize(vertices[vertices_index].getVertexCount() + 4 * (textElement.text.size + 1));
textElement.position = glyphPos;
for(size_t i = 0; i < textElement.text.size; ++i)
{
sf::Uint32 codePoint = textElement.text[i];
- float kerning = font->getKerning(prevCodePoint, codePoint, characterSize);
+ float kerning = ff->getKerning(prevCodePoint, codePoint, characterSize);
prevCodePoint = codePoint;
glyphPos.x += kerning;
@@ -222,10 +257,10 @@ namespace QuickMedia
{
case ' ':
{
- vertices[vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertexStart + 1] = { sf::Vector2f(glyphPos.x + hspace, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertexStart + 2] = { sf::Vector2f(glyphPos.x + hspace, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 1] = { sf::Vector2f(glyphPos.x + hspace, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(glyphPos.x + hspace, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
glyphPos.x += hspace;
if(glyphPos.x > maxWidth * 0.5f)
{
@@ -236,10 +271,10 @@ namespace QuickMedia
}
case '\t':
{
- vertices[vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertexStart + 1] = { sf::Vector2f(glyphPos.x + hspace * TAB_WIDTH, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertexStart + 2] = { sf::Vector2f(glyphPos.x + hspace * TAB_WIDTH, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 1] = { sf::Vector2f(glyphPos.x + hspace * TAB_WIDTH, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(glyphPos.x + hspace * TAB_WIDTH, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 3] = { sf::Vector2f(glyphPos.x, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
glyphPos.x += (hspace * TAB_WIDTH);
if(glyphPos.x > maxWidth * 0.5f)
{
@@ -250,17 +285,17 @@ namespace QuickMedia
}
case '\n':
{
- vertices[vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertexStart + 1] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertexStart + 2] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertexStart + 3] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 0] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 1] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 2] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertexStart + 3] = { sf::Vector2f(0.0f, glyphPos.y), sf::Color::Transparent, sf::Vector2f() };
glyphPos.x = 0.0f;
glyphPos.y += floor(vspace + lineSpacing);
continue;
}
}
- const sf::Glyph &glyph = font->getGlyph(codePoint, characterSize, false);
+ const sf::Glyph &glyph = ff->getGlyph(codePoint, characterSize, false);
if(glyphPos.x + glyph.advance > maxWidth)
{
// If there was a space in the text and text width is too long, then we need to word wrap at space index instead,
@@ -271,7 +306,7 @@ namespace QuickMedia
{
for(size_t k = 0; k < 4; ++k)
{
- sf::Vector2f &vertexPos = vertices[vertexOffset + j * 4 + k].position;
+ sf::Vector2f &vertexPos = vertices[vertices_index][vertexOffset + j * 4 + k].position;
vertexPos.x -= lastSpacingAccumulatedOffset;
vertexPos.y += floor(vspace + lineSpacing);
}
@@ -299,18 +334,18 @@ namespace QuickMedia
sf::Color fontColor = (textElement.type == TextElement::Type::TEXT ? color : urlColor);
- vertices[vertexStart + 0] = { vertexTopLeft, fontColor, textureTopLeft };
- vertices[vertexStart + 1] = { vertexTopRight, fontColor, textureTopRight };
- vertices[vertexStart + 2] = { vertexBottomRight, fontColor, textureBottomRight };
- vertices[vertexStart + 3] = { vertexBottomLeft, fontColor, textureBottomLeft };
+ vertices[vertices_index][vertexStart + 0] = { vertexTopLeft, fontColor, textureTopLeft };
+ vertices[vertices_index][vertexStart + 1] = { vertexTopRight, fontColor, textureTopRight };
+ vertices[vertices_index][vertexStart + 2] = { vertexBottomRight, fontColor, textureBottomRight };
+ vertices[vertices_index][vertexStart + 3] = { vertexBottomLeft, fontColor, textureBottomLeft };
glyphPos.x += glyph.advance + characterSpacing;
}
- vertices[vertices.getVertexCount() - 4] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices.getVertexCount() - 3] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices.getVertexCount() - 2] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
- vertices[vertices.getVertexCount() - 1] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertices[vertices_index].getVertexCount() - 4] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertices[vertices_index].getVertexCount() - 3] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertices[vertices_index].getVertexCount() - 2] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
+ vertices[vertices_index][vertices[vertices_index].getVertexCount() - 1] = { sf::Vector2f(glyphPos.x, glyphPos.y - vspace), sf::Color::Transparent, sf::Vector2f() };
prevCodePoint = 0;
}
@@ -318,16 +353,19 @@ namespace QuickMedia
boundingBox.height = glyphPos.y + lineSpacing;
boundingBox.height += vspace;
- usize numVertices = vertices.getVertexCount();
- for(usize i = 0; i < numVertices; i += 4)
- {
- const sf::Vertex &bottomRight = vertices[i + 2];
- boundingBox.width = std::max(boundingBox.width, bottomRight.position.x);
+ for(size_t vertices_index = 0; vertices_index < 2; ++vertices_index) {
+ usize numVertices = vertices[vertices_index].getVertexCount();
+ for(usize i = 0; i < numVertices; i += 4)
+ {
+ const sf::Vertex &bottomRight = vertices[vertices_index][i + 2];
+ boundingBox.width = std::max(boundingBox.width, bottomRight.position.x);
+ }
}
dirty = false;
}
+#if 0
void Text::updateCaret()
{
assert(!dirty && !dirtyText);
@@ -339,6 +377,7 @@ namespace QuickMedia
return;
}
+
switch(caretMoveDirection)
{
case CaretMoveDirection::UP:
@@ -380,13 +419,13 @@ namespace QuickMedia
caretPosition = topLeftVertex.position;
}
}
-
+
bool Text::isCaretAtEnd() const
{
assert(!dirty && !dirtyText);
return textElements[0].text.size == 0 || caretIndex == (int)textElements[0].text.size;
}
-
+
// TODO: This can be optimized by using binary search
int Text::getStartOfLine(int startIndex) const
{
@@ -604,21 +643,22 @@ namespace QuickMedia
dirtyCaret = true;
}
}
-
+#endif
bool Text::draw(sf::RenderTarget &target)
{
updateGeometry();
+#if 0
if(dirtyCaret || caretMoveDirection != CaretMoveDirection::NONE)
{
updateCaret();
dirtyCaret = false;
caretMoveDirection = CaretMoveDirection::NONE;
}
+#endif
float vspace = font->getLineSpacing(characterSize);
- sf::RenderStates states;
sf::Vector2f pos = position;
pos.y += floor(vspace); // Origin is at bottom left, we want it to be at top left
@@ -633,22 +673,29 @@ namespace QuickMedia
if(!editable && visible && lastSeenTimer.getElapsedTime().asMilliseconds() > 3000)
{
visible = false;
- vertices.resize(0);
+ vertices[0].resize(0);
+ vertices[1].resize(0);
}
return false;
}
- if(!visible)
+ if(!visible) {
+ visible = true;
updateGeometry(true);
+ }
- states.transform.translate(pos);
- states.texture = &font->getTexture(characterSize);
- target.draw(vertices, states);
+ const sf::Font *fonts[] = { font, cjk_font };
+ for(size_t i = 0; i < 2; ++i) {
+ sf::RenderStates states;
+ states.transform.translate(pos);
+ states.texture = &fonts[i]->getTexture(characterSize);
+ target.draw(vertices[i], states);
+ }
lastSeenTimer.restart();
- visible = true;
pos.y -= floor(vspace);
+#if 0
if(!editable) return true;
//float rows = floor(totalHeight / (vspace + lineSpacing));
@@ -659,5 +706,8 @@ namespace QuickMedia
caretRect.setPosition(sf::Vector2f(floor(pos.x + caretPosition.x), floor(pos.y + caretRow * (vspace + lineSpacing))));
target.draw(caretRect);
return true;
+#else
+ return true;
+#endif
}
}