Skip to content

Commit 8be7531

Browse files
committed
适配上游C++现代化和Boost移除(基于上游arch1t3cht#182
- 适配上游提交 10843b9:C++20标准、Boost移除、std::filesystem替换 - 修复编译错误:agi::make_unique→std::make_unique、fs_fwd.h→filesystem - 修复链接错误:统一函数签名为string_view - 修复thesaurus透明比较器、dispatch DoInvoke签名、mkv_wrap string_view适配 - 保留自定义功能:压缩字幕支持(mkv_wrap)、DirectWrite字体(font_file_lister_gdi) - .gitignore:排除docs/*.md和docs/*.txt临时文件
1 parent 141255d commit 8be7531

316 files changed

Lines changed: 3774 additions & 3722 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,10 @@ subprojects/expat-*
6060
subprojects/vpl
6161
subprojects/libvpl-*
6262
subprojects/wxWidgets-master
63+
subprojects/curl-*
64+
subprojects/LuaJIT-*
6365
.idea
6466
.vscode
67+
# Documentation (generated/temporary)
68+
docs/*.md
69+
docs/*.txt

automation/tests/aegisub.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020

2121
#include <libaegisub/dispatch.h>
2222
#include <libaegisub/log.h>
23+
#include <libaegisub/util.h>
2324

24-
#include <boost/locale/generator.hpp>
2525
#include <cstdio>
2626
#include <cstdlib>
2727

@@ -36,7 +36,7 @@ void check(lua_State *L, int status) {
3636
}
3737

3838
int close_and_exit(lua_State *L) {
39-
int status = lua_tointeger(L, 1);
39+
int status = (int)lua_tointeger(L, 1);
4040
lua_close(L);
4141
exit(status);
4242
}
@@ -48,7 +48,7 @@ int main(int argc, char **argv) {
4848
return 1;
4949
}
5050

51-
std::locale::global(boost::locale::generator().generate(""));
51+
agi::util::InitLocale();
5252
agi::dispatch::Init([](agi::dispatch::Thunk f) { });
5353
agi::log::log = new agi::log::LogSink;
5454

libaegisub/ass/dialogue_parser.cpp

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,20 @@
1616

1717
#include "libaegisub/ass/dialogue_parser.h"
1818

19+
#include "libaegisub/exception.h"
1920
#include "libaegisub/spellchecker.h"
20-
21-
#include <boost/locale/boundary/index.hpp>
22-
#include <boost/locale/boundary/segment.hpp>
23-
#include <boost/locale/boundary/types.hpp>
21+
#include "libaegisub/unicode.h"
2422

2523
namespace {
2624

27-
typedef std::vector<agi::ass::DialogueToken> TokenVec;
25+
using TokenVec = std::vector<agi::ass::DialogueToken>;
2826
using namespace agi::ass;
2927
namespace dt = DialogueTokenType;
3028
namespace ss = SyntaxStyle;
3129

3230
class SyntaxHighlighter {
3331
TokenVec ranges;
34-
std::string const& text;
32+
std::string_view text;
3533
agi::SpellChecker *spellchecker;
3634

3735
void SetStyling(size_t len, int type) {
@@ -42,7 +40,7 @@ class SyntaxHighlighter {
4240
}
4341

4442
public:
45-
SyntaxHighlighter(std::string const& text, agi::SpellChecker *spellchecker)
43+
SyntaxHighlighter(std::string_view text, agi::SpellChecker *spellchecker)
4644
: text(text)
4745
, spellchecker(spellchecker)
4846
{ }
@@ -97,7 +95,7 @@ class SyntaxHighlighter {
9795
};
9896

9997
class WordSplitter {
100-
std::string const& text;
98+
std::string_view text;
10199
std::vector<DialogueToken> &tokens;
102100
size_t pos = 0;
103101

@@ -113,14 +111,34 @@ class WordSplitter {
113111
}
114112

115113
void SplitText(size_t &i) {
116-
using namespace boost::locale::boundary;
117-
ssegment_index map(word, text.begin() + pos, text.begin() + pos + tokens[i].length);
118-
for (auto const& segment : map) {
119-
auto len = static_cast<size_t>(distance(begin(segment), end(segment)));
120-
if (segment.rule() & word_letters)
121-
SwitchTo(i, dt::WORD, len);
122-
else
123-
SwitchTo(i, dt::TEXT, len);
114+
UErrorCode err = U_ZERO_ERROR;
115+
thread_local std::unique_ptr<icu::BreakIterator> bi(icu::BreakIterator::createWordInstance(icu::Locale::getDefault(), err));
116+
agi::UTextPtr ut(utext_openUTF8(nullptr, text.data() + pos, tokens[i].length, &err));
117+
bi->setText(ut.get(), err);
118+
if (U_FAILURE(err)) throw agi::InternalError(u_errorName(err));
119+
size_t pos = 0;
120+
while (bi->next() != UBRK_DONE) {
121+
auto len = bi->current() - pos;
122+
123+
std::vector<int32_t> rules(8);
124+
int n = bi->getRuleStatusVec(rules.data(), rules.size(), err);
125+
if (err == U_BUFFER_OVERFLOW_ERROR) {
126+
err = U_ZERO_ERROR;
127+
bi->getRuleStatusVec(rules.data(), rules.size(), err);
128+
}
129+
130+
if (U_FAILURE(err)) throw agi::InternalError(u_errorName(err));
131+
132+
auto token_type = dt::TEXT;
133+
134+
for (size_t i = 0; i < n; i++) {
135+
if (rules[i] >= UBRK_WORD_LETTER && rules[i] < UBRK_WORD_IDEO_LIMIT) {
136+
token_type = dt::WORD;
137+
break;
138+
}
139+
}
140+
SwitchTo(i, token_type, len);
141+
pos = bi->current();
124142
}
125143
}
126144

@@ -183,7 +201,7 @@ class WordSplitter {
183201
}
184202

185203
public:
186-
WordSplitter(std::string const& text, std::vector<DialogueToken> &tokens)
204+
WordSplitter(std::string_view text, std::vector<DialogueToken> &tokens)
187205
: text(text)
188206
, tokens(tokens)
189207
{ }
@@ -204,14 +222,15 @@ class WordSplitter {
204222
};
205223
}
206224

207-
namespace agi {
208-
namespace ass {
225+
namespace agi::ass {
209226

210-
std::vector<DialogueToken> SyntaxHighlight(std::string const& text, std::vector<DialogueToken> const& tokens, SpellChecker *spellchecker) {
227+
std::vector<DialogueToken> SyntaxHighlight(std::string_view text,
228+
std::vector<DialogueToken> const& tokens,
229+
SpellChecker *spellchecker) {
211230
return SyntaxHighlighter(text, spellchecker).Highlight(tokens);
212231
}
213232

214-
void MarkDrawings(std::string const& str, std::vector<DialogueToken> &tokens) {
233+
void MarkDrawings(std::string_view str, std::vector<DialogueToken> &tokens) {
215234
if (tokens.empty()) return;
216235

217236
size_t last_ovr_end = 0;
@@ -233,7 +252,7 @@ void MarkDrawings(std::string const& str, std::vector<DialogueToken> &tokens) {
233252
tokens[i].type = dt::DRAWING_FULL;
234253
break;
235254
case dt::TAG_NAME:
236-
if (i + 3 < tokens.size() && (len == 4 || len == 5) && !strncmp(str.c_str() + pos + len - 4, "clip", 4)) {
255+
if (i + 3 < tokens.size() && (len == 4 || len == 5) && !strncmp(str.data() + pos + len - 4, "clip", 4)) {
237256
if (tokens[i + 1].type != dt::OPEN_PAREN)
238257
goto tag_p;
239258

@@ -318,10 +337,8 @@ void MarkDrawings(std::string const& str, std::vector<DialogueToken> &tokens) {
318337
}
319338
}
320339

321-
void SplitWords(std::string const& str, std::vector<DialogueToken> &tokens) {
340+
void SplitWords(std::string_view str, std::vector<DialogueToken> &tokens) {
322341
MarkDrawings(str, tokens);
323342
WordSplitter(str, tokens).SplitWords();
324343
}
325-
326-
}
327344
}

libaegisub/ass/karaoke.cpp

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// Copyright (c) 2022, Thomas Goyne <plorkyeran@aegisub.org>
2+
//
3+
// Permission to use, copy, modify, and distribute this software for any
4+
// purpose with or without fee is hereby granted, provided that the above
5+
// copyright notice and this permission notice appear in all copies.
6+
//
7+
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10+
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12+
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13+
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14+
//
15+
// Aegisub Project http://www.aegisub.org/
16+
17+
#include <libaegisub/ass/karaoke.h>
18+
19+
#include <libaegisub/karaoke_matcher.h>
20+
#include <libaegisub/format.h>
21+
22+
#include <boost/algorithm/string/predicate.hpp>
23+
#include <boost/algorithm/string/trim.hpp>
24+
25+
namespace agi::ass {
26+
std::string KaraokeSyllable::GetText(bool k_tag) const {
27+
std::string ret;
28+
29+
if (k_tag)
30+
ret = agi::format("{%s%d}", tag_type, ((duration + 5) / 10));
31+
32+
std::string_view sv = text;
33+
size_t idx = 0;
34+
for (auto const& ovr : ovr_tags) {
35+
ret += sv.substr(idx, ovr.first - idx);
36+
ret += ovr.second;
37+
idx = ovr.first;
38+
}
39+
ret += sv.substr(idx);
40+
return ret;
41+
}
42+
43+
void Karaoke::SetLine(std::vector<KaraokeSyllable>&& syls, bool auto_split, std::optional<int> end_time) {
44+
this->syls = std::move(syls);
45+
46+
if (end_time) {
47+
Normalize(*end_time);
48+
}
49+
50+
// Add karaoke splits at each space
51+
if (auto_split && size() == 1) {
52+
AutoSplit();
53+
}
54+
55+
AnnounceSyllablesChanged();
56+
}
57+
58+
void Karaoke::Normalize(int end_time) {
59+
auto& last_syl = syls.back();
60+
int last_end = last_syl.start_time + last_syl.duration;
61+
62+
// Total duration is shorter than the line length so just extend the last
63+
// syllable; this has no effect on rendering but is easier to work with
64+
if (last_end < end_time)
65+
last_syl.duration += end_time - last_end;
66+
else if (last_end > end_time) {
67+
// Truncate any syllables that extend past the end of the line
68+
for (auto& syl : syls) {
69+
if (syl.start_time > end_time) {
70+
syl.start_time = end_time;
71+
syl.duration = 0;
72+
}
73+
else {
74+
syl.duration = std::min(syl.duration, end_time - syl.start_time);
75+
}
76+
}
77+
}
78+
}
79+
80+
void Karaoke::AutoSplit() {
81+
size_t pos;
82+
while ((pos = syls.back().text.find(' ')) != std::string::npos)
83+
DoAddSplit(syls.size() - 1, pos + 1);
84+
}
85+
86+
std::string Karaoke::GetText() const {
87+
std::string text;
88+
text.reserve(size() * 10);
89+
90+
for (auto const& syl : syls)
91+
text += syl.GetText(true);
92+
93+
return text;
94+
}
95+
96+
std::string_view Karaoke::GetTagType() const {
97+
return begin()->tag_type;
98+
}
99+
100+
void Karaoke::SetTagType(std::string_view new_type) {
101+
for (auto& syl : syls)
102+
syl.tag_type = new_type;
103+
}
104+
105+
void Karaoke::DoAddSplit(size_t syl_idx, size_t pos) {
106+
syls.insert(syls.begin() + syl_idx + 1, KaraokeSyllable());
107+
KaraokeSyllable &syl = syls[syl_idx];
108+
KaraokeSyllable &new_syl = syls[syl_idx + 1];
109+
110+
// If the syl is empty or the user is adding a syllable past the last
111+
// character then pos will be out of bounds. Doing this is a bit goofy,
112+
// but it's sometimes required for complex karaoke scripts
113+
if (pos < syl.text.size()) {
114+
new_syl.text = syl.text.substr(pos);
115+
syl.text = syl.text.substr(0, pos);
116+
}
117+
118+
if (new_syl.text.empty())
119+
new_syl.duration = 0;
120+
else if (syl.text.empty()) {
121+
new_syl.duration = syl.duration;
122+
syl.duration = 0;
123+
}
124+
else {
125+
new_syl.duration = (syl.duration * new_syl.text.size() / (syl.text.size() + new_syl.text.size()) + 5) / 10 * 10;
126+
syl.duration -= new_syl.duration;
127+
}
128+
129+
assert(syl.duration >= 0);
130+
131+
new_syl.start_time = syl.start_time + syl.duration;
132+
new_syl.tag_type = syl.tag_type;
133+
134+
// Move all override tags after the split to the new syllable and fix the indices
135+
size_t text_len = syl.text.size();
136+
for (auto it = syl.ovr_tags.begin(); it != syl.ovr_tags.end(); ) {
137+
if (it->first < text_len)
138+
++it;
139+
else {
140+
new_syl.ovr_tags[it->first - text_len] = it->second;
141+
syl.ovr_tags.erase(it++);
142+
}
143+
}
144+
}
145+
146+
void Karaoke::AddSplit(size_t syl_idx, size_t pos) {
147+
DoAddSplit(syl_idx, pos);
148+
AnnounceSyllablesChanged();
149+
}
150+
151+
void Karaoke::RemoveSplit(size_t syl_idx) {
152+
// Don't allow removing the first syllable
153+
if (syl_idx == 0) return;
154+
155+
KaraokeSyllable &syl = syls[syl_idx];
156+
KaraokeSyllable &prev = syls[syl_idx - 1];
157+
158+
prev.duration += syl.duration;
159+
for (auto const& tag : syl.ovr_tags)
160+
prev.ovr_tags[tag.first + prev.text.size()] = tag.second;
161+
prev.text += syl.text;
162+
163+
syls.erase(syls.begin() + syl_idx);
164+
165+
AnnounceSyllablesChanged();
166+
}
167+
168+
void Karaoke::SetStartTime(size_t syl_idx, int time) {
169+
// Don't allow moving the first syllable
170+
if (syl_idx == 0) return;
171+
172+
KaraokeSyllable &syl = syls[syl_idx];
173+
KaraokeSyllable &prev = syls[syl_idx - 1];
174+
175+
assert(time >= prev.start_time);
176+
assert(time <= syl.start_time + syl.duration);
177+
178+
int delta = time - syl.start_time;
179+
syl.start_time = time;
180+
syl.duration -= delta;
181+
prev.duration += delta;
182+
}
183+
184+
void Karaoke::SetLineTimes(int start_time, int end_time) {
185+
assert(end_time >= start_time);
186+
187+
size_t idx = 0;
188+
// Chop off any portion of syllables starting before the new start_time
189+
do {
190+
int delta = start_time - syls[idx].start_time;
191+
syls[idx].start_time = start_time;
192+
syls[idx].duration = std::max(0, syls[idx].duration - delta);
193+
} while (++idx < syls.size() && syls[idx].start_time < start_time);
194+
195+
// And truncate any syllables ending after the new end_time
196+
idx = syls.size() - 1;
197+
while (syls[idx].start_time > end_time) {
198+
syls[idx].start_time = end_time;
199+
syls[idx].duration = 0;
200+
--idx;
201+
}
202+
syls[idx].duration = end_time - syls[idx].start_time;
203+
}
204+
205+
} // namespace agi::ass

libaegisub/ass/time.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ static void decompose_time(int ms_time, int& h, int& m, int& s, int& ms) {
3939
namespace agi {
4040
Time::Time(int time) : time(util::mid(0, time, MAX_TIME)) { }
4141

42-
Time::Time(std::string const& text) {
42+
Time::Time(std::string_view text) {
4343
int after_decimal = -1;
4444
int current = 0;
4545
for (char c : text) {

0 commit comments

Comments
 (0)