Skip to content

Commit a9e2074

Browse files
committed
Removing deprecated wstring_convert
1 parent 8281031 commit a9e2074

File tree

5 files changed

+193
-32
lines changed

5 files changed

+193
-32
lines changed

engine/core/subsystem/UISystem.cpp

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
#include "UISystem.h"
66

7-
#include <locale>
8-
#include <codecvt>
97
#include <algorithm>
108
#include "Scene.h"
119
#include "Input.h"
@@ -1501,10 +1499,7 @@ bool UISystem::eventOnCharInput(wchar_t codepoint){
15011499
TextComponent& text = scene->getComponent<TextComponent>(textedit.text);
15021500
if (codepoint == '\b'){
15031501
if (text.text.length() > 0){
1504-
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t> > convert;
1505-
std::wstring utf16OldText = convert.from_bytes(text.text);
1506-
1507-
text.text = convert.to_bytes(utf16OldText.substr(0, utf16OldText.size() - 1));
1502+
StringUtils::eraseLastCodepointUtf8(text.text);
15081503
}
15091504
}else{
15101505
text.text = text.text + StringUtils::toUTF8(codepoint);
@@ -1590,10 +1585,9 @@ bool UISystem::eventOnPointerDown(float x, float y){
15901585
TextEditComponent& textedit = scene->getComponent<TextEditComponent>(lastUIFromPointer);
15911586
TextComponent& text = scene->getComponent<TextComponent>(textedit.text);
15921587

1593-
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;
1594-
std::wstring utf16Text = convert.from_bytes(text.text);
1595-
1596-
System::instance().showVirtualKeyboard(utf16Text);
1588+
bool hadInvalid = false;
1589+
std::wstring wideText = StringUtils::utf8ToWString(text.text, hadInvalid);
1590+
System::instance().showVirtualKeyboard(wideText);
15971591
}else{
15981592
System::instance().hideVirtualKeyboard();
15991593
}

engine/core/util/STBText.cpp

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
#include <string>
88
#include "Log.h"
99
#include "io/Data.h"
10-
#include <codecvt>
11-
#include <locale>
1210
#include "DefaultFont.h"
11+
#include "StringUtils.h"
1312

1413
using namespace Supernova;
1514

@@ -155,14 +154,10 @@ TextureData* STBText::load(const std::string& fontpath, unsigned int fontSize){
155154

156155
void STBText::createText(const std::string& text, Buffer* buffer, std::vector<uint16_t>& indices, std::vector<Vector2>& charPositions,
157156
unsigned int& width, unsigned int& height, bool fixedWidth, bool fixedHeight, bool multiline, bool invert){
158-
159-
std::wstring_convert< std::codecvt_utf8_utf16<wchar_t> > convert;
160-
std::wstring utf16String;
161157

162-
try {
163-
utf16String = convert.from_bytes( text );
164-
} catch(const std::range_error& e) {
165-
utf16String = convert.from_bytes(text.substr(0, convert.converted()));
158+
bool hadInvalid = false;
159+
std::vector<uint32_t> codepoints = StringUtils::decodeUtf8ToCodepoints(text, hadInvalid);
160+
if (hadInvalid) {
166161
Log::warn("Invalid character");
167162
}
168163

@@ -175,8 +170,8 @@ void STBText::createText(const std::string& text, Buffer* buffer, std::vector<ui
175170
if (multiline && fixedWidth){
176171

177172
int lastSpace = 0;
178-
for (int i = 0; i < utf16String.size(); i++){
179-
int intchar = uint_least32_t(utf16String[i]);
173+
for (int i = 0; i < (int)codepoints.size(); i++){
174+
int intchar = (int)codepoints[(size_t)i];
180175
if (intchar == 32){ //space
181176
lastSpace = i;
182177
}
@@ -187,13 +182,13 @@ void STBText::createText(const std::string& text, Buffer* buffer, std::vector<ui
187182
stbtt_aligned_quad quad;
188183
stbtt_GetPackedQuad(charInfo, atlasWidth, atlasHeight, intchar - firstChar, &offsetX, &offsetY, &quad, 1);
189184

190-
if (offsetX > width){
185+
if (offsetX > (float)width){
191186
if (lastSpace > 0){
192-
utf16String[lastSpace] = '\n';
187+
codepoints[(size_t)lastSpace] = '\n';
193188
i = lastSpace;
194189
lastSpace = 0;
195190
}else{
196-
utf16String.insert(i, { '\n' });
191+
codepoints.insert(codepoints.begin() + i, (uint32_t)'\n');
197192
}
198193
offsetX = 0;
199194
}
@@ -209,9 +204,9 @@ void STBText::createText(const std::string& text, Buffer* buffer, std::vector<ui
209204
int lineCount = 1;
210205
charPositions.clear();
211206

212-
for (int i = 0; i < utf16String.size(); i++){
207+
for (int i = 0; i < (int)codepoints.size(); i++){
213208

214-
int intchar = uint_least32_t(utf16String[i]);
209+
int intchar = (int)codepoints[(size_t)i];
215210

216211
if (intchar == 10){ //\n
217212
offsetY += lineHeight;
@@ -277,7 +272,7 @@ void STBText::createText(const std::string& text, Buffer* buffer, std::vector<ui
277272

278273
}
279274
//Empty text
280-
if (utf16String.size() == 0){
275+
if (codepoints.size() == 0){
281276
buffer->addVector3(atrVertice, Vector3(0.0f, 0.0f, 0.0f));
282277
buffer->addVector3(atrVertice, Vector3(0.0f, 0.0f, 0.0f));
283278
buffer->addVector3(atrVertice, Vector3(0.0f, 0.0f, 0.0f));

engine/core/util/STBText.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef STBText_h
66
#define STBText_h
77

8+
#include <cstdint>
9+
#include <string>
810
#include <vector>
911
#include "math/Vector2.h"
1012
#include "math/Vector3.h"

engine/core/util/StringUtils.cpp

Lines changed: 164 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,172 @@
44

55
#include "StringUtils.h"
66

7-
#include <codecvt>
8-
#include <iterator>
9-
#include <locale>
7+
#include <cstdint>
8+
#include <limits>
109

1110
using namespace Supernova;
1211

1312
std::string StringUtils::toUTF8(wchar_t codepoint){
14-
std::wstring_convert<std::codecvt_utf8<wchar_t>,wchar_t> convert;
15-
return convert.to_bytes(codepoint);
13+
uint32_t cp = static_cast<uint32_t>(codepoint);
14+
15+
// Replace invalid Unicode scalar values.
16+
if (cp > 0x10FFFFu || (cp >= 0xD800u && cp <= 0xDFFFu)) {
17+
cp = 0xFFFDu;
18+
}
19+
20+
std::string out;
21+
if (cp <= 0x7Fu) {
22+
out.push_back(static_cast<char>(cp));
23+
} else if (cp <= 0x7FFu) {
24+
out.push_back(static_cast<char>(0xC0u | (cp >> 6)));
25+
out.push_back(static_cast<char>(0x80u | (cp & 0x3Fu)));
26+
} else if (cp <= 0xFFFFu) {
27+
out.push_back(static_cast<char>(0xE0u | (cp >> 12)));
28+
out.push_back(static_cast<char>(0x80u | ((cp >> 6) & 0x3Fu)));
29+
out.push_back(static_cast<char>(0x80u | (cp & 0x3Fu)));
30+
} else {
31+
out.push_back(static_cast<char>(0xF0u | (cp >> 18)));
32+
out.push_back(static_cast<char>(0x80u | ((cp >> 12) & 0x3Fu)));
33+
out.push_back(static_cast<char>(0x80u | ((cp >> 6) & 0x3Fu)));
34+
out.push_back(static_cast<char>(0x80u | (cp & 0x3Fu)));
35+
}
36+
return out;
37+
}
38+
39+
std::vector<uint32_t> StringUtils::decodeUtf8ToCodepoints(const std::string& text, bool& hadInvalid) {
40+
hadInvalid = false;
41+
42+
std::vector<uint32_t> out;
43+
out.reserve(text.size());
44+
45+
const auto* bytes = reinterpret_cast<const unsigned char*>(text.data());
46+
size_t i = 0;
47+
while (i < text.size()) {
48+
uint32_t cp = 0;
49+
unsigned char b0 = bytes[i];
50+
51+
if (b0 <= 0x7F) {
52+
cp = b0;
53+
i += 1;
54+
} else if ((b0 & 0xE0) == 0xC0) {
55+
if (i + 1 >= text.size()) {
56+
hadInvalid = true;
57+
cp = 0xFFFD;
58+
i += 1;
59+
} else {
60+
unsigned char b1 = bytes[i + 1];
61+
uint32_t v = ((uint32_t)(b0 & 0x1F) << 6) | (uint32_t)(b1 & 0x3F);
62+
if ((b1 & 0xC0) != 0x80 || v < 0x80) {
63+
hadInvalid = true;
64+
cp = 0xFFFD;
65+
i += 1;
66+
} else {
67+
cp = v;
68+
i += 2;
69+
}
70+
}
71+
} else if ((b0 & 0xF0) == 0xE0) {
72+
if (i + 2 >= text.size()) {
73+
hadInvalid = true;
74+
cp = 0xFFFD;
75+
i += 1;
76+
} else {
77+
unsigned char b1 = bytes[i + 1];
78+
unsigned char b2 = bytes[i + 2];
79+
uint32_t v = ((uint32_t)(b0 & 0x0F) << 12) | ((uint32_t)(b1 & 0x3F) << 6) | (uint32_t)(b2 & 0x3F);
80+
if ((b1 & 0xC0) != 0x80 || (b2 & 0xC0) != 0x80 || v < 0x800 || (v >= 0xD800 && v <= 0xDFFF)) {
81+
hadInvalid = true;
82+
cp = 0xFFFD;
83+
i += 1;
84+
} else {
85+
cp = v;
86+
i += 3;
87+
}
88+
}
89+
} else if ((b0 & 0xF8) == 0xF0) {
90+
if (i + 3 >= text.size()) {
91+
hadInvalid = true;
92+
cp = 0xFFFD;
93+
i += 1;
94+
} else {
95+
unsigned char b1 = bytes[i + 1];
96+
unsigned char b2 = bytes[i + 2];
97+
unsigned char b3 = bytes[i + 3];
98+
uint32_t v = ((uint32_t)(b0 & 0x07) << 18) | ((uint32_t)(b1 & 0x3F) << 12) |
99+
((uint32_t)(b2 & 0x3F) << 6) | (uint32_t)(b3 & 0x3F);
100+
if ((b1 & 0xC0) != 0x80 || (b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || v < 0x10000 || v > 0x10FFFF) {
101+
hadInvalid = true;
102+
cp = 0xFFFD;
103+
i += 1;
104+
} else {
105+
cp = v;
106+
i += 4;
107+
}
108+
}
109+
} else {
110+
hadInvalid = true;
111+
cp = 0xFFFD;
112+
i += 1;
113+
}
114+
115+
out.push_back(cp);
116+
}
117+
118+
return out;
119+
}
120+
121+
std::wstring StringUtils::utf8ToWString(const std::string& text, bool& hadInvalid) {
122+
std::vector<uint32_t> cps = decodeUtf8ToCodepoints(text, hadInvalid);
123+
124+
std::wstring out;
125+
out.reserve(cps.size());
126+
127+
if constexpr (sizeof(wchar_t) == 2) {
128+
for (uint32_t cp : cps) {
129+
if (cp <= 0xFFFFu) {
130+
out.push_back(static_cast<wchar_t>(cp));
131+
} else {
132+
cp -= 0x10000u;
133+
wchar_t high = static_cast<wchar_t>(0xD800u + (cp >> 10));
134+
wchar_t low = static_cast<wchar_t>(0xDC00u + (cp & 0x3FFu));
135+
out.push_back(high);
136+
out.push_back(low);
137+
}
138+
}
139+
} else {
140+
for (uint32_t cp : cps) {
141+
if (cp > static_cast<uint32_t>(std::numeric_limits<wchar_t>::max())) {
142+
out.push_back(static_cast<wchar_t>(0xFFFDu));
143+
hadInvalid = true;
144+
} else {
145+
out.push_back(static_cast<wchar_t>(cp));
146+
}
147+
}
148+
}
149+
150+
return out;
151+
}
152+
153+
void StringUtils::eraseLastCodepointUtf8(std::string& text) {
154+
if (text.empty()) {
155+
return;
156+
}
157+
158+
// Remove trailing UTF-8 continuation bytes (10xxxxxx)
159+
size_t i = text.size();
160+
while (i > 0) {
161+
unsigned char c = static_cast<unsigned char>(text[i - 1]);
162+
if ((c & 0xC0) != 0x80) {
163+
break;
164+
}
165+
--i;
166+
}
167+
168+
// If we ended up at 0, the string was all continuation bytes; clear it.
169+
if (i == 0) {
170+
text.clear();
171+
return;
172+
}
173+
174+
text.erase(i - 1);
16175
}

engine/core/util/StringUtils.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,23 @@
66
#define StringUtils_h
77

88
#include "Export.h"
9+
#include <cstdint>
910
#include <string>
11+
#include <vector>
1012

1113
namespace Supernova{
1214
class SUPERNOVA_API StringUtils{
1315
public:
1416
static std::string toUTF8(wchar_t codepoint);
17+
18+
// Decodes UTF-8 into Unicode codepoints. Invalid sequences are replaced with U+FFFD and hadInvalid is set.
19+
static std::vector<uint32_t> decodeUtf8ToCodepoints(const std::string& text, bool& hadInvalid);
20+
21+
// Converts UTF-8 to std::wstring (UTF-16 on Windows; UTF-32 elsewhere). Invalid sequences become U+FFFD.
22+
static std::wstring utf8ToWString(const std::string& text, bool& hadInvalid);
23+
24+
// Removes the last UTF-8 encoded Unicode scalar (safe for multi-byte characters).
25+
static void eraseLastCodepointUtf8(std::string& text);
1526
};
1627
}
1728

0 commit comments

Comments
 (0)