@webxr-jp/texture-compression 0.1.0
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 +158 -0
- package/dist/index.cjs +200 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +147 -0
- package/dist/index.d.ts +147 -0
- package/dist/index.js +157 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
- package/wasm/LICENSE +201 -0
- package/wasm/basis_encoder.js +6201 -0
- package/wasm/basis_encoder.wasm +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# @webxr-jp/texture-compression
|
|
2
|
+
|
|
3
|
+
avatar-optimizer向けのテクスチャ圧縮ユーティリティパッケージ。Basis Universal WASMを使用してKTX2形式(UASTC)でテクスチャを圧縮します。
|
|
4
|
+
|
|
5
|
+
## 特徴
|
|
6
|
+
|
|
7
|
+
- **KTX2形式出力**: GPU対応の圧縮テクスチャフォーマット
|
|
8
|
+
- **UASTC圧縮**: 高品質なユニバーサルテクスチャ圧縮
|
|
9
|
+
- **Zstandard超圧縮**: オプションでさらなるファイルサイズ削減
|
|
10
|
+
- **ブラウザ専用**: WebAssemblyベースで高速処理
|
|
11
|
+
|
|
12
|
+
## インストール
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @webxr-jp/texture-compression
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 使用方法
|
|
19
|
+
|
|
20
|
+
### 基本的な圧縮
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { compressToKtx2, initBasisEncoder } from '@webxr-jp/texture-compression'
|
|
24
|
+
|
|
25
|
+
// WASMモジュールを初期化(初回のみ)
|
|
26
|
+
const initResult = await initBasisEncoder()
|
|
27
|
+
if (initResult.isErr()) {
|
|
28
|
+
console.error('初期化エラー:', initResult.error)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// RGBAピクセルデータを圧縮
|
|
33
|
+
const imageData = new Uint8Array(width * height * 4) // RGBA
|
|
34
|
+
const result = await compressToKtx2(imageData, width, height)
|
|
35
|
+
|
|
36
|
+
if (result.isOk()) {
|
|
37
|
+
const ktx2Data = result.value.data // Uint8Array
|
|
38
|
+
console.log(
|
|
39
|
+
`圧縮完了: ${result.value.originalSize} → ${result.value.compressedSize} bytes`,
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### オプション設定
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { compressToKtx2, UastcQuality } from '@webxr-jp/texture-compression'
|
|
48
|
+
|
|
49
|
+
const result = await compressToKtx2(imageData, width, height, {
|
|
50
|
+
quality: UastcQuality.Default, // 品質レベル (0-4)
|
|
51
|
+
compressionLevel: 3, // 圧縮レベル (0-5)
|
|
52
|
+
generateMipmaps: false, // ミップマップ生成
|
|
53
|
+
supercompression: true, // Zstandard超圧縮
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Y軸反転
|
|
58
|
+
|
|
59
|
+
WebGLテクスチャ座標系(左下原点)からKTX2座標系(左上原点)への変換が必要な場合:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { flipImageY, compressToKtx2 } from '@webxr-jp/texture-compression'
|
|
63
|
+
|
|
64
|
+
const flippedData = flipImageY(imageData, width, height)
|
|
65
|
+
const result = await compressToKtx2(flippedData, width, height)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### エンコーダー管理
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import {
|
|
72
|
+
initBasisEncoder,
|
|
73
|
+
disposeBasisEncoder,
|
|
74
|
+
isBasisEncoderReady,
|
|
75
|
+
} from '@webxr-jp/texture-compression'
|
|
76
|
+
|
|
77
|
+
// 初期化確認
|
|
78
|
+
if (!isBasisEncoderReady()) {
|
|
79
|
+
await initBasisEncoder()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 使用後にリソース解放(オプション)
|
|
83
|
+
disposeBasisEncoder()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## API
|
|
87
|
+
|
|
88
|
+
### `compressToKtx2(imageData, width, height, options?)`
|
|
89
|
+
|
|
90
|
+
RGBAピクセルデータをKTX2形式に圧縮します。
|
|
91
|
+
|
|
92
|
+
- `imageData`: `Uint8Array` - RGBAピクセルデータ(width × height × 4 bytes)
|
|
93
|
+
- `width`: `number` - 画像の幅
|
|
94
|
+
- `height`: `number` - 画像の高さ
|
|
95
|
+
- `options`: `Ktx2CompressionOptions` - 圧縮オプション(省略可)
|
|
96
|
+
|
|
97
|
+
戻り値: `ResultAsync<Ktx2CompressionResult, CompressionError>`
|
|
98
|
+
|
|
99
|
+
### `flipImageY(imageData, width, height)`
|
|
100
|
+
|
|
101
|
+
Y軸を反転したピクセルデータを生成します。
|
|
102
|
+
|
|
103
|
+
### `initBasisEncoder(wasmDir?)`
|
|
104
|
+
|
|
105
|
+
BasisEncoder WASMモジュールを初期化します。
|
|
106
|
+
|
|
107
|
+
- `wasmDir`: `string` - WASMファイルのディレクトリURL(省略時はパッケージ内のwasmディレクトリ)
|
|
108
|
+
|
|
109
|
+
### `disposeBasisEncoder()`
|
|
110
|
+
|
|
111
|
+
キャッシュされたモジュールを解放します。
|
|
112
|
+
|
|
113
|
+
### `isBasisEncoderReady()`
|
|
114
|
+
|
|
115
|
+
WASMモジュールが初期化済みかどうかを返します。
|
|
116
|
+
|
|
117
|
+
## 型定義
|
|
118
|
+
|
|
119
|
+
### `Ktx2CompressionOptions`
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
interface Ktx2CompressionOptions {
|
|
123
|
+
quality?: UastcQuality // UASTC品質レベル (デフォルト: Default)
|
|
124
|
+
compressionLevel?: number // 圧縮レベル 0-5 (デフォルト: 3)
|
|
125
|
+
generateMipmaps?: boolean // ミップマップ生成 (デフォルト: false)
|
|
126
|
+
supercompression?: boolean // Zstandard超圧縮 (デフォルト: true)
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `UastcQuality`
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
enum UastcQuality {
|
|
134
|
+
Fastest = 0, // 最速、最低品質
|
|
135
|
+
Faster = 1, // 高速
|
|
136
|
+
Default = 2, // デフォルト
|
|
137
|
+
Slower = 3, // 低速、高品質
|
|
138
|
+
VerySlow = 4, // 最高品質
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### `Ktx2CompressionResult`
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
interface Ktx2CompressionResult {
|
|
146
|
+
data: Uint8Array // 圧縮後のKTX2バイナリデータ
|
|
147
|
+
originalSize: number // 元のサイズ (bytes)
|
|
148
|
+
compressedSize: number // 圧縮後のサイズ (bytes)
|
|
149
|
+
width: number // 画像の幅
|
|
150
|
+
height: number // 画像の高さ
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 注意事項
|
|
155
|
+
|
|
156
|
+
- ブラウザ環境専用です(Node.js非対応)
|
|
157
|
+
- 2のべき乗でないサイズの画像は警告が出ますが処理は継続します
|
|
158
|
+
- 大きなテクスチャの場合、出力バッファは最大24MBに制限されています
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
UastcQuality: () => UastcQuality,
|
|
34
|
+
compressToKtx2: () => compressToKtx2,
|
|
35
|
+
disposeBasisEncoder: () => disposeBasisEncoder,
|
|
36
|
+
flipImageY: () => flipImageY,
|
|
37
|
+
getCachedBasisEncoder: () => getCachedBasisEncoder,
|
|
38
|
+
initBasisEncoder: () => initBasisEncoder,
|
|
39
|
+
isBasisEncoderReady: () => isBasisEncoderReady
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(index_exports);
|
|
42
|
+
|
|
43
|
+
// src/types.ts
|
|
44
|
+
var UastcQuality = /* @__PURE__ */ ((UastcQuality2) => {
|
|
45
|
+
UastcQuality2[UastcQuality2["Fastest"] = 0] = "Fastest";
|
|
46
|
+
UastcQuality2[UastcQuality2["Faster"] = 1] = "Faster";
|
|
47
|
+
UastcQuality2[UastcQuality2["Default"] = 2] = "Default";
|
|
48
|
+
UastcQuality2[UastcQuality2["Slower"] = 3] = "Slower";
|
|
49
|
+
UastcQuality2[UastcQuality2["VerySlow"] = 4] = "VerySlow";
|
|
50
|
+
return UastcQuality2;
|
|
51
|
+
})(UastcQuality || {});
|
|
52
|
+
|
|
53
|
+
// src/compress.ts
|
|
54
|
+
var import_neverthrow2 = require("neverthrow");
|
|
55
|
+
|
|
56
|
+
// src/encoder.ts
|
|
57
|
+
var import_neverthrow = require("neverthrow");
|
|
58
|
+
var import_basis_encoder = __toESM(require("../wasm/basis_encoder.js"), 1);
|
|
59
|
+
var import_basis_encoder2 = __toESM(require("../wasm/basis_encoder.wasm?url"), 1);
|
|
60
|
+
var cachedModule = null;
|
|
61
|
+
function initBasisEncoder() {
|
|
62
|
+
if (cachedModule) {
|
|
63
|
+
return import_neverthrow.ResultAsync.fromSafePromise(Promise.resolve(cachedModule));
|
|
64
|
+
}
|
|
65
|
+
return import_neverthrow.ResultAsync.fromPromise(loadBasisModule(), (error) => ({
|
|
66
|
+
type: "WASM_LOAD_ERROR",
|
|
67
|
+
message: `Basis WASM \u30E2\u30B8\u30E5\u30FC\u30EB\u306E\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557: ${error instanceof Error ? error.message : String(error)}`
|
|
68
|
+
})).map((module2) => {
|
|
69
|
+
cachedModule = module2;
|
|
70
|
+
return module2;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function disposeBasisEncoder() {
|
|
74
|
+
cachedModule = null;
|
|
75
|
+
}
|
|
76
|
+
function getCachedBasisEncoder() {
|
|
77
|
+
return cachedModule;
|
|
78
|
+
}
|
|
79
|
+
function isBasisEncoderReady() {
|
|
80
|
+
return cachedModule !== null;
|
|
81
|
+
}
|
|
82
|
+
async function loadBasisModule() {
|
|
83
|
+
const moduleObj = await (0, import_basis_encoder.default)({
|
|
84
|
+
locateFile: () => import_basis_encoder2.default
|
|
85
|
+
});
|
|
86
|
+
if (moduleObj.initializeBasis) {
|
|
87
|
+
moduleObj.initializeBasis();
|
|
88
|
+
}
|
|
89
|
+
if (!moduleObj.BasisEncoder) {
|
|
90
|
+
throw new Error("BasisEncoder class not found in module after init");
|
|
91
|
+
}
|
|
92
|
+
return moduleObj;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/compress.ts
|
|
96
|
+
var DEFAULT_OPTIONS = {
|
|
97
|
+
quality: 2 /* Default */,
|
|
98
|
+
compressionLevel: 3,
|
|
99
|
+
generateMipmaps: true,
|
|
100
|
+
supercompression: true
|
|
101
|
+
};
|
|
102
|
+
var MAX_OUTPUT_SIZE = 1024 * 1024 * 24;
|
|
103
|
+
function compressToKtx2(imageData, width, height, options) {
|
|
104
|
+
const validationError = validateInput(imageData, width, height);
|
|
105
|
+
if (validationError) {
|
|
106
|
+
return (0, import_neverthrow2.errAsync)(validationError);
|
|
107
|
+
}
|
|
108
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
109
|
+
return initBasisEncoder().andThen(
|
|
110
|
+
(module2) => import_neverthrow2.ResultAsync.fromPromise(
|
|
111
|
+
encodeWithBasis(module2, imageData, width, height, opts),
|
|
112
|
+
(error) => ({
|
|
113
|
+
type: "COMPRESSION_ERROR",
|
|
114
|
+
message: `KTX2\u30A8\u30F3\u30B3\u30FC\u30C9\u306B\u5931\u6557: ${error instanceof Error ? error.message : String(error)}`
|
|
115
|
+
})
|
|
116
|
+
)
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
function validateInput(imageData, width, height) {
|
|
120
|
+
if (width <= 0 || height <= 0) {
|
|
121
|
+
return {
|
|
122
|
+
type: "INVALID_INPUT",
|
|
123
|
+
message: `\u5E45\u3068\u9AD8\u3055\u306F\u6B63\u306E\u5024\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059: width=${width}, height=${height}`
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const expectedSize = width * height * 4;
|
|
127
|
+
if (imageData.length !== expectedSize) {
|
|
128
|
+
return {
|
|
129
|
+
type: "INVALID_INPUT",
|
|
130
|
+
message: `\u753B\u50CF\u30C7\u30FC\u30BF\u30B5\u30A4\u30BA\u304C\u4E0D\u6B63\u3067\u3059: expected=${expectedSize}, actual=${imageData.length}`
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (!isPowerOfTwo(width) || !isPowerOfTwo(height)) {
|
|
134
|
+
console.warn(
|
|
135
|
+
`[texture-compression] \u5E45\u30FB\u9AD8\u3055\u304C2\u306E\u3079\u304D\u4E57\u3067\u306A\u3044\u5834\u5408\u3001\u4E00\u90E8\u306EGPU\u3067\u554F\u984C\u304C\u767A\u751F\u3059\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059: ${width}x${height}`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
function isPowerOfTwo(n) {
|
|
141
|
+
return n > 0 && (n & n - 1) === 0;
|
|
142
|
+
}
|
|
143
|
+
async function encodeWithBasis(module2, imageData, width, height, options) {
|
|
144
|
+
const encoder = new module2.BasisEncoder();
|
|
145
|
+
try {
|
|
146
|
+
encoder.setCreateKTX2File(true);
|
|
147
|
+
encoder.setUASTC(true);
|
|
148
|
+
encoder.setKTX2UASTCSupercompression(options.supercompression);
|
|
149
|
+
encoder.setPackUASTCFlags(options.quality);
|
|
150
|
+
encoder.setCompressionLevel(options.compressionLevel);
|
|
151
|
+
encoder.setMipGen(options.generateMipmaps);
|
|
152
|
+
const success = encoder.setSliceSourceImage(
|
|
153
|
+
0,
|
|
154
|
+
imageData,
|
|
155
|
+
width,
|
|
156
|
+
height,
|
|
157
|
+
module2.ldr_image_type.cRGBA32.value
|
|
158
|
+
);
|
|
159
|
+
if (!success) {
|
|
160
|
+
throw new Error("\u30BD\u30FC\u30B9\u753B\u50CF\u306E\u8A2D\u5B9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
161
|
+
}
|
|
162
|
+
const outputBuffer = new Uint8Array(MAX_OUTPUT_SIZE);
|
|
163
|
+
const outputSize = encoder.encode(outputBuffer);
|
|
164
|
+
if (outputSize === 0) {
|
|
165
|
+
throw new Error("\u30A8\u30F3\u30B3\u30FC\u30C9\u7D50\u679C\u304C\u7A7A\u3067\u3059");
|
|
166
|
+
}
|
|
167
|
+
const ktx2Data = new Uint8Array(outputBuffer.buffer, 0, outputSize);
|
|
168
|
+
return {
|
|
169
|
+
data: ktx2Data.slice(),
|
|
170
|
+
// コピーを返す
|
|
171
|
+
originalSize: imageData.length,
|
|
172
|
+
compressedSize: outputSize,
|
|
173
|
+
width,
|
|
174
|
+
height
|
|
175
|
+
};
|
|
176
|
+
} finally {
|
|
177
|
+
encoder.delete();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function flipImageY(imageData, width, height) {
|
|
181
|
+
const rowSize = width * 4;
|
|
182
|
+
const flipped = new Uint8Array(imageData.length);
|
|
183
|
+
for (let y = 0; y < height; y++) {
|
|
184
|
+
const srcOffset = y * rowSize;
|
|
185
|
+
const dstOffset = (height - 1 - y) * rowSize;
|
|
186
|
+
flipped.set(imageData.subarray(srcOffset, srcOffset + rowSize), dstOffset);
|
|
187
|
+
}
|
|
188
|
+
return flipped;
|
|
189
|
+
}
|
|
190
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
191
|
+
0 && (module.exports = {
|
|
192
|
+
UastcQuality,
|
|
193
|
+
compressToKtx2,
|
|
194
|
+
disposeBasisEncoder,
|
|
195
|
+
flipImageY,
|
|
196
|
+
getCachedBasisEncoder,
|
|
197
|
+
initBasisEncoder,
|
|
198
|
+
isBasisEncoderReady
|
|
199
|
+
});
|
|
200
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/compress.ts","../src/encoder.ts"],"sourcesContent":["/**\n * @webxr-jp/texture-compression\n *\n * テクスチャ圧縮ユーティリティパッケージ\n * avatar-optimizerで利用するためのWASMベースのKTX2圧縮機能を提供\n */\n\n// 型定義\nexport type {\n CompressionError,\n Ktx2CompressionOptions,\n Ktx2CompressionResult,\n} from './types'\nexport { UastcQuality } from './types'\n\n// 圧縮API\nexport { compressToKtx2, flipImageY } from './compress'\n\n// エンコーダー管理\nexport {\n disposeBasisEncoder,\n getCachedBasisEncoder,\n initBasisEncoder,\n isBasisEncoderReady,\n} from './encoder'\n","/**\n * テクスチャ圧縮関連の型定義\n */\n\n/** UASTC品質レベル */\nexport enum UastcQuality {\n /** 最速、最低品質 */\n Fastest = 0,\n /** 高速 */\n Faster = 1,\n /** デフォルト */\n Default = 2,\n /** 低速、高品質 */\n Slower = 3,\n /** 最高品質 */\n VerySlow = 4,\n}\n\n/** KTX2圧縮オプション */\nexport interface Ktx2CompressionOptions {\n /** UASTC品質レベル (0-4, デフォルト: 2) */\n quality?: UastcQuality\n /** 圧縮レベル (0-5, デフォルト: 3) */\n compressionLevel?: number\n /** ミップマップ生成 (デフォルト: false) */\n generateMipmaps?: boolean\n /** Zstandard超圧縮を使用 (デフォルト: true) */\n supercompression?: boolean\n}\n\n/** KTX2圧縮結果 */\nexport interface Ktx2CompressionResult {\n /** 圧縮後のKTX2バイナリデータ */\n data: Uint8Array\n /** 元のサイズ (bytes) */\n originalSize: number\n /** 圧縮後のサイズ (bytes) */\n compressedSize: number\n /** 画像の幅 */\n width: number\n /** 画像の高さ */\n height: number\n}\n\n/** エラー型 */\nexport type CompressionError =\n | { type: 'WASM_LOAD_ERROR'; message: string }\n | { type: 'COMPRESSION_ERROR'; message: string }\n | { type: 'INVALID_INPUT'; message: string }\n\n/**\n * BasisEncoderモジュールの型定義\n * Emscriptenでビルドされたbasis_encoder.jsが公開するAPI\n */\nexport interface BasisEncoderModule {\n /** BasisEncoderクラス */\n BasisEncoder: new () => BasisEncoder\n /** LDR画像タイプ列挙(生ピクセルデータにはcRGBA32を使用) */\n ldr_image_type: {\n cRGBA32: { value: number }\n cPNGImage: { value: number }\n cJPGImage: { value: number }\n }\n}\n\n/**\n * BasisEncoderインスタンスの型定義\n */\nexport interface BasisEncoder {\n /** KTX2ファイル生成を設定 */\n setCreateKTX2File(create: boolean): void\n /** UASTC超圧縮(Zstandard)を設定 */\n setKTX2UASTCSupercompression(enable: boolean): void\n /** UASTCモードを有効化 */\n setUASTC(enable: boolean): void\n /** スライスソース画像を設定(LDR) */\n setSliceSourceImage(\n sliceIndex: number,\n imageData: Uint8Array,\n width: number,\n height: number,\n imageType: number,\n ): boolean\n /** UASTC品質フラグを設定 */\n setPackUASTCFlags(flags: number): void\n /** 圧縮レベルを設定 */\n setCompressionLevel(level: number): void\n /** ミップマップ生成を設定 */\n setMipGen(generate: boolean): void\n /** エンコードを実行 */\n encode(outputBuffer: Uint8Array): number\n /** リソースを解放 */\n delete(): void\n}\n\n/** BASIS WASM ファクトリ関数の型 */\nexport type BasisModuleFactory = (\n moduleOverrides?: Record<string, unknown>,\n) => Promise<BasisEncoderModule>\n","/**\n * KTX2テクスチャ圧縮ロジック\n */\n\nimport { errAsync, ResultAsync } from 'neverthrow'\nimport { initBasisEncoder } from './encoder'\nimport {\n BasisEncoderModule,\n CompressionError,\n Ktx2CompressionOptions,\n Ktx2CompressionResult,\n UastcQuality,\n} from './types'\n\n/** デフォルトの圧縮オプション */\nconst DEFAULT_OPTIONS: Required<Ktx2CompressionOptions> = {\n quality: UastcQuality.Default,\n compressionLevel: 3,\n generateMipmaps: true,\n supercompression: true,\n}\n\n/** 出力バッファの最大サイズ(24MB) */\nconst MAX_OUTPUT_SIZE = 1024 * 1024 * 24\n\n/**\n * RGBAピクセルデータをKTX2形式に圧縮\n *\n * @param imageData - RGBAピクセルデータ(width * height * 4 bytes)\n * @param width - 画像の幅\n * @param height - 画像の高さ\n * @param options - 圧縮オプション\n * @returns 圧縮結果\n */\nexport function compressToKtx2(\n imageData: Uint8Array,\n width: number,\n height: number,\n options?: Ktx2CompressionOptions,\n): ResultAsync<Ktx2CompressionResult, CompressionError> {\n // 入力検証\n const validationError = validateInput(imageData, width, height)\n if (validationError) {\n return errAsync(validationError)\n }\n\n const opts = { ...DEFAULT_OPTIONS, ...options }\n\n return initBasisEncoder().andThen((module) =>\n ResultAsync.fromPromise(\n encodeWithBasis(module, imageData, width, height, opts),\n (error) => ({\n type: 'COMPRESSION_ERROR' as const,\n message: `KTX2エンコードに失敗: ${error instanceof Error ? error.message : String(error)}`,\n }),\n ),\n )\n}\n\n/**\n * 入力データを検証\n * @returns エラーがあればCompressionError、なければnull\n */\nfunction validateInput(\n imageData: Uint8Array,\n width: number,\n height: number,\n): CompressionError | null {\n if (width <= 0 || height <= 0) {\n return {\n type: 'INVALID_INPUT',\n message: `幅と高さは正の値である必要があります: width=${width}, height=${height}`,\n }\n }\n\n const expectedSize = width * height * 4\n if (imageData.length !== expectedSize) {\n return {\n type: 'INVALID_INPUT',\n message: `画像データサイズが不正です: expected=${expectedSize}, actual=${imageData.length}`,\n }\n }\n\n // 2のべき乗チェック(推奨だが必須ではない)\n if (!isPowerOfTwo(width) || !isPowerOfTwo(height)) {\n console.warn(\n `[texture-compression] 幅・高さが2のべき乗でない場合、一部のGPUで問題が発生する可能性があります: ${width}x${height}`,\n )\n }\n\n return null\n}\n\n/**\n * 2のべき乗かチェック\n */\nfunction isPowerOfTwo(n: number): boolean {\n return n > 0 && (n & (n - 1)) === 0\n}\n\n/**\n * BasisEncoderを使用してエンコード\n */\nasync function encodeWithBasis(\n module: BasisEncoderModule,\n imageData: Uint8Array,\n width: number,\n height: number,\n options: Required<Ktx2CompressionOptions>,\n): Promise<Ktx2CompressionResult> {\n const encoder = new module.BasisEncoder()\n\n try {\n // KTX2出力を有効化\n encoder.setCreateKTX2File(true)\n\n // UASTCモードを有効化\n encoder.setUASTC(true)\n\n // 超圧縮(Zstandard)を設定\n encoder.setKTX2UASTCSupercompression(options.supercompression)\n\n // 品質設定\n encoder.setPackUASTCFlags(options.quality)\n\n // 圧縮レベル\n encoder.setCompressionLevel(options.compressionLevel)\n\n // ミップマップ生成\n encoder.setMipGen(options.generateMipmaps)\n\n // ソース画像を設定(RGBA32生データ)\n const success = encoder.setSliceSourceImage(\n 0,\n imageData,\n width,\n height,\n module.ldr_image_type.cRGBA32.value,\n )\n\n if (!success) {\n throw new Error('ソース画像の設定に失敗しました')\n }\n\n // 出力バッファを確保\n const outputBuffer = new Uint8Array(MAX_OUTPUT_SIZE)\n\n // エンコード実行\n const outputSize = encoder.encode(outputBuffer)\n\n if (outputSize === 0) {\n throw new Error('エンコード結果が空です')\n }\n\n // 結果を切り出し\n const ktx2Data = new Uint8Array(outputBuffer.buffer, 0, outputSize)\n\n return {\n data: ktx2Data.slice(), // コピーを返す\n originalSize: imageData.length,\n compressedSize: outputSize,\n width,\n height,\n }\n } finally {\n // リソース解放\n encoder.delete()\n }\n}\n\n/**\n * Y軸を反転したピクセルデータを生成\n * WebGLテクスチャ座標系(左下原点)からKTX2座標系(左上原点)への変換\n *\n * @param imageData - 元のRGBAピクセルデータ\n * @param width - 画像の幅\n * @param height - 画像の高さ\n * @returns Y軸反転されたピクセルデータ\n */\nexport function flipImageY(\n imageData: Uint8Array,\n width: number,\n height: number,\n): Uint8Array {\n const rowSize = width * 4\n const flipped = new Uint8Array(imageData.length)\n\n for (let y = 0; y < height; y++) {\n const srcOffset = y * rowSize\n const dstOffset = (height - 1 - y) * rowSize\n flipped.set(imageData.subarray(srcOffset, srcOffset + rowSize), dstOffset)\n }\n\n return flipped\n}\n","/**\n * BasisEncoder WASM モジュールのロードと管理\n * ブラウザ環境専用\n */\n\nimport { ResultAsync } from 'neverthrow'\n// @ts-expect-error - Emscripten module\nimport BASIS from '../wasm/basis_encoder.js'\n// @ts-expect-error - Vite ?url import\nimport wasmUrl from '../wasm/basis_encoder.wasm?url'\nimport { BasisEncoderModule, CompressionError } from './types'\n\n/** モジュールキャッシュ */\nlet cachedModule: BasisEncoderModule | null = null\n\n/**\n * BasisEncoder WASMモジュールを初期化(ブラウザ環境専用)\n *\n * @returns 初期化されたBasisEncoderModule\n */\nexport function initBasisEncoder(): ResultAsync<\n BasisEncoderModule,\n CompressionError\n> {\n // キャッシュがあれば返す\n if (cachedModule) {\n return ResultAsync.fromSafePromise(Promise.resolve(cachedModule))\n }\n\n return ResultAsync.fromPromise(loadBasisModule(), (error) => ({\n type: 'WASM_LOAD_ERROR' as const,\n message: `Basis WASM モジュールの読み込みに失敗: ${error instanceof Error ? error.message : String(error)}`,\n })).map((module) => {\n cachedModule = module\n return module\n })\n}\n\n/**\n * BasisEncoderモジュールを解放\n */\nexport function disposeBasisEncoder(): void {\n cachedModule = null\n}\n\n/**\n * キャッシュされたモジュールを取得(初期化済みの場合のみ)\n */\nexport function getCachedBasisEncoder(): BasisEncoderModule | null {\n return cachedModule\n}\n\n/**\n * WASMがすでにロード済みかチェック\n */\nexport function isBasisEncoderReady(): boolean {\n return cachedModule !== null\n}\n\n/** 拡張されたモジュール型(initializeBasisを含む) */\ninterface BasisEncoderModuleWithInit extends BasisEncoderModule {\n initializeBasis?: () => void\n}\n\n/**\n * BASISモジュールをロード\n * locateFileでWASMのURLを指定\n */\nasync function loadBasisModule(): Promise<BasisEncoderModule> {\n const moduleObj = (await BASIS({\n locateFile: () => wasmUrl,\n })) as BasisEncoderModuleWithInit\n\n // Basisエンコーダーの初期化(必須)\n if (moduleObj.initializeBasis) {\n moduleObj.initializeBasis()\n }\n\n // BasisEncoderクラスが存在するか確認\n if (!moduleObj.BasisEncoder) {\n throw new Error('BasisEncoder class not found in module after init')\n }\n\n return moduleObj as BasisEncoderModule\n}\n"],"mappings":"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","names":["UastcQuality","import_neverthrow","import_basis_encoder","module","BASIS","wasmUrl","module"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { ResultAsync } from 'neverthrow';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* テクスチャ圧縮関連の型定義
|
|
5
|
+
*/
|
|
6
|
+
/** UASTC品質レベル */
|
|
7
|
+
declare enum UastcQuality {
|
|
8
|
+
/** 最速、最低品質 */
|
|
9
|
+
Fastest = 0,
|
|
10
|
+
/** 高速 */
|
|
11
|
+
Faster = 1,
|
|
12
|
+
/** デフォルト */
|
|
13
|
+
Default = 2,
|
|
14
|
+
/** 低速、高品質 */
|
|
15
|
+
Slower = 3,
|
|
16
|
+
/** 最高品質 */
|
|
17
|
+
VerySlow = 4
|
|
18
|
+
}
|
|
19
|
+
/** KTX2圧縮オプション */
|
|
20
|
+
interface Ktx2CompressionOptions {
|
|
21
|
+
/** UASTC品質レベル (0-4, デフォルト: 2) */
|
|
22
|
+
quality?: UastcQuality;
|
|
23
|
+
/** 圧縮レベル (0-5, デフォルト: 3) */
|
|
24
|
+
compressionLevel?: number;
|
|
25
|
+
/** ミップマップ生成 (デフォルト: false) */
|
|
26
|
+
generateMipmaps?: boolean;
|
|
27
|
+
/** Zstandard超圧縮を使用 (デフォルト: true) */
|
|
28
|
+
supercompression?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** KTX2圧縮結果 */
|
|
31
|
+
interface Ktx2CompressionResult {
|
|
32
|
+
/** 圧縮後のKTX2バイナリデータ */
|
|
33
|
+
data: Uint8Array;
|
|
34
|
+
/** 元のサイズ (bytes) */
|
|
35
|
+
originalSize: number;
|
|
36
|
+
/** 圧縮後のサイズ (bytes) */
|
|
37
|
+
compressedSize: number;
|
|
38
|
+
/** 画像の幅 */
|
|
39
|
+
width: number;
|
|
40
|
+
/** 画像の高さ */
|
|
41
|
+
height: number;
|
|
42
|
+
}
|
|
43
|
+
/** エラー型 */
|
|
44
|
+
type CompressionError = {
|
|
45
|
+
type: 'WASM_LOAD_ERROR';
|
|
46
|
+
message: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'COMPRESSION_ERROR';
|
|
49
|
+
message: string;
|
|
50
|
+
} | {
|
|
51
|
+
type: 'INVALID_INPUT';
|
|
52
|
+
message: string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* BasisEncoderモジュールの型定義
|
|
56
|
+
* Emscriptenでビルドされたbasis_encoder.jsが公開するAPI
|
|
57
|
+
*/
|
|
58
|
+
interface BasisEncoderModule {
|
|
59
|
+
/** BasisEncoderクラス */
|
|
60
|
+
BasisEncoder: new () => BasisEncoder;
|
|
61
|
+
/** LDR画像タイプ列挙(生ピクセルデータにはcRGBA32を使用) */
|
|
62
|
+
ldr_image_type: {
|
|
63
|
+
cRGBA32: {
|
|
64
|
+
value: number;
|
|
65
|
+
};
|
|
66
|
+
cPNGImage: {
|
|
67
|
+
value: number;
|
|
68
|
+
};
|
|
69
|
+
cJPGImage: {
|
|
70
|
+
value: number;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* BasisEncoderインスタンスの型定義
|
|
76
|
+
*/
|
|
77
|
+
interface BasisEncoder {
|
|
78
|
+
/** KTX2ファイル生成を設定 */
|
|
79
|
+
setCreateKTX2File(create: boolean): void;
|
|
80
|
+
/** UASTC超圧縮(Zstandard)を設定 */
|
|
81
|
+
setKTX2UASTCSupercompression(enable: boolean): void;
|
|
82
|
+
/** UASTCモードを有効化 */
|
|
83
|
+
setUASTC(enable: boolean): void;
|
|
84
|
+
/** スライスソース画像を設定(LDR) */
|
|
85
|
+
setSliceSourceImage(sliceIndex: number, imageData: Uint8Array, width: number, height: number, imageType: number): boolean;
|
|
86
|
+
/** UASTC品質フラグを設定 */
|
|
87
|
+
setPackUASTCFlags(flags: number): void;
|
|
88
|
+
/** 圧縮レベルを設定 */
|
|
89
|
+
setCompressionLevel(level: number): void;
|
|
90
|
+
/** ミップマップ生成を設定 */
|
|
91
|
+
setMipGen(generate: boolean): void;
|
|
92
|
+
/** エンコードを実行 */
|
|
93
|
+
encode(outputBuffer: Uint8Array): number;
|
|
94
|
+
/** リソースを解放 */
|
|
95
|
+
delete(): void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* KTX2テクスチャ圧縮ロジック
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* RGBAピクセルデータをKTX2形式に圧縮
|
|
104
|
+
*
|
|
105
|
+
* @param imageData - RGBAピクセルデータ(width * height * 4 bytes)
|
|
106
|
+
* @param width - 画像の幅
|
|
107
|
+
* @param height - 画像の高さ
|
|
108
|
+
* @param options - 圧縮オプション
|
|
109
|
+
* @returns 圧縮結果
|
|
110
|
+
*/
|
|
111
|
+
declare function compressToKtx2(imageData: Uint8Array, width: number, height: number, options?: Ktx2CompressionOptions): ResultAsync<Ktx2CompressionResult, CompressionError>;
|
|
112
|
+
/**
|
|
113
|
+
* Y軸を反転したピクセルデータを生成
|
|
114
|
+
* WebGLテクスチャ座標系(左下原点)からKTX2座標系(左上原点)への変換
|
|
115
|
+
*
|
|
116
|
+
* @param imageData - 元のRGBAピクセルデータ
|
|
117
|
+
* @param width - 画像の幅
|
|
118
|
+
* @param height - 画像の高さ
|
|
119
|
+
* @returns Y軸反転されたピクセルデータ
|
|
120
|
+
*/
|
|
121
|
+
declare function flipImageY(imageData: Uint8Array, width: number, height: number): Uint8Array;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* BasisEncoder WASM モジュールのロードと管理
|
|
125
|
+
* ブラウザ環境専用
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* BasisEncoder WASMモジュールを初期化(ブラウザ環境専用)
|
|
130
|
+
*
|
|
131
|
+
* @returns 初期化されたBasisEncoderModule
|
|
132
|
+
*/
|
|
133
|
+
declare function initBasisEncoder(): ResultAsync<BasisEncoderModule, CompressionError>;
|
|
134
|
+
/**
|
|
135
|
+
* BasisEncoderモジュールを解放
|
|
136
|
+
*/
|
|
137
|
+
declare function disposeBasisEncoder(): void;
|
|
138
|
+
/**
|
|
139
|
+
* キャッシュされたモジュールを取得(初期化済みの場合のみ)
|
|
140
|
+
*/
|
|
141
|
+
declare function getCachedBasisEncoder(): BasisEncoderModule | null;
|
|
142
|
+
/**
|
|
143
|
+
* WASMがすでにロード済みかチェック
|
|
144
|
+
*/
|
|
145
|
+
declare function isBasisEncoderReady(): boolean;
|
|
146
|
+
|
|
147
|
+
export { type CompressionError, type Ktx2CompressionOptions, type Ktx2CompressionResult, UastcQuality, compressToKtx2, disposeBasisEncoder, flipImageY, getCachedBasisEncoder, initBasisEncoder, isBasisEncoderReady };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { ResultAsync } from 'neverthrow';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* テクスチャ圧縮関連の型定義
|
|
5
|
+
*/
|
|
6
|
+
/** UASTC品質レベル */
|
|
7
|
+
declare enum UastcQuality {
|
|
8
|
+
/** 最速、最低品質 */
|
|
9
|
+
Fastest = 0,
|
|
10
|
+
/** 高速 */
|
|
11
|
+
Faster = 1,
|
|
12
|
+
/** デフォルト */
|
|
13
|
+
Default = 2,
|
|
14
|
+
/** 低速、高品質 */
|
|
15
|
+
Slower = 3,
|
|
16
|
+
/** 最高品質 */
|
|
17
|
+
VerySlow = 4
|
|
18
|
+
}
|
|
19
|
+
/** KTX2圧縮オプション */
|
|
20
|
+
interface Ktx2CompressionOptions {
|
|
21
|
+
/** UASTC品質レベル (0-4, デフォルト: 2) */
|
|
22
|
+
quality?: UastcQuality;
|
|
23
|
+
/** 圧縮レベル (0-5, デフォルト: 3) */
|
|
24
|
+
compressionLevel?: number;
|
|
25
|
+
/** ミップマップ生成 (デフォルト: false) */
|
|
26
|
+
generateMipmaps?: boolean;
|
|
27
|
+
/** Zstandard超圧縮を使用 (デフォルト: true) */
|
|
28
|
+
supercompression?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** KTX2圧縮結果 */
|
|
31
|
+
interface Ktx2CompressionResult {
|
|
32
|
+
/** 圧縮後のKTX2バイナリデータ */
|
|
33
|
+
data: Uint8Array;
|
|
34
|
+
/** 元のサイズ (bytes) */
|
|
35
|
+
originalSize: number;
|
|
36
|
+
/** 圧縮後のサイズ (bytes) */
|
|
37
|
+
compressedSize: number;
|
|
38
|
+
/** 画像の幅 */
|
|
39
|
+
width: number;
|
|
40
|
+
/** 画像の高さ */
|
|
41
|
+
height: number;
|
|
42
|
+
}
|
|
43
|
+
/** エラー型 */
|
|
44
|
+
type CompressionError = {
|
|
45
|
+
type: 'WASM_LOAD_ERROR';
|
|
46
|
+
message: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'COMPRESSION_ERROR';
|
|
49
|
+
message: string;
|
|
50
|
+
} | {
|
|
51
|
+
type: 'INVALID_INPUT';
|
|
52
|
+
message: string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* BasisEncoderモジュールの型定義
|
|
56
|
+
* Emscriptenでビルドされたbasis_encoder.jsが公開するAPI
|
|
57
|
+
*/
|
|
58
|
+
interface BasisEncoderModule {
|
|
59
|
+
/** BasisEncoderクラス */
|
|
60
|
+
BasisEncoder: new () => BasisEncoder;
|
|
61
|
+
/** LDR画像タイプ列挙(生ピクセルデータにはcRGBA32を使用) */
|
|
62
|
+
ldr_image_type: {
|
|
63
|
+
cRGBA32: {
|
|
64
|
+
value: number;
|
|
65
|
+
};
|
|
66
|
+
cPNGImage: {
|
|
67
|
+
value: number;
|
|
68
|
+
};
|
|
69
|
+
cJPGImage: {
|
|
70
|
+
value: number;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* BasisEncoderインスタンスの型定義
|
|
76
|
+
*/
|
|
77
|
+
interface BasisEncoder {
|
|
78
|
+
/** KTX2ファイル生成を設定 */
|
|
79
|
+
setCreateKTX2File(create: boolean): void;
|
|
80
|
+
/** UASTC超圧縮(Zstandard)を設定 */
|
|
81
|
+
setKTX2UASTCSupercompression(enable: boolean): void;
|
|
82
|
+
/** UASTCモードを有効化 */
|
|
83
|
+
setUASTC(enable: boolean): void;
|
|
84
|
+
/** スライスソース画像を設定(LDR) */
|
|
85
|
+
setSliceSourceImage(sliceIndex: number, imageData: Uint8Array, width: number, height: number, imageType: number): boolean;
|
|
86
|
+
/** UASTC品質フラグを設定 */
|
|
87
|
+
setPackUASTCFlags(flags: number): void;
|
|
88
|
+
/** 圧縮レベルを設定 */
|
|
89
|
+
setCompressionLevel(level: number): void;
|
|
90
|
+
/** ミップマップ生成を設定 */
|
|
91
|
+
setMipGen(generate: boolean): void;
|
|
92
|
+
/** エンコードを実行 */
|
|
93
|
+
encode(outputBuffer: Uint8Array): number;
|
|
94
|
+
/** リソースを解放 */
|
|
95
|
+
delete(): void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* KTX2テクスチャ圧縮ロジック
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* RGBAピクセルデータをKTX2形式に圧縮
|
|
104
|
+
*
|
|
105
|
+
* @param imageData - RGBAピクセルデータ(width * height * 4 bytes)
|
|
106
|
+
* @param width - 画像の幅
|
|
107
|
+
* @param height - 画像の高さ
|
|
108
|
+
* @param options - 圧縮オプション
|
|
109
|
+
* @returns 圧縮結果
|
|
110
|
+
*/
|
|
111
|
+
declare function compressToKtx2(imageData: Uint8Array, width: number, height: number, options?: Ktx2CompressionOptions): ResultAsync<Ktx2CompressionResult, CompressionError>;
|
|
112
|
+
/**
|
|
113
|
+
* Y軸を反転したピクセルデータを生成
|
|
114
|
+
* WebGLテクスチャ座標系(左下原点)からKTX2座標系(左上原点)への変換
|
|
115
|
+
*
|
|
116
|
+
* @param imageData - 元のRGBAピクセルデータ
|
|
117
|
+
* @param width - 画像の幅
|
|
118
|
+
* @param height - 画像の高さ
|
|
119
|
+
* @returns Y軸反転されたピクセルデータ
|
|
120
|
+
*/
|
|
121
|
+
declare function flipImageY(imageData: Uint8Array, width: number, height: number): Uint8Array;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* BasisEncoder WASM モジュールのロードと管理
|
|
125
|
+
* ブラウザ環境専用
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* BasisEncoder WASMモジュールを初期化(ブラウザ環境専用)
|
|
130
|
+
*
|
|
131
|
+
* @returns 初期化されたBasisEncoderModule
|
|
132
|
+
*/
|
|
133
|
+
declare function initBasisEncoder(): ResultAsync<BasisEncoderModule, CompressionError>;
|
|
134
|
+
/**
|
|
135
|
+
* BasisEncoderモジュールを解放
|
|
136
|
+
*/
|
|
137
|
+
declare function disposeBasisEncoder(): void;
|
|
138
|
+
/**
|
|
139
|
+
* キャッシュされたモジュールを取得(初期化済みの場合のみ)
|
|
140
|
+
*/
|
|
141
|
+
declare function getCachedBasisEncoder(): BasisEncoderModule | null;
|
|
142
|
+
/**
|
|
143
|
+
* WASMがすでにロード済みかチェック
|
|
144
|
+
*/
|
|
145
|
+
declare function isBasisEncoderReady(): boolean;
|
|
146
|
+
|
|
147
|
+
export { type CompressionError, type Ktx2CompressionOptions, type Ktx2CompressionResult, UastcQuality, compressToKtx2, disposeBasisEncoder, flipImageY, getCachedBasisEncoder, initBasisEncoder, isBasisEncoderReady };
|