@next2d/builder 0.1.8 → 0.1.11

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.
Files changed (67) hide show
  1. package/dist/cli.d.ts +59 -0
  2. package/dist/cli.js +141 -0
  3. package/dist/constants.d.ts +53 -0
  4. package/dist/constants.js +64 -0
  5. package/dist/context.d.ts +29 -0
  6. package/dist/context.js +19 -0
  7. package/dist/electron.d.ts +10 -0
  8. package/dist/electron.js +142 -0
  9. package/dist/index.js +18 -790
  10. package/dist/native.d.ts +27 -0
  11. package/dist/native.js +123 -0
  12. package/dist/utils.d.ts +27 -0
  13. package/dist/utils.js +40 -0
  14. package/dist/web.d.ts +18 -0
  15. package/dist/web.js +70 -0
  16. package/dist/xbox.d.ts +62 -0
  17. package/dist/xbox.js +426 -0
  18. package/package.json +11 -9
  19. package/templates/xbox/CMakeLists.txt +47 -11
  20. package/templates/xbox/README.md +18 -0
  21. package/templates/xbox/js/bootstrap.js +120 -0
  22. package/templates/xbox/src/AssetLoader.cpp +115 -12
  23. package/templates/xbox/src/AssetLoader.h +4 -0
  24. package/templates/xbox/src/EmbeddedAssets.cpp +149 -0
  25. package/templates/xbox/src/EmbeddedAssets.h +60 -0
  26. package/templates/xbox/src/EventLoop.cpp +0 -15
  27. package/templates/xbox/src/bindings/Audio.cpp +118 -22
  28. package/templates/xbox/src/bindings/Canvas.cpp +32 -2
  29. package/templates/xbox/src/bindings/Canvas2D.cpp +495 -39
  30. package/templates/xbox/src/bindings/Console.cpp +6 -1
  31. package/templates/xbox/src/bindings/DomShims.cpp +18 -2
  32. package/templates/xbox/src/bindings/EventTarget.cpp +9 -0
  33. package/templates/xbox/src/bindings/Image.cpp +83 -57
  34. package/templates/xbox/src/bindings/Polyfills.cpp +671 -236
  35. package/templates/xbox/src/bindings/RasterCore.h +105 -1
  36. package/templates/xbox/src/bindings/Video.cpp +57 -3
  37. package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +29 -3
  38. package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +4 -1
  39. package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +32 -0
  40. package/templates/xbox/src/gpu/DawnContext.cpp +167 -16
  41. package/templates/xbox/src/main.cpp +385 -16
  42. package/templates/xbox/src/platform/AudioDecoder.cpp +121 -0
  43. package/templates/xbox/src/platform/AudioDecoder.h +18 -0
  44. package/templates/xbox/src/platform/AudioEngine.cpp +28 -3
  45. package/templates/xbox/src/platform/DecodeQueue.cpp +136 -0
  46. package/templates/xbox/src/platform/DecodeQueue.h +27 -0
  47. package/templates/xbox/src/platform/ImageDecoder.cpp +44 -0
  48. package/templates/xbox/src/platform/ImageDecoder.h +18 -0
  49. package/templates/xbox/src/platform/StbTextRasterizer.cpp +324 -0
  50. package/templates/xbox/src/platform/StbTextRasterizer.h +37 -0
  51. package/templates/xbox/src/platform/TextRasterizer.cpp +32 -0
  52. package/templates/xbox/src/platform/WicDecoder.cpp +19 -0
  53. package/templates/xbox/src/v8/CodeCache.h +223 -0
  54. package/templates/xbox/src/v8/V8Runtime.cpp +34 -33
  55. package/templates/xbox/src/v8/V8Util.h +54 -4
  56. package/templates/xbox/src/worker/WorkerRuntime.cpp +165 -65
  57. package/templates/xbox/src/worker/WorkerRuntime.h +23 -3
  58. package/templates/xbox/tests/check_local.sh +5 -1
  59. package/templates/xbox/tests/gamecore_api_probe.cpp +172 -0
  60. package/templates/xbox/tests/raster_test.cpp +50 -0
  61. package/templates/xbox/tests/windows_platform_test.cpp +75 -3
  62. package/templates/xbox/third_party/LICENSES.md +14 -0
  63. package/templates/xbox/third_party/dr_mp3.h +5412 -0
  64. package/templates/xbox/third_party/dr_wav.h +9131 -0
  65. package/templates/xbox/third_party/stb_image.h +7988 -0
  66. package/templates/xbox/third_party/stb_truetype.h +5079 -0
  67. package/templates/xbox/third_party/stb_vorbis.c +5584 -0
@@ -1,12 +1,18 @@
1
1
  #include "AudioEngine.h"
2
+ #include "AudioDecoder.h"
2
3
 
3
4
  #include <xaudio2.h>
5
+ #include <iostream>
6
+
7
+ // Media Foundation / shlwapi はコンソール (Game Core OS) に無いデスクトップ専用 API。
8
+ // 第一経路は AudioDecoder (dr_mp3/dr_wav/stb_vorbis) で、MF は AAC 等の
9
+ // デスクトップ限定フォールバックのみ。XAudio2 は両プラットフォームで使える。
10
+ #if !NEXT2D_XBOX_CONSOLE
4
11
  #include <mfapi.h>
5
12
  #include <mfidl.h>
6
13
  #include <mfreadwrite.h>
7
14
  #include <shlwapi.h>
8
15
  #include <wrl/client.h>
9
- #include <iostream>
10
16
 
11
17
  #pragma comment(lib, "mfplat.lib")
12
18
  #pragma comment(lib, "mfreadwrite.lib")
@@ -14,6 +20,7 @@
14
20
  #pragma comment(lib, "shlwapi.lib")
15
21
 
16
22
  using Microsoft::WRL::ComPtr;
23
+ #endif
17
24
 
18
25
  namespace next2d {
19
26
 
@@ -97,15 +104,19 @@ AudioEngine::~AudioEngine()
97
104
  if (xaudio_) {
98
105
  xaudio_->Release();
99
106
  }
107
+ #if !NEXT2D_XBOX_CONSOLE
100
108
  MFShutdown();
109
+ #endif
101
110
  }
102
111
 
103
112
  bool AudioEngine::Initialize()
104
113
  {
114
+ #if !NEXT2D_XBOX_CONSOLE
105
115
  if (FAILED(MFStartup(MF_VERSION))) {
106
116
  std::cerr << "[Audio] MFStartup failed" << std::endl;
107
117
  return false;
108
118
  }
119
+ #endif
109
120
  if (FAILED(XAudio2Create(&xaudio_, 0, XAUDIO2_DEFAULT_PROCESSOR))) {
110
121
  std::cerr << "[Audio] XAudio2Create failed" << std::endl;
111
122
  return false;
@@ -119,8 +130,21 @@ bool AudioEngine::Initialize()
119
130
 
120
131
  bool AudioEngine::Decode(const std::vector<uint8_t>& input, PcmBuffer& out)
121
132
  {
122
- // メモリストリームから Media Foundation の SourceReader を作り、
123
- // 32bit float PCM へ変換して読み出す。
133
+ // 第一候補: ポータブルデコーダ (dr_mp3/dr_wav/stb_vorbis)。
134
+ // コンソール (Game Core) には Media Foundation が無いため、共通経路を先に
135
+ // 通すことで PC 検証と実機の挙動を一致させる。
136
+ if (DecodeAudio(input, out)) {
137
+ return true;
138
+ }
139
+
140
+ #if NEXT2D_XBOX_CONSOLE
141
+ // コンソール: MF が無いため共通デコーダで扱えない形式は失敗とする
142
+ // (呼び出し側の graceful degradation = 無音バッファで進行)。
143
+ return false;
144
+ #else
145
+ // フォールバック (デスクトップのみ): Media Foundation の SourceReader。
146
+ // AAC/WMA 等、MP3/WAV/OGG 以外の形式への保険。
147
+ // メモリストリームから 32bit float PCM へ変換して読み出す。
124
148
  ComPtr<IStream> stream =
125
149
  SHCreateMemStream(input.data(), static_cast<UINT>(input.size()));
126
150
  if (!stream) {
@@ -180,6 +204,7 @@ bool AudioEngine::Decode(const std::vector<uint8_t>& input, PcmBuffer& out)
180
204
  }
181
205
 
182
206
  return !out.samples.empty();
207
+ #endif // NEXT2D_XBOX_CONSOLE
183
208
  }
184
209
 
185
210
  std::shared_ptr<AudioVoice> AudioEngine::CreateVoice(std::shared_ptr<PcmBuffer> buffer)
@@ -0,0 +1,136 @@
1
+ #include "DecodeQueue.h"
2
+
3
+ #ifdef _WIN32
4
+ #include <objbase.h>
5
+ #endif
6
+
7
+ #include <algorithm>
8
+ #include <condition_variable>
9
+ #include <deque>
10
+ #include <mutex>
11
+ #include <thread>
12
+ #include <vector>
13
+
14
+ namespace next2d::decodequeue {
15
+
16
+ namespace {
17
+
18
+ struct Job {
19
+ std::function<void()> work;
20
+ std::function<void()> complete;
21
+ };
22
+
23
+ struct Queue {
24
+ std::mutex mutex;
25
+ std::condition_variable cv;
26
+ std::deque<Job> pending; // 未処理 (プールスレッドが取る)
27
+ std::deque<std::function<void()>> done; // work 完了済み (Pump が実行)
28
+ std::vector<std::thread> threads;
29
+ bool stop = false;
30
+
31
+ void ThreadMain()
32
+ {
33
+ #ifdef _WIN32
34
+ // WIC / Media Foundation は COM を要する。プールスレッド毎に MTA で初期化する
35
+ // (per-call 実装なので MTA スレッド間で共有状態は無い)。
36
+ CoInitializeEx(nullptr, COINIT_MULTITHREADED);
37
+ #endif
38
+ for (;;) {
39
+ Job job;
40
+ {
41
+ std::unique_lock<std::mutex> lock(mutex);
42
+ cv.wait(lock, [this] { return stop || !pending.empty(); });
43
+ if (stop) {
44
+ break;
45
+ }
46
+ job = std::move(pending.front());
47
+ pending.pop_front();
48
+ }
49
+ if (job.work) {
50
+ job.work();
51
+ }
52
+ {
53
+ std::lock_guard<std::mutex> lock(mutex);
54
+ done.push_back(std::move(job.complete));
55
+ }
56
+ }
57
+ #ifdef _WIN32
58
+ CoUninitialize();
59
+ #endif
60
+ }
61
+
62
+ void EnsureThreads()
63
+ {
64
+ if (!threads.empty()) {
65
+ return;
66
+ }
67
+ // デコードは CPU バウンド。コア数の半分 (2〜4) で遷移時のバースト
68
+ // (画像数十枚 + BGM) を並列処理しつつ、メイン/GC スレッドを圧迫しない。
69
+ const unsigned hw = std::thread::hardware_concurrency();
70
+ const unsigned count = std::clamp(hw / 2u, 2u, 4u);
71
+ threads.reserve(count);
72
+ for (unsigned i = 0; i < count; ++i) {
73
+ threads.emplace_back([this] { ThreadMain(); });
74
+ }
75
+ }
76
+ };
77
+
78
+ Queue& Q()
79
+ {
80
+ static Queue q;
81
+ return q;
82
+ }
83
+
84
+ } // namespace
85
+
86
+ void Submit(std::function<void()> work, std::function<void()> complete)
87
+ {
88
+ Queue& q = Q();
89
+ {
90
+ std::lock_guard<std::mutex> lock(q.mutex);
91
+ if (q.stop) {
92
+ return; // Shutdown 後の Submit は破棄 (終了処理中のロード要求)
93
+ }
94
+ q.EnsureThreads();
95
+ q.pending.push_back({std::move(work), std::move(complete)});
96
+ }
97
+ q.cv.notify_one();
98
+ }
99
+
100
+ void Pump()
101
+ {
102
+ Queue& q = Q();
103
+ std::deque<std::function<void()>> ready;
104
+ {
105
+ std::lock_guard<std::mutex> lock(q.mutex);
106
+ ready.swap(q.done);
107
+ }
108
+ for (auto& complete : ready) {
109
+ if (complete) {
110
+ complete();
111
+ }
112
+ }
113
+ }
114
+
115
+ void Shutdown()
116
+ {
117
+ Queue& q = Q();
118
+ {
119
+ std::lock_guard<std::mutex> lock(q.mutex);
120
+ q.stop = true;
121
+ }
122
+ q.cv.notify_all();
123
+ for (auto& t : q.threads) {
124
+ if (t.joinable()) {
125
+ t.join();
126
+ }
127
+ }
128
+ q.threads.clear();
129
+ // 未実行の work / complete を破棄する。complete は v8::Global を捕捉して
130
+ // いるため、この関数は Isolate 破棄前に呼ばれる必要がある (main.cpp の後始末順)。
131
+ std::lock_guard<std::mutex> lock(q.mutex);
132
+ q.pending.clear();
133
+ q.done.clear();
134
+ }
135
+
136
+ } // namespace next2d::decodequeue
@@ -0,0 +1,27 @@
1
+ // DecodeQueue: リソースデコード用のバックグラウンドスレッドプール。
2
+ //
3
+ // 画像 (WIC) / 音声 (Media Foundation) のデコードはメインスレッドで同期実行すると
4
+ // 画面遷移時に 1 秒超のフレームヒッチになる (--perf 計測で特定)。work を
5
+ // プールスレッドへ逃がし、complete をメインループの Pump() で実行することで
6
+ // ヒッチを解消し、複数リソースの並列デコードでロード時間も短縮する。
7
+ // スレッドは Xbox で許可されている (禁止は動的コード生成のみ)。
8
+ //
9
+ // 契約:
10
+ // - work : プールスレッドで実行される。V8 / JS ヒープに触れてはならない。
11
+ // (WIC/MF は per-call 実装で COM はスレッド毎に MTA 初期化済み)
12
+ // - complete: work 完了後、メインスレッドの Pump() から実行される。V8 可。
13
+ // (v8::Global を捕捉してよい。Pump は isolate/コンテキストスコープ外で
14
+ // 呼ばれるため、complete 側で HandleScope/Context::Scope を張ること)
15
+ // - Shutdown: 全スレッドを join し、未実行の work / complete を破棄する。
16
+ // 捕捉された v8::Global の破棄があるため Isolate 破棄前に呼ぶこと。
17
+ #pragma once
18
+
19
+ #include <functional>
20
+
21
+ namespace next2d::decodequeue {
22
+
23
+ void Submit(std::function<void()> work, std::function<void()> complete);
24
+ void Pump();
25
+ void Shutdown();
26
+
27
+ } // namespace next2d::decodequeue
@@ -0,0 +1,44 @@
1
+ #include "ImageDecoder.h"
2
+
3
+ // stb_image: public domain のヘッダオンリー画像デコーダ (third_party/)。
4
+ // メモリ入力のみ使用するため stdio を切る。
5
+ #define STB_IMAGE_IMPLEMENTATION
6
+ #define STBI_NO_STDIO
7
+ #include "third_party/stb_image.h"
8
+
9
+ #if defined(_WIN32) && !NEXT2D_XBOX_CONSOLE
10
+ #include "WicDecoder.h"
11
+ #endif
12
+
13
+ #include <cstring>
14
+
15
+ namespace next2d {
16
+
17
+ bool DecodeImage(const std::vector<uint8_t>& input, DecodedImage& out)
18
+ {
19
+ if (input.empty()) {
20
+ return false;
21
+ }
22
+
23
+ // 1) stb_image (PNG/JPEG/GIF/BMP/TGA/PSD)。コンソールでも動く共通経路。
24
+ int w = 0, h = 0, comp = 0;
25
+ stbi_uc* pixels = stbi_load_from_memory(
26
+ input.data(), static_cast<int>(input.size()), &w, &h, &comp, 4);
27
+ if (pixels) {
28
+ out.width = static_cast<uint32_t>(w);
29
+ out.height = static_cast<uint32_t>(h);
30
+ out.rgba.assign(pixels, pixels + static_cast<size_t>(w) * h * 4);
31
+ stbi_image_free(pixels);
32
+ return true;
33
+ }
34
+
35
+ #if defined(_WIN32) && !NEXT2D_XBOX_CONSOLE
36
+ // 2) デスクトップのみ: WIC フォールバック (TIFF/HEIF 等 stb 非対応形式の保険)。
37
+ // コンソールには WIC が無いためこの経路はコンパイルされない。
38
+ return DecodeImageWithWIC(input, out);
39
+ #else
40
+ return false;
41
+ #endif
42
+ }
43
+
44
+ } // namespace next2d
@@ -0,0 +1,18 @@
1
+ // 画像デコード (エンコード済みバイト列 -> RGBA8)。
2
+ //
3
+ // 第一候補は stb_image (PNG/JPEG/GIF/BMP、全プラットフォーム共通)。
4
+ // コンソール (Game Core OS) には WIC が存在しないため、WIC 依存を第一経路から
5
+ // 外すことで PC 検証と実機の挙動を一致させる。デスクトップのみ、stb が扱えない
6
+ // 形式 (TIFF/HEIF 等) への保険として WIC フォールバックを残す。
7
+ #pragma once
8
+
9
+ #include "ImageTypes.h"
10
+
11
+ #include <cstdint>
12
+ #include <vector>
13
+
14
+ namespace next2d {
15
+
16
+ bool DecodeImage(const std::vector<uint8_t>& input, DecodedImage& out);
17
+
18
+ } // namespace next2d
@@ -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