@next2d/builder 0.1.5 → 0.1.7
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/README.md +15 -0
- package/dist/index.js +315 -3
- package/package.json +10 -8
- package/templates/xbox/CMakeLists.txt +124 -0
- package/templates/xbox/MicrosoftGame.config +36 -0
- package/templates/xbox/README.md +372 -0
- package/templates/xbox/cmake/FetchDawn.cmake +42 -0
- package/templates/xbox/cmake/FindV8.cmake +68 -0
- package/templates/xbox/js/bootstrap.js +143 -0
- package/templates/xbox/js/selftest.js +515 -0
- package/templates/xbox/src/AssetLoader.cpp +106 -0
- package/templates/xbox/src/AssetLoader.h +35 -0
- package/templates/xbox/src/EventLoop.cpp +162 -0
- package/templates/xbox/src/EventLoop.h +73 -0
- package/templates/xbox/src/HostContext.h +58 -0
- package/templates/xbox/src/bindings/Audio.cpp +320 -0
- package/templates/xbox/src/bindings/Bindings.h +75 -0
- package/templates/xbox/src/bindings/Canvas.cpp +171 -0
- package/templates/xbox/src/bindings/Canvas2D.cpp +462 -0
- package/templates/xbox/src/bindings/Console.cpp +52 -0
- package/templates/xbox/src/bindings/DomShims.cpp +221 -0
- package/templates/xbox/src/bindings/EventTarget.cpp +127 -0
- package/templates/xbox/src/bindings/EventTarget.h +17 -0
- package/templates/xbox/src/bindings/Fetch.cpp +149 -0
- package/templates/xbox/src/bindings/Gamepad.cpp +79 -0
- package/templates/xbox/src/bindings/Image.cpp +160 -0
- package/templates/xbox/src/bindings/ImageSource.h +26 -0
- package/templates/xbox/src/bindings/Network.cpp +303 -0
- package/templates/xbox/src/bindings/RasterCore.h +265 -0
- package/templates/xbox/src/bindings/Timers.cpp +87 -0
- package/templates/xbox/src/bindings/Video.cpp +235 -0
- package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +1318 -0
- package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +119 -0
- package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +245 -0
- package/templates/xbox/src/gpu/DawnContext.cpp +170 -0
- package/templates/xbox/src/gpu/DawnContext.h +64 -0
- package/templates/xbox/src/main.cpp +368 -0
- package/templates/xbox/src/platform/AudioEngine.cpp +184 -0
- package/templates/xbox/src/platform/AudioEngine.h +65 -0
- package/templates/xbox/src/platform/GamepadManager.cpp +83 -0
- package/templates/xbox/src/platform/GamepadManager.h +40 -0
- package/templates/xbox/src/platform/ImageTypes.h +17 -0
- package/templates/xbox/src/platform/TextRasterizer.cpp +183 -0
- package/templates/xbox/src/platform/TextRasterizer.h +35 -0
- package/templates/xbox/src/platform/WicDecoder.cpp +63 -0
- package/templates/xbox/src/platform/WicDecoder.h +16 -0
- package/templates/xbox/src/v8/V8Runtime.cpp +285 -0
- package/templates/xbox/src/v8/V8Runtime.h +68 -0
- package/templates/xbox/src/v8/V8Util.h +56 -0
- package/templates/xbox/src/worker/WorkerRuntime.cpp +453 -0
- package/templates/xbox/src/worker/WorkerRuntime.h +108 -0
- package/templates/xbox/tests/check_local.sh +56 -0
- package/templates/xbox/tests/raster_test.cpp +279 -0
- package/templates/xbox/tests/windows_platform_test.cpp +187 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// GamepadManager: GameInput でゲームパッド状態をポーリングし、
|
|
2
|
+
// W3C Gamepad API (standard mapping) 相当のスナップショットへ変換する。
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <array>
|
|
6
|
+
#include <cstdint>
|
|
7
|
+
#include <vector>
|
|
8
|
+
|
|
9
|
+
struct IGameInput;
|
|
10
|
+
|
|
11
|
+
namespace next2d {
|
|
12
|
+
|
|
13
|
+
// standard mapping: 17 ボタン / 4 軸
|
|
14
|
+
struct GamepadSnapshot {
|
|
15
|
+
bool connected = false;
|
|
16
|
+
std::array<float, 4> axes = {0, 0, 0, 0}; // LX, LY, RX, RY
|
|
17
|
+
std::array<float, 17> buttons = {}; // 押下量 (0.0-1.0)
|
|
18
|
+
std::array<bool, 17> pressed = {};
|
|
19
|
+
double timestamp = 0.0;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
class GamepadManager {
|
|
23
|
+
public:
|
|
24
|
+
GamepadManager();
|
|
25
|
+
~GamepadManager();
|
|
26
|
+
|
|
27
|
+
bool Initialize();
|
|
28
|
+
|
|
29
|
+
// 毎フレーム呼ぶ。now_ms は EventLoop::Now()。
|
|
30
|
+
void Poll(double now_ms);
|
|
31
|
+
|
|
32
|
+
// 現在の全パッドスナップショット (最大 4)
|
|
33
|
+
const std::vector<GamepadSnapshot>& snapshots() const { return snapshots_; }
|
|
34
|
+
|
|
35
|
+
private:
|
|
36
|
+
IGameInput* game_input_ = nullptr;
|
|
37
|
+
std::vector<GamepadSnapshot> snapshots_;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
} // namespace next2d
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// 画像デコード結果の共有型。V8 に依存しないため、単体テスト
|
|
2
|
+
// (tests/windows_platform_test.cpp) からも利用できる。
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
namespace next2d {
|
|
9
|
+
|
|
10
|
+
// createImageBitmap / transferToImageBitmap が返す ImageBitmap の実体。
|
|
11
|
+
struct DecodedImage {
|
|
12
|
+
uint32_t width = 0;
|
|
13
|
+
uint32_t height = 0;
|
|
14
|
+
std::vector<uint8_t> rgba; // RGBA8 straight alpha
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
} // namespace next2d
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#include "TextRasterizer.h"
|
|
2
|
+
|
|
3
|
+
#include "../bindings/RasterCore.h"
|
|
4
|
+
|
|
5
|
+
#include <algorithm>
|
|
6
|
+
#include <cmath>
|
|
7
|
+
|
|
8
|
+
#include <d2d1.h>
|
|
9
|
+
#include <dwrite.h>
|
|
10
|
+
#include <wincodec.h>
|
|
11
|
+
#include <wrl/client.h>
|
|
12
|
+
|
|
13
|
+
#pragma comment(lib, "d2d1.lib")
|
|
14
|
+
#pragma comment(lib, "dwrite.lib")
|
|
15
|
+
|
|
16
|
+
using Microsoft::WRL::ComPtr;
|
|
17
|
+
|
|
18
|
+
namespace next2d {
|
|
19
|
+
|
|
20
|
+
namespace {
|
|
21
|
+
|
|
22
|
+
IDWriteFactory* DWrite()
|
|
23
|
+
{
|
|
24
|
+
static ComPtr<IDWriteFactory> factory;
|
|
25
|
+
if (!factory) {
|
|
26
|
+
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
|
|
27
|
+
reinterpret_cast<IUnknown**>(factory.GetAddressOf()));
|
|
28
|
+
}
|
|
29
|
+
return factory.Get();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
ID2D1Factory* D2D()
|
|
33
|
+
{
|
|
34
|
+
static ComPtr<ID2D1Factory> factory;
|
|
35
|
+
if (!factory) {
|
|
36
|
+
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, factory.GetAddressOf());
|
|
37
|
+
}
|
|
38
|
+
return factory.Get();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
IWICImagingFactory* WIC()
|
|
42
|
+
{
|
|
43
|
+
static ComPtr<IWICImagingFactory> factory;
|
|
44
|
+
if (!factory) {
|
|
45
|
+
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
|
46
|
+
CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER,
|
|
47
|
+
IID_PPV_ARGS(factory.GetAddressOf()));
|
|
48
|
+
}
|
|
49
|
+
return factory.Get();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// CSS font 文字列から family を抽出 (px サイズは raster::FontPixelSize)。
|
|
53
|
+
std::wstring FontFamily(const std::string& font)
|
|
54
|
+
{
|
|
55
|
+
// 末尾のカンマ区切りの先頭ファミリを採用。無ければ Segoe UI。
|
|
56
|
+
const auto pos = font.find("px");
|
|
57
|
+
std::string rest = (pos != std::string::npos && pos + 2 < font.size())
|
|
58
|
+
? font.substr(pos + 2) : "sans-serif";
|
|
59
|
+
// trim + 先頭ファミリ
|
|
60
|
+
size_t s = rest.find_first_not_of(" \t");
|
|
61
|
+
if (s == std::string::npos) return L"Segoe UI";
|
|
62
|
+
rest = rest.substr(s);
|
|
63
|
+
const auto comma = rest.find(',');
|
|
64
|
+
if (comma != std::string::npos) rest = rest.substr(0, comma);
|
|
65
|
+
// クォート除去
|
|
66
|
+
rest.erase(std::remove(rest.begin(), rest.end(), '\''), rest.end());
|
|
67
|
+
rest.erase(std::remove(rest.begin(), rest.end(), '"'), rest.end());
|
|
68
|
+
if (rest == "sans-serif" || rest.empty()) return L"Segoe UI";
|
|
69
|
+
if (rest == "serif") return L"Times New Roman";
|
|
70
|
+
if (rest == "monospace") return L"Consolas";
|
|
71
|
+
return std::wstring(rest.begin(), rest.end());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
ComPtr<IDWriteTextLayout> MakeLayout(const std::string& css_font, const std::wstring& wtext)
|
|
75
|
+
{
|
|
76
|
+
if (!DWrite()) return nullptr;
|
|
77
|
+
ComPtr<IDWriteTextFormat> format;
|
|
78
|
+
const bool bold = css_font.find("bold") != std::string::npos;
|
|
79
|
+
const bool italic = css_font.find("italic") != std::string::npos;
|
|
80
|
+
DWrite()->CreateTextFormat(
|
|
81
|
+
FontFamily(css_font).c_str(), nullptr,
|
|
82
|
+
bold ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL,
|
|
83
|
+
italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,
|
|
84
|
+
DWRITE_FONT_STRETCH_NORMAL,
|
|
85
|
+
static_cast<float>(raster::FontPixelSize(css_font)), L"en-us", &format);
|
|
86
|
+
if (!format) return nullptr;
|
|
87
|
+
ComPtr<IDWriteTextLayout> layout;
|
|
88
|
+
DWrite()->CreateTextLayout(wtext.c_str(), static_cast<UINT32>(wtext.size()),
|
|
89
|
+
format.Get(), 100000.0f, 100000.0f, &layout);
|
|
90
|
+
return layout;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
} // namespace
|
|
94
|
+
|
|
95
|
+
bool MeasureTextWithDWrite(const std::string& css_font, const std::wstring& text,
|
|
96
|
+
TextMetricsInfo& out)
|
|
97
|
+
{
|
|
98
|
+
ComPtr<IDWriteTextLayout> layout = MakeLayout(css_font, text);
|
|
99
|
+
if (!layout) return false;
|
|
100
|
+
|
|
101
|
+
DWRITE_TEXT_METRICS tm = {};
|
|
102
|
+
layout->GetMetrics(&tm);
|
|
103
|
+
out.width = tm.widthIncludingTrailingWhitespace;
|
|
104
|
+
|
|
105
|
+
UINT32 lineCount = 0;
|
|
106
|
+
layout->GetLineMetrics(nullptr, 0, &lineCount);
|
|
107
|
+
if (lineCount > 0) {
|
|
108
|
+
std::vector<DWRITE_LINE_METRICS> lines(lineCount);
|
|
109
|
+
layout->GetLineMetrics(lines.data(), lineCount, &lineCount);
|
|
110
|
+
out.ascent = lines[0].baseline;
|
|
111
|
+
out.descent = lines[0].height - lines[0].baseline;
|
|
112
|
+
} else {
|
|
113
|
+
const double px = raster::FontPixelSize(css_font);
|
|
114
|
+
out.ascent = px * 0.8;
|
|
115
|
+
out.descent = px * 0.2;
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
bool RasterizeTextWithDWrite(const std::string& css_font, const std::wstring& text,
|
|
121
|
+
uint8_t r, uint8_t g, uint8_t b, TextBitmap& out)
|
|
122
|
+
{
|
|
123
|
+
if (text.empty() || !DWrite() || !D2D() || !WIC()) return false;
|
|
124
|
+
ComPtr<IDWriteTextLayout> layout = MakeLayout(css_font, text);
|
|
125
|
+
if (!layout) return false;
|
|
126
|
+
|
|
127
|
+
DWRITE_TEXT_METRICS tm = {};
|
|
128
|
+
layout->GetMetrics(&tm);
|
|
129
|
+
const int tw = std::max(1, static_cast<int>(std::ceil(tm.widthIncludingTrailingWhitespace)) + 2);
|
|
130
|
+
const int th = std::max(1, static_cast<int>(std::ceil(tm.height)) + 2);
|
|
131
|
+
|
|
132
|
+
// fillText の x,y はベースライン起点。レイアウトは左上起点なので baseline を返す。
|
|
133
|
+
out.baseline = raster::FontPixelSize(css_font) * 0.8;
|
|
134
|
+
UINT32 lineCount = 0; layout->GetLineMetrics(nullptr, 0, &lineCount);
|
|
135
|
+
if (lineCount > 0) {
|
|
136
|
+
std::vector<DWRITE_LINE_METRICS> lines(lineCount);
|
|
137
|
+
layout->GetLineMetrics(lines.data(), lineCount, &lineCount);
|
|
138
|
+
out.baseline = lines[0].baseline;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
ComPtr<IWICBitmap> wic;
|
|
142
|
+
if (FAILED(WIC()->CreateBitmap(tw, th, GUID_WICPixelFormat32bppPBGRA,
|
|
143
|
+
WICBitmapCacheOnLoad, &wic))) return false;
|
|
144
|
+
ComPtr<ID2D1RenderTarget> rt;
|
|
145
|
+
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
|
|
146
|
+
D2D1_RENDER_TARGET_TYPE_SOFTWARE,
|
|
147
|
+
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED));
|
|
148
|
+
if (FAILED(D2D()->CreateWicBitmapRenderTarget(wic.Get(), props, &rt))) return false;
|
|
149
|
+
ComPtr<ID2D1SolidColorBrush> brush;
|
|
150
|
+
rt->CreateSolidColorBrush(
|
|
151
|
+
D2D1::ColorF(r / 255.f, g / 255.f, b / 255.f, 1.0f), &brush);
|
|
152
|
+
|
|
153
|
+
rt->BeginDraw();
|
|
154
|
+
rt->Clear(D2D1::ColorF(0, 0));
|
|
155
|
+
rt->DrawTextLayout(D2D1::Point2F(0, 0), layout.Get(), brush.Get());
|
|
156
|
+
if (FAILED(rt->EndDraw())) return false;
|
|
157
|
+
|
|
158
|
+
// 読み戻し (PBGRA -> straight RGBA)
|
|
159
|
+
ComPtr<IWICBitmapLock> lock;
|
|
160
|
+
WICRect rc = {0, 0, tw, th};
|
|
161
|
+
if (FAILED(wic->Lock(&rc, WICBitmapLockRead, &lock))) return false;
|
|
162
|
+
UINT stride = 0, size = 0; BYTE* data = nullptr;
|
|
163
|
+
lock->GetStride(&stride); lock->GetDataPointer(&size, &data);
|
|
164
|
+
|
|
165
|
+
out.width = tw;
|
|
166
|
+
out.height = th;
|
|
167
|
+
out.rgba.assign(static_cast<size_t>(tw) * th * 4, 0);
|
|
168
|
+
for (int yy = 0; yy < th; ++yy) {
|
|
169
|
+
for (int xx = 0; xx < tw; ++xx) {
|
|
170
|
+
const BYTE* px = data + yy * stride + xx * 4;
|
|
171
|
+
const double a = px[3] / 255.0;
|
|
172
|
+
if (a <= 0.0) continue;
|
|
173
|
+
uint8_t* dst = &out.rgba[(static_cast<size_t>(yy) * tw + xx) * 4];
|
|
174
|
+
dst[0] = static_cast<uint8_t>(px[2] / a); // R (un-premultiply)
|
|
175
|
+
dst[1] = static_cast<uint8_t>(px[1] / a); // G
|
|
176
|
+
dst[2] = static_cast<uint8_t>(px[0] / a); // B
|
|
177
|
+
dst[3] = px[3]; // A
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
} // namespace next2d
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// DirectWrite / Direct2D によるテキストラスタライズ。V8 非依存。
|
|
2
|
+
// Canvas2D.cpp (fillText/strokeText/measureText) と単体テストの両方から使う。
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <vector>
|
|
8
|
+
|
|
9
|
+
namespace next2d {
|
|
10
|
+
|
|
11
|
+
// テキストのラスタライズ結果 (straight RGBA)。
|
|
12
|
+
struct TextBitmap {
|
|
13
|
+
int width = 0;
|
|
14
|
+
int height = 0;
|
|
15
|
+
double baseline = 0; // 先頭行のベースライン位置 (上端から)
|
|
16
|
+
std::vector<uint8_t> rgba; // width*height*4
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// テキストのメトリクス。
|
|
20
|
+
struct TextMetricsInfo {
|
|
21
|
+
double width = 0;
|
|
22
|
+
double ascent = 0;
|
|
23
|
+
double descent = 0;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// CSS font 文字列 ("bold 24px Arial" 等) と UTF-16 テキストからメトリクスを取得する。
|
|
27
|
+
// DirectWrite が使えない場合は false (呼び出し側で近似値へフォールバック)。
|
|
28
|
+
bool MeasureTextWithDWrite(const std::string& css_font, const std::wstring& text,
|
|
29
|
+
TextMetricsInfo& out);
|
|
30
|
+
|
|
31
|
+
// テキストを指定色でラスタライズする。alpha はグリフ形状から得る。
|
|
32
|
+
bool RasterizeTextWithDWrite(const std::string& css_font, const std::wstring& text,
|
|
33
|
+
uint8_t r, uint8_t g, uint8_t b, TextBitmap& out);
|
|
34
|
+
|
|
35
|
+
} // namespace next2d
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#include "WicDecoder.h"
|
|
2
|
+
|
|
3
|
+
#include <wincodec.h>
|
|
4
|
+
#include <wrl/client.h>
|
|
5
|
+
|
|
6
|
+
using Microsoft::WRL::ComPtr;
|
|
7
|
+
|
|
8
|
+
namespace next2d {
|
|
9
|
+
|
|
10
|
+
// WIC で任意フォーマットの画像を RGBA8 にデコードする。
|
|
11
|
+
bool DecodeImageWithWIC(const std::vector<uint8_t>& input, DecodedImage& out)
|
|
12
|
+
{
|
|
13
|
+
ComPtr<IWICImagingFactory> factory;
|
|
14
|
+
if (FAILED(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER,
|
|
15
|
+
IID_PPV_ARGS(&factory)))) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
ComPtr<IWICStream> stream;
|
|
20
|
+
if (FAILED(factory->CreateStream(&stream))) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (FAILED(stream->InitializeFromMemory(
|
|
24
|
+
const_cast<uint8_t*>(input.data()), static_cast<DWORD>(input.size())))) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
ComPtr<IWICBitmapDecoder> decoder;
|
|
29
|
+
if (FAILED(factory->CreateDecoderFromStream(
|
|
30
|
+
stream.Get(), nullptr, WICDecodeMetadataCacheOnDemand, &decoder))) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
ComPtr<IWICBitmapFrameDecode> frame;
|
|
35
|
+
if (FAILED(decoder->GetFrame(0, &frame))) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
ComPtr<IWICFormatConverter> converter;
|
|
40
|
+
if (FAILED(factory->CreateFormatConverter(&converter))) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (FAILED(converter->Initialize(
|
|
44
|
+
frame.Get(), GUID_WICPixelFormat32bppRGBA,
|
|
45
|
+
WICBitmapDitherTypeNone, nullptr, 0.0, WICBitmapPaletteTypeCustom))) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
UINT w = 0, h = 0;
|
|
50
|
+
converter->GetSize(&w, &h);
|
|
51
|
+
out.width = w;
|
|
52
|
+
out.height = h;
|
|
53
|
+
out.rgba.resize(static_cast<size_t>(w) * h * 4);
|
|
54
|
+
|
|
55
|
+
const UINT stride = w * 4;
|
|
56
|
+
if (FAILED(converter->CopyPixels(
|
|
57
|
+
nullptr, stride, static_cast<UINT>(out.rgba.size()), out.rgba.data()))) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
} // namespace next2d
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// WIC (Windows Imaging Component) による画像デコード。V8 非依存。
|
|
2
|
+
// Image.cpp (createImageBitmap / Image.src) と単体テストの両方から使う。
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include "ImageTypes.h"
|
|
6
|
+
|
|
7
|
+
#include <cstdint>
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
namespace next2d {
|
|
11
|
+
|
|
12
|
+
// 任意フォーマット(PNG/JPEG/GIF/BMP 等)のバイト列を RGBA8 straight へデコードする。
|
|
13
|
+
// 呼び出し前に COM が初期化されている必要がある (CoInitializeEx)。
|
|
14
|
+
bool DecodeImageWithWIC(const std::vector<uint8_t>& input, DecodedImage& out);
|
|
15
|
+
|
|
16
|
+
} // namespace next2d
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#include "V8Runtime.h"
|
|
2
|
+
|
|
3
|
+
#include "V8Util.h"
|
|
4
|
+
#include "HostContext.h"
|
|
5
|
+
#include "bindings/Bindings.h"
|
|
6
|
+
|
|
7
|
+
#include <filesystem>
|
|
8
|
+
#include <fstream>
|
|
9
|
+
#include <iostream>
|
|
10
|
+
#include <sstream>
|
|
11
|
+
|
|
12
|
+
namespace fs = std::filesystem;
|
|
13
|
+
|
|
14
|
+
namespace next2d {
|
|
15
|
+
|
|
16
|
+
std::unique_ptr<v8::Platform> V8Runtime::platform_;
|
|
17
|
+
|
|
18
|
+
// isolate data slot 1: V8Runtime* (ESM resolve コールバックから参照)
|
|
19
|
+
constexpr uint32_t kRuntimeSlot = 1;
|
|
20
|
+
|
|
21
|
+
namespace {
|
|
22
|
+
|
|
23
|
+
std::string ReadTextFile(const std::string& path)
|
|
24
|
+
{
|
|
25
|
+
std::ifstream ifs(path, std::ios::binary);
|
|
26
|
+
if (!ifs) {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
std::ostringstream oss;
|
|
30
|
+
oss << ifs.rdbuf();
|
|
31
|
+
return oss.str();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
} // namespace
|
|
35
|
+
|
|
36
|
+
V8Runtime::V8Runtime() = default;
|
|
37
|
+
|
|
38
|
+
V8Runtime::~V8Runtime()
|
|
39
|
+
{
|
|
40
|
+
Dispose();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
void V8Runtime::InitializeProcess(const char* exec_path)
|
|
44
|
+
{
|
|
45
|
+
// Xbox(GDK) / Nintendo Switch のリテール環境は動的コード生成(JIT)を禁止する。
|
|
46
|
+
// V8 を jitless (Ignition インタプリタのみ・TurboFan/Sparkplug/RWXページ無し) で動かす。
|
|
47
|
+
// prebuilt V8 (build-v8.yml) は v8_jitless=true + turbofan/wasm 無効でビルドされる。
|
|
48
|
+
// wasm/opt 系フラグはそのビルドに存在しないため渡さない (未知フラグ警告を避ける)。
|
|
49
|
+
v8::V8::SetFlagsFromString("--jitless");
|
|
50
|
+
|
|
51
|
+
v8::V8::InitializeICUDefaultLocation(exec_path);
|
|
52
|
+
v8::V8::InitializeExternalStartupData(exec_path);
|
|
53
|
+
platform_ = v8::platform::NewDefaultPlatform();
|
|
54
|
+
v8::V8::InitializePlatform(platform_.get());
|
|
55
|
+
v8::V8::Initialize();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
void V8Runtime::ShutdownProcess()
|
|
59
|
+
{
|
|
60
|
+
v8::V8::Dispose();
|
|
61
|
+
v8::V8::DisposePlatform();
|
|
62
|
+
platform_.reset();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
bool V8Runtime::Initialize(HostContext* host)
|
|
66
|
+
{
|
|
67
|
+
host_ = host;
|
|
68
|
+
|
|
69
|
+
create_params_.array_buffer_allocator =
|
|
70
|
+
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
|
|
71
|
+
|
|
72
|
+
isolate_ = v8::Isolate::New(create_params_);
|
|
73
|
+
isolate_->SetData(kHostContextSlot, host);
|
|
74
|
+
isolate_->SetData(kRuntimeSlot, this);
|
|
75
|
+
|
|
76
|
+
// 例外時に未処理を潰さないよう、明示的に MicrotasksPolicy を制御する
|
|
77
|
+
isolate_->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
|
|
78
|
+
|
|
79
|
+
v8::Isolate::Scope isolate_scope(isolate_);
|
|
80
|
+
v8::HandleScope handle_scope(isolate_);
|
|
81
|
+
|
|
82
|
+
v8::Local<v8::Context> context = v8::Context::New(isolate_);
|
|
83
|
+
context_.Reset(isolate_, context);
|
|
84
|
+
|
|
85
|
+
v8::Context::Scope context_scope(context);
|
|
86
|
+
|
|
87
|
+
// ブラウザ相当グローバルをインストール
|
|
88
|
+
InstallGlobalBindings(isolate_, context->Global(), host_);
|
|
89
|
+
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
void V8Runtime::Dispose()
|
|
94
|
+
{
|
|
95
|
+
module_cache_.clear();
|
|
96
|
+
module_paths_.clear();
|
|
97
|
+
context_.Reset();
|
|
98
|
+
|
|
99
|
+
if (isolate_) {
|
|
100
|
+
isolate_->Dispose();
|
|
101
|
+
isolate_ = nullptr;
|
|
102
|
+
}
|
|
103
|
+
if (create_params_.array_buffer_allocator) {
|
|
104
|
+
delete create_params_.array_buffer_allocator;
|
|
105
|
+
create_params_.array_buffer_allocator = nullptr;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
v8::Local<v8::Context> V8Runtime::context() const
|
|
110
|
+
{
|
|
111
|
+
return context_.Get(isolate_);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
bool V8Runtime::RunScript(const std::string& source, const std::string& name)
|
|
115
|
+
{
|
|
116
|
+
v8::Isolate::Scope isolate_scope(isolate_);
|
|
117
|
+
v8::HandleScope handle_scope(isolate_);
|
|
118
|
+
v8::Local<v8::Context> ctx = context();
|
|
119
|
+
v8::Context::Scope context_scope(ctx);
|
|
120
|
+
|
|
121
|
+
v8::TryCatch try_catch(isolate_);
|
|
122
|
+
|
|
123
|
+
v8::ScriptOrigin origin(v8util::Str(isolate_, name));
|
|
124
|
+
v8::Local<v8::String> src = v8util::Str(isolate_, source);
|
|
125
|
+
|
|
126
|
+
v8::Local<v8::Script> script;
|
|
127
|
+
if (!v8::Script::Compile(ctx, src, &origin).ToLocal(&script)) {
|
|
128
|
+
ReportException(&try_catch);
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
v8::Local<v8::Value> result;
|
|
133
|
+
if (!script->Run(ctx).ToLocal(&result)) {
|
|
134
|
+
ReportException(&try_catch);
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
PumpMicrotasks();
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
v8::MaybeLocal<v8::Module> V8Runtime::LoadModule(const std::string& path,
|
|
143
|
+
const std::string& /*referrer_dir*/)
|
|
144
|
+
{
|
|
145
|
+
const std::string abs = fs::weakly_canonical(path).string();
|
|
146
|
+
|
|
147
|
+
auto cached = module_cache_.find(abs);
|
|
148
|
+
if (cached != module_cache_.end()) {
|
|
149
|
+
return cached->second.Get(isolate_);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const std::string source = ReadTextFile(abs);
|
|
153
|
+
if (source.empty() && !fs::exists(abs)) {
|
|
154
|
+
v8util::ThrowTypeError(isolate_, "Module not found: " + abs);
|
|
155
|
+
return v8::MaybeLocal<v8::Module>();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
v8::Local<v8::String> src = v8util::Str(isolate_, source);
|
|
159
|
+
|
|
160
|
+
v8::ScriptOrigin origin(
|
|
161
|
+
v8util::Str(isolate_, abs),
|
|
162
|
+
0, 0, false, -1, v8::Local<v8::Value>(),
|
|
163
|
+
false, false, /*is_module*/ true
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
v8::ScriptCompiler::Source compiler_source(src, origin);
|
|
167
|
+
v8::Local<v8::Module> module;
|
|
168
|
+
if (!v8::ScriptCompiler::CompileModule(isolate_, &compiler_source).ToLocal(&module)) {
|
|
169
|
+
return v8::MaybeLocal<v8::Module>();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
module_cache_[abs].Reset(isolate_, module);
|
|
173
|
+
module_paths_[module->GetIdentityHash()] = abs;
|
|
174
|
+
|
|
175
|
+
// 依存モジュールを再帰的にロード
|
|
176
|
+
v8::Local<v8::FixedArray> requests = module->GetModuleRequests();
|
|
177
|
+
const std::string dir = fs::path(abs).parent_path().string();
|
|
178
|
+
for (int i = 0; i < requests->Length(); ++i) {
|
|
179
|
+
v8::Local<v8::ModuleRequest> request =
|
|
180
|
+
requests->Get(context(), i).As<v8::ModuleRequest>();
|
|
181
|
+
const std::string specifier =
|
|
182
|
+
v8util::ToStdString(isolate_, request->GetSpecifier());
|
|
183
|
+
const std::string resolved = (fs::path(dir) / specifier).string();
|
|
184
|
+
if (LoadModule(resolved, dir).IsEmpty()) {
|
|
185
|
+
return v8::MaybeLocal<v8::Module>();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return module;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
v8::MaybeLocal<v8::Module> V8Runtime::ResolveModuleCallback(
|
|
193
|
+
v8::Local<v8::Context> context,
|
|
194
|
+
v8::Local<v8::String> specifier,
|
|
195
|
+
v8::Local<v8::FixedArray> /*import_assertions*/,
|
|
196
|
+
v8::Local<v8::Module> referrer)
|
|
197
|
+
{
|
|
198
|
+
v8::Isolate* isolate = context->GetIsolate();
|
|
199
|
+
auto* self = static_cast<V8Runtime*>(isolate->GetData(kRuntimeSlot));
|
|
200
|
+
|
|
201
|
+
auto it = self->module_paths_.find(referrer->GetIdentityHash());
|
|
202
|
+
const std::string referrer_dir = (it != self->module_paths_.end())
|
|
203
|
+
? fs::path(it->second).parent_path().string()
|
|
204
|
+
: std::string();
|
|
205
|
+
|
|
206
|
+
const std::string spec = v8util::ToStdString(isolate, specifier);
|
|
207
|
+
const std::string resolved = (fs::path(referrer_dir) / spec).string();
|
|
208
|
+
return self->LoadModule(resolved, referrer_dir);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
bool V8Runtime::RunModule(const std::string& /*source*/, const std::string& path)
|
|
212
|
+
{
|
|
213
|
+
v8::Isolate::Scope isolate_scope(isolate_);
|
|
214
|
+
v8::HandleScope handle_scope(isolate_);
|
|
215
|
+
v8::Local<v8::Context> ctx = context();
|
|
216
|
+
v8::Context::Scope context_scope(ctx);
|
|
217
|
+
|
|
218
|
+
v8::TryCatch try_catch(isolate_);
|
|
219
|
+
|
|
220
|
+
v8::Local<v8::Module> module;
|
|
221
|
+
if (!LoadModule(path, fs::path(path).parent_path().string()).ToLocal(&module)) {
|
|
222
|
+
ReportException(&try_catch);
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (module->InstantiateModule(ctx, ResolveModuleCallback).IsNothing()) {
|
|
227
|
+
ReportException(&try_catch);
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
v8::Local<v8::Value> result;
|
|
232
|
+
if (!module->Evaluate(ctx).ToLocal(&result)) {
|
|
233
|
+
ReportException(&try_catch);
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// トップレベル await の完了を待つためマイクロタスクを回す
|
|
238
|
+
PumpMicrotasks();
|
|
239
|
+
|
|
240
|
+
if (module->GetStatus() == v8::Module::kErrored) {
|
|
241
|
+
v8::Local<v8::Value> exception = module->GetException();
|
|
242
|
+
std::cerr << "[V8] Module evaluation error: "
|
|
243
|
+
<< v8util::ToStdString(isolate_, exception) << std::endl;
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
void V8Runtime::PumpMicrotasks()
|
|
251
|
+
{
|
|
252
|
+
isolate_->PerformMicrotaskCheckpoint();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
void V8Runtime::PumpPlatformTasks()
|
|
256
|
+
{
|
|
257
|
+
while (v8::platform::PumpMessageLoop(platform_.get(), isolate_)) {
|
|
258
|
+
// フォアグラウンドタスクを空になるまで処理
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
void V8Runtime::ReportException(v8::TryCatch* try_catch)
|
|
263
|
+
{
|
|
264
|
+
v8::HandleScope handle_scope(isolate_);
|
|
265
|
+
const std::string exception = v8util::ToStdString(isolate_, try_catch->Exception());
|
|
266
|
+
|
|
267
|
+
v8::Local<v8::Message> message = try_catch->Message();
|
|
268
|
+
if (message.IsEmpty()) {
|
|
269
|
+
std::cerr << "[V8] " << exception << std::endl;
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
v8::Local<v8::Context> ctx = context();
|
|
274
|
+
const std::string filename =
|
|
275
|
+
v8util::ToStdString(isolate_, message->GetScriptOrigin().ResourceName());
|
|
276
|
+
const int linenum = message->GetLineNumber(ctx).FromMaybe(0);
|
|
277
|
+
std::cerr << "[V8] " << filename << ":" << linenum << ": " << exception << std::endl;
|
|
278
|
+
|
|
279
|
+
v8::Local<v8::Value> stack_trace;
|
|
280
|
+
if (try_catch->StackTrace(ctx).ToLocal(&stack_trace)) {
|
|
281
|
+
std::cerr << v8util::ToStdString(isolate_, stack_trace) << std::endl;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
} // namespace next2d
|