@next2d/builder 0.1.4 → 0.1.6

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 (55) hide show
  1. package/README.md +11 -0
  2. package/dist/index.js +247 -3
  3. package/package.json +11 -9
  4. package/templates/xbox/CMakeLists.txt +121 -0
  5. package/templates/xbox/MicrosoftGame.config +36 -0
  6. package/templates/xbox/README.md +319 -0
  7. package/templates/xbox/cmake/FetchDawn.cmake +42 -0
  8. package/templates/xbox/cmake/FindV8.cmake +62 -0
  9. package/templates/xbox/js/bootstrap.js +143 -0
  10. package/templates/xbox/js/selftest.js +515 -0
  11. package/templates/xbox/src/AssetLoader.cpp +106 -0
  12. package/templates/xbox/src/AssetLoader.h +35 -0
  13. package/templates/xbox/src/EventLoop.cpp +162 -0
  14. package/templates/xbox/src/EventLoop.h +73 -0
  15. package/templates/xbox/src/HostContext.h +58 -0
  16. package/templates/xbox/src/bindings/Audio.cpp +320 -0
  17. package/templates/xbox/src/bindings/Bindings.h +75 -0
  18. package/templates/xbox/src/bindings/Canvas.cpp +171 -0
  19. package/templates/xbox/src/bindings/Canvas2D.cpp +462 -0
  20. package/templates/xbox/src/bindings/Console.cpp +52 -0
  21. package/templates/xbox/src/bindings/DomShims.cpp +221 -0
  22. package/templates/xbox/src/bindings/EventTarget.cpp +127 -0
  23. package/templates/xbox/src/bindings/EventTarget.h +17 -0
  24. package/templates/xbox/src/bindings/Fetch.cpp +149 -0
  25. package/templates/xbox/src/bindings/Gamepad.cpp +79 -0
  26. package/templates/xbox/src/bindings/Image.cpp +160 -0
  27. package/templates/xbox/src/bindings/ImageSource.h +26 -0
  28. package/templates/xbox/src/bindings/Network.cpp +303 -0
  29. package/templates/xbox/src/bindings/RasterCore.h +265 -0
  30. package/templates/xbox/src/bindings/Timers.cpp +87 -0
  31. package/templates/xbox/src/bindings/Video.cpp +235 -0
  32. package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +1318 -0
  33. package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +119 -0
  34. package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +245 -0
  35. package/templates/xbox/src/gpu/DawnContext.cpp +170 -0
  36. package/templates/xbox/src/gpu/DawnContext.h +64 -0
  37. package/templates/xbox/src/main.cpp +368 -0
  38. package/templates/xbox/src/platform/AudioEngine.cpp +184 -0
  39. package/templates/xbox/src/platform/AudioEngine.h +65 -0
  40. package/templates/xbox/src/platform/GamepadManager.cpp +83 -0
  41. package/templates/xbox/src/platform/GamepadManager.h +40 -0
  42. package/templates/xbox/src/platform/ImageTypes.h +17 -0
  43. package/templates/xbox/src/platform/TextRasterizer.cpp +183 -0
  44. package/templates/xbox/src/platform/TextRasterizer.h +35 -0
  45. package/templates/xbox/src/platform/WicDecoder.cpp +63 -0
  46. package/templates/xbox/src/platform/WicDecoder.h +16 -0
  47. package/templates/xbox/src/v8/V8Runtime.cpp +284 -0
  48. package/templates/xbox/src/v8/V8Runtime.h +68 -0
  49. package/templates/xbox/src/v8/V8Util.h +56 -0
  50. package/templates/xbox/src/worker/WorkerRuntime.cpp +453 -0
  51. package/templates/xbox/src/worker/WorkerRuntime.h +108 -0
  52. package/templates/xbox/tests/check_local.sh +56 -0
  53. package/templates/xbox/tests/raster_test.bin +0 -0
  54. package/templates/xbox/tests/raster_test.cpp +279 -0
  55. package/templates/xbox/tests/windows_platform_test.cpp +187 -0
@@ -0,0 +1,171 @@
1
+ #include "Bindings.h"
2
+
3
+ #include "HostContext.h"
4
+ #include "EventTarget.h"
5
+ #include "ImageSource.h"
6
+ #include "v8/V8Util.h"
7
+
8
+ namespace next2d {
9
+
10
+ using v8util::Str;
11
+ using v8util::SetMethod;
12
+ using v8util::SetValue;
13
+ using v8util::ToStdString;
14
+
15
+ namespace {
16
+
17
+ // getContext(type) を backend に応じてディスパッチする共通実装。
18
+ // '2d' → Canvas2D、'webgpu' → WebGPU(Dawn)、'webgl2' → WebGL2(Switch 用・Xboxではnull)。
19
+ void GetContextImpl(const v8::FunctionCallbackInfo<v8::Value>& args, bool /*offscreen*/)
20
+ {
21
+ v8::Isolate* isolate = args.GetIsolate();
22
+ HostContext* host = HostContext::From(isolate);
23
+ const std::string type = args.Length() > 0 ? ToStdString(isolate, args[0]) : "";
24
+
25
+ v8::Local<v8::Object> self = args.This();
26
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
27
+ int width = host->viewport_width;
28
+ int height = host->viewport_height;
29
+ v8::Local<v8::Value> wv, hv;
30
+ if (self->Get(ctx, Str(isolate, "width")).ToLocal(&wv) && wv->IsNumber()) {
31
+ width = static_cast<int>(wv.As<v8::Number>()->Value());
32
+ }
33
+ if (self->Get(ctx, Str(isolate, "height")).ToLocal(&hv) && hv->IsNumber()) {
34
+ height = static_cast<int>(hv.As<v8::Number>()->Value());
35
+ }
36
+
37
+ // 同一 canvas への同型 getContext は同じインスタンスを返す(ブラウザ準拠)。
38
+ // player は offscreen.getContext('2d') で描画後、canvas を copyExternalImageToTexture へ
39
+ // 渡すため、2D コンテキスト(と描画済みピクセル)を canvas 上に保持する必要がある。
40
+ const char* cache_key =
41
+ (type == "2d") ? "__ctx2d" :
42
+ (type == "webgpu") ? "__ctxgpu" : "__ctxgl";
43
+ v8::Local<v8::Value> cached;
44
+ if (self->Get(ctx, Str(isolate, cache_key)).ToLocal(&cached) && cached->IsObject()) {
45
+ args.GetReturnValue().Set(cached);
46
+ return;
47
+ }
48
+
49
+ v8::Local<v8::Object> context;
50
+ if (type == "2d") {
51
+ context = CreateCanvas2DContext(isolate, host, width, height);
52
+ } else if (type == "webgpu" && host->backend == GraphicsBackend::WebGPU) {
53
+ context = CreateWebGPUCanvasContext(isolate, host);
54
+ } else if ((type == "webgl2" || type == "webgl") && host->backend == GraphicsBackend::WebGL2) {
55
+ // «SWITCH» WebGL2(GL) バインディングをここで返す。Xbox(WebGPU)では未使用。
56
+ args.GetReturnValue().SetNull();
57
+ return;
58
+ } else {
59
+ args.GetReturnValue().SetNull();
60
+ return;
61
+ }
62
+
63
+ SetValue(isolate, context, "canvas", self);
64
+ SetValue(isolate, self, cache_key, context);
65
+ args.GetReturnValue().Set(context);
66
+ }
67
+
68
+ void Canvas_GetContext(const v8::FunctionCallbackInfo<v8::Value>& args)
69
+ {
70
+ GetContextImpl(args, false);
71
+ }
72
+
73
+ void Offscreen_GetContext(const v8::FunctionCallbackInfo<v8::Value>& args)
74
+ {
75
+ GetContextImpl(args, true);
76
+ }
77
+
78
+ void GetBoundingClientRect(const v8::FunctionCallbackInfo<v8::Value>& a)
79
+ {
80
+ v8::Isolate* iso = a.GetIsolate();
81
+ HostContext* h = HostContext::From(iso);
82
+ v8::Local<v8::Object> rect = v8::Object::New(iso);
83
+ SetValue(iso, rect, "x", v8::Number::New(iso, 0));
84
+ SetValue(iso, rect, "y", v8::Number::New(iso, 0));
85
+ SetValue(iso, rect, "width", v8::Number::New(iso, h->viewport_width));
86
+ SetValue(iso, rect, "height", v8::Number::New(iso, h->viewport_height));
87
+ SetValue(iso, rect, "left", v8::Number::New(iso, 0));
88
+ SetValue(iso, rect, "top", v8::Number::New(iso, 0));
89
+ a.GetReturnValue().Set(rect);
90
+ }
91
+
92
+ // canvas.transferControlToOffscreen()
93
+ void TransferControlToOffscreen(const v8::FunctionCallbackInfo<v8::Value>& args)
94
+ {
95
+ v8::Isolate* isolate = args.GetIsolate();
96
+ HostContext* host = HostContext::From(isolate);
97
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
98
+
99
+ int width = host->viewport_width;
100
+ int height = host->viewport_height;
101
+ v8::Local<v8::Value> wv, hv;
102
+ if (args.This()->Get(ctx, Str(isolate, "width")).ToLocal(&wv) && wv->IsNumber()) {
103
+ width = static_cast<int>(wv.As<v8::Number>()->Value());
104
+ }
105
+ if (args.This()->Get(ctx, Str(isolate, "height")).ToLocal(&hv) && hv->IsNumber()) {
106
+ height = static_cast<int>(hv.As<v8::Number>()->Value());
107
+ }
108
+ args.GetReturnValue().Set(CreateOffscreenCanvas(isolate, host, width, height));
109
+ }
110
+
111
+ // OffscreenCanvas.transferToImageBitmap(): 2D 内容を ImageBitmap 化する (Video フレーム経路)。
112
+ void TransferToImageBitmap(const v8::FunctionCallbackInfo<v8::Value>& args)
113
+ {
114
+ v8::Isolate* isolate = args.GetIsolate();
115
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
116
+ v8::Local<v8::Value> ctx2d;
117
+ if (!args.This()->Get(ctx, Str(isolate, "__ctx2d")).ToLocal(&ctx2d) || !ctx2d->IsObject()) {
118
+ args.GetReturnValue().SetNull();
119
+ return;
120
+ }
121
+ const uint8_t* rgba = nullptr; uint32_t w = 0, h = 0;
122
+ if (!GetCanvas2DPixels(ctx2d.As<v8::Object>(), &rgba, &w, &h)) {
123
+ args.GetReturnValue().SetNull();
124
+ return;
125
+ }
126
+ auto* img = new DecodedImage();
127
+ img->width = w; img->height = h;
128
+ img->rgba.assign(rgba, rgba + static_cast<size_t>(w) * h * 4);
129
+ args.GetReturnValue().Set(WrapImageBitmap(isolate, img));
130
+ }
131
+
132
+ } // namespace
133
+
134
+ v8::Local<v8::Object> CreateCanvasElement(v8::Isolate* isolate, HostContext* host,
135
+ int width, int height)
136
+ {
137
+ v8::Local<v8::Object> canvas = v8::Object::New(isolate);
138
+ SetValue(isolate, canvas, "width",
139
+ v8::Integer::New(isolate, width ? width : host->viewport_width));
140
+ SetValue(isolate, canvas, "height",
141
+ v8::Integer::New(isolate, height ? height : host->viewport_height));
142
+ SetValue(isolate, canvas, "clientWidth", v8::Integer::New(isolate, host->viewport_width));
143
+ SetValue(isolate, canvas, "clientHeight", v8::Integer::New(isolate, host->viewport_height));
144
+ SetValue(isolate, canvas, "style", v8::Object::New(isolate));
145
+ SetMethod(isolate, canvas, "getContext", Canvas_GetContext);
146
+ SetMethod(isolate, canvas, "transferControlToOffscreen", TransferControlToOffscreen);
147
+ InstallEventTarget(isolate, canvas); // pointer/wheel リスナを保持
148
+ SetMethod(isolate, canvas, "getBoundingClientRect", GetBoundingClientRect);
149
+ // 主要 canvas を HostContext から参照できるよう記録 (WndProc の入力配送先)
150
+ HostContext::From(isolate)->main_canvas.Reset(isolate, canvas);
151
+ return canvas;
152
+ }
153
+
154
+ v8::Local<v8::Object> CreateOffscreenCanvas(v8::Isolate* isolate, HostContext* host,
155
+ int width, int height)
156
+ {
157
+ v8::Local<v8::Object> canvas = v8::Object::New(isolate);
158
+ // Worker への transfer 検出用マーカー (WorkerRuntime の serializer が参照)
159
+ SetValue(isolate, canvas, "__isOffscreenCanvas", v8::Boolean::New(isolate, true));
160
+ SetValue(isolate, canvas, "width",
161
+ v8::Integer::New(isolate, width ? width : host->viewport_width));
162
+ SetValue(isolate, canvas, "height",
163
+ v8::Integer::New(isolate, height ? height : host->viewport_height));
164
+ SetMethod(isolate, canvas, "getContext", Offscreen_GetContext);
165
+ SetMethod(isolate, canvas, "transferToImageBitmap", TransferToImageBitmap);
166
+ SetMethod(isolate, canvas, "addEventListener", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
167
+ SetMethod(isolate, canvas, "removeEventListener", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
168
+ return canvas;
169
+ }
170
+
171
+ } // namespace next2d
@@ -0,0 +1,462 @@
1
+ // CanvasRenderingContext2D の最小実装。
2
+ //
3
+ // player の実使用は「パスベースのヒット判定(setTransform/beginPath/moveTo/lineTo/arc/
4
+ // isPointInPath)」が中心で、fill/stroke はメッシュ生成やテキスト下地に使われる。
5
+ // ラスタライズ純ロジックは RasterCore.h (V8/Windows 非依存、単体テスト対象)。
6
+ // このファイルは V8 バインディングと DirectWrite/Direct2D テキスト描画の層。
7
+ #include "Bindings.h"
8
+
9
+ #include "HostContext.h"
10
+ #include "ImageSource.h"
11
+ #include "RasterCore.h"
12
+ #include "platform/TextRasterizer.h"
13
+ #include "v8/V8Util.h"
14
+
15
+ #include <Windows.h>
16
+
17
+ #include <algorithm>
18
+ #include <cstdio>
19
+ #include <cstring>
20
+ #include <memory>
21
+ #include <string>
22
+ #include <vector>
23
+
24
+ namespace next2d {
25
+
26
+ using v8util::Str;
27
+ using v8util::SetMethod;
28
+ using v8util::SetValue;
29
+ using v8util::ToStdString;
30
+
31
+ namespace {
32
+
33
+ using raster::Mat;
34
+ using raster::Point;
35
+ using raster::RGBA;
36
+ using raster::SubPath;
37
+ using raster::ParseColor;
38
+ using raster::FontPixelSize;
39
+ using raster::Apply;
40
+ using raster::Multiply;
41
+
42
+ struct State {
43
+ Mat transform;
44
+ RGBA fill{0,0,0,255};
45
+ RGBA stroke{0,0,0,255};
46
+ double line_width = 1.0;
47
+ double global_alpha = 1.0;
48
+ std::string font = "10px sans-serif";
49
+ // クリップ矩形 (デバイス座標)。TextField はテキスト枠(矩形パス)を clip() で
50
+ // 指定して文字のはみ出しを抑える。
51
+ raster::ClipRect clip;
52
+ };
53
+
54
+ // 2D コンテキストの実体。JS オブジェクトの内部フィールドに保持する。
55
+ struct Canvas2D {
56
+ raster::Surface surface;
57
+ std::vector<SubPath> path;
58
+ State state;
59
+ std::vector<State> stack;
60
+
61
+ Canvas2D(int w, int h) : surface(w, h) {}
62
+ };
63
+
64
+ // --- 内部アクセサ ---------------------------------------------------------
65
+ Canvas2D* Self(v8::Local<v8::Object> obj)
66
+ {
67
+ return static_cast<Canvas2D*>(obj->GetInternalField(0).As<v8::External>()->Value());
68
+ }
69
+
70
+ double Arg(const v8::FunctionCallbackInfo<v8::Value>& a, int i, double fb = 0.0)
71
+ {
72
+ return (i < a.Length() && a[i]->IsNumber()) ? a[i].As<v8::Number>()->Value() : fb;
73
+ }
74
+
75
+ void Blend(Canvas2D* c, int x, int y, const RGBA& col, double alpha)
76
+ {
77
+ raster::Blend(c->surface, c->state.clip, x, y, col, alpha);
78
+ }
79
+
80
+ // 現在の変換を適用して点をパスに追加
81
+ void AddPoint(Canvas2D* c, double x, double y, bool new_sub)
82
+ {
83
+ Point p = Apply(c->state.transform, x, y);
84
+ if (new_sub || c->path.empty()) {
85
+ c->path.push_back(SubPath{});
86
+ }
87
+ c->path.back().pts.push_back(p);
88
+ }
89
+
90
+ // --- パス構築 -------------------------------------------------------------
91
+ void BeginPath(const v8::FunctionCallbackInfo<v8::Value>& a) { Self(a.This())->path.clear(); }
92
+ void MoveTo(const v8::FunctionCallbackInfo<v8::Value>& a) { AddPoint(Self(a.This()), Arg(a,0), Arg(a,1), true); }
93
+ void LineTo(const v8::FunctionCallbackInfo<v8::Value>& a) { AddPoint(Self(a.This()), Arg(a,0), Arg(a,1), false); }
94
+ void ClosePath(const v8::FunctionCallbackInfo<v8::Value>& a) {
95
+ Canvas2D* c = Self(a.This());
96
+ if (!c->path.empty()) c->path.back().closed = true;
97
+ }
98
+
99
+ void BezierCurveTo(const v8::FunctionCallbackInfo<v8::Value>& a)
100
+ {
101
+ Canvas2D* c = Self(a.This());
102
+ if (c->path.empty() || c->path.back().pts.empty()) AddPoint(c, 0, 0, true);
103
+ // 直前点(変換済み)から制御点(変換して)へフラット化
104
+ Point p0 = c->path.back().pts.back();
105
+ Point p1 = Apply(c->state.transform, Arg(a,0), Arg(a,1));
106
+ Point p2 = Apply(c->state.transform, Arg(a,2), Arg(a,3));
107
+ Point p3 = Apply(c->state.transform, Arg(a,4), Arg(a,5));
108
+ raster::FlattenCubic(c->path.back().pts, p0, p1, p2, p3);
109
+ }
110
+
111
+ void QuadraticCurveTo(const v8::FunctionCallbackInfo<v8::Value>& a)
112
+ {
113
+ Canvas2D* c = Self(a.This());
114
+ if (c->path.empty() || c->path.back().pts.empty()) AddPoint(c, 0, 0, true);
115
+ Point p0 = c->path.back().pts.back();
116
+ Point p1 = Apply(c->state.transform, Arg(a,0), Arg(a,1));
117
+ Point p2 = Apply(c->state.transform, Arg(a,2), Arg(a,3));
118
+ raster::FlattenQuadratic(c->path.back().pts, p0, p1, p2);
119
+ }
120
+
121
+ void Arc(const v8::FunctionCallbackInfo<v8::Value>& a)
122
+ {
123
+ Canvas2D* c = Self(a.This());
124
+ bool ccw = a.Length() > 5 && a[5]->BooleanValue(a.GetIsolate());
125
+ // 既存サブパスが無ければ開始。あれば現在サブパスへ連結する。
126
+ if (c->path.empty()) c->path.push_back(SubPath{});
127
+ raster::FlattenArc(c->path.back().pts, c->state.transform,
128
+ Arg(a,0), Arg(a,1), Arg(a,2), Arg(a,3), Arg(a,4), ccw);
129
+ }
130
+
131
+ void Rect(const v8::FunctionCallbackInfo<v8::Value>& a)
132
+ {
133
+ Canvas2D* c = Self(a.This());
134
+ double x = Arg(a,0), y = Arg(a,1), w = Arg(a,2), h = Arg(a,3);
135
+ c->path.push_back(SubPath{});
136
+ const double corners[4][2] = { {x,y}, {x+w,y}, {x+w,y+h}, {x,y+h} };
137
+ for (auto& pt : corners) {
138
+ c->path.back().pts.push_back(Apply(c->state.transform, pt[0], pt[1]));
139
+ }
140
+ c->path.back().closed = true;
141
+ }
142
+
143
+ // --- ラスタライズ ---------------------------------------------------------
144
+ void Fill(const v8::FunctionCallbackInfo<v8::Value>& a)
145
+ {
146
+ Canvas2D* c = Self(a.This());
147
+ raster::FillPath(c->surface, c->state.clip, c->path, c->state.fill, c->state.global_alpha);
148
+ }
149
+
150
+ void Stroke(const v8::FunctionCallbackInfo<v8::Value>& a)
151
+ {
152
+ Canvas2D* c = Self(a.This());
153
+ raster::StrokePath(c->surface, c->state.clip, c->path, c->state.stroke, c->state.global_alpha);
154
+ }
155
+
156
+ // isPointInPath(x, y): nonzero winding。ヒット判定の中核。
157
+ // 引数は「変換後(デバイス)座標」。player は setTransform でパスを組んだのち
158
+ // デバイス座標で判定するため、パスへは変換適用済み。
159
+ void IsPointInPath(const v8::FunctionCallbackInfo<v8::Value>& a)
160
+ {
161
+ Canvas2D* c = Self(a.This());
162
+ a.GetReturnValue().Set(v8::Boolean::New(
163
+ a.GetIsolate(), raster::PointInPath(c->path, Arg(a,0), Arg(a,1))));
164
+ }
165
+
166
+ // --- 変換 ----------------------------------------------------------------
167
+ void SetTransform(const v8::FunctionCallbackInfo<v8::Value>& a)
168
+ {
169
+ Self(a.This())->state.transform =
170
+ { Arg(a,0,1), Arg(a,1), Arg(a,2), Arg(a,3,1), Arg(a,4), Arg(a,5) };
171
+ }
172
+ void Transform(const v8::FunctionCallbackInfo<v8::Value>& a)
173
+ {
174
+ Canvas2D* c = Self(a.This());
175
+ c->state.transform = Multiply(c->state.transform,
176
+ { Arg(a,0,1), Arg(a,1), Arg(a,2), Arg(a,3,1), Arg(a,4), Arg(a,5) });
177
+ }
178
+ void Translate(const v8::FunctionCallbackInfo<v8::Value>& a)
179
+ {
180
+ Canvas2D* c = Self(a.This());
181
+ c->state.transform = Multiply(c->state.transform, {1,0,0,1, Arg(a,0), Arg(a,1)});
182
+ }
183
+ void Scale(const v8::FunctionCallbackInfo<v8::Value>& a)
184
+ {
185
+ Canvas2D* c = Self(a.This());
186
+ c->state.transform = Multiply(c->state.transform, {Arg(a,0,1),0,0,Arg(a,1,1),0,0});
187
+ }
188
+ void Rotate(const v8::FunctionCallbackInfo<v8::Value>& a)
189
+ {
190
+ Canvas2D* c = Self(a.This());
191
+ double r = Arg(a,0);
192
+ c->state.transform = Multiply(c->state.transform,
193
+ {std::cos(r), std::sin(r), -std::sin(r), std::cos(r), 0, 0});
194
+ }
195
+ void Save(const v8::FunctionCallbackInfo<v8::Value>& a)
196
+ {
197
+ Canvas2D* c = Self(a.This()); c->stack.push_back(c->state);
198
+ }
199
+ void Restore(const v8::FunctionCallbackInfo<v8::Value>& a)
200
+ {
201
+ Canvas2D* c = Self(a.This());
202
+ if (!c->stack.empty()) { c->state = c->stack.back(); c->stack.pop_back(); }
203
+ }
204
+ // clip(): 現在パスの外接矩形をクリップ領域として設定する (デバイス座標)。
205
+ // 既存クリップとは積集合を取る。player の TextField 枠は矩形パスなので外接矩形で十分。
206
+ // «EXTEND» 任意形状の厳密クリップは per-pixel マスクが必要 (現状は矩形近似)。
207
+ void Clip(const v8::FunctionCallbackInfo<v8::Value>& a)
208
+ {
209
+ Canvas2D* c = Self(a.This());
210
+ double minx, miny, maxx, maxy;
211
+ if (!raster::PathBounds(c->path, &minx, &miny, &maxx, &maxy)) return;
212
+ raster::IntersectClip(c->state.clip,
213
+ std::floor(minx), std::floor(miny), std::ceil(maxx), std::ceil(maxy));
214
+ }
215
+
216
+ // --- 矩形 / クリア --------------------------------------------------------
217
+ void ClearRect(const v8::FunctionCallbackInfo<v8::Value>& a)
218
+ {
219
+ Canvas2D* c = Self(a.This());
220
+ raster::ClearRect(c->surface, Arg(a,0), Arg(a,1), Arg(a,2), Arg(a,3));
221
+ }
222
+ void FillRect(const v8::FunctionCallbackInfo<v8::Value>& a)
223
+ {
224
+ Canvas2D* c = Self(a.This());
225
+ Point p = Apply(c->state.transform, Arg(a,0), Arg(a,1));
226
+ double w = Arg(a,2), h = Arg(a,3);
227
+ for (int yy = static_cast<int>(p.y); yy < p.y + h; ++yy)
228
+ for (int xx = static_cast<int>(p.x); xx < p.x + w; ++xx)
229
+ Blend(c, xx, yy, c->state.fill, c->state.global_alpha);
230
+ }
231
+
232
+ // --- テキスト (DirectWrite / Direct2D — platform/TextRasterizer) -----------
233
+ // NOTE: 実装済みだが実機での見た目(ベースライン位置/合字/絵文字)は要検証。
234
+ static std::wstring ToWide(const std::string& s)
235
+ {
236
+ if (s.empty()) return {};
237
+ int n = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), static_cast<int>(s.size()), nullptr, 0);
238
+ std::wstring w(n, 0);
239
+ MultiByteToWideChar(CP_UTF8, 0, s.c_str(), static_cast<int>(s.size()), w.data(), n);
240
+ return w;
241
+ }
242
+
243
+ void MeasureText(const v8::FunctionCallbackInfo<v8::Value>& a)
244
+ {
245
+ v8::Isolate* iso = a.GetIsolate();
246
+ Canvas2D* c = Self(a.This());
247
+ const std::wstring wtext = ToWide(a.Length() > 0 ? ToStdString(iso, a[0]) : "");
248
+
249
+ // DirectWrite が使えない場合は px サイズからの近似メトリクス
250
+ TextMetricsInfo m;
251
+ if (!MeasureTextWithDWrite(c->state.font, wtext, m)) {
252
+ const double px = FontPixelSize(c->state.font);
253
+ m.width = 0;
254
+ m.ascent = px * 0.8;
255
+ m.descent = px * 0.2;
256
+ }
257
+ v8::Local<v8::Object> metrics = v8::Object::New(iso);
258
+ SetValue(iso, metrics, "width", v8::Number::New(iso, m.width));
259
+ SetValue(iso, metrics, "actualBoundingBoxAscent", v8::Number::New(iso, m.ascent));
260
+ SetValue(iso, metrics, "actualBoundingBoxDescent", v8::Number::New(iso, m.descent));
261
+ SetValue(iso, metrics, "fontBoundingBoxAscent", v8::Number::New(iso, m.ascent));
262
+ SetValue(iso, metrics, "fontBoundingBoxDescent", v8::Number::New(iso, m.descent));
263
+ a.GetReturnValue().Set(metrics);
264
+ }
265
+
266
+ // text をラスタライズして surface へ合成する。
267
+ void RasterizeText(Canvas2D* c, const std::wstring& wtext, double x, double y, const RGBA& col)
268
+ {
269
+ TextBitmap bmp;
270
+ if (!RasterizeTextWithDWrite(c->state.font, wtext, col.r, col.g, col.b, bmp)) return;
271
+
272
+ // fillText の x,y はベースライン起点。ビットマップは左上起点なので baseline を引く。
273
+ Point origin = Apply(c->state.transform, x, y - bmp.baseline);
274
+ for (int yy = 0; yy < bmp.height; ++yy) {
275
+ for (int xx = 0; xx < bmp.width; ++xx) {
276
+ const uint8_t* px = &bmp.rgba[(static_cast<size_t>(yy) * bmp.width + xx) * 4];
277
+ if (px[3] == 0) continue;
278
+ Blend(c, static_cast<int>(origin.x) + xx, static_cast<int>(origin.y) + yy,
279
+ RGBA{px[0], px[1], px[2], px[3]}, c->state.global_alpha);
280
+ }
281
+ }
282
+ }
283
+
284
+ void FillText(const v8::FunctionCallbackInfo<v8::Value>& a)
285
+ {
286
+ v8::Isolate* iso = a.GetIsolate();
287
+ Canvas2D* c = Self(a.This());
288
+ const std::wstring wtext = ToWide(a.Length() > 0 ? ToStdString(iso, a[0]) : "");
289
+ RasterizeText(c, wtext, Arg(a,1), Arg(a,2), c->state.fill);
290
+ }
291
+
292
+ // strokeText: 近似としてグリフを塗りで描画するが色は strokeStyle を使う。
293
+ void StrokeText(const v8::FunctionCallbackInfo<v8::Value>& a)
294
+ {
295
+ v8::Isolate* iso = a.GetIsolate();
296
+ Canvas2D* c = Self(a.This());
297
+ const std::wstring wtext = ToWide(a.Length() > 0 ? ToStdString(iso, a[0]) : "");
298
+ RasterizeText(c, wtext, Arg(a,1), Arg(a,2), c->state.stroke);
299
+ }
300
+
301
+ // drawImage(source, dx, dy [, dw, dh]) — 画像/canvas を surface へ合成 (最近傍)。
302
+ void DrawImage(const v8::FunctionCallbackInfo<v8::Value>& a)
303
+ {
304
+ v8::Isolate* iso = a.GetIsolate();
305
+ Canvas2D* c = Self(a.This());
306
+ const uint8_t* src = nullptr; uint32_t sw = 0, sh = 0;
307
+ if (a.Length() < 1 || !GetImageSourcePixels(iso, a[0], &src, &sw, &sh) || !src) {
308
+ return;
309
+ }
310
+ double dx = Arg(a,1), dy = Arg(a,2);
311
+ double dw = Arg(a,3, static_cast<double>(sw)), dh = Arg(a,4, static_cast<double>(sh));
312
+ for (int yy = 0; yy < static_cast<int>(dh); ++yy) {
313
+ for (int xx = 0; xx < static_cast<int>(dw); ++xx) {
314
+ uint32_t u = static_cast<uint32_t>(xx * sw / dw);
315
+ uint32_t v = static_cast<uint32_t>(yy * sh / dh);
316
+ const uint8_t* p = &src[(static_cast<size_t>(v) * sw + u) * 4];
317
+ RGBA s{p[0], p[1], p[2], p[3]};
318
+ Point d = Apply(c->state.transform, dx + xx, dy + yy);
319
+ Blend(c, static_cast<int>(d.x), static_cast<int>(d.y), s, c->state.global_alpha);
320
+ }
321
+ }
322
+ }
323
+
324
+ // --- ImageData -----------------------------------------------------------
325
+ void GetImageData(const v8::FunctionCallbackInfo<v8::Value>& a)
326
+ {
327
+ v8::Isolate* iso = a.GetIsolate();
328
+ v8::Local<v8::Context> ctx = iso->GetCurrentContext();
329
+ Canvas2D* c = Self(a.This());
330
+ int x = static_cast<int>(Arg(a,0)), y = static_cast<int>(Arg(a,1));
331
+ int w = static_cast<int>(Arg(a,2, c->surface.width));
332
+ int h = static_cast<int>(Arg(a,3, c->surface.height));
333
+
334
+ size_t bytes = static_cast<size_t>(w) * h * 4;
335
+ auto store = v8::ArrayBuffer::NewBackingStore(iso, bytes);
336
+ auto* dst = static_cast<uint8_t*>(store->Data());
337
+ for (int yy = 0; yy < h; ++yy)
338
+ for (int xx = 0; xx < w; ++xx) {
339
+ int sx = x + xx, sy = y + yy;
340
+ size_t di = (static_cast<size_t>(yy)*w+xx)*4;
341
+ if (sx>=0&&sy>=0&&sx<c->surface.width&&sy<c->surface.height) {
342
+ std::memcpy(&dst[di],
343
+ &c->surface.pixels[(static_cast<size_t>(sy)*c->surface.width+sx)*4], 4);
344
+ }
345
+ }
346
+ auto ab = v8::ArrayBuffer::New(iso, std::move(store));
347
+ auto arr = v8::Uint8ClampedArray::New(ab, 0, bytes);
348
+ v8::Local<v8::Object> image_data = v8::Object::New(iso);
349
+ SetValue(iso, image_data, "width", v8::Integer::New(iso, w));
350
+ SetValue(iso, image_data, "height", v8::Integer::New(iso, h));
351
+ image_data->Set(ctx, Str(iso, "data"), arr).Check();
352
+ a.GetReturnValue().Set(image_data);
353
+ }
354
+
355
+ // GC 時に Canvas2D 実体を解放
356
+ void ReleaseCanvas2D(const v8::WeakCallbackInfo<Canvas2D>& info) { delete info.GetParameter(); }
357
+
358
+ // fillStyle / strokeStyle / lineWidth / globalAlpha / font アクセサ
359
+ void InstallAccessors(v8::Isolate* isolate, v8::Local<v8::Object> obj)
360
+ {
361
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
362
+ auto set_str = [&](const char* name, void(*setter)(Canvas2D*, const std::string&)) {
363
+ obj->SetNativeDataProperty(ctx, Str(isolate, name),
364
+ nullptr,
365
+ [](v8::Local<v8::Name>, v8::Local<v8::Value> value,
366
+ const v8::PropertyCallbackInfo<void>& info) {
367
+ // setter は External(data) 経由で受け取る
368
+ auto* fn = reinterpret_cast<void(*)(Canvas2D*, const std::string&)>(
369
+ info.Data().As<v8::External>()->Value());
370
+ fn(Self(info.This()), ToStdString(info.GetIsolate(), value));
371
+ },
372
+ v8::External::New(isolate, reinterpret_cast<void*>(setter))).Check();
373
+ };
374
+ // fillStyle は player が色文字列の正規化に「代入後の読み出し」を使うため getter も持たせる。
375
+ obj->SetNativeDataProperty(ctx, Str(isolate, "fillStyle"),
376
+ [](v8::Local<v8::Name>, const v8::PropertyCallbackInfo<v8::Value>& info) {
377
+ RGBA c = Self(info.This())->state.fill;
378
+ char buf[8];
379
+ std::snprintf(buf, sizeof(buf), "#%02x%02x%02x", c.r, c.g, c.b);
380
+ info.GetReturnValue().Set(v8util::Str(info.GetIsolate(), buf));
381
+ },
382
+ [](v8::Local<v8::Name>, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info) {
383
+ Self(info.This())->state.fill = ParseColor(ToStdString(info.GetIsolate(), value));
384
+ }).Check();
385
+ set_str("strokeStyle", [](Canvas2D* c, const std::string& s){ c->state.stroke = ParseColor(s); });
386
+ set_str("font", [](Canvas2D* c, const std::string& s){ c->state.font = s; });
387
+
388
+ obj->SetNativeDataProperty(ctx, Str(isolate, "lineWidth"), nullptr,
389
+ [](v8::Local<v8::Name>, v8::Local<v8::Value> v, const v8::PropertyCallbackInfo<void>& info){
390
+ if (v->IsNumber()) Self(info.This())->state.line_width = v.As<v8::Number>()->Value();
391
+ }).Check();
392
+ obj->SetNativeDataProperty(ctx, Str(isolate, "globalAlpha"), nullptr,
393
+ [](v8::Local<v8::Name>, v8::Local<v8::Value> v, const v8::PropertyCallbackInfo<void>& info){
394
+ if (v->IsNumber()) Self(info.This())->state.global_alpha = v.As<v8::Number>()->Value();
395
+ }).Check();
396
+ }
397
+
398
+ } // namespace
399
+
400
+ // ImageSource.h: Canvas2D コンテキストオブジェクトから RGBA を取得する。
401
+ bool GetCanvas2DPixels(v8::Local<v8::Object> context2d,
402
+ const uint8_t** out_rgba, uint32_t* out_width, uint32_t* out_height)
403
+ {
404
+ if (context2d->InternalFieldCount() < 1) {
405
+ return false;
406
+ }
407
+ auto* c = static_cast<Canvas2D*>(
408
+ context2d->GetInternalField(0).As<v8::External>()->Value());
409
+ if (!c) return false;
410
+ *out_rgba = c->surface.pixels.data();
411
+ *out_width = static_cast<uint32_t>(c->surface.width);
412
+ *out_height = static_cast<uint32_t>(c->surface.height);
413
+ return true;
414
+ }
415
+
416
+ v8::Local<v8::Object> CreateCanvas2DContext(v8::Isolate* isolate, HostContext* /*host*/,
417
+ int width, int height)
418
+ {
419
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
420
+
421
+ v8::Local<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(isolate);
422
+ tmpl->SetInternalFieldCount(1);
423
+ v8::Local<v8::Object> obj = tmpl->NewInstance(ctx).ToLocalChecked();
424
+
425
+ auto* impl = new Canvas2D(width > 0 ? width : 1, height > 0 ? height : 1);
426
+ obj->SetInternalField(0, v8::External::New(isolate, impl));
427
+ auto* handle = new v8::Global<v8::Object>(isolate, obj);
428
+ handle->SetWeak(impl, ReleaseCanvas2D, v8::WeakCallbackType::kParameter);
429
+
430
+ SetMethod(isolate, obj, "beginPath", BeginPath);
431
+ SetMethod(isolate, obj, "moveTo", MoveTo);
432
+ SetMethod(isolate, obj, "lineTo", LineTo);
433
+ SetMethod(isolate, obj, "closePath", ClosePath);
434
+ SetMethod(isolate, obj, "bezierCurveTo", BezierCurveTo);
435
+ SetMethod(isolate, obj, "quadraticCurveTo", QuadraticCurveTo);
436
+ SetMethod(isolate, obj, "arc", Arc);
437
+ SetMethod(isolate, obj, "rect", Rect);
438
+ SetMethod(isolate, obj, "fill", Fill);
439
+ SetMethod(isolate, obj, "stroke", Stroke);
440
+ SetMethod(isolate, obj, "clip", Clip);
441
+ SetMethod(isolate, obj, "isPointInPath", IsPointInPath);
442
+ SetMethod(isolate, obj, "setTransform", SetTransform);
443
+ SetMethod(isolate, obj, "transform", Transform);
444
+ SetMethod(isolate, obj, "translate", Translate);
445
+ SetMethod(isolate, obj, "scale", Scale);
446
+ SetMethod(isolate, obj, "rotate", Rotate);
447
+ SetMethod(isolate, obj, "save", Save);
448
+ SetMethod(isolate, obj, "restore", Restore);
449
+ SetMethod(isolate, obj, "clearRect", ClearRect);
450
+ SetMethod(isolate, obj, "fillRect", FillRect);
451
+ SetMethod(isolate, obj, "measureText", MeasureText);
452
+ SetMethod(isolate, obj, "fillText", FillText);
453
+ SetMethod(isolate, obj, "strokeText", StrokeText);
454
+ SetMethod(isolate, obj, "drawImage", DrawImage);
455
+ SetMethod(isolate, obj, "getImageData", GetImageData);
456
+ SetMethod(isolate, obj, "setLineDash", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
457
+
458
+ InstallAccessors(isolate, obj);
459
+ return obj;
460
+ }
461
+
462
+ } // namespace next2d
@@ -0,0 +1,52 @@
1
+ #include "Bindings.h"
2
+
3
+ #include "v8/V8Util.h"
4
+
5
+ #include <iostream>
6
+
7
+ namespace next2d {
8
+
9
+ using v8util::Str;
10
+ using v8util::ToStdString;
11
+
12
+ namespace {
13
+
14
+ std::string FormatArgs(v8::Isolate* isolate, const v8::FunctionCallbackInfo<v8::Value>& args)
15
+ {
16
+ std::string out;
17
+ for (int i = 0; i < args.Length(); ++i) {
18
+ if (i > 0) {
19
+ out += ' ';
20
+ }
21
+ out += ToStdString(isolate, args[i]);
22
+ }
23
+ return out;
24
+ }
25
+
26
+ void Log(const v8::FunctionCallbackInfo<v8::Value>& args)
27
+ {
28
+ std::cout << FormatArgs(args.GetIsolate(), args) << std::endl;
29
+ }
30
+
31
+ void Error(const v8::FunctionCallbackInfo<v8::Value>& args)
32
+ {
33
+ std::cerr << FormatArgs(args.GetIsolate(), args) << std::endl;
34
+ }
35
+
36
+ } // namespace
37
+
38
+ void InstallConsole(v8::Isolate* isolate, v8::Local<v8::Object> global)
39
+ {
40
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
41
+ v8::Local<v8::Object> console = v8::Object::New(isolate);
42
+
43
+ v8util::SetMethod(isolate, console, "log", Log);
44
+ v8util::SetMethod(isolate, console, "info", Log);
45
+ v8util::SetMethod(isolate, console, "debug", Log);
46
+ v8util::SetMethod(isolate, console, "warn", Error);
47
+ v8util::SetMethod(isolate, console, "error", Error);
48
+
49
+ global->Set(ctx, Str(isolate, "console"), console).Check();
50
+ }
51
+
52
+ } // namespace next2d