From bbd9dfc4a9618d04247fe18d1c96d64d5c36af78 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Fri, 13 Sep 2019 21:36:14 +0200 Subject: [PATCH] implement word wrap in text node --- src/font.cpp | 92 ++++++++++++++++++++++++++++++++++++++-------------- src/font.h | 7 ++++ src/util.cpp | 17 ---------- src/util.h | 19 ++++++++++- 4 files changed, 92 insertions(+), 43 deletions(-) diff --git a/src/font.cpp b/src/font.cpp index e5d4a59..d4dfc1d 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -72,6 +72,42 @@ void FontManager::change_scale(float scale) f.second.change_scale(scale); } +std::vector TextMesh::tokenize(const std::string& s, const Font& f) const noexcept +{ + std::vector parts; + std::array delims = { ' ', '\n', ',', '.', ':', '?', '!' }; + std::string tmp; + bool wrap = false; + for (char c : s) + { + bool is_delim = std::find(delims.begin(), delims.end(), c) != delims.end(); + wrap |= is_delim; + if (wrap && !is_delim) + { + parts.push_back(tmp); + tmp.clear(); + wrap = false; + } + tmp.push_back(c); + } + if (!tmp.empty()) + parts.push_back(tmp); + + std::vector ret; + for (auto p : parts) + { + float x = 0; + float y = 0; + for (char c : p) + { + stbtt_aligned_quad q; + stbtt_GetBakedQuad((stbtt_bakedchar*)f.chars.data(), f.w, f.h, c - f.start_char, &x, &y, &q, true); + } + ret.emplace_back(p, x * f.scale); + } + return ret; +} + bool TextMesh::create() { App::I->render_task([this] @@ -98,42 +134,48 @@ void TextMesh::update(kFont id, const char* text) auto& f = FontManager::get(id); if (f.chars.size()) { - const auto len = strlen(text); float x = 0; float y = 0; std::vector v; std::vector idx; glm::vec2 bbmin(FLT_MAX); glm::vec2 bbmax(-FLT_MAX); - for (int i = 0; i < len; i++) + + std::vector parts = max_width > 0 ? + tokenize(text, f) : + std::vector{ Token(text, 0.f) }; + + for (auto p : parts) { - if (text[i] == '\n') - { - x = 0; - y += f.size * f.scale; - continue; - } - if (max_width > 0 && x > max_width * f.scale /*font scale factor*/) + if (max_width > 0 && x + p.w > max_width * f.scale /*font scale factor*/) { x = 0; y += f.size * f.scale; } - int c = text[i] - f.start_char; - stbtt_aligned_quad q; - stbtt_GetBakedQuad((stbtt_bakedchar*)f.chars.data(), f.w, f.h, c, &x, &y, &q, true); - auto n = (int)v.size(); - v.emplace_back(q.x0/f.scale, q.y1/f.scale, q.s0, q.t1); - v.emplace_back(q.x0/f.scale, q.y0/f.scale, q.s0, q.t0); - v.emplace_back(q.x1/f.scale, q.y0/f.scale, q.s1, q.t0); - v.emplace_back(q.x1/f.scale, q.y1/f.scale, q.s1, q.t1); - idx.push_back(n+0); - idx.push_back(n+1); - idx.push_back(n+2); - idx.push_back(n+0); - idx.push_back(n+2); - idx.push_back(n+3); - bbmin = glm::min(bbmin, { q.x0/f.scale, q.y0/f.scale }); - bbmax = glm::max(bbmax, { q.x1/f.scale, q.y1/f.scale }); + for (char c : p.s) + { + if (c == '\n') + { + x = 0; + y += f.size * f.scale; + continue; + } + stbtt_aligned_quad q; + stbtt_GetBakedQuad((stbtt_bakedchar*)f.chars.data(), f.w, f.h, c - f.start_char, &x, &y, &q, true); + auto n = (int)v.size(); + v.emplace_back(q.x0 / f.scale, q.y1 / f.scale, q.s0, q.t1); + v.emplace_back(q.x0 / f.scale, q.y0 / f.scale, q.s0, q.t0); + v.emplace_back(q.x1 / f.scale, q.y0 / f.scale, q.s1, q.t0); + v.emplace_back(q.x1 / f.scale, q.y1 / f.scale, q.s1, q.t1); + idx.push_back(n + 0); + idx.push_back(n + 1); + idx.push_back(n + 2); + idx.push_back(n + 0); + idx.push_back(n + 2); + idx.push_back(n + 3); + bbmin = glm::min(bbmin, { q.x0 / f.scale, q.y0 / f.scale }); + bbmax = glm::max(bbmax, { q.x1 / f.scale, q.y1 / f.scale }); + } } for (auto& vi : v) vi -= glm::vec4(bbmin, 0, 0); diff --git a/src/font.h b/src/font.h index 9b9f7ad..f411d36 100644 --- a/src/font.h +++ b/src/font.h @@ -44,6 +44,13 @@ public: class TextMesh { + struct Token + { + std::string s; + float w; + Token(const std::string& str, float width) : s(str), w(width) {} + }; + std::vector tokenize(const std::string& s, const Font& f) const noexcept; public: GLuint font_array = 0; int font_array_count = 0; diff --git a/src/util.cpp b/src/util.cpp index ace842c..bdb944f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -544,23 +544,6 @@ glm::vec3 convert_rgb2hsv(const glm::vec3 c) return glm::vec3(fabs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } -std::vector split(const std::string& subject, char d, int max_split/* = 0*/) -{ - std::vector ret; - size_t start = 0; - size_t n = subject.find_first_of(d); - while (n != std::string::npos) - { - ret.push_back(subject.substr(start, n - start)); - start = n + 1; - if (max_split && ret.size() == max_split) - break; - n = subject.find_first_of(d, start); - } - ret.push_back(subject.substr(start)); - return ret; -} - std::string unescape(const std::string& s) { std::string res; diff --git a/src/util.h b/src/util.h index 1e64509..e4ddeb3 100644 --- a/src/util.h +++ b/src/util.h @@ -45,6 +45,24 @@ std::vector poly_remove_duplicate(const std::vector& v, const float toller template<> std::vector poly_remove_duplicate(const std::vector& v, const float tollerance); +template +std::vector split(const std::string& subject, T d, int max_split = 0) +{ + std::vector ret; + size_t start = 0; + size_t n = subject.find_first_of(d); + while (n != std::string::npos) + { + ret.push_back(subject.substr(start, n - start)); + start = n + 1; + if (max_split && ret.size() == max_split) + break; + n = subject.find_first_of(d, start); + } + ret.push_back(subject.substr(start)); + return ret; +} + // from {origin, size} to {min, max} glm::vec4 rect_to_box(const glm::vec4& rect); // from {min, max} to {origin, size} @@ -88,7 +106,6 @@ uint32_t convert_rgb_long(glm::vec3 rgb); glm::vec3 convert_hsv2rgb(const glm::vec3 c); glm::vec3 convert_rgb2hsv(const glm::vec3 c); -std::vector split(const std::string& subject, char d, int max_split = 0); std::string unescape(const std::string& s); std::wstring str2wstr(const std::string& str); std::string wstr2str(const std::wstring& wstr);