@next2d/builder 0.1.7 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +59 -0
- package/dist/cli.js +141 -0
- package/dist/constants.d.ts +53 -0
- package/dist/constants.js +64 -0
- package/dist/context.d.ts +29 -0
- package/dist/context.js +19 -0
- package/dist/electron.d.ts +10 -0
- package/dist/electron.js +142 -0
- package/dist/index.js +18 -754
- package/dist/native.d.ts +27 -0
- package/dist/native.js +123 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.js +40 -0
- package/dist/web.d.ts +18 -0
- package/dist/web.js +70 -0
- package/dist/xbox.d.ts +62 -0
- package/dist/xbox.js +426 -0
- package/package.json +12 -9
- package/templates/xbox/CMakeLists.txt +116 -11
- package/templates/xbox/README.md +34 -8
- package/templates/xbox/cmake/FetchDawn.cmake +47 -6
- package/templates/xbox/cmake/FindV8.cmake +15 -0
- package/templates/xbox/js/bootstrap.js +151 -28
- package/templates/xbox/js/selftest.js +740 -90
- package/templates/xbox/src/AssetLoader.cpp +115 -12
- package/templates/xbox/src/AssetLoader.h +4 -0
- package/templates/xbox/src/EmbeddedAssets.cpp +149 -0
- package/templates/xbox/src/EmbeddedAssets.h +60 -0
- package/templates/xbox/src/EventLoop.cpp +13 -1
- package/templates/xbox/src/EventLoop.h +12 -0
- package/templates/xbox/src/bindings/Audio.cpp +211 -31
- package/templates/xbox/src/bindings/Bindings.h +10 -0
- package/templates/xbox/src/bindings/Canvas.cpp +77 -2
- package/templates/xbox/src/bindings/Canvas2D.cpp +496 -43
- package/templates/xbox/src/bindings/Console.cpp +6 -1
- package/templates/xbox/src/bindings/DomShims.cpp +143 -0
- package/templates/xbox/src/bindings/EventTarget.cpp +11 -0
- package/templates/xbox/src/bindings/Fetch.cpp +2 -8
- package/templates/xbox/src/bindings/Image.cpp +243 -15
- package/templates/xbox/src/bindings/Polyfills.cpp +1226 -0
- package/templates/xbox/src/bindings/RasterCore.h +105 -1
- package/templates/xbox/src/bindings/Video.cpp +153 -10
- package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +844 -18
- package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +7 -9
- package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +32 -0
- package/templates/xbox/src/gpu/DawnContext.cpp +245 -7
- package/templates/xbox/src/gpu/DawnContext.h +18 -0
- package/templates/xbox/src/main.cpp +459 -8
- package/templates/xbox/src/platform/AudioDecoder.cpp +121 -0
- package/templates/xbox/src/platform/AudioDecoder.h +18 -0
- package/templates/xbox/src/platform/AudioEngine.cpp +37 -6
- package/templates/xbox/src/platform/DecodeQueue.cpp +136 -0
- package/templates/xbox/src/platform/DecodeQueue.h +27 -0
- package/templates/xbox/src/platform/ImageDecoder.cpp +44 -0
- package/templates/xbox/src/platform/ImageDecoder.h +18 -0
- package/templates/xbox/src/platform/StbTextRasterizer.cpp +324 -0
- package/templates/xbox/src/platform/StbTextRasterizer.h +37 -0
- package/templates/xbox/src/platform/TextRasterizer.cpp +32 -0
- package/templates/xbox/src/platform/WicDecoder.cpp +19 -0
- package/templates/xbox/src/v8/CodeCache.h +223 -0
- package/templates/xbox/src/v8/V8Runtime.cpp +76 -11
- package/templates/xbox/src/v8/V8Util.h +79 -0
- package/templates/xbox/src/v8/WeakHandle.h +45 -0
- package/templates/xbox/src/worker/WorkerRuntime.cpp +432 -48
- package/templates/xbox/src/worker/WorkerRuntime.h +43 -13
- package/templates/xbox/tests/check_local.sh +7 -2
- package/templates/xbox/tests/gamecore_api_probe.cpp +172 -0
- package/templates/xbox/tests/raster_test.cpp +50 -0
- package/templates/xbox/tests/windows_platform_test.cpp +75 -3
- package/templates/xbox/third_party/LICENSES.md +14 -0
- package/templates/xbox/third_party/dr_mp3.h +5412 -0
- package/templates/xbox/third_party/dr_wav.h +9131 -0
- package/templates/xbox/third_party/stb_image.h +7988 -0
- package/templates/xbox/third_party/stb_truetype.h +5079 -0
- package/templates/xbox/third_party/stb_vorbis.c +5584 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#include "StbTextRasterizer.h"
|
|
2
|
+
|
|
3
|
+
#define STB_TRUETYPE_IMPLEMENTATION
|
|
4
|
+
#define STBTT_STATIC
|
|
5
|
+
#include "third_party/stb_truetype.h"
|
|
6
|
+
|
|
7
|
+
#include <algorithm>
|
|
8
|
+
#include <cctype>
|
|
9
|
+
#include <cmath>
|
|
10
|
+
#include <cstring>
|
|
11
|
+
|
|
12
|
+
namespace next2d::stbtext {
|
|
13
|
+
|
|
14
|
+
namespace {
|
|
15
|
+
|
|
16
|
+
struct Font {
|
|
17
|
+
std::string name_lower; // 登録名 (小文字)
|
|
18
|
+
bool bold = false; // 名前に "bold" を含むか
|
|
19
|
+
std::vector<uint8_t> data; // TTF/OTF 本体 (fontinfo が参照し続けるため保持)
|
|
20
|
+
stbtt_fontinfo info = {};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// 登録フォント。ゲーム起動時に main が登録し、以後は読み取りのみ
|
|
24
|
+
// (worker も協調シングルスレッドなので排他は不要)。
|
|
25
|
+
std::vector<Font>& Fonts()
|
|
26
|
+
{
|
|
27
|
+
static std::vector<Font> fonts;
|
|
28
|
+
return fonts;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
std::string ToLower(std::string s)
|
|
32
|
+
{
|
|
33
|
+
std::transform(s.begin(), s.end(), s.begin(),
|
|
34
|
+
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
|
35
|
+
return s;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// CSS font 文字列から px サイズ / bold / ファミリ名リストを取り出す。
|
|
39
|
+
// 例: "bold 24px 'Noto Sans JP', sans-serif"
|
|
40
|
+
struct CssFont {
|
|
41
|
+
double px = 16.0;
|
|
42
|
+
bool bold = false;
|
|
43
|
+
std::vector<std::string> families; // 小文字・引用符除去済み
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
CssFont ParseCssFont(const std::string& css)
|
|
47
|
+
{
|
|
48
|
+
CssFont out;
|
|
49
|
+
const std::string lower = ToLower(css);
|
|
50
|
+
|
|
51
|
+
if (lower.find("bold") != std::string::npos) {
|
|
52
|
+
out.bold = true;
|
|
53
|
+
} else {
|
|
54
|
+
// 数値ウェイト (600 以上を bold 扱い)。"NNNpx" と混同しないよう
|
|
55
|
+
// 独立トークンのみ見る。
|
|
56
|
+
size_t pos = 0;
|
|
57
|
+
while (pos < lower.size()) {
|
|
58
|
+
const size_t start = lower.find_first_not_of(" \t", pos);
|
|
59
|
+
if (start == std::string::npos) break;
|
|
60
|
+
size_t end = lower.find_first_of(" \t", start);
|
|
61
|
+
if (end == std::string::npos) end = lower.size();
|
|
62
|
+
const std::string token = lower.substr(start, end - start);
|
|
63
|
+
char* endp = nullptr;
|
|
64
|
+
const long w = std::strtol(token.c_str(), &endp, 10);
|
|
65
|
+
if (endp && *endp == '\0' && w >= 600 && w <= 1000) {
|
|
66
|
+
out.bold = true;
|
|
67
|
+
}
|
|
68
|
+
pos = end;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// "NNpx" (小数許容)
|
|
73
|
+
const size_t px_pos = lower.find("px");
|
|
74
|
+
if (px_pos != std::string::npos) {
|
|
75
|
+
size_t num_start = px_pos;
|
|
76
|
+
while (num_start > 0 &&
|
|
77
|
+
(std::isdigit(static_cast<unsigned char>(lower[num_start - 1])) ||
|
|
78
|
+
lower[num_start - 1] == '.')) {
|
|
79
|
+
--num_start;
|
|
80
|
+
}
|
|
81
|
+
if (num_start < px_pos) {
|
|
82
|
+
out.px = std::strtod(lower.substr(num_start, px_pos - num_start).c_str(), nullptr);
|
|
83
|
+
}
|
|
84
|
+
if (out.px <= 0) {
|
|
85
|
+
out.px = 16.0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// px の後ろがファミリリスト (カンマ区切り、引用符除去)
|
|
89
|
+
size_t fam_start = lower.find_first_of(" \t", px_pos);
|
|
90
|
+
if (fam_start != std::string::npos) {
|
|
91
|
+
std::string families = lower.substr(fam_start + 1);
|
|
92
|
+
size_t pos = 0;
|
|
93
|
+
while (pos <= families.size()) {
|
|
94
|
+
size_t comma = families.find(',', pos);
|
|
95
|
+
if (comma == std::string::npos) comma = families.size();
|
|
96
|
+
std::string fam = families.substr(pos, comma - pos);
|
|
97
|
+
// trim + 引用符除去
|
|
98
|
+
const auto is_trim = [](char c) {
|
|
99
|
+
return c == ' ' || c == '\t' || c == '\'' || c == '"';
|
|
100
|
+
};
|
|
101
|
+
while (!fam.empty() && is_trim(fam.front())) fam.erase(fam.begin());
|
|
102
|
+
while (!fam.empty() && is_trim(fam.back())) fam.pop_back();
|
|
103
|
+
if (!fam.empty()) {
|
|
104
|
+
out.families.push_back(fam);
|
|
105
|
+
}
|
|
106
|
+
pos = comma + 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// wstring -> Unicode コードポイント列。
|
|
114
|
+
// Windows は wchar_t=UTF-16 (サロゲートペア復号)、他は UTF-32。
|
|
115
|
+
std::vector<uint32_t> ToCodepoints(const std::wstring& text)
|
|
116
|
+
{
|
|
117
|
+
std::vector<uint32_t> cps;
|
|
118
|
+
cps.reserve(text.size());
|
|
119
|
+
if (sizeof(wchar_t) == 2) {
|
|
120
|
+
for (size_t i = 0; i < text.size(); ++i) {
|
|
121
|
+
const uint32_t c = static_cast<uint16_t>(text[i]);
|
|
122
|
+
if (c >= 0xD800 && c <= 0xDBFF && i + 1 < text.size()) {
|
|
123
|
+
const uint32_t lo = static_cast<uint16_t>(text[i + 1]);
|
|
124
|
+
if (lo >= 0xDC00 && lo <= 0xDFFF) {
|
|
125
|
+
cps.push_back(0x10000 + ((c - 0xD800) << 10) + (lo - 0xDC00));
|
|
126
|
+
++i;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
cps.push_back(c);
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
for (wchar_t c : text) {
|
|
134
|
+
cps.push_back(static_cast<uint32_t>(c));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return cps;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// css のファミリ指定と bold から最適な登録フォントを選ぶ。
|
|
141
|
+
// 優先: ファミリ一致 + bold 一致 > ファミリ一致 > bold 一致 > 先頭。
|
|
142
|
+
const Font* SelectFont(const CssFont& css)
|
|
143
|
+
{
|
|
144
|
+
const auto& fonts = Fonts();
|
|
145
|
+
if (fonts.empty()) {
|
|
146
|
+
return nullptr;
|
|
147
|
+
}
|
|
148
|
+
const Font* family_match = nullptr;
|
|
149
|
+
for (const auto& fam : css.families) {
|
|
150
|
+
for (const auto& f : fonts) {
|
|
151
|
+
if (f.name_lower.find(fam) == std::string::npos &&
|
|
152
|
+
fam.find(f.name_lower) == std::string::npos) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (f.bold == css.bold) {
|
|
156
|
+
return &f; // ファミリ+bold 完全一致
|
|
157
|
+
}
|
|
158
|
+
if (!family_match) {
|
|
159
|
+
family_match = &f;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (family_match) {
|
|
164
|
+
return family_match;
|
|
165
|
+
}
|
|
166
|
+
for (const auto& f : fonts) {
|
|
167
|
+
if (f.bold == css.bold) {
|
|
168
|
+
return &f;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return &fonts.front();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 計測とラスタライズで共有するレイアウト計算。
|
|
175
|
+
struct Layout {
|
|
176
|
+
const Font* font = nullptr;
|
|
177
|
+
float scale = 0;
|
|
178
|
+
int ascent = 0, descent = 0; // フォント単位
|
|
179
|
+
double width = 0; // px
|
|
180
|
+
std::vector<uint32_t> cps;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
bool ComputeLayout(const std::string& css_font, const std::wstring& text, Layout& lo)
|
|
184
|
+
{
|
|
185
|
+
const CssFont css = ParseCssFont(css_font);
|
|
186
|
+
lo.font = SelectFont(css);
|
|
187
|
+
if (!lo.font) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
lo.scale = stbtt_ScaleForPixelHeight(&lo.font->info, static_cast<float>(css.px));
|
|
191
|
+
int line_gap = 0;
|
|
192
|
+
stbtt_GetFontVMetrics(&lo.font->info, &lo.ascent, &lo.descent, &line_gap);
|
|
193
|
+
lo.cps = ToCodepoints(text);
|
|
194
|
+
|
|
195
|
+
double x = 0;
|
|
196
|
+
for (size_t i = 0; i < lo.cps.size(); ++i) {
|
|
197
|
+
int advance = 0, lsb = 0;
|
|
198
|
+
stbtt_GetCodepointHMetrics(&lo.font->info, static_cast<int>(lo.cps[i]), &advance, &lsb);
|
|
199
|
+
x += advance * lo.scale;
|
|
200
|
+
if (i + 1 < lo.cps.size()) {
|
|
201
|
+
x += stbtt_GetCodepointKernAdvance(
|
|
202
|
+
&lo.font->info,
|
|
203
|
+
static_cast<int>(lo.cps[i]),
|
|
204
|
+
static_cast<int>(lo.cps[i + 1])) * lo.scale;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
lo.width = x;
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
} // namespace
|
|
212
|
+
|
|
213
|
+
bool RegisterFont(const std::string& name, std::vector<uint8_t> data)
|
|
214
|
+
{
|
|
215
|
+
if (data.empty()) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
Font font;
|
|
219
|
+
font.name_lower = ToLower(name);
|
|
220
|
+
font.bold = font.name_lower.find("bold") != std::string::npos;
|
|
221
|
+
font.data = std::move(data);
|
|
222
|
+
const int offset = stbtt_GetFontOffsetForIndex(font.data.data(), 0);
|
|
223
|
+
if (offset < 0 || !stbtt_InitFont(&font.info, font.data.data(), offset)) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
Fonts().push_back(std::move(font));
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
bool HasFonts()
|
|
231
|
+
{
|
|
232
|
+
return !Fonts().empty();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
bool MeasureText(const std::string& css_font, const std::wstring& text,
|
|
236
|
+
TextMetricsInfo& out)
|
|
237
|
+
{
|
|
238
|
+
Layout lo;
|
|
239
|
+
if (!ComputeLayout(css_font, text, lo)) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
out.width = lo.width;
|
|
243
|
+
out.ascent = lo.ascent * lo.scale;
|
|
244
|
+
out.descent = -lo.descent * lo.scale; // stbtt の descent は負値
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
bool RasterizeText(const std::string& css_font, const std::wstring& text,
|
|
249
|
+
uint8_t r, uint8_t g, uint8_t b, TextBitmap& out)
|
|
250
|
+
{
|
|
251
|
+
if (text.empty()) {
|
|
252
|
+
return false; // DirectWrite 実装と同じ契約 (空文字は false)
|
|
253
|
+
}
|
|
254
|
+
Layout lo;
|
|
255
|
+
if (!ComputeLayout(css_font, text, lo)) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const double ascent_px = lo.ascent * lo.scale;
|
|
260
|
+
const double descent_px = -lo.descent * lo.scale;
|
|
261
|
+
const int width = std::max(1, static_cast<int>(std::ceil(lo.width)) + 2);
|
|
262
|
+
const int height = std::max(1, static_cast<int>(std::ceil(ascent_px + descent_px)) + 2);
|
|
263
|
+
const int baseline = static_cast<int>(std::ceil(ascent_px)) + 1;
|
|
264
|
+
|
|
265
|
+
// 8bit カバレッジへ全グリフを合成してから RGBA 化する
|
|
266
|
+
std::vector<uint8_t> coverage(static_cast<size_t>(width) * height, 0);
|
|
267
|
+
double pen_x = 1.0;
|
|
268
|
+
for (size_t i = 0; i < lo.cps.size(); ++i) {
|
|
269
|
+
const int cp = static_cast<int>(lo.cps[i]);
|
|
270
|
+
int advance = 0, lsb = 0;
|
|
271
|
+
stbtt_GetCodepointHMetrics(&lo.font->info, cp, &advance, &lsb);
|
|
272
|
+
|
|
273
|
+
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
|
274
|
+
const float shift_x =
|
|
275
|
+
static_cast<float>(pen_x + lsb * lo.scale - std::floor(pen_x + lsb * lo.scale));
|
|
276
|
+
stbtt_GetCodepointBitmapBoxSubpixel(
|
|
277
|
+
&lo.font->info, cp, lo.scale, lo.scale, shift_x, 0, &x0, &y0, &x1, &y1);
|
|
278
|
+
|
|
279
|
+
const int gw = x1 - x0;
|
|
280
|
+
const int gh = y1 - y0;
|
|
281
|
+
if (gw > 0 && gh > 0) {
|
|
282
|
+
std::vector<uint8_t> glyph(static_cast<size_t>(gw) * gh);
|
|
283
|
+
stbtt_MakeCodepointBitmapSubpixel(
|
|
284
|
+
&lo.font->info, glyph.data(), gw, gh, gw,
|
|
285
|
+
lo.scale, lo.scale, shift_x, 0, cp);
|
|
286
|
+
|
|
287
|
+
const int dst_x = static_cast<int>(std::floor(pen_x + lsb * lo.scale));
|
|
288
|
+
const int dst_y = baseline + y0;
|
|
289
|
+
for (int yy = 0; yy < gh; ++yy) {
|
|
290
|
+
const int py = dst_y + yy;
|
|
291
|
+
if (py < 0 || py >= height) continue;
|
|
292
|
+
for (int xx = 0; xx < gw; ++xx) {
|
|
293
|
+
const int px = dst_x + xx;
|
|
294
|
+
if (px < 0 || px >= width) continue;
|
|
295
|
+
uint8_t& dst = coverage[static_cast<size_t>(py) * width + px];
|
|
296
|
+
dst = std::max(dst, glyph[static_cast<size_t>(yy) * gw + xx]);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
pen_x += advance * lo.scale;
|
|
302
|
+
if (i + 1 < lo.cps.size()) {
|
|
303
|
+
pen_x += stbtt_GetCodepointKernAdvance(
|
|
304
|
+
&lo.font->info, cp, static_cast<int>(lo.cps[i + 1])) * lo.scale;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
out.width = width;
|
|
309
|
+
out.height = height;
|
|
310
|
+
out.baseline = baseline;
|
|
311
|
+
out.rgba.assign(static_cast<size_t>(width) * height * 4, 0);
|
|
312
|
+
for (size_t i = 0; i < coverage.size(); ++i) {
|
|
313
|
+
const uint8_t a = coverage[i];
|
|
314
|
+
if (!a) continue;
|
|
315
|
+
uint8_t* dst = &out.rgba[i * 4];
|
|
316
|
+
dst[0] = r;
|
|
317
|
+
dst[1] = g;
|
|
318
|
+
dst[2] = b;
|
|
319
|
+
dst[3] = a; // straight alpha (TextBitmap の契約)
|
|
320
|
+
}
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
} // namespace next2d::stbtext
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// stb_truetype によるテキスト計測/ラスタライズ (全プラットフォーム共通)。
|
|
2
|
+
//
|
|
3
|
+
// コンソール (Game Core OS) には DirectWrite が無いため、TextRasterizer.cpp の
|
|
4
|
+
// コンソール分岐がこの実装へ委譲する (デスクトップは従来どおり DirectWrite)。
|
|
5
|
+
// システムフォントにも依存できないため、フォントは実行時に登録する:
|
|
6
|
+
// - 埋め込み pak 内の *.ttf/*.otf (ゲーム資材。main.cpp が起動時に登録)
|
|
7
|
+
// - exe 隣接の fonts/*.ttf (開発ビルド用)
|
|
8
|
+
// フォント未登録の場合は false を返し、呼び出し側 (Canvas2D) が近似値へ
|
|
9
|
+
// フォールバックする (TextRasterizer.h と同じ契約)。
|
|
10
|
+
//
|
|
11
|
+
// 制限 (DirectWrite との差): 合字/シェーピング無し (CJK は影響なし)、
|
|
12
|
+
// bold は登録フォントに bold バリアントがある場合のみ反映。
|
|
13
|
+
#pragma once
|
|
14
|
+
|
|
15
|
+
#include "TextRasterizer.h" // TextBitmap / TextMetricsInfo
|
|
16
|
+
|
|
17
|
+
#include <cstdint>
|
|
18
|
+
#include <string>
|
|
19
|
+
#include <vector>
|
|
20
|
+
|
|
21
|
+
namespace next2d::stbtext {
|
|
22
|
+
|
|
23
|
+
// TTF/OTF バイト列をフォントとして登録する (name はファイル名 stem 等の識別子。
|
|
24
|
+
// "bold" を含む名前は bold バリアントとして扱う)。不正なフォントは false。
|
|
25
|
+
bool RegisterFont(const std::string& name, std::vector<uint8_t> data);
|
|
26
|
+
|
|
27
|
+
// 登録済みフォントが 1 つ以上あるか。
|
|
28
|
+
bool HasFonts();
|
|
29
|
+
|
|
30
|
+
// css_font 例: "bold 24px Arial", "16px 'Noto Sans JP', sans-serif"
|
|
31
|
+
bool MeasureText(const std::string& css_font, const std::wstring& text,
|
|
32
|
+
TextMetricsInfo& out);
|
|
33
|
+
|
|
34
|
+
bool RasterizeText(const std::string& css_font, const std::wstring& text,
|
|
35
|
+
uint8_t r, uint8_t g, uint8_t b, TextBitmap& out);
|
|
36
|
+
|
|
37
|
+
} // namespace next2d::stbtext
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
#include "TextRasterizer.h"
|
|
2
2
|
|
|
3
|
+
// DirectWrite / Direct2D / WIC はコンソール (Game Core OS) に存在しない
|
|
4
|
+
// デスクトップ専用 API。コンソールでは false を返し、呼び出し側
|
|
5
|
+
// (Canvas2D の fillText/measureText) が近似値へフォールバックする
|
|
6
|
+
// (契約は TextRasterizer.h に明記済み)。
|
|
7
|
+
// 実機でのテキスト描画品質は devkit 段階の課題 (stb_truetype + フォント同梱)。
|
|
8
|
+
#if !NEXT2D_XBOX_CONSOLE
|
|
9
|
+
|
|
3
10
|
#include "../bindings/RasterCore.h"
|
|
4
11
|
|
|
5
12
|
#include <algorithm>
|
|
@@ -181,3 +188,28 @@ bool RasterizeTextWithDWrite(const std::string& css_font, const std::wstring& te
|
|
|
181
188
|
}
|
|
182
189
|
|
|
183
190
|
} // namespace next2d
|
|
191
|
+
|
|
192
|
+
#else // NEXT2D_XBOX_CONSOLE
|
|
193
|
+
|
|
194
|
+
// コンソール: stb_truetype 実装へ委譲する。フォントは main.cpp が起動時に
|
|
195
|
+
// 埋め込み資材 (*.ttf/*.otf) から登録する。未登録なら false を返し、
|
|
196
|
+
// 呼び出し側 (Canvas2D) が近似値へフォールバックする。
|
|
197
|
+
#include "StbTextRasterizer.h"
|
|
198
|
+
|
|
199
|
+
namespace next2d {
|
|
200
|
+
|
|
201
|
+
bool MeasureTextWithDWrite(const std::string& css_font, const std::wstring& text,
|
|
202
|
+
TextMetricsInfo& out)
|
|
203
|
+
{
|
|
204
|
+
return stbtext::MeasureText(css_font, text, out);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
bool RasterizeTextWithDWrite(const std::string& css_font, const std::wstring& text,
|
|
208
|
+
uint8_t r, uint8_t g, uint8_t b, TextBitmap& out)
|
|
209
|
+
{
|
|
210
|
+
return stbtext::RasterizeText(css_font, text, r, g, b, out);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
} // namespace next2d
|
|
214
|
+
|
|
215
|
+
#endif // !NEXT2D_XBOX_CONSOLE
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#include "WicDecoder.h"
|
|
2
2
|
|
|
3
|
+
// WIC はコンソール (Game Core OS) に存在しないデスクトップ専用 API。
|
|
4
|
+
// 第一経路は ImageDecoder (stb_image) で、本ファイルはデスクトップの
|
|
5
|
+
// フォールバック (TIFF/HEIF 等 stb 非対応形式) のみを担う。
|
|
6
|
+
#if !NEXT2D_XBOX_CONSOLE
|
|
7
|
+
|
|
3
8
|
#include <wincodec.h>
|
|
4
9
|
#include <wrl/client.h>
|
|
5
10
|
|
|
@@ -61,3 +66,17 @@ bool DecodeImageWithWIC(const std::vector<uint8_t>& input, DecodedImage& out)
|
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
} // namespace next2d
|
|
69
|
+
|
|
70
|
+
#else // NEXT2D_XBOX_CONSOLE
|
|
71
|
+
|
|
72
|
+
namespace next2d {
|
|
73
|
+
|
|
74
|
+
// コンソール: WIC 無し。ImageDecoder (stb_image) が全デコードを担う。
|
|
75
|
+
bool DecodeImageWithWIC(const std::vector<uint8_t>&, DecodedImage&)
|
|
76
|
+
{
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
} // namespace next2d
|
|
81
|
+
|
|
82
|
+
#endif // !NEXT2D_XBOX_CONSOLE
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// V8 バイトコードキャッシュ。
|
|
2
|
+
//
|
|
3
|
+
// player+framework のバンドル (数 MB) は毎起動フルパースがかかり、jitless
|
|
4
|
+
// (Ignition) では起動時間の主要因になる。初回起動時に eager コンパイルして
|
|
5
|
+
// バイトコードをディスクへ保存し、2 回目以降はそれを消費してパースを省略する。
|
|
6
|
+
// 生成物はデータであり動的コード生成ではないため、コンソール (JIT 禁止) でも使える。
|
|
7
|
+
//
|
|
8
|
+
// - 保存先: %LOCALAPPDATA%\Next2D\codecache\<hash>.v8bc
|
|
9
|
+
// (DomShims の localStorage バックエンドと同じ配置方針。環境変数が無ければ無効化)
|
|
10
|
+
// - キー: スクリプト名 + ソース内容の FNV-1a。ソースが変われば別ファイルになる。
|
|
11
|
+
// - V8 のバージョン/フラグ不一致は V8 自身が検出して reject する (その場で作り直す)。
|
|
12
|
+
// - すべてのエラーは graceful degrade (キャッシュ無しの通常コンパイルに落ちる)。
|
|
13
|
+
// - 小さいスクリプトはパースが安く I/O の方が高いため対象外 (kMinSourceSize)。
|
|
14
|
+
#pragma once
|
|
15
|
+
|
|
16
|
+
#include <v8.h>
|
|
17
|
+
|
|
18
|
+
#include <chrono>
|
|
19
|
+
#include <cstdint>
|
|
20
|
+
#include <cstdio>
|
|
21
|
+
#include <cstdlib>
|
|
22
|
+
#include <cstring>
|
|
23
|
+
#include <filesystem>
|
|
24
|
+
#include <fstream>
|
|
25
|
+
#include <memory>
|
|
26
|
+
#include <string>
|
|
27
|
+
#include <vector>
|
|
28
|
+
|
|
29
|
+
namespace next2d::codecache {
|
|
30
|
+
|
|
31
|
+
// キャッシュ対象の最小ソースサイズ。bootstrap.js (十数 KB) は対象外、
|
|
32
|
+
// ゲーム/player バンドル (数百 KB〜数 MB) が対象になる境界。
|
|
33
|
+
inline constexpr size_t kMinSourceSize = 64 * 1024;
|
|
34
|
+
|
|
35
|
+
inline const std::filesystem::path& CacheDir()
|
|
36
|
+
{
|
|
37
|
+
static const std::filesystem::path dir = []() -> std::filesystem::path {
|
|
38
|
+
const char* base = std::getenv("LOCALAPPDATA");
|
|
39
|
+
if (!base || !*base) {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
std::error_code ec;
|
|
43
|
+
std::filesystem::path d = std::filesystem::path(base) / "Next2D" / "codecache";
|
|
44
|
+
std::filesystem::create_directories(d, ec);
|
|
45
|
+
if (ec) {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
// ゲーム更新でソースが変わるとキャッシュはファイル名ごと変わり、旧ファイルは
|
|
49
|
+
// 参照されない孤児になる。30 日アクセスの無い .v8bc を起動時に掃除する
|
|
50
|
+
// (Load が使用時に write time を touch するため実質 LRU)。
|
|
51
|
+
const auto now = std::filesystem::file_time_type::clock::now();
|
|
52
|
+
std::error_code iter_ec;
|
|
53
|
+
for (const auto& entry : std::filesystem::directory_iterator(d, iter_ec)) {
|
|
54
|
+
std::error_code e2;
|
|
55
|
+
if (entry.path().extension() != ".v8bc") {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const auto t = std::filesystem::last_write_time(entry.path(), e2);
|
|
59
|
+
if (!e2 && now - t > std::chrono::hours(24 * 30)) {
|
|
60
|
+
std::filesystem::remove(entry.path(), e2);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return d;
|
|
64
|
+
}();
|
|
65
|
+
return dir;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
inline uint64_t Fnv1a(const void* data, size_t size, uint64_t hash = 1469598103934665603ull)
|
|
69
|
+
{
|
|
70
|
+
const auto* p = static_cast<const unsigned char*>(data);
|
|
71
|
+
for (size_t i = 0; i < size; ++i) {
|
|
72
|
+
hash ^= p[i];
|
|
73
|
+
hash *= 1099511628211ull;
|
|
74
|
+
}
|
|
75
|
+
return hash;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
inline std::filesystem::path CacheFileFor(const std::string& name, const std::string& source)
|
|
79
|
+
{
|
|
80
|
+
const std::filesystem::path& dir = CacheDir();
|
|
81
|
+
if (dir.empty()) {
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
uint64_t h = Fnv1a(name.data(), name.size());
|
|
85
|
+
h = Fnv1a(source.data(), source.size(), h);
|
|
86
|
+
char buf[17] = {};
|
|
87
|
+
std::snprintf(buf, sizeof(buf), "%016llx", static_cast<unsigned long long>(h));
|
|
88
|
+
return dir / (std::string(buf) + ".v8bc");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
inline bool Eligible(const std::string& source)
|
|
92
|
+
{
|
|
93
|
+
return source.size() >= kMinSourceSize && !CacheDir().empty();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// キャッシュファイルを読む。戻り値の所有権は ScriptCompiler::Source が取る
|
|
97
|
+
// (BufferOwned なので CachedData 破棄時に delete[] される)。無ければ nullptr。
|
|
98
|
+
inline v8::ScriptCompiler::CachedData* Load(const std::string& name, const std::string& source)
|
|
99
|
+
{
|
|
100
|
+
const std::filesystem::path file = CacheFileFor(name, source);
|
|
101
|
+
if (file.empty()) {
|
|
102
|
+
return nullptr;
|
|
103
|
+
}
|
|
104
|
+
std::ifstream ifs(file, std::ios::binary);
|
|
105
|
+
if (!ifs) {
|
|
106
|
+
return nullptr;
|
|
107
|
+
}
|
|
108
|
+
std::vector<char> buf(
|
|
109
|
+
(std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
|
110
|
+
if (buf.empty()) {
|
|
111
|
+
return nullptr;
|
|
112
|
+
}
|
|
113
|
+
// 使用時に write time を更新し、CacheDir の 30 日掃除を LRU として機能させる
|
|
114
|
+
std::error_code ec;
|
|
115
|
+
std::filesystem::last_write_time(
|
|
116
|
+
file, std::filesystem::file_time_type::clock::now(), ec);
|
|
117
|
+
|
|
118
|
+
auto* data = new uint8_t[buf.size()];
|
|
119
|
+
std::memcpy(data, buf.data(), buf.size());
|
|
120
|
+
return new v8::ScriptCompiler::CachedData(
|
|
121
|
+
data, static_cast<int>(buf.size()),
|
|
122
|
+
v8::ScriptCompiler::CachedData::BufferOwned);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
inline void Store(const std::string& name, const std::string& source,
|
|
126
|
+
const v8::ScriptCompiler::CachedData* data)
|
|
127
|
+
{
|
|
128
|
+
if (!data || !data->data || data->length <= 0) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const std::filesystem::path file = CacheFileFor(name, source);
|
|
132
|
+
if (file.empty()) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
std::ofstream ofs(file, std::ios::binary | std::ios::trunc);
|
|
136
|
+
if (ofs) {
|
|
137
|
+
ofs.write(reinterpret_cast<const char*>(data->data), data->length);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// classic script をキャッシュ込みでコンパイルする。
|
|
142
|
+
// - キャッシュ有り: kConsumeCodeCache で消費 (V8 が reject したら作り直して保存)
|
|
143
|
+
// - キャッシュ無し: kEagerCompile で全関数を一括コンパイルして保存
|
|
144
|
+
// (初回はその分遅いが、以降の起動は lazy compile も含めて丸ごと省ける)
|
|
145
|
+
// - 対象外サイズ: 従来どおりの lazy コンパイル
|
|
146
|
+
inline v8::MaybeLocal<v8::Script> Compile(v8::Local<v8::Context> ctx,
|
|
147
|
+
v8::Local<v8::String> src,
|
|
148
|
+
v8::ScriptOrigin& origin,
|
|
149
|
+
const std::string& name,
|
|
150
|
+
const std::string& source_str)
|
|
151
|
+
{
|
|
152
|
+
if (!Eligible(source_str)) {
|
|
153
|
+
v8::ScriptCompiler::Source source(src, origin);
|
|
154
|
+
return v8::ScriptCompiler::Compile(ctx, &source);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (v8::ScriptCompiler::CachedData* cached = Load(name, source_str)) {
|
|
158
|
+
v8::ScriptCompiler::Source source(src, origin, cached);
|
|
159
|
+
v8::Local<v8::Script> script;
|
|
160
|
+
if (!v8::ScriptCompiler::Compile(
|
|
161
|
+
ctx, &source, v8::ScriptCompiler::kConsumeCodeCache).ToLocal(&script)) {
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
if (source.GetCachedData() && source.GetCachedData()->rejected) {
|
|
165
|
+
std::unique_ptr<v8::ScriptCompiler::CachedData> fresh(
|
|
166
|
+
v8::ScriptCompiler::CreateCodeCache(script->GetUnboundScript()));
|
|
167
|
+
Store(name, source_str, fresh.get());
|
|
168
|
+
}
|
|
169
|
+
return script;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
v8::ScriptCompiler::Source source(src, origin);
|
|
173
|
+
v8::Local<v8::Script> script;
|
|
174
|
+
if (!v8::ScriptCompiler::Compile(
|
|
175
|
+
ctx, &source, v8::ScriptCompiler::kEagerCompile).ToLocal(&script)) {
|
|
176
|
+
return {};
|
|
177
|
+
}
|
|
178
|
+
std::unique_ptr<v8::ScriptCompiler::CachedData> fresh(
|
|
179
|
+
v8::ScriptCompiler::CreateCodeCache(script->GetUnboundScript()));
|
|
180
|
+
Store(name, source_str, fresh.get());
|
|
181
|
+
return script;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ES module 版。規約は Compile と同じ。
|
|
185
|
+
inline v8::MaybeLocal<v8::Module> CompileModule(v8::Isolate* isolate,
|
|
186
|
+
v8::Local<v8::String> src,
|
|
187
|
+
v8::ScriptOrigin& origin,
|
|
188
|
+
const std::string& name,
|
|
189
|
+
const std::string& source_str)
|
|
190
|
+
{
|
|
191
|
+
if (!Eligible(source_str)) {
|
|
192
|
+
v8::ScriptCompiler::Source source(src, origin);
|
|
193
|
+
return v8::ScriptCompiler::CompileModule(isolate, &source);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (v8::ScriptCompiler::CachedData* cached = Load(name, source_str)) {
|
|
197
|
+
v8::ScriptCompiler::Source source(src, origin, cached);
|
|
198
|
+
v8::Local<v8::Module> module;
|
|
199
|
+
if (!v8::ScriptCompiler::CompileModule(
|
|
200
|
+
isolate, &source, v8::ScriptCompiler::kConsumeCodeCache).ToLocal(&module)) {
|
|
201
|
+
return {};
|
|
202
|
+
}
|
|
203
|
+
if (source.GetCachedData() && source.GetCachedData()->rejected) {
|
|
204
|
+
std::unique_ptr<v8::ScriptCompiler::CachedData> fresh(
|
|
205
|
+
v8::ScriptCompiler::CreateCodeCache(module->GetUnboundModuleScript()));
|
|
206
|
+
Store(name, source_str, fresh.get());
|
|
207
|
+
}
|
|
208
|
+
return module;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
v8::ScriptCompiler::Source source(src, origin);
|
|
212
|
+
v8::Local<v8::Module> module;
|
|
213
|
+
if (!v8::ScriptCompiler::CompileModule(
|
|
214
|
+
isolate, &source, v8::ScriptCompiler::kEagerCompile).ToLocal(&module)) {
|
|
215
|
+
return {};
|
|
216
|
+
}
|
|
217
|
+
std::unique_ptr<v8::ScriptCompiler::CachedData> fresh(
|
|
218
|
+
v8::ScriptCompiler::CreateCodeCache(module->GetUnboundModuleScript()));
|
|
219
|
+
Store(name, source_str, fresh.get());
|
|
220
|
+
return module;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
} // namespace next2d::codecache
|