gosu 0.12.1 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gosu/Audio.hpp +23 -25
- data/Gosu/Graphics.hpp +16 -12
- data/Gosu/Image.hpp +3 -0
- data/Gosu/Version.hpp +2 -2
- data/lib/gosu.rb +2 -2
- data/lib/gosu/compat.rb +1 -1
- data/lib/gosu/patches.rb +5 -0
- data/lib/gosu/swig_patches.rb +1 -1
- data/rdoc/gosu.rb +10 -10
- data/src/Audio.cpp +93 -228
- data/src/AudioImpl.cpp +94 -0
- data/src/AudioImpl.hpp +33 -0
- data/src/AudioToolboxFile.hpp +14 -18
- data/src/Bitmap.cpp +36 -30
- data/src/BitmapIO.cpp +14 -23
- data/src/BlockAllocator.cpp +7 -10
- data/src/BlockAllocator.hpp +2 -4
- data/src/Channel.cpp +89 -0
- data/src/Color.cpp +4 -9
- data/src/DirectoriesApple.cpp +13 -13
- data/src/DirectoriesUnix.cpp +8 -7
- data/src/DirectoriesWin.cpp +12 -11
- data/src/EmptyImageData.hpp +54 -0
- data/src/FileUnix.cpp +12 -9
- data/src/FileWin.cpp +8 -7
- data/src/Font.cpp +12 -13
- data/src/FormattedString.cpp +237 -0
- data/src/FormattedString.hpp +14 -265
- data/src/GosuViewController.cpp +2 -5
- data/src/Graphics.cpp +38 -39
- data/src/IO.cpp +11 -10
- data/src/Image.cpp +16 -9
- data/src/Input.cpp +16 -15
- data/src/InputUIKit.cpp +8 -7
- data/src/Macro.cpp +11 -11
- data/src/Math.cpp +9 -8
- data/src/RubyGosu.cxx +129 -99
- data/src/TextApple.cpp +19 -13
- data/src/TextInput.cpp +23 -22
- data/src/TextWin.cpp +17 -19
- data/src/Texture.cpp +15 -10
- data/src/Transform.cpp +13 -17
- data/src/Utility.cpp +3 -2
- data/src/UtilityApple.cpp +10 -11
- data/src/UtilityWin.cpp +2 -1
- data/src/Version.cpp +5 -4
- data/src/WinMain.cpp +3 -3
- data/src/WinUtility.cpp +7 -6
- data/src/Window.cpp +11 -10
- data/src/WindowUIKit.cpp +9 -8
- data/src/stb_image.h +782 -480
- data/src/stb_image_write.h +425 -15
- data/src/stb_vorbis.c +82 -32
- metadata +8 -4
- data/src/ALChannelManagement.hpp +0 -119
data/src/FileWin.cpp
CHANGED
@@ -4,7 +4,8 @@
|
|
4
4
|
#include "WinUtility.hpp"
|
5
5
|
#include <Gosu/IO.hpp>
|
6
6
|
#include <Gosu/Utility.hpp>
|
7
|
-
#include <windows.h>
|
7
|
+
#include <windows.h>
|
8
|
+
using namespace std;
|
8
9
|
|
9
10
|
// TODO: Error checking
|
10
11
|
|
@@ -20,7 +21,7 @@ struct Gosu::File::Impl
|
|
20
21
|
}
|
21
22
|
};
|
22
23
|
|
23
|
-
Gosu::File::File(const
|
24
|
+
Gosu::File::File(const string& filename, FileMode mode)
|
24
25
|
: pimpl(new Impl)
|
25
26
|
{
|
26
27
|
DWORD access;
|
@@ -38,7 +39,7 @@ Gosu::File::File(const std::string& filename, FileMode mode)
|
|
38
39
|
DWORD share_mode = FILE_SHARE_READ;
|
39
40
|
DWORD creation_disp = (mode == FM_READ) ? OPEN_EXISTING : OPEN_ALWAYS;
|
40
41
|
|
41
|
-
|
42
|
+
wstring wfilename = utf8_to_wstring(filename);
|
42
43
|
pimpl->handle = CreateFileW(wfilename.c_str(), access, share_mode, 0, creation_disp,
|
43
44
|
FILE_ATTRIBUTE_NORMAL, 0);
|
44
45
|
if (pimpl->handle == INVALID_HANDLE_VALUE) {
|
@@ -53,12 +54,12 @@ Gosu::File::~File()
|
|
53
54
|
{
|
54
55
|
}
|
55
56
|
|
56
|
-
|
57
|
+
size_t Gosu::File::size() const
|
57
58
|
{
|
58
59
|
return GetFileSize(pimpl->handle, 0);
|
59
60
|
}
|
60
61
|
|
61
|
-
void Gosu::File::resize(
|
62
|
+
void Gosu::File::resize(size_t new_size)
|
62
63
|
{
|
63
64
|
if (SetFilePointer(pimpl->handle, new_size, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
|
64
65
|
throw_last_winapi_error("setting the file pointer");
|
@@ -66,7 +67,7 @@ void Gosu::File::resize(std::size_t new_size)
|
|
66
67
|
winapi_check(SetEndOfFile(pimpl->handle), "resizing a file");
|
67
68
|
}
|
68
69
|
|
69
|
-
void Gosu::File::read(
|
70
|
+
void Gosu::File::read(size_t offset, size_t length, void* dest_buffer) const
|
70
71
|
{
|
71
72
|
if (SetFilePointer(pimpl->handle, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
|
72
73
|
throw_last_winapi_error("setting the file pointer");
|
@@ -75,7 +76,7 @@ void Gosu::File::read(std::size_t offset, std::size_t length, void* dest_buffer)
|
|
75
76
|
winapi_check(ReadFile(pimpl->handle, dest_buffer, length, &dummy, 0));
|
76
77
|
}
|
77
78
|
|
78
|
-
void Gosu::File::write(
|
79
|
+
void Gosu::File::write(size_t offset, size_t length, const void* source_buffer)
|
79
80
|
{
|
80
81
|
if (SetFilePointer(pimpl->handle, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
|
81
82
|
throw_last_winapi_error("setting the file pointer");
|
data/src/Font.cpp
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
#include <Gosu/Image.hpp>
|
6
6
|
#include <Gosu/Math.hpp>
|
7
7
|
#include <Gosu/Text.hpp>
|
8
|
+
#include <Gosu/Utility.hpp>
|
8
9
|
#include <array>
|
9
10
|
#include <cassert>
|
10
11
|
#include <map>
|
@@ -19,11 +20,11 @@ struct Gosu::Font::Impl
|
|
19
20
|
// the first plane will ever be touched.
|
20
21
|
struct CharInfo
|
21
22
|
{
|
22
|
-
|
23
|
+
unique_ptr<Image> image;
|
23
24
|
double factor;
|
24
25
|
};
|
25
26
|
typedef array<CharInfo, 65536> Plane;
|
26
|
-
|
27
|
+
unique_ptr<Plane> planes[16][FF_COMBINATIONS];
|
27
28
|
|
28
29
|
map<string, shared_ptr<Image>> entity_cache;
|
29
30
|
|
@@ -57,7 +58,7 @@ struct Gosu::Font::Impl
|
|
57
58
|
|
58
59
|
if (info.image.get()) return *info.image;
|
59
60
|
|
60
|
-
|
61
|
+
string char_string = wstring_to_utf8(wstring(1, wc));
|
61
62
|
// TODO: Would be nice to have.
|
62
63
|
// if (is_formatting_char(wc))
|
63
64
|
// char_string.clear();
|
@@ -102,7 +103,7 @@ unsigned Gosu::Font::flags() const
|
|
102
103
|
|
103
104
|
double Gosu::Font::text_width(const string& text, double scale_x) const
|
104
105
|
{
|
105
|
-
|
106
|
+
wstring wtext = utf8_to_wstring(text);
|
106
107
|
FormattedString fs(wtext.c_str(), flags());
|
107
108
|
double result = 0;
|
108
109
|
for (unsigned i = 0; i < fs.length(); ++i) {
|
@@ -116,15 +117,15 @@ double Gosu::Font::text_width(const string& text, double scale_x) const
|
|
116
117
|
void Gosu::Font::draw(const string& text, double x, double y, ZPos z,
|
117
118
|
double scale_x, double scale_y, Color c, AlphaMode mode) const
|
118
119
|
{
|
119
|
-
|
120
|
+
wstring wtext = utf8_to_wstring(text);
|
120
121
|
FormattedString fs(wtext.c_str(), flags());
|
121
122
|
|
122
123
|
for (unsigned i = 0; i < fs.length(); ++i) {
|
123
124
|
const Image& image = pimpl->image_at(fs, i);
|
124
125
|
double factor = pimpl->factor_at(fs, i);
|
125
|
-
|
126
|
-
|
127
|
-
|
126
|
+
Color color = fs.entity_at(i)
|
127
|
+
? Color(fs.color_at(i).alpha() * c.alpha() / 255, 255, 255, 255)
|
128
|
+
: multiply(fs.color_at(i), c);
|
128
129
|
image.draw(x, y, z, scale_x * factor, scale_y * factor, color, mode);
|
129
130
|
x += image.width() * scale_x * factor;
|
130
131
|
}
|
@@ -149,17 +150,15 @@ void Gosu::Font::set_image(wchar_t wc, const Image& image)
|
|
149
150
|
void Gosu::Font::set_image(wchar_t wc, unsigned font_flags, const Image& image)
|
150
151
|
{
|
151
152
|
Impl::CharInfo& ci = pimpl->char_info(wc, font_flags);
|
152
|
-
if (ci.image.get())
|
153
|
-
|
154
|
-
}
|
155
|
-
ci.image.reset(new Gosu::Image(image));
|
153
|
+
if (ci.image.get()) throw logic_error("Cannot set image for the same character twice");
|
154
|
+
ci.image.reset(new Image(image));
|
156
155
|
ci.factor = 1.0;
|
157
156
|
}
|
158
157
|
|
159
158
|
void Gosu::Font::draw_rot(const string& text, double x, double y, ZPos z, double angle,
|
160
159
|
double scale_x, double scale_y, Color c, AlphaMode mode) const
|
161
160
|
{
|
162
|
-
|
161
|
+
Graphics::transform(rotate(angle, x, y), [&] {
|
163
162
|
draw(text, x, y, z, scale_x, scale_y, c, mode);
|
164
163
|
});
|
165
164
|
}
|
@@ -0,0 +1,237 @@
|
|
1
|
+
#include "FormattedString.hpp"
|
2
|
+
#include <Gosu/Utility.hpp>
|
3
|
+
#include <cwchar>
|
4
|
+
#include <cwctype>
|
5
|
+
using namespace std;
|
6
|
+
|
7
|
+
static unsigned flags(int b, int u, int i)
|
8
|
+
{
|
9
|
+
unsigned flags = 0;
|
10
|
+
if (b > 0) flags |= Gosu::FF_BOLD;
|
11
|
+
if (u > 0) flags |= Gosu::FF_UNDERLINE;
|
12
|
+
if (i > 0) flags |= Gosu::FF_ITALIC;
|
13
|
+
return flags;
|
14
|
+
}
|
15
|
+
|
16
|
+
bool Gosu::FormattedString::FormattedChar::same_style_as(const FormattedChar& other) const
|
17
|
+
{
|
18
|
+
return wc && other.wc && color == other.color && flags == other.flags;
|
19
|
+
}
|
20
|
+
|
21
|
+
Gosu::FormattedString::FormattedString()
|
22
|
+
{
|
23
|
+
}
|
24
|
+
|
25
|
+
Gosu::FormattedString::FormattedString(const wchar_t* html, unsigned base_flags)
|
26
|
+
{
|
27
|
+
// Remove \r characters if existent. Avoid a copy if we don't need one.
|
28
|
+
wstring unixified;
|
29
|
+
// We have to explicitly qualify wcschr to avoid an ambiguity on macOS.
|
30
|
+
if (std::wcschr(html, L'\r')) {
|
31
|
+
unixified.resize(wcslen(html));
|
32
|
+
unsigned pos = 0;
|
33
|
+
while (*html) {
|
34
|
+
if (*html != '\r') {
|
35
|
+
unixified[pos++] = *html;
|
36
|
+
}
|
37
|
+
++html;
|
38
|
+
}
|
39
|
+
unixified.resize(pos);
|
40
|
+
html = unixified.c_str();
|
41
|
+
}
|
42
|
+
|
43
|
+
size_t len = wcslen(html);
|
44
|
+
|
45
|
+
// Just skip all this if there are entities or formatting tags in the string.
|
46
|
+
if (wcscspn(html, L"<&") == len) {
|
47
|
+
simple_string = html;
|
48
|
+
simple_flags = base_flags;
|
49
|
+
return;
|
50
|
+
}
|
51
|
+
|
52
|
+
unsigned pos = 0;
|
53
|
+
int b = (base_flags & FF_BOLD) ? 1 : 0,
|
54
|
+
u = (base_flags & FF_UNDERLINE) ? 1 : 0,
|
55
|
+
i = (base_flags & FF_ITALIC) ? 1 : 0;
|
56
|
+
vector<Color> c;
|
57
|
+
c.push_back(0xffffffff);
|
58
|
+
while (pos < len) {
|
59
|
+
if (!wcsncmp(html + pos, L"<b>", 3)) {
|
60
|
+
b += 1;
|
61
|
+
pos += 3;
|
62
|
+
continue;
|
63
|
+
}
|
64
|
+
if (!wcsncmp(html + pos, L"</b>", 4)) {
|
65
|
+
b -= 1;
|
66
|
+
pos += 4;
|
67
|
+
continue;
|
68
|
+
}
|
69
|
+
if (!wcsncmp(html + pos, L"<u>", 3)) {
|
70
|
+
u += 1;
|
71
|
+
pos += 3;
|
72
|
+
continue;
|
73
|
+
}
|
74
|
+
if (!wcsncmp(html + pos, L"</u>", 4)) {
|
75
|
+
u -= 1;
|
76
|
+
pos += 4;
|
77
|
+
continue;
|
78
|
+
}
|
79
|
+
if (!wcsncmp(html + pos, L"<i>", 3)) {
|
80
|
+
i += 1;
|
81
|
+
pos += 3;
|
82
|
+
continue;
|
83
|
+
}
|
84
|
+
if (!wcsncmp(html + pos, L"</i>", 4)) {
|
85
|
+
i -= 1;
|
86
|
+
pos += 4;
|
87
|
+
continue;
|
88
|
+
}
|
89
|
+
if (!wcsncmp(html + pos, L"<c=", 3) && len >= pos + 10 && html[pos + 9] == L'>') {
|
90
|
+
unsigned rgb = static_cast<uint32_t>(wcstoul(html + pos + 3, 0, 16));
|
91
|
+
c.push_back(0xff000000 | rgb);
|
92
|
+
pos += 10;
|
93
|
+
continue;
|
94
|
+
}
|
95
|
+
if (!wcsncmp(html + pos, L"<c=", 3) && len >= pos + 12 && html[pos + 11] == L'>') {
|
96
|
+
unsigned argb = static_cast<uint32_t>(wcstoul(html + pos + 3, 0, 16));
|
97
|
+
c.push_back(argb);
|
98
|
+
pos += 12;
|
99
|
+
continue;
|
100
|
+
}
|
101
|
+
if (!wcsncmp(html + pos, L"</c>", 4)) {
|
102
|
+
if (c.size() > 1) {
|
103
|
+
c.pop_back();
|
104
|
+
}
|
105
|
+
pos += 4;
|
106
|
+
continue;
|
107
|
+
}
|
108
|
+
if (!wcsncmp(html + pos, L"<", 4)) {
|
109
|
+
FormattedChar fc = { L'<', c.back(), flags(b, u, i) };
|
110
|
+
characters.push_back(fc);
|
111
|
+
pos += 4;
|
112
|
+
continue;
|
113
|
+
}
|
114
|
+
if (!wcsncmp(html + pos, L">", 4)) {
|
115
|
+
FormattedChar fc = { L'>', c.back(), flags(b, u, i) };
|
116
|
+
characters.push_back(fc);
|
117
|
+
pos += 4;
|
118
|
+
continue;
|
119
|
+
}
|
120
|
+
if (!wcsncmp(html + pos, L"&", 5)) {
|
121
|
+
FormattedChar fc = { L'&', c.back(), flags(b, u, i) };
|
122
|
+
characters.push_back(fc);
|
123
|
+
pos += 5;
|
124
|
+
continue;
|
125
|
+
}
|
126
|
+
if (html[pos] == L'&' && html[pos + 1]) {
|
127
|
+
int end_of_entity = pos + 1;
|
128
|
+
while (html[end_of_entity] != L';') {
|
129
|
+
if (!iswalnum(static_cast<wint_t>(html[end_of_entity]))) {
|
130
|
+
goto normal_character;
|
131
|
+
}
|
132
|
+
end_of_entity += 1;
|
133
|
+
if (end_of_entity >= len) {
|
134
|
+
goto normal_character;
|
135
|
+
}
|
136
|
+
}
|
137
|
+
wstring entity(html + pos + 1, html + end_of_entity);
|
138
|
+
FormattedChar fc = { 0, c.back(), 0, wstring_to_utf8(entity) };
|
139
|
+
if (!is_entity(fc.entity)) {
|
140
|
+
goto normal_character;
|
141
|
+
}
|
142
|
+
characters.push_back(fc);
|
143
|
+
pos = end_of_entity + 1;
|
144
|
+
continue;
|
145
|
+
}
|
146
|
+
|
147
|
+
normal_character:
|
148
|
+
FormattedChar fc = { html[pos], c.back(), flags(b, u, i) };
|
149
|
+
characters.push_back(fc);
|
150
|
+
pos += 1;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
wstring Gosu::FormattedString::unformat() const
|
155
|
+
{
|
156
|
+
if (characters.empty()) return simple_string;
|
157
|
+
|
158
|
+
wstring result(characters.size(), 0);
|
159
|
+
for (int i = 0; i < characters.size(); ++i) {
|
160
|
+
result[i] = characters[i].wc;
|
161
|
+
}
|
162
|
+
return result;
|
163
|
+
}
|
164
|
+
|
165
|
+
const char* Gosu::FormattedString::entity_at(unsigned index) const
|
166
|
+
{
|
167
|
+
if (characters.empty()) return nullptr;
|
168
|
+
if (characters[index].wc != 0) return nullptr;
|
169
|
+
if (characters[index].entity.empty()) return nullptr;
|
170
|
+
|
171
|
+
return characters[index].entity.c_str();
|
172
|
+
}
|
173
|
+
|
174
|
+
wchar_t Gosu::FormattedString::char_at(unsigned index) const
|
175
|
+
{
|
176
|
+
return characters.empty() ? simple_string[index] : characters[index].wc;
|
177
|
+
}
|
178
|
+
|
179
|
+
unsigned Gosu::FormattedString::flags_at(unsigned index) const
|
180
|
+
{
|
181
|
+
return characters.empty() ? simple_flags : characters[index].flags;
|
182
|
+
}
|
183
|
+
|
184
|
+
Gosu::Color Gosu::FormattedString::color_at(unsigned index) const
|
185
|
+
{
|
186
|
+
return characters.empty() ? Color::WHITE : characters[index].color;
|
187
|
+
}
|
188
|
+
|
189
|
+
size_t Gosu::FormattedString::length() const
|
190
|
+
{
|
191
|
+
return characters.empty() ? simple_string.length() : characters.size();
|
192
|
+
}
|
193
|
+
|
194
|
+
Gosu::FormattedString Gosu::FormattedString::range(size_t begin, size_t end) const
|
195
|
+
{
|
196
|
+
FormattedString result;
|
197
|
+
if (characters.empty()) {
|
198
|
+
result.simple_string.assign(simple_string.begin() + begin,
|
199
|
+
simple_string.begin() + end);
|
200
|
+
result.simple_flags = simple_flags;
|
201
|
+
}
|
202
|
+
else {
|
203
|
+
result.characters.assign(characters.begin() + begin,
|
204
|
+
characters.begin() + end);
|
205
|
+
}
|
206
|
+
return result;
|
207
|
+
}
|
208
|
+
|
209
|
+
vector<Gosu::FormattedString> Gosu::FormattedString::split_lines() const
|
210
|
+
{
|
211
|
+
vector<FormattedString> result;
|
212
|
+
unsigned begin = 0;
|
213
|
+
for (unsigned cur = 0; cur < length(); ++cur) {
|
214
|
+
if (char_at(cur) == L'\n') {
|
215
|
+
result.push_back(range(begin, cur));
|
216
|
+
begin = cur + 1;
|
217
|
+
}
|
218
|
+
}
|
219
|
+
result.push_back(range(begin, length()));
|
220
|
+
return result;
|
221
|
+
}
|
222
|
+
|
223
|
+
vector<Gosu::FormattedString> Gosu::FormattedString::split_parts() const
|
224
|
+
{
|
225
|
+
if (characters.empty()) return vector<FormattedString>(1, *this);
|
226
|
+
|
227
|
+
vector<FormattedString> result;
|
228
|
+
unsigned begin = 0;
|
229
|
+
for (unsigned cur = 1; cur < length(); ++cur) {
|
230
|
+
if (!characters[begin].same_style_as(characters[cur])) {
|
231
|
+
result.push_back(range(begin, cur));
|
232
|
+
begin = cur;
|
233
|
+
}
|
234
|
+
}
|
235
|
+
result.push_back(range(begin, length()));
|
236
|
+
return result;
|
237
|
+
}
|
data/src/FormattedString.hpp
CHANGED
@@ -3,13 +3,6 @@
|
|
3
3
|
#include "GraphicsImpl.hpp"
|
4
4
|
#include <Gosu/Color.hpp>
|
5
5
|
#include <Gosu/GraphicsBase.hpp>
|
6
|
-
#include <Gosu/Utility.hpp>
|
7
|
-
#include <cassert>
|
8
|
-
#include <cstdint>
|
9
|
-
#include <cwchar>
|
10
|
-
#include <cwctype>
|
11
|
-
#include <stdexcept>
|
12
|
-
#include <utility>
|
13
6
|
#include <vector>
|
14
7
|
|
15
8
|
namespace Gosu
|
@@ -19,14 +12,11 @@ namespace Gosu
|
|
19
12
|
struct FormattedChar
|
20
13
|
{
|
21
14
|
wchar_t wc;
|
22
|
-
|
15
|
+
Color color;
|
23
16
|
unsigned flags;
|
24
17
|
std::string entity;
|
25
18
|
|
26
|
-
bool same_style_as(const FormattedChar& other) const
|
27
|
-
{
|
28
|
-
return wc && other.wc && color == other.color && flags == other.flags;
|
29
|
-
}
|
19
|
+
bool same_style_as(const FormattedChar& other) const;
|
30
20
|
};
|
31
21
|
|
32
22
|
// If characters.empty(), use these for the whole string.
|
@@ -35,264 +25,23 @@ namespace Gosu
|
|
35
25
|
// If not characters.empty(), ignore above fields and use this.
|
36
26
|
std::vector<FormattedChar> characters;
|
37
27
|
|
38
|
-
static unsigned flags(int b, int u, int i)
|
39
|
-
{
|
40
|
-
unsigned flags = 0;
|
41
|
-
if (b > 0) flags |= FF_BOLD;
|
42
|
-
if (u > 0) flags |= FF_UNDERLINE;
|
43
|
-
if (i > 0) flags |= FF_ITALIC;
|
44
|
-
return flags;
|
45
|
-
}
|
46
|
-
|
47
28
|
public:
|
48
|
-
FormattedString()
|
49
|
-
|
50
|
-
}
|
29
|
+
FormattedString();
|
30
|
+
FormattedString(const wchar_t* html, unsigned base_flags);
|
51
31
|
|
52
|
-
|
53
|
-
{
|
54
|
-
// Remove \r characters if existent. Avoid a copy if we don't need one.
|
55
|
-
std::wstring unixified;
|
56
|
-
if (std::wcschr(html, L'\r')) {
|
57
|
-
unixified.resize(std::wcslen(html));
|
58
|
-
unsigned pos = 0;
|
59
|
-
while (*html) {
|
60
|
-
if (*html != '\r') {
|
61
|
-
unixified[pos++] = *html;
|
62
|
-
}
|
63
|
-
++html;
|
64
|
-
}
|
65
|
-
unixified.resize(pos);
|
66
|
-
html = unixified.c_str();
|
67
|
-
}
|
68
|
-
|
69
|
-
std::size_t len = std::wcslen(html);
|
70
|
-
|
71
|
-
// Just skip all this if there are entities or formatting tags in the string.
|
72
|
-
if (std::wcscspn(html, L"<&") == len) {
|
73
|
-
simple_string = html;
|
74
|
-
simple_flags = base_flags;
|
75
|
-
return;
|
76
|
-
}
|
77
|
-
|
78
|
-
unsigned pos = 0;
|
79
|
-
int b = (base_flags & FF_BOLD) ? 1 : 0,
|
80
|
-
u = (base_flags & FF_UNDERLINE) ? 1 : 0,
|
81
|
-
i = (base_flags & FF_ITALIC) ? 1 : 0;
|
82
|
-
std::vector<Gosu::Color> c;
|
83
|
-
c.push_back(0xffffffff);
|
84
|
-
while (pos < len) {
|
85
|
-
if (!std::wcsncmp(html + pos, L"<b>", 3)) {
|
86
|
-
b += 1;
|
87
|
-
pos += 3;
|
88
|
-
continue;
|
89
|
-
}
|
90
|
-
if (!std::wcsncmp(html + pos, L"</b>", 4)) {
|
91
|
-
b -= 1;
|
92
|
-
pos += 4;
|
93
|
-
continue;
|
94
|
-
}
|
95
|
-
if (!std::wcsncmp(html + pos, L"<u>", 3)) {
|
96
|
-
u += 1;
|
97
|
-
pos += 3;
|
98
|
-
continue;
|
99
|
-
}
|
100
|
-
if (!std::wcsncmp(html + pos, L"</u>", 4)) {
|
101
|
-
u -= 1;
|
102
|
-
pos += 4;
|
103
|
-
continue;
|
104
|
-
}
|
105
|
-
if (!std::wcsncmp(html + pos, L"<i>", 3)) {
|
106
|
-
i += 1;
|
107
|
-
pos += 3;
|
108
|
-
continue;
|
109
|
-
}
|
110
|
-
if (!std::wcsncmp(html + pos, L"</i>", 4)) {
|
111
|
-
i -= 1;
|
112
|
-
pos += 4;
|
113
|
-
continue;
|
114
|
-
}
|
115
|
-
if (!std::wcsncmp(html + pos, L"<c=", 3) && len >= pos + 10
|
116
|
-
&& html[pos + 9] == L'>') {
|
117
|
-
using namespace std;
|
118
|
-
unsigned rgb = static_cast<std::uint32_t>(wcstoul(html + pos + 3, 0, 16));
|
119
|
-
c.push_back(0xff000000 | rgb);
|
120
|
-
pos += 10;
|
121
|
-
continue;
|
122
|
-
}
|
123
|
-
if (!std::wcsncmp(html + pos, L"<c=", 3) && len >= pos + 12
|
124
|
-
&& html[pos + 11] == L'>') {
|
125
|
-
using namespace std;
|
126
|
-
unsigned argb = static_cast<std::uint32_t>(wcstoul(html + pos + 3, 0, 16));
|
127
|
-
c.push_back(argb);
|
128
|
-
pos += 12;
|
129
|
-
continue;
|
130
|
-
}
|
131
|
-
if (!std::wcsncmp(html + pos, L"</c>", 4)) {
|
132
|
-
if (c.size() > 1) {
|
133
|
-
c.pop_back();
|
134
|
-
}
|
135
|
-
pos += 4;
|
136
|
-
continue;
|
137
|
-
}
|
138
|
-
if (!std::wcsncmp(html + pos, L"<", 4)) {
|
139
|
-
FormattedChar fc = { L'<', c.back(), flags(b, u, i) };
|
140
|
-
characters.push_back(fc);
|
141
|
-
pos += 4;
|
142
|
-
continue;
|
143
|
-
}
|
144
|
-
if (!std::wcsncmp(html + pos, L">", 4)) {
|
145
|
-
FormattedChar fc = { L'>', c.back(), flags(b, u, i) };
|
146
|
-
characters.push_back(fc);
|
147
|
-
pos += 4;
|
148
|
-
continue;
|
149
|
-
}
|
150
|
-
if (!std::wcsncmp(html + pos, L"&", 5)) {
|
151
|
-
FormattedChar fc = { L'&', c.back(), flags(b, u, i) };
|
152
|
-
characters.push_back(fc);
|
153
|
-
pos += 5;
|
154
|
-
continue;
|
155
|
-
}
|
156
|
-
if (html[pos] == L'&' && html[pos + 1]) {
|
157
|
-
int end_of_entity = pos + 1;
|
158
|
-
while (html[end_of_entity] != L';') {
|
159
|
-
using namespace std;
|
160
|
-
if (!iswalnum(static_cast<wint_t>(html[end_of_entity]))) {
|
161
|
-
goto normal_character;
|
162
|
-
}
|
163
|
-
end_of_entity += 1;
|
164
|
-
if (end_of_entity >= len) {
|
165
|
-
goto normal_character;
|
166
|
-
}
|
167
|
-
}
|
168
|
-
std::wstring entity(html + pos + 1, html + end_of_entity);
|
169
|
-
FormattedChar fc = { 0, c.back(), 0, wstring_to_utf8(entity) };
|
170
|
-
if (!is_entity(fc.entity)) {
|
171
|
-
goto normal_character;
|
172
|
-
}
|
173
|
-
characters.push_back(fc);
|
174
|
-
pos = end_of_entity + 1;
|
175
|
-
continue;
|
176
|
-
}
|
177
|
-
|
178
|
-
normal_character:
|
179
|
-
FormattedChar fc = { html[pos], c.back(), flags(b, u, i) };
|
180
|
-
characters.push_back(fc);
|
181
|
-
pos += 1;
|
182
|
-
}
|
183
|
-
}
|
32
|
+
std::wstring unformat() const;
|
184
33
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
}
|
190
|
-
|
191
|
-
std::wstring result(characters.size(), 0);
|
192
|
-
for (int i = 0; i < characters.size(); ++i) {
|
193
|
-
result[i] = characters[i].wc;
|
194
|
-
}
|
195
|
-
return result;
|
196
|
-
}
|
34
|
+
const char* entity_at(unsigned index) const;
|
35
|
+
wchar_t char_at(unsigned index) const;
|
36
|
+
unsigned flags_at(unsigned index) const;
|
37
|
+
Color color_at(unsigned index) const;
|
197
38
|
|
198
|
-
|
199
|
-
{
|
200
|
-
if (characters.empty()) {
|
201
|
-
return nullptr;
|
202
|
-
}
|
203
|
-
|
204
|
-
if (characters[index].wc != 0 || characters[index].entity.empty()) {
|
205
|
-
return nullptr;
|
206
|
-
}
|
207
|
-
|
208
|
-
return characters[index].entity.c_str();
|
209
|
-
}
|
39
|
+
std::size_t length() const;
|
210
40
|
|
211
|
-
|
212
|
-
{
|
213
|
-
if (characters.empty()) {
|
214
|
-
return simple_string[index];
|
215
|
-
}
|
216
|
-
else {
|
217
|
-
return characters[index].wc;
|
218
|
-
}
|
219
|
-
}
|
41
|
+
FormattedString range(std::size_t begin, std::size_t end) const;
|
220
42
|
|
221
|
-
|
222
|
-
|
223
|
-
if (characters.empty()) {
|
224
|
-
return simple_flags;
|
225
|
-
}
|
226
|
-
else {
|
227
|
-
return characters[index].flags;
|
228
|
-
}
|
229
|
-
}
|
230
|
-
|
231
|
-
Gosu::Color color_at(unsigned index) const
|
232
|
-
{
|
233
|
-
if (characters.empty()) {
|
234
|
-
return Color::WHITE;
|
235
|
-
}
|
236
|
-
else {
|
237
|
-
return characters[index].color;
|
238
|
-
}
|
239
|
-
}
|
240
|
-
|
241
|
-
std::size_t length() const
|
242
|
-
{
|
243
|
-
if (std::size_t len = characters.size()) {
|
244
|
-
return len;
|
245
|
-
}
|
246
|
-
else {
|
247
|
-
return simple_string.length();
|
248
|
-
}
|
249
|
-
}
|
250
|
-
|
251
|
-
FormattedString range(std::size_t begin, std::size_t end) const
|
252
|
-
{
|
253
|
-
FormattedString result;
|
254
|
-
if (characters.empty()) {
|
255
|
-
result.simple_string.assign(simple_string.begin() + begin,
|
256
|
-
simple_string.begin() + end);
|
257
|
-
result.simple_flags = simple_flags;
|
258
|
-
}
|
259
|
-
else {
|
260
|
-
result.characters.assign(characters.begin() + begin,
|
261
|
-
characters.begin() + end);
|
262
|
-
}
|
263
|
-
return result;
|
264
|
-
}
|
265
|
-
|
266
|
-
std::vector<FormattedString> split_lines() const
|
267
|
-
{
|
268
|
-
std::vector<FormattedString> result;
|
269
|
-
unsigned begin = 0;
|
270
|
-
for (unsigned cur = 0; cur < length(); ++cur) {
|
271
|
-
if (char_at(cur) == L'\n') {
|
272
|
-
result.push_back(range(begin, cur));
|
273
|
-
begin = cur + 1;
|
274
|
-
}
|
275
|
-
}
|
276
|
-
result.push_back(range(begin, length()));
|
277
|
-
return result;
|
278
|
-
}
|
279
|
-
|
280
|
-
std::vector<FormattedString> split_parts() const
|
281
|
-
{
|
282
|
-
if (characters.empty()) {
|
283
|
-
return std::vector<FormattedString>(1, *this);
|
284
|
-
}
|
285
|
-
|
286
|
-
std::vector<FormattedString> result;
|
287
|
-
unsigned begin = 0;
|
288
|
-
for (unsigned cur = 1; cur < length(); ++cur) {
|
289
|
-
if (!characters[begin].same_style_as(characters[cur])) {
|
290
|
-
result.push_back(range(begin, cur));
|
291
|
-
begin = cur;
|
292
|
-
}
|
293
|
-
}
|
294
|
-
result.push_back(range(begin, length()));
|
295
|
-
return result;
|
296
|
-
}
|
43
|
+
std::vector<FormattedString> split_lines() const;
|
44
|
+
std::vector<FormattedString> split_parts() const;
|
297
45
|
};
|
298
46
|
}
|
47
|
+
|