@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 +193 -6
- package/package.json +5 -4
- package/src/Command/service/CommandInitializeContextService.d.ts +4 -4
- package/src/Command/service/CommandInitializeContextService.js +43 -15
- package/src/Command/usecase/CommandCaptureUseCase.d.ts +3 -1
- package/src/Command/usecase/CommandCaptureUseCase.js +13 -2
- package/src/Command/usecase/CommandRenderUseCase.js +1 -1
- package/src/CommandController.js +2 -2
- package/src/DisplayObjectContainer/usecase/DisplayObjectContainerRenderUseCase.js +64 -4
- package/src/RendererUtil.d.ts +8 -7
- package/src/RendererUtil.js +4 -4
- package/src/Shape/service/ShapeCommandService.js +8 -16
- package/src/Shape/usecase/ShapeRenderUseCase.js +15 -9
- package/src/TextField/usecase/TextFieldRenderUseCase.js +10 -7
- package/src/Video/usecase/VideoRenderUseCase.js +10 -4
- package/src/interface/IMessage.d.ts +2 -0
package/README.md
CHANGED
|
@@ -1,11 +1,198 @@
|
|
|
1
|
-
@next2d/renderer
|
|
2
|
-
=============
|
|
1
|
+
# @next2d/renderer
|
|
3
2
|
|
|
4
|
-
##
|
|
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
|
-
##
|
|
11
|
-
|
|
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": "
|
|
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": "
|
|
28
|
-
"@next2d/
|
|
29
|
-
"@next2d/
|
|
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
|
|
5
|
-
*
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
package/src/CommandController.js
CHANGED
|
@@ -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
|
-
|
|
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
|
};
|
package/src/RendererUtil.d.ts
CHANGED
|
@@ -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 {
|
|
10
|
+
* @type {WebGLContext | WebGPUContext}
|
|
10
11
|
* @public
|
|
11
12
|
*/
|
|
12
|
-
export declare let $context:
|
|
13
|
+
export declare let $context: WebGLContext | WebGPUContext;
|
|
13
14
|
/**
|
|
14
|
-
* @description Next2D
|
|
15
|
-
* Set the drawing context of Next2D
|
|
15
|
+
* @description Next2Dの描画コンテキストを設定(WebGLまたはWebGPU)
|
|
16
|
+
* Set the drawing context of Next2D (WebGL or WebGPU)
|
|
16
17
|
*
|
|
17
|
-
* @param {
|
|
18
|
+
* @param {WebGLContext | WebGPUContext} context
|
|
18
19
|
* @return {void}
|
|
19
20
|
* @method
|
|
20
21
|
* @public
|
|
21
22
|
*/
|
|
22
|
-
export declare const $setContext: (context:
|
|
23
|
+
export declare const $setContext: (context: WebGLContext | WebGPUContext) => void;
|
|
23
24
|
/**
|
|
24
25
|
* @type {CanvasToWebGLContext}
|
|
25
26
|
* @public
|
package/src/RendererUtil.js
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export const $samples = 4;
|
|
6
6
|
/**
|
|
7
|
-
* @type {
|
|
7
|
+
* @type {WebGLContext | WebGPUContext}
|
|
8
8
|
* @public
|
|
9
9
|
*/
|
|
10
10
|
export let $context;
|
|
11
11
|
/**
|
|
12
|
-
* @description Next2D
|
|
13
|
-
* Set the drawing context of Next2D
|
|
12
|
+
* @description Next2Dの描画コンテキストを設定(WebGLまたはWebGPU)
|
|
13
|
+
* Set the drawing context of Next2D (WebGL or WebGPU)
|
|
14
14
|
*
|
|
15
|
-
* @param {
|
|
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 =
|
|
161
|
-
|
|
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 =
|
|
186
|
-
|
|
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 =
|
|
215
|
-
|
|
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 =
|
|
244
|
-
|
|
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 =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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.
|
|
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 =
|
|
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.
|
|
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 =
|
|
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,
|
|
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 =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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.
|
|
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 =
|
|
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,
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
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,
|
|
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
|
}
|