@next2d/renderer 2.0.0 → 3.0.1

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 CHANGED
@@ -1,11 +1,198 @@
1
- @next2d/renderer
2
- =============
1
+ # @next2d/renderer
3
2
 
4
- ## Installation
3
+ ## 概要 / Overview
5
4
 
6
- ```
5
+ `@next2d/renderer`は、Web Workerで動作するOffscreenCanvasベースのレンダリングエンジンです。Next2Dアプリケーション向けに高性能でノンブロッキングなレンダリングを提供します。レンダリング処理を別スレッドにオフロードすることで、スムーズなUI操作と効率的なグラフィックス処理を実現します。
6
+
7
+ `@next2d/renderer` is an OffscreenCanvas-based rendering engine that runs in a Web Worker, providing high-performance, non-blocking rendering for Next2D applications. By offloading rendering operations to a separate thread, it ensures smooth UI interactions and efficient graphics processing.
8
+
9
+ ## 主な特徴 / Key Features
10
+
11
+ - **Web Workerアーキテクチャ**: ノンブロッキングレンダリングのため完全にWeb Worker内で動作
12
+ - Runs entirely in a Web Worker for non-blocking rendering
13
+ - **OffscreenCanvas**: ハードウェアアクセラレーションによるグラフィックス処理にOffscreenCanvas APIを活用
14
+ - Leverages OffscreenCanvas API for hardware-accelerated graphics
15
+ - **コマンドキューパターン**: レンダリングコマンドを非同期で効率的に処理
16
+ - Efficiently processes rendering commands asynchronously
17
+ - **モジュラー設計**: 各表示オブジェクトタイプごとにサービスとユースケースをクリーンに分離
18
+ - Clean separation of services and use cases for each display object type
19
+
20
+ ## インストール / Installation
21
+
22
+ ```bash
7
23
  npm install @next2d/renderer
8
24
  ```
9
25
 
10
- ## License
11
- This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details.
26
+ ## アーキテクチャ / Architecture
27
+
28
+ ### ディレクトリ構造 / Directory Structure
29
+
30
+ ```
31
+ src/
32
+ ├── index.ts # Workerエントリポイント / Worker entry point
33
+ ├── CommandController.ts # メインコマンドキューコントローラー / Main command queue controller
34
+ ├── RendererUtil.ts # レンダリング用ユーティリティ関数 / Utility functions for rendering
35
+ ├── Command/
36
+ │ ├── service/ # コマンド処理サービス / Command processing services
37
+ │ │ ├── CommandInitializeContextService.ts
38
+ │ │ ├── CommandResizeService.ts
39
+ │ │ └── CommandRemoveCacheService.ts
40
+ │ └── usecase/ # コマンドユースケース / Command use cases
41
+ │ ├── CommandRenderUseCase.ts
42
+ │ └── CommandCaptureUseCase.ts
43
+ ├── DisplayObject/
44
+ │ └── service/ # 基本表示オブジェクトサービス / Base display object services
45
+ │ └── DisplayObjectGetBlendModeService.ts
46
+ ├── DisplayObjectContainer/
47
+ │ └── usecase/ # コンテナレンダリングロジック / Container rendering logic
48
+ │ ├── DisplayObjectContainerRenderUseCase.ts
49
+ │ └── DisplayObjectContainerClipRenderUseCase.ts
50
+ ├── Shape/
51
+ │ ├── service/ # Shapeコマンド処理 / Shape command processing
52
+ │ │ └── ShapeCommandService.ts
53
+ │ └── usecase/ # Shapeレンダリングロジック / Shape rendering logic
54
+ │ ├── ShapeRenderUseCase.ts
55
+ │ └── ShapeClipRenderUseCase.ts
56
+ ├── TextField/
57
+ │ ├── service/ # テキスト処理サービス / Text processing services
58
+ │ │ ├── TextFieldGenerateFontStyleService.ts
59
+ │ │ └── TextFiledGetAlignOffsetService.ts
60
+ │ └── usecase/ # テキストレンダリングロジック / Text rendering logic
61
+ │ ├── TextFieldRenderUseCase.ts
62
+ │ └── TextFieldDrawOffscreenCanvasUseCase.ts
63
+ ├── Video/
64
+ │ └── usecase/ # ビデオレンダリングロジック / Video rendering logic
65
+ │ └── VideoRenderUseCase.ts
66
+ └── interface/ # TypeScriptインターフェース / TypeScript interfaces
67
+ ├── IMessage.ts
68
+ ├── INode.ts
69
+ ├── IRGBA.ts
70
+ ├── IBlendMode.ts
71
+ ├── ITextFormat.ts
72
+ ├── ITextObject.ts
73
+ └── ...
74
+ ```
75
+
76
+ ### コンポーネントの役割 / Component Roles
77
+
78
+ - **index.ts**: メッセージイベントリスナーをセットアップするWorkerエントリポイント / Worker entry point that sets up the message event listener
79
+ - **CommandController**: コマンドキューを管理し、適切なハンドラーにコマンドをディスパッチ / Manages the command queue and dispatches commands to appropriate handlers
80
+ - **Command/service/**: 低レベルのコマンド処理(コンテキスト初期化、リサイズ、キャッシュ削除) / Low-level command processing (context initialization, resize, cache removal)
81
+ - **Command/usecase/**: 高レベルのコマンド操作(レンダリング、キャプチャ) / High-level command operations (render, capture)
82
+ - **DisplayObject/service/**: すべての表示オブジェクト共通のサービス / Shared services for all display objects
83
+ - **DisplayObjectContainer/usecase/**: コンテナオブジェクトのレンダリングロジック / Rendering logic for container objects
84
+ - **Shape/service/ & usecase/**: ベクターグラフィックスのレンダリング / Vector graphics rendering
85
+ - **TextField/service/ & usecase/**: テキストレンダリングとフォント処理 / Text rendering and font processing
86
+ - **Video/usecase/**: ビデオ要素のレンダリング / Video element rendering
87
+ - **RendererUtil.ts**: レンダリング操作用の共通ユーティリティ関数 / Common utility functions for rendering operations
88
+ - **interface/**: TypeScript型定義とインターフェース / TypeScript type definitions and interfaces
89
+
90
+ ## メッセージフロー / Message Flow
91
+
92
+ ```mermaid
93
+ sequenceDiagram
94
+ participant MT as Main Thread / メインスレッド
95
+ participant WW as Web Worker
96
+ participant CC as CommandController
97
+ participant UC as Use Case / ユースケース
98
+ participant Ctx as OffscreenCanvas Context
99
+
100
+ MT->>WW: postMessage(command)
101
+ WW->>CC: queue.push(message)
102
+
103
+ alt state === "deactivate"
104
+ CC->>CC: state = "active"
105
+ loop while queue.length > 0
106
+ CC->>CC: message = queue.shift()
107
+
108
+ alt command === "initialize"
109
+ CC->>UC: commandInitializeContextService()
110
+ UC->>Ctx: setup OffscreenCanvas context / コンテキストをセットアップ
111
+ else command === "resize"
112
+ CC->>UC: commandResizeService()
113
+ UC->>Ctx: resize canvas / キャンバスをリサイズ
114
+ else command === "render"
115
+ CC->>UC: commandRenderUseCase()
116
+ UC->>Ctx: render display objects / 表示オブジェクトをレンダリング
117
+ UC->>CC: rendering complete / レンダリング完了
118
+ CC->>MT: postMessage({ message: "render", buffer })
119
+ else command === "capture"
120
+ CC->>UC: commandCaptureUseCase()
121
+ UC->>Ctx: capture frame / フレームをキャプチャ
122
+ UC->>CC: capture complete / キャプチャ完了
123
+ CC->>MT: postMessage({ message: "capture", imageBitmap })
124
+ else command === "removeCache"
125
+ CC->>UC: commandRemoveCacheService()
126
+ else command === "cacheClear"
127
+ CC->>UC: $cacheStore.reset()
128
+ end
129
+ end
130
+ CC->>CC: state = "deactivate"
131
+ end
132
+ ```
133
+
134
+ ## コマンドキューパターン / Command Queue Pattern
135
+
136
+ レンダラーは、非同期レンダリング操作を効率的に管理するためにコマンドキューパターンを使用しています。
137
+
138
+ The renderer uses a command queue pattern to efficiently manage asynchronous rendering operations.
139
+
140
+ ### キュー管理 / Queue Management
141
+
142
+ 1. **メッセージ受信 / Message Reception**: メインスレッドからメッセージが到着すると、`queue`配列にプッシュされます / When a message arrives from the main thread, it's pushed into the `queue` array
143
+ 2. **状態チェック / State Check**: Workerが`deactivate`状態(アイドル)の場合、実行を開始します / If the worker is in `deactivate` state (idle), it begins execution
144
+ 3. **逐次処理 / Sequential Processing**: `queue.shift()`を使用してコマンドを1つずつ処理します / Commands are processed one by one using `queue.shift()`
145
+ 4. **状態管理 / State Management**: Workerは`active`と`deactivate`状態の間で遷移します / The worker transitions between `active` and `deactivate` states
146
+
147
+ ### サポートされるコマンド / Supported Commands
148
+
149
+ - **initialize**: デバイスピクセル比を使用してOffscreenCanvasコンテキストをセットアップ / Set up OffscreenCanvas context with device pixel ratio
150
+ - **resize**: キャンバスの寸法を更新し、必要に応じてキャッシュをクリア / Update canvas dimensions and clear cache if needed
151
+ - **render**: 表示オブジェクトのレンダリングパイプラインを実行 / Execute rendering pipeline for display objects
152
+ - **capture**: 現在のフレームをImageBitmapとしてキャプチャ / Capture current frame as ImageBitmap
153
+ - **removeCache**: 特定のキャッシュアイテムを削除 / Remove specific cached items
154
+ - **cacheClear**: キャッシュストア全体をクリア / Clear entire cache store
155
+
156
+ ### メリット / Benefits
157
+
158
+ - **ノンブロッキング / Non-blocking**: レンダリング中もメインスレッドはレスポンシブなまま / Main thread remains responsive during rendering
159
+ - **効率的 / Efficient**: コマンドはバッチ処理され、非同期で処理されます / Commands are batched and processed asynchronously
160
+ - **スレッドセーフ / Thread-safe**: Workerの分離により競合状態を防止 / Worker isolation prevents race conditions
161
+ - **転送可能オブジェクト / Transferable Objects**: ゼロコピーメッセージパッシングのために転送可能オブジェクト(ArrayBuffer、ImageBitmap)を使用 / Uses Transferable objects (ArrayBuffer, ImageBitmap) for zero-copy message passing
162
+
163
+ ## 使用例 / Usage Example
164
+
165
+ ```typescript
166
+ // メインスレッド / Main thread
167
+ const worker = new Worker('renderer.js');
168
+ const canvas = document.getElementById('canvas') as HTMLCanvasElement;
169
+ const offscreen = canvas.transferControlToOffscreen();
170
+
171
+ // レンダラーの初期化 / Initialize renderer
172
+ worker.postMessage({
173
+ command: 'initialize',
174
+ canvas: offscreen,
175
+ devicePixelRatio: window.devicePixelRatio
176
+ }, [offscreen]);
177
+
178
+ // フレームのレンダリング / Render frame
179
+ worker.postMessage({
180
+ command: 'render',
181
+ buffer: renderDataBuffer,
182
+ length: dataLength,
183
+ imageBitmaps: bitmaps
184
+ }, [renderDataBuffer.buffer, ...bitmaps]);
185
+
186
+ // 完了の待機 / Listen for completion
187
+ worker.addEventListener('message', (event) => {
188
+ if (event.data.message === 'render') {
189
+ // レンダリング完了、バッファが返却されました
190
+ // Rendering complete, buffer returned
191
+ console.log('フレームがレンダリングされました / Frame rendered');
192
+ }
193
+ });
194
+ ```
195
+
196
+ ## ライセンス / License
197
+
198
+ This project is licensed under the [MIT License](LICENSE) - see the LICENSE file for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@next2d/renderer",
3
- "version": "2.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "Next2D Renderer Package",
5
5
  "author": "Toshiyuki Ienaga<ienaga@next2d.app> (https://github.com/ienaga/)",
6
6
  "license": "MIT",
@@ -24,8 +24,9 @@
24
24
  "url": "git+https://github.com/Next2D/Player.git"
25
25
  },
26
26
  "dependencies": {
27
- "@next2d/webgl": "2.0.0",
28
- "@next2d/cache": "2.0.0",
29
- "@next2d/texture-packer": "2.0.0"
27
+ "@next2d/webgl": "3.0.1",
28
+ "@next2d/webgpu": "3.0.1",
29
+ "@next2d/cache": "3.0.1",
30
+ "@next2d/texture-packer": "3.0.1"
30
31
  }
31
32
  }
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @description OffscreenCanvasからWebGL2のコンテキストを取得
3
- * Get WebGL2 context from OffscreenCanvas
2
+ * @description OffscreenCanvasからWebGL2またはWebGPUのコンテキストを取得
3
+ * Get WebGL2 or WebGPU context from OffscreenCanvas
4
4
  *
5
5
  * @param {OffscreenCanvas} canvas
6
6
  * @param {number} device_pixel_ratio
7
- * @return {void}
7
+ * @return {Promise<void>}
8
8
  * @method
9
9
  * @public
10
10
  */
11
- export declare const execute: (canvas: OffscreenCanvas, device_pixel_ratio: number) => void;
11
+ export declare const execute: (canvas: OffscreenCanvas, device_pixel_ratio: number) => Promise<void>;
@@ -1,27 +1,55 @@
1
- import { Context } from "@next2d/webgl";
1
+ import { Context as WebGLContext } from "@next2d/webgl";
2
+ import { Context as WebGPUContext } from "@next2d/webgpu";
2
3
  import { $setCanvas, $setContext, $samples } from "../../RendererUtil";
3
4
  /**
4
- * @description OffscreenCanvasからWebGL2のコンテキストを取得
5
- * Get WebGL2 context from OffscreenCanvas
5
+ * @description 開発時用のフラグ
6
+ * Flag for development use
7
+ *
8
+ * @type {boolean}
9
+ * @private
10
+ */
11
+ const useWebGPU = true;
12
+ /**
13
+ * @description OffscreenCanvasからWebGL2またはWebGPUのコンテキストを取得
14
+ * Get WebGL2 or WebGPU context from OffscreenCanvas
6
15
  *
7
16
  * @param {OffscreenCanvas} canvas
8
17
  * @param {number} device_pixel_ratio
9
- * @return {void}
18
+ * @return {Promise<void>}
10
19
  * @method
11
20
  * @public
12
21
  */
13
- export const execute = (canvas, device_pixel_ratio) => {
22
+ export const execute = async (canvas, device_pixel_ratio) => {
14
23
  // Set OffscreenCanvas
15
24
  $setCanvas(canvas);
16
- const gl = canvas.getContext("webgl2", {
17
- "stencil": true,
18
- "premultipliedAlpha": true,
19
- "antialias": false,
20
- "depth": false
21
- });
22
- if (!gl) {
23
- throw new Error("webgl2 is not supported.");
25
+ if (useWebGPU && "gpu" in navigator) {
26
+ const gpu = navigator.gpu;
27
+ const adapter = await gpu.requestAdapter();
28
+ if (!adapter) {
29
+ throw new Error("WebGPU adapter not available");
30
+ }
31
+ const device = await adapter.requestDevice();
32
+ if (!device) {
33
+ throw new Error("WebGPU device not available");
34
+ }
35
+ const context = canvas.getContext("webgpu");
36
+ if (!context) {
37
+ throw new Error("WebGPU context not available");
38
+ }
39
+ const preferredFormat = gpu.getPreferredCanvasFormat();
40
+ $setContext(new WebGPUContext(device, context, preferredFormat, device_pixel_ratio));
41
+ }
42
+ else {
43
+ const gl = canvas.getContext("webgl2", {
44
+ "stencil": true,
45
+ "premultipliedAlpha": true,
46
+ "antialias": false,
47
+ "depth": false
48
+ });
49
+ if (!gl) {
50
+ throw new Error("webgl2 is not supported.");
51
+ }
52
+ // Set CanvasToWebGLContext
53
+ $setContext(new WebGLContext(gl, $samples, device_pixel_ratio));
24
54
  }
25
- // Set CanvasToWebGLContext
26
- $setContext(new Context(gl, $samples, device_pixel_ratio));
27
55
  };
@@ -5,9 +5,11 @@
5
5
  * @param {Float32Array} render_queue
6
6
  * @param {number} width
7
7
  * @param {number} height
8
+ * @param {number} [bg_color=0xffffff]
9
+ * @param {number} [bg_alpha=0]
8
10
  * @param {ImageBitmap[]} [image_bitmaps=null]
9
11
  * @return {Promise<ImageBitmap>}
10
12
  * @method
11
13
  * @protected
12
14
  */
13
- export declare const execute: (render_queue: Float32Array, width: number, height: number, image_bitmaps: ImageBitmap[] | null) => Promise<ImageBitmap>;
15
+ export declare const execute: (render_queue: Float32Array, width: number, height: number, bg_color: number | undefined, bg_alpha: number | undefined, image_bitmaps: ImageBitmap[] | null) => Promise<ImageBitmap>;
@@ -10,18 +10,26 @@ import { $context } from "../../RendererUtil";
10
10
  * @param {Float32Array} render_queue
11
11
  * @param {number} width
12
12
  * @param {number} height
13
+ * @param {number} [bg_color=0xffffff]
14
+ * @param {number} [bg_alpha=0]
13
15
  * @param {ImageBitmap[]} [image_bitmaps=null]
14
16
  * @return {Promise<ImageBitmap>}
15
17
  * @method
16
18
  * @protected
17
19
  */
18
- export const execute = async (render_queue, width, height, image_bitmaps) => {
20
+ export const execute = async (render_queue, width, height, bg_color = 0x000000, bg_alpha = 0, image_bitmaps) => {
19
21
  // reset transfer bounds
20
22
  $context.clearTransferBounds();
21
23
  let index = 1;
22
24
  // reset
23
25
  $context.reset();
24
26
  $context.setTransform(1, 0, 0, 1, 0, 0);
27
+ // cache current background color
28
+ const red = $context.$clearColorR;
29
+ const green = $context.$clearColorG;
30
+ const blue = $context.$clearColorB;
31
+ const alpha = $context.$clearColorA;
32
+ $context.updateBackgroundColor((bg_color >> 16 & 0xff) / 255, (bg_color >> 8 & 0xff) / 255, (bg_color & 0xff) / 255, bg_alpha);
25
33
  $context.fillBackgroundColor();
26
34
  while (render_queue.length > index) {
27
35
  // hidden
@@ -49,5 +57,8 @@ export const execute = async (render_queue, width, height, image_bitmaps) => {
49
57
  }
50
58
  }
51
59
  $context.drawArraysInstanced();
52
- return await $context.createImageBitmap(width, height);
60
+ const imageBitmap = await $context.createImageBitmap(width, height);
61
+ // reset background color
62
+ $context.updateBackgroundColor(red, green, blue, alpha);
63
+ return imageBitmap;
53
64
  };
@@ -30,7 +30,7 @@ export const execute = (render_queue, image_bitmaps) => {
30
30
  $context.updateBackgroundColor(0, 0, 0, 0);
31
31
  }
32
32
  else {
33
- $context.updateBackgroundColor($color >> 16 & 0xff / 255, $color >> 8 & 0xff / 255, $color & 0xff / 255, 1);
33
+ $context.updateBackgroundColor(($color >> 16 & 0xff) / 255, ($color >> 8 & 0xff) / 255, ($color & 0xff) / 255, 1);
34
34
  }
35
35
  }
36
36
  // reset
@@ -73,7 +73,7 @@ export class CommandController {
73
73
  commandResizeService(object.buffer[0], object.buffer[1], Boolean(object.buffer[2]));
74
74
  break;
75
75
  case "initialize":
76
- commandInitializeContextService(object.canvas, object.devicePixelRatio);
76
+ await commandInitializeContextService(object.canvas, object.devicePixelRatio);
77
77
  break;
78
78
  case "removeCache":
79
79
  commandRemoveCacheService(object.buffer);
@@ -83,7 +83,7 @@ export class CommandController {
83
83
  break;
84
84
  case "capture":
85
85
  {
86
- const imageBitmap = await commandCaptureUseCase(object.buffer.subarray(0, object.length), object.width, object.height, object.imageBitmaps);
86
+ const imageBitmap = await commandCaptureUseCase(object.buffer.subarray(0, object.length), object.width, object.height, object.bgColor, object.bgAlpha, object.imageBitmaps);
87
87
  // 描画完了したらメインスレッドにbufferを返却する
88
88
  globalThis.postMessage({
89
89
  "message": "capture",
@@ -4,6 +4,7 @@ import { execute as shapeClipRenderUseCase } from "../../Shape/usecase/ShapeClip
4
4
  import { execute as textFieldRenderUseCase } from "../../TextField/usecase/TextFieldRenderUseCase";
5
5
  import { execute as videoRenderUseCase } from "../../Video/usecase/VideoRenderUseCase";
6
6
  import { execute as displayObjectContainerClipRenderUseCase } from "./DisplayObjectContainerClipRenderUseCase";
7
+ import { execute as displayObjectGetBlendModeService } from "../../DisplayObject/service/DisplayObjectGetBlendModeService";
7
8
  /**
8
9
  * @description DisplayObjectContainerの描画を実行します。
9
10
  * Execute the drawing of DisplayObjectContainer.
@@ -16,6 +17,63 @@ import { execute as displayObjectContainerClipRenderUseCase } from "./DisplayObj
16
17
  * @protected
17
18
  */
18
19
  export const execute = (render_queue, index, image_bitmaps) => {
20
+ let endClipDepth = 0;
21
+ let canRenderMask = true;
22
+ // use layer
23
+ const blendMode = displayObjectGetBlendModeService(render_queue[index++]);
24
+ const useLayer = Boolean(render_queue[index++]);
25
+ // layer size
26
+ let layerWidth = 0;
27
+ let layerHeight = 0;
28
+ let useFilter = false;
29
+ let uniqueKey = "";
30
+ let filterKey = "";
31
+ let filterBounds = null;
32
+ let filterParams = null;
33
+ let matrix = null;
34
+ let colorTransform = null;
35
+ if (useLayer) {
36
+ layerWidth = render_queue[index++];
37
+ layerHeight = render_queue[index++];
38
+ useFilter = Boolean(render_queue[index++]);
39
+ if (useFilter) {
40
+ // フィルターパス: filterCache/uniqueKey/filterKey を読む
41
+ const filterCache = Boolean(render_queue[index++]);
42
+ uniqueKey = `${render_queue[index++]}`;
43
+ filterKey = `${render_queue[index++]}`;
44
+ if (filterCache) {
45
+ filterBounds = render_queue.subarray(index, index + 4);
46
+ index += 4;
47
+ matrix = render_queue.subarray(index, index + 6);
48
+ index += 6;
49
+ colorTransform = render_queue.subarray(index, index + 8);
50
+ index += 8;
51
+ // キャッシュされたフィルターテクスチャを描画
52
+ $context.containerDrawCachedFilter(blendMode, matrix, colorTransform, filterBounds, uniqueKey, filterKey);
53
+ return index;
54
+ }
55
+ filterBounds = render_queue.subarray(index, index + 4);
56
+ index += 4;
57
+ matrix = render_queue.subarray(index, index + 6);
58
+ index += 6;
59
+ colorTransform = render_queue.subarray(index, index + 8);
60
+ index += 8;
61
+ const length = render_queue[index++];
62
+ filterParams = render_queue.subarray(index, index + length);
63
+ index += length;
64
+ }
65
+ else {
66
+ // ブレンドのみパス: matrix + colorTransform
67
+ matrix = render_queue.subarray(index, index + 6);
68
+ index += 6;
69
+ colorTransform = render_queue.subarray(index, index + 8);
70
+ index += 8;
71
+ }
72
+ }
73
+ // コンテナのフィルター/ブレンド用にレイヤーを開始
74
+ if (useLayer) {
75
+ $context.containerBeginLayer(layerWidth, layerHeight);
76
+ }
19
77
  const useMaskDisplayObject = Boolean(render_queue[index++]);
20
78
  if (useMaskDisplayObject) {
21
79
  // これまでの描画データを描画して初期化
@@ -39,8 +97,6 @@ export const execute = (render_queue, index, image_bitmaps) => {
39
97
  }
40
98
  $context.endMask();
41
99
  }
42
- let endClipDepth = 0;
43
- let canRenderMask = true;
44
100
  const length = render_queue[index++];
45
101
  for (let idx = 0; length > idx; idx++) {
46
102
  const depth = render_queue[index++];
@@ -89,7 +145,8 @@ export const execute = (render_queue, index, image_bitmaps) => {
89
145
  continue;
90
146
  }
91
147
  // hidden
92
- if (!render_queue[index++]) {
148
+ const hidden = render_queue[index++];
149
+ if (!hidden) {
93
150
  continue;
94
151
  }
95
152
  const type = render_queue[index++];
@@ -107,7 +164,6 @@ export const execute = (render_queue, index, image_bitmaps) => {
107
164
  index = videoRenderUseCase(render_queue, index, image_bitmaps);
108
165
  break;
109
166
  default:
110
- console.error("unknown type", type);
111
167
  break;
112
168
  }
113
169
  }
@@ -116,5 +172,9 @@ export const execute = (render_queue, index, image_bitmaps) => {
116
172
  $context.restore();
117
173
  $context.leaveMask();
118
174
  }
175
+ // コンテナのフィルター/ブレンド結果をメインに合成
176
+ if (useLayer) {
177
+ $context.containerEndLayer(blendMode, matrix, colorTransform, useFilter, filterBounds, filterParams, uniqueKey, filterKey);
178
+ }
119
179
  return index;
120
180
  };
@@ -1,4 +1,5 @@
1
- import type { Context } from "@next2d/webgl";
1
+ import type { Context as WebGLContext } from "@next2d/webgl";
2
+ import type { Context as WebGPUContext } from "@next2d/webgpu";
2
3
  import type { IRGBA } from "./interface/IRGBA";
3
4
  /**
4
5
  * @type {number}
@@ -6,20 +7,20 @@ import type { IRGBA } from "./interface/IRGBA";
6
7
  */
7
8
  export declare const $samples: number;
8
9
  /**
9
- * @type {Context}
10
+ * @type {WebGLContext | WebGPUContext}
10
11
  * @public
11
12
  */
12
- export declare let $context: Context;
13
+ export declare let $context: WebGLContext | WebGPUContext;
13
14
  /**
14
- * @description Next2DWebGLの描画コンテキストを設定
15
- * Set the drawing context of Next2D's WebGL
15
+ * @description Next2Dの描画コンテキストを設定(WebGLまたはWebGPU)
16
+ * Set the drawing context of Next2D (WebGL or WebGPU)
16
17
  *
17
- * @param {number} context
18
+ * @param {WebGLContext | WebGPUContext} context
18
19
  * @return {void}
19
20
  * @method
20
21
  * @public
21
22
  */
22
- export declare const $setContext: (context: Context) => void;
23
+ export declare const $setContext: (context: WebGLContext | WebGPUContext) => void;
23
24
  /**
24
25
  * @type {CanvasToWebGLContext}
25
26
  * @public
@@ -4,15 +4,15 @@
4
4
  */
5
5
  export const $samples = 4;
6
6
  /**
7
- * @type {Context}
7
+ * @type {WebGLContext | WebGPUContext}
8
8
  * @public
9
9
  */
10
10
  export let $context;
11
11
  /**
12
- * @description Next2DWebGLの描画コンテキストを設定
13
- * Set the drawing context of Next2D's WebGL
12
+ * @description Next2Dの描画コンテキストを設定(WebGLまたはWebGPU)
13
+ * Set the drawing context of Next2D (WebGL or WebGPU)
14
14
  *
15
- * @param {number} context
15
+ * @param {WebGLContext | WebGPUContext} context
16
16
  * @return {void}
17
17
  * @method
18
18
  * @public
@@ -157,10 +157,8 @@ export const execute = (commands, is_clip = false) => {
157
157
  commands[index++] // alpha
158
158
  );
159
159
  }
160
- const matrix = new Float32Array([
161
- commands[index++], commands[index++], commands[index++],
162
- commands[index++], commands[index++], commands[index++]
163
- ]);
160
+ const matrix = commands.subarray(index, index + 6);
161
+ index += 6;
164
162
  const spread = commands[index++];
165
163
  const interpolation = commands[index++];
166
164
  const focal = commands[index++];
@@ -182,10 +180,8 @@ export const execute = (commands, is_clip = false) => {
182
180
  const length = commands[index++];
183
181
  const buffer = new Uint8Array(commands.subarray(index, index + length));
184
182
  index += length;
185
- const matrix = new Float32Array([
186
- commands[index++], commands[index++], commands[index++],
187
- commands[index++], commands[index++], commands[index++]
188
- ]);
183
+ const matrix = commands.subarray(index, index + 6);
184
+ index += 6;
189
185
  $context.bitmapFill(buffer, matrix, width, height, Boolean(commands[index++]), Boolean(commands[index++]));
190
186
  }
191
187
  break;
@@ -211,10 +207,8 @@ export const execute = (commands, is_clip = false) => {
211
207
  commands[index++] // alpha
212
208
  );
213
209
  }
214
- const matrix = new Float32Array([
215
- commands[index++], commands[index++], commands[index++],
216
- commands[index++], commands[index++], commands[index++]
217
- ]);
210
+ const matrix = commands.subarray(index, index + 6);
211
+ index += 6;
218
212
  const spread = commands[index++];
219
213
  const interpolation = commands[index++];
220
214
  const focal = commands[index++];
@@ -240,10 +234,8 @@ export const execute = (commands, is_clip = false) => {
240
234
  const length = commands[index++];
241
235
  const buffer = new Uint8Array(commands.subarray(index, index + length));
242
236
  index += length;
243
- const matrix = new Float32Array([
244
- commands[index++], commands[index++], commands[index++],
245
- commands[index++], commands[index++], commands[index++]
246
- ]);
237
+ const matrix = commands.subarray(index, index + 6);
238
+ index += 6;
247
239
  $context.bitmapStroke(buffer, matrix, width, height, Boolean(commands[index++]), Boolean(commands[index++]));
248
240
  }
249
241
  break;
@@ -30,10 +30,10 @@ export const execute = (render_queue, index) => {
30
30
  // cache uniqueKey
31
31
  const uniqueKey = `${render_queue[index++]}`;
32
32
  const cacheKey = render_queue[index++];
33
- const xScale = Math.round(Math.sqrt(matrix[0] * matrix[0]
34
- + matrix[1] * matrix[1]) * 10000) / 10000;
35
- const yScale = Math.round(Math.sqrt(matrix[2] * matrix[2]
36
- + matrix[3] * matrix[3]) * 10000) / 10000;
33
+ const xScale = render_queue[index++];
34
+ const yScale = render_queue[index++];
35
+ // フィルターキャッシュ用のユニークキー(instanceId)
36
+ const filterKey = `${render_queue[index++]}`;
37
37
  let node;
38
38
  const hasCache = render_queue[index++];
39
39
  if (!hasCache) {
@@ -56,10 +56,13 @@ export const execute = (render_queue, index) => {
56
56
  $cacheStore.set(uniqueKey, `${cacheKey}`, node);
57
57
  // fixed logic
58
58
  const currentAttachment = $context.currentAttachmentObject;
59
- $context.bind($context.atlasAttachmentObject);
59
+ const atlasAttachment = $context.atlasAttachmentObject;
60
+ if (atlasAttachment) {
61
+ $context.bind(atlasAttachment);
62
+ }
60
63
  $context.reset();
61
64
  $context.beginNodeRendering(node);
62
- const offsetY = $context.atlasAttachmentObject.height - node.y - height;
65
+ const offsetY = atlasAttachment ? atlasAttachment.height - node.y - height : 0;
63
66
  $context.setTransform(1, 0, 0, 1, node.x, offsetY);
64
67
  if (isDrawable) {
65
68
  shapeCommandService(commands);
@@ -81,12 +84,15 @@ export const execute = (render_queue, index) => {
81
84
  $cacheStore.set(uniqueKey, `${cacheKey}`, node);
82
85
  // fixed logic
83
86
  const currentAttachment = $context.currentAttachmentObject;
84
- $context.bind($context.atlasAttachmentObject);
87
+ const atlasAttachment = $context.atlasAttachmentObject;
88
+ if (atlasAttachment) {
89
+ $context.bind(atlasAttachment);
90
+ }
85
91
  // 初期化して、描画範囲を初期化
86
92
  $context.reset();
87
93
  $context.beginNodeRendering(node);
88
94
  // matrix設定
89
- const offsetY = $context.atlasAttachmentObject.height - node.y - height;
95
+ const offsetY = atlasAttachment ? atlasAttachment.height - node.y - height : 0;
90
96
  $context.setTransform(xScale, 0, 0, yScale, -xMin * xScale + node.x, -yMin * yScale + offsetY);
91
97
  if (gridData) {
92
98
  gridData[24] = node.x;
@@ -121,7 +127,7 @@ export const execute = (render_queue, index) => {
121
127
  const params = render_queue.subarray(index, index + length);
122
128
  const width = Math.ceil(Math.abs(bounds[2] - bounds[0]));
123
129
  const height = Math.ceil(Math.abs(bounds[3] - bounds[1]));
124
- $context.applyFilter(node, uniqueKey, updated, width, height, isBitmap, matrix, colorTransform, displayObjectGetBlendModeService(blendMode), filterBounds, params);
130
+ $context.applyFilter(node, filterKey, updated, width, height, isBitmap, matrix, colorTransform, displayObjectGetBlendModeService(blendMode), filterBounds, params);
125
131
  index += length;
126
132
  return index;
127
133
  }
@@ -34,10 +34,10 @@ export const execute = (render_queue, index) => {
34
34
  const cacheKey = render_queue[index++];
35
35
  // text state
36
36
  const changed = Boolean(render_queue[index++]);
37
- const xScale = Math.round(Math.sqrt(matrix[0] * matrix[0]
38
- + matrix[1] * matrix[1]) * 10000) / 10000;
39
- const yScale = Math.round(Math.sqrt(matrix[2] * matrix[2]
40
- + matrix[3] * matrix[3]) * 10000) / 10000;
37
+ const xScale = render_queue[index++];
38
+ const yScale = render_queue[index++];
39
+ // フィルターキャッシュ用のユニークキー(instanceId)
40
+ const filterKey = `${render_queue[index++]}`;
41
41
  let node;
42
42
  const hasCache = render_queue[index++];
43
43
  if (!hasCache) {
@@ -95,10 +95,13 @@ export const execute = (render_queue, index) => {
95
95
  const canvas = textFieldDrawOffscreenCanvasUseCase(JSON.parse($textDecoder.decode(buffer)), textSetting, xScale, yScale);
96
96
  // fixed logic
97
97
  const currentAttachment = $context.currentAttachmentObject;
98
- $context.bind($context.atlasAttachmentObject);
98
+ const atlasAttachment = $context.atlasAttachmentObject;
99
+ if (atlasAttachment) {
100
+ $context.bind(atlasAttachment);
101
+ }
99
102
  $context.reset();
100
103
  $context.beginNodeRendering(node);
101
- const offsetY = $context.atlasAttachmentObject.height - node.y - height;
104
+ const offsetY = atlasAttachment ? atlasAttachment.height - node.y - height : 0;
102
105
  $context.setTransform(1, 0, 0, 1, node.x, offsetY);
103
106
  $context.drawElement(node, canvas);
104
107
  $context.endNodeRendering();
@@ -123,7 +126,7 @@ export const execute = (render_queue, index) => {
123
126
  const params = render_queue.subarray(index, index + length);
124
127
  const width = Math.ceil(Math.abs(bounds[2] - bounds[0]));
125
128
  const height = Math.ceil(Math.abs(bounds[3] - bounds[1]));
126
- $context.applyFilter(node, uniqueKey, Boolean(Math.max(+changed, +updated)), width, height, false, matrix, colorTransform, displayObjectGetBlendModeService(blendMode), filterBounds, params);
129
+ $context.applyFilter(node, filterKey, Boolean(Math.max(+changed, +updated)), width, height, false, matrix, colorTransform, displayObjectGetBlendModeService(blendMode), filterBounds, params);
127
130
  index += length;
128
131
  return index;
129
132
  }
@@ -29,6 +29,8 @@ export const execute = (render_queue, index, image_bitmaps) => {
29
29
  const cacheKey = "0";
30
30
  // video state
31
31
  const changed = Boolean(render_queue[index++]);
32
+ // フィルターキャッシュ用のユニークキー(instanceId)
33
+ const filterKey = `${render_queue[index++]}`;
32
34
  let node;
33
35
  const hasCache = render_queue[index++];
34
36
  if (!hasCache) {
@@ -44,13 +46,17 @@ export const execute = (render_queue, index, image_bitmaps) => {
44
46
  if (image_bitmaps && image_bitmaps.length) {
45
47
  // fixed logic
46
48
  const currentAttachment = $context.currentAttachmentObject;
47
- $context.bind($context.atlasAttachmentObject);
49
+ const atlasAttachment = $context.atlasAttachmentObject;
50
+ if (atlasAttachment) {
51
+ $context.bind(atlasAttachment);
52
+ }
48
53
  $context.reset();
49
54
  $context.beginNodeRendering(node);
50
- const offsetY = $context.atlasAttachmentObject.height - node.y - height;
55
+ const offsetY = atlasAttachment ? atlasAttachment.height - node.y - height : 0;
51
56
  $context.setTransform(1, 0, 0, 1, node.x, offsetY);
52
57
  const imageBitmap = image_bitmaps.shift();
53
- $context.drawElement(node, imageBitmap);
58
+ // Video用にflipY: trueを指定(WebGPUでは画像の座標系変換が必要)
59
+ $context.drawElement(node, imageBitmap, true);
54
60
  $context.endNodeRendering();
55
61
  if (currentAttachment) {
56
62
  $context.bind(currentAttachment);
@@ -74,7 +80,7 @@ export const execute = (render_queue, index, image_bitmaps) => {
74
80
  const params = render_queue.subarray(index, index + length);
75
81
  const width = Math.ceil(Math.abs(bounds[2] - bounds[0]));
76
82
  const height = Math.ceil(Math.abs(bounds[3] - bounds[1]));
77
- $context.applyFilter(node, uniqueKey, Boolean(Math.max(+changed, +updated)), width, height, true, matrix, colorTransform, displayObjectGetBlendModeService(blendMode), filterBounds, params);
83
+ $context.applyFilter(node, filterKey, Boolean(Math.max(+changed, +updated)), width, height, true, matrix, colorTransform, displayObjectGetBlendModeService(blendMode), filterBounds, params);
78
84
  index += length;
79
85
  return index;
80
86
  }
@@ -8,4 +8,6 @@ export interface IMessage {
8
8
  canvas?: OffscreenCanvas;
9
9
  devicePixelRatio?: number;
10
10
  id?: string;
11
+ bgColor: number;
12
+ bgAlpha: number;
11
13
  }