@aura3d/assets 4.0.3 → 4.0.4
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/dist/DataTextureLoader.d.ts +16 -0
- package/dist/DataTextureLoader.d.ts.map +1 -0
- package/dist/DataTextureLoader.js +29 -0
- package/dist/DataTextureLoader.js.map +1 -0
- package/dist/EXRLoader.d.ts +78 -0
- package/dist/EXRLoader.d.ts.map +1 -0
- package/dist/EXRLoader.js +1398 -0
- package/dist/EXRLoader.js.map +1 -0
- package/dist/FileLoader.d.ts +32 -0
- package/dist/FileLoader.d.ts.map +1 -0
- package/dist/FileLoader.js +79 -0
- package/dist/FileLoader.js.map +1 -0
- package/dist/GLTFRenderResources.d.ts +9 -0
- package/dist/GLTFRenderResources.d.ts.map +1 -1
- package/dist/GLTFRenderResources.js +47 -4
- package/dist/GLTFRenderResources.js.map +1 -1
- package/dist/GLTFSharedTextureCache.d.ts +40 -0
- package/dist/GLTFSharedTextureCache.d.ts.map +1 -0
- package/dist/GLTFSharedTextureCache.js +68 -0
- package/dist/GLTFSharedTextureCache.js.map +1 -0
- package/dist/ImageBitmapLoader.d.ts +27 -0
- package/dist/ImageBitmapLoader.d.ts.map +1 -0
- package/dist/ImageBitmapLoader.js +51 -0
- package/dist/ImageBitmapLoader.js.map +1 -0
- package/dist/ImageLoader.d.ts.map +1 -1
- package/dist/ImageLoader.js +12 -5
- package/dist/ImageLoader.js.map +1 -1
- package/dist/LoadContext.d.ts +4 -1
- package/dist/LoadContext.d.ts.map +1 -1
- package/dist/LoadContext.js +7 -2
- package/dist/LoadContext.js.map +1 -1
- package/dist/LoaderFetch.d.ts +12 -0
- package/dist/LoaderFetch.d.ts.map +1 -0
- package/dist/LoaderFetch.js +54 -0
- package/dist/LoaderFetch.js.map +1 -0
- package/dist/TextureLoader.d.ts.map +1 -1
- package/dist/TextureLoader.js +3 -1
- package/dist/TextureLoader.js.map +1 -1
- package/dist/asset-corpus/ProductionGLTFRenderPipeline.d.ts +6 -0
- package/dist/asset-corpus/ProductionGLTFRenderPipeline.d.ts.map +1 -1
- package/dist/asset-corpus/ProductionGLTFRenderPipeline.js +2 -1
- package/dist/asset-corpus/ProductionGLTFRenderPipeline.js.map +1 -1
- package/dist/browser-index.d.ts +9 -0
- package/dist/browser-index.d.ts.map +1 -1
- package/dist/browser-index.js +5 -0
- package/dist/browser-index.js.map +1 -1
- package/dist/gltf-runtime.d.ts +2 -0
- package/dist/gltf-runtime.d.ts.map +1 -1
- package/dist/gltf-runtime.js +1 -0
- package/dist/gltf-runtime.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/loaders/TextureLoader.d.ts +27 -1
- package/dist/loaders/TextureLoader.d.ts.map +1 -1
- package/dist/loaders/TextureLoader.js +62 -4
- package/dist/loaders/TextureLoader.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,1398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* threejs-parity AD-03 (docs/workflows/threejs-parity-gaps.md §6 lane L4):
|
|
3
|
+
* real OpenEXR decode — the scanline/tile pipeline is a faithful port of
|
|
4
|
+
* three.js r185 `examples/jsm/loaders/EXRLoader.js` (itself a port of the
|
|
5
|
+
* TinyEXR/OpenEXR reference implementation), trimmed to the compression
|
|
6
|
+
* family this lane requires: NO_COMPRESSION, RLE, ZIPS, ZIP and PIZ, with
|
|
7
|
+
* HALF and FLOAT channel pixel types. PXR24, B44/A, DWA/B and deep data
|
|
8
|
+
* throw `unsupported-format` rather than silently mis-decoding.
|
|
9
|
+
*
|
|
10
|
+
* Output mirrors three's `EXRLoader` result shape: `RGBAFormat` RGBA pixels
|
|
11
|
+
* as `Uint16Array` half floats (default) or `Float32Array`, `flipY: false`,
|
|
12
|
+
* `generateMipmaps: false` — feedable straight into `texture.data`.
|
|
13
|
+
*/
|
|
14
|
+
import { AssetLoadError } from "./AssetLoader.js";
|
|
15
|
+
const warn = (message) => {
|
|
16
|
+
if (typeof console !== "undefined" && typeof console.warn === "function")
|
|
17
|
+
console.warn(message);
|
|
18
|
+
};
|
|
19
|
+
/** IEEE-754 half encode (three's `DataUtils.toHalfFloat`, round-to-nearest). */
|
|
20
|
+
function toHalfFloat(value) {
|
|
21
|
+
if (Number.isNaN(value))
|
|
22
|
+
return 0x7c00 | 1;
|
|
23
|
+
if (value === 0)
|
|
24
|
+
return value === -Infinity || Object.is(value, -0) ? 0x8000 : 0;
|
|
25
|
+
const sign = value < 0 ? 0x8000 : 0;
|
|
26
|
+
const abs = Math.abs(value);
|
|
27
|
+
if (abs === Infinity)
|
|
28
|
+
return sign | 0x7c00;
|
|
29
|
+
if (abs >= 65520)
|
|
30
|
+
return sign | 0x7bff;
|
|
31
|
+
if (abs < 6.103515625e-5) {
|
|
32
|
+
return sign | Math.round(abs / 5.9604644775390625e-8);
|
|
33
|
+
}
|
|
34
|
+
const exponent = Math.floor(Math.log2(abs));
|
|
35
|
+
const mantissa = Math.round((abs / Math.pow(2, exponent) - 1) * 1024);
|
|
36
|
+
return sign | ((exponent + 15) << 10) | (mantissa & 0x3ff);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* zlib inflate for ZIP/ZIPS blocks. `DecompressionStream("deflate")` covers
|
|
40
|
+
* browsers and Node >= 18; the `node:zlib` fallback keeps older Node test
|
|
41
|
+
* environments working. Three vendors fflate's `unzlibSync` — identical zlib
|
|
42
|
+
* semantics either way.
|
|
43
|
+
*/
|
|
44
|
+
async function inflateZlib(compressed) {
|
|
45
|
+
if (typeof DecompressionStream === "function") {
|
|
46
|
+
const stream = new Blob([compressed]).stream().pipeThrough(new DecompressionStream("deflate"));
|
|
47
|
+
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
48
|
+
}
|
|
49
|
+
// Runtime-only Node fallback. The specifier is a variable so bundlers
|
|
50
|
+
// (esbuild/vite/rollup) cannot resolve it — browser entries must never
|
|
51
|
+
// graph a Node builtin even behind a capability probe (bundle-size gate).
|
|
52
|
+
const zlibModule = "node:zlib";
|
|
53
|
+
const zlib = (await import(zlibModule));
|
|
54
|
+
const out = zlib.inflateSync(compressed);
|
|
55
|
+
return new Uint8Array(out.buffer, out.byteOffset, out.byteLength);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parse an EXR `ArrayBuffer` into interleaved pixel data — the async sibling
|
|
59
|
+
* of three's `EXRLoader.parse` (async only because ZIP/ZIPS inflate via
|
|
60
|
+
* `DecompressionStream` is async).
|
|
61
|
+
*/
|
|
62
|
+
export async function parseEXR(buffer, options = {}) {
|
|
63
|
+
const outputType = options.type ?? "half";
|
|
64
|
+
const outputFormat = options.outputFormat ?? "rgba";
|
|
65
|
+
const partIndex = Math.max(0, options.part ?? 0);
|
|
66
|
+
const USHORT_RANGE = (1 << 16);
|
|
67
|
+
const BITMAP_SIZE = (USHORT_RANGE >> 3);
|
|
68
|
+
const HUF_ENCBITS = 16; // literal (value) bit length
|
|
69
|
+
const HUF_DECBITS = 14; // decoding bit size (>= 8)
|
|
70
|
+
const HUF_ENCSIZE = (1 << HUF_ENCBITS) + 1; // encoding table size
|
|
71
|
+
const HUF_DECSIZE = 1 << HUF_DECBITS; // decoding table size
|
|
72
|
+
const HUF_DECMASK = HUF_DECSIZE - 1;
|
|
73
|
+
const NBITS = 16;
|
|
74
|
+
const A_OFFSET = 1 << (NBITS - 1);
|
|
75
|
+
const MOD_MASK = (1 << NBITS) - 1;
|
|
76
|
+
const SHORT_ZEROCODE_RUN = 59;
|
|
77
|
+
const LONG_ZEROCODE_RUN = 63;
|
|
78
|
+
const SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;
|
|
79
|
+
const ULONG_SIZE = 8;
|
|
80
|
+
const FLOAT32_SIZE = 4;
|
|
81
|
+
const INT32_SIZE = 4;
|
|
82
|
+
const INT16_SIZE = 2;
|
|
83
|
+
const INT8_SIZE = 1;
|
|
84
|
+
const STATIC_HUFFMAN = 0;
|
|
85
|
+
const DEFLATE = 1;
|
|
86
|
+
const UNKNOWN = 0;
|
|
87
|
+
const LOSSY_DCT = 1;
|
|
88
|
+
const RLE = 2;
|
|
89
|
+
void HUF_DECMASK;
|
|
90
|
+
void NBITS;
|
|
91
|
+
void A_OFFSET;
|
|
92
|
+
void MOD_MASK;
|
|
93
|
+
void ULONG_SIZE;
|
|
94
|
+
void FLOAT32_SIZE;
|
|
95
|
+
void STATIC_HUFFMAN;
|
|
96
|
+
void DEFLATE;
|
|
97
|
+
void UNKNOWN;
|
|
98
|
+
void LOSSY_DCT;
|
|
99
|
+
void RLE;
|
|
100
|
+
const logBase = Math.pow(2.7182818, 2.2);
|
|
101
|
+
void logBase;
|
|
102
|
+
function parseNullTerminatedString(buffer, offset) {
|
|
103
|
+
const uintBuffer = new Uint8Array(buffer);
|
|
104
|
+
let endOffset = 0;
|
|
105
|
+
while (uintBuffer[offset.value + endOffset] != 0) {
|
|
106
|
+
endOffset += 1;
|
|
107
|
+
}
|
|
108
|
+
const stringValue = new TextDecoder().decode(uintBuffer.slice(offset.value, offset.value + endOffset));
|
|
109
|
+
offset.value = offset.value + endOffset + 1;
|
|
110
|
+
return stringValue;
|
|
111
|
+
}
|
|
112
|
+
function parseFixedLengthString(buffer, offset, size) {
|
|
113
|
+
const stringValue = new TextDecoder().decode(new Uint8Array(buffer).slice(offset.value, offset.value + size));
|
|
114
|
+
offset.value = offset.value + size;
|
|
115
|
+
return stringValue;
|
|
116
|
+
}
|
|
117
|
+
function parseRational(dataView, offset) {
|
|
118
|
+
const x = parseInt32(dataView, offset);
|
|
119
|
+
const y = parseUint32(dataView, offset);
|
|
120
|
+
return [x, y];
|
|
121
|
+
}
|
|
122
|
+
function parseTimecode(dataView, offset) {
|
|
123
|
+
const x = parseUint32(dataView, offset);
|
|
124
|
+
const y = parseUint32(dataView, offset);
|
|
125
|
+
return [x, y];
|
|
126
|
+
}
|
|
127
|
+
function parseInt32(dataView, offset) {
|
|
128
|
+
const Int32 = dataView.getInt32(offset.value, true);
|
|
129
|
+
offset.value = offset.value + INT32_SIZE;
|
|
130
|
+
return Int32;
|
|
131
|
+
}
|
|
132
|
+
function parseUint32(dataView, offset) {
|
|
133
|
+
const Uint32 = dataView.getUint32(offset.value, true);
|
|
134
|
+
offset.value = offset.value + INT32_SIZE;
|
|
135
|
+
return Uint32;
|
|
136
|
+
}
|
|
137
|
+
function parseUint8Array(uInt8Array, offset) {
|
|
138
|
+
const Uint8 = uInt8Array[offset.value];
|
|
139
|
+
offset.value = offset.value + INT8_SIZE;
|
|
140
|
+
return Uint8;
|
|
141
|
+
}
|
|
142
|
+
function parseUint8(dataView, offset) {
|
|
143
|
+
const Uint8 = dataView.getUint8(offset.value);
|
|
144
|
+
offset.value = offset.value + INT8_SIZE;
|
|
145
|
+
return Uint8;
|
|
146
|
+
}
|
|
147
|
+
const parseInt64 = function (dataView, offset) {
|
|
148
|
+
const int = Number(dataView.getBigInt64(offset.value, true));
|
|
149
|
+
offset.value += ULONG_SIZE;
|
|
150
|
+
return int;
|
|
151
|
+
};
|
|
152
|
+
function parseFloat32(dataView, offset) {
|
|
153
|
+
const float = dataView.getFloat32(offset.value, true);
|
|
154
|
+
offset.value += FLOAT32_SIZE;
|
|
155
|
+
return float;
|
|
156
|
+
}
|
|
157
|
+
function decodeFloat32(dataView, offset) {
|
|
158
|
+
return toHalfFloat(parseFloat32(dataView, offset));
|
|
159
|
+
}
|
|
160
|
+
// https://stackoverflow.com/questions/5678432/decompressing-half-precision-floats-in-javascript
|
|
161
|
+
function decodeFloat16(binary) {
|
|
162
|
+
const exponent = (binary & 0x7C00) >> 10, fraction = binary & 0x03FF;
|
|
163
|
+
return (binary >> 15 ? -1 : 1) * (exponent ?
|
|
164
|
+
(exponent === 0x1F ?
|
|
165
|
+
fraction ? NaN : Infinity :
|
|
166
|
+
Math.pow(2, exponent - 15) * (1 + fraction / 0x400)) :
|
|
167
|
+
6.103515625e-5 * (fraction / 0x400));
|
|
168
|
+
}
|
|
169
|
+
function parseUint16(dataView, offset) {
|
|
170
|
+
const Uint16 = dataView.getUint16(offset.value, true);
|
|
171
|
+
offset.value += INT16_SIZE;
|
|
172
|
+
return Uint16;
|
|
173
|
+
}
|
|
174
|
+
function parseFloat16(buffer, offset) {
|
|
175
|
+
return decodeFloat16(parseUint16(buffer, offset));
|
|
176
|
+
}
|
|
177
|
+
function parseChlist(dataView, buffer, offset, size) {
|
|
178
|
+
const startOffset = offset.value;
|
|
179
|
+
const channels = [];
|
|
180
|
+
while (offset.value < (startOffset + size - 1)) {
|
|
181
|
+
const name = parseNullTerminatedString(buffer, offset);
|
|
182
|
+
const pixelType = parseInt32(dataView, offset);
|
|
183
|
+
const pLinear = parseUint8(dataView, offset);
|
|
184
|
+
offset.value += 3; // reserved, three chars
|
|
185
|
+
const xSampling = parseInt32(dataView, offset);
|
|
186
|
+
const ySampling = parseInt32(dataView, offset);
|
|
187
|
+
channels.push({
|
|
188
|
+
name: name,
|
|
189
|
+
pixelType: pixelType,
|
|
190
|
+
pLinear: pLinear,
|
|
191
|
+
xSampling: xSampling,
|
|
192
|
+
ySampling: ySampling
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
offset.value += 1;
|
|
196
|
+
return channels;
|
|
197
|
+
}
|
|
198
|
+
function parseChromaticities(dataView, offset) {
|
|
199
|
+
const redX = parseFloat32(dataView, offset);
|
|
200
|
+
const redY = parseFloat32(dataView, offset);
|
|
201
|
+
const greenX = parseFloat32(dataView, offset);
|
|
202
|
+
const greenY = parseFloat32(dataView, offset);
|
|
203
|
+
const blueX = parseFloat32(dataView, offset);
|
|
204
|
+
const blueY = parseFloat32(dataView, offset);
|
|
205
|
+
const whiteX = parseFloat32(dataView, offset);
|
|
206
|
+
const whiteY = parseFloat32(dataView, offset);
|
|
207
|
+
return { redX: redX, redY: redY, greenX: greenX, greenY: greenY, blueX: blueX, blueY: blueY, whiteX: whiteX, whiteY: whiteY };
|
|
208
|
+
}
|
|
209
|
+
function parseCompression(dataView, offset) {
|
|
210
|
+
const compressionCodes = [
|
|
211
|
+
'NO_COMPRESSION',
|
|
212
|
+
'RLE_COMPRESSION',
|
|
213
|
+
'ZIPS_COMPRESSION',
|
|
214
|
+
'ZIP_COMPRESSION',
|
|
215
|
+
'PIZ_COMPRESSION',
|
|
216
|
+
'PXR24_COMPRESSION',
|
|
217
|
+
'B44_COMPRESSION',
|
|
218
|
+
'B44A_COMPRESSION',
|
|
219
|
+
'DWAA_COMPRESSION',
|
|
220
|
+
'DWAB_COMPRESSION'
|
|
221
|
+
];
|
|
222
|
+
const compression = parseUint8(dataView, offset);
|
|
223
|
+
return compressionCodes[compression];
|
|
224
|
+
}
|
|
225
|
+
function parseBox2i(dataView, offset) {
|
|
226
|
+
const xMin = parseInt32(dataView, offset);
|
|
227
|
+
const yMin = parseInt32(dataView, offset);
|
|
228
|
+
const xMax = parseInt32(dataView, offset);
|
|
229
|
+
const yMax = parseInt32(dataView, offset);
|
|
230
|
+
return { xMin: xMin, yMin: yMin, xMax: xMax, yMax: yMax };
|
|
231
|
+
}
|
|
232
|
+
function parseLineOrder(dataView, offset) {
|
|
233
|
+
const lineOrders = [
|
|
234
|
+
'INCREASING_Y',
|
|
235
|
+
'DECREASING_Y',
|
|
236
|
+
'RANDOM_Y',
|
|
237
|
+
];
|
|
238
|
+
const lineOrder = parseUint8(dataView, offset);
|
|
239
|
+
return lineOrders[lineOrder];
|
|
240
|
+
}
|
|
241
|
+
function parseEnvmap(dataView, offset) {
|
|
242
|
+
const envmaps = [
|
|
243
|
+
'ENVMAP_LATLONG',
|
|
244
|
+
'ENVMAP_CUBE'
|
|
245
|
+
];
|
|
246
|
+
const envmap = parseUint8(dataView, offset);
|
|
247
|
+
return envmaps[envmap];
|
|
248
|
+
}
|
|
249
|
+
function parseTiledesc(dataView, offset) {
|
|
250
|
+
const levelModes = [
|
|
251
|
+
'ONE_LEVEL',
|
|
252
|
+
'MIPMAP_LEVELS',
|
|
253
|
+
'RIPMAP_LEVELS',
|
|
254
|
+
];
|
|
255
|
+
const roundingModes = [
|
|
256
|
+
'ROUND_DOWN',
|
|
257
|
+
'ROUND_UP',
|
|
258
|
+
];
|
|
259
|
+
const xSize = parseUint32(dataView, offset);
|
|
260
|
+
const ySize = parseUint32(dataView, offset);
|
|
261
|
+
const modes = parseUint8(dataView, offset);
|
|
262
|
+
return {
|
|
263
|
+
xSize: xSize,
|
|
264
|
+
ySize: ySize,
|
|
265
|
+
levelMode: levelModes[modes & 0xf],
|
|
266
|
+
roundingMode: roundingModes[modes >> 4]
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function parseV2f(dataView, offset) {
|
|
270
|
+
const x = parseFloat32(dataView, offset);
|
|
271
|
+
const y = parseFloat32(dataView, offset);
|
|
272
|
+
return [x, y];
|
|
273
|
+
}
|
|
274
|
+
function parseV3f(dataView, offset) {
|
|
275
|
+
const x = parseFloat32(dataView, offset);
|
|
276
|
+
const y = parseFloat32(dataView, offset);
|
|
277
|
+
const z = parseFloat32(dataView, offset);
|
|
278
|
+
return [x, y, z];
|
|
279
|
+
}
|
|
280
|
+
function parseValue(dataView, buffer, offset, type, size) {
|
|
281
|
+
if (type === 'string' || type === 'stringvector' || type === 'iccProfile') {
|
|
282
|
+
return parseFixedLengthString(buffer, offset, size);
|
|
283
|
+
}
|
|
284
|
+
else if (type === 'chlist') {
|
|
285
|
+
return parseChlist(dataView, buffer, offset, size);
|
|
286
|
+
}
|
|
287
|
+
else if (type === 'chromaticities') {
|
|
288
|
+
return parseChromaticities(dataView, offset);
|
|
289
|
+
}
|
|
290
|
+
else if (type === 'compression') {
|
|
291
|
+
return parseCompression(dataView, offset);
|
|
292
|
+
}
|
|
293
|
+
else if (type === 'box2i') {
|
|
294
|
+
return parseBox2i(dataView, offset);
|
|
295
|
+
}
|
|
296
|
+
else if (type === 'envmap') {
|
|
297
|
+
return parseEnvmap(dataView, offset);
|
|
298
|
+
}
|
|
299
|
+
else if (type === 'tiledesc') {
|
|
300
|
+
return parseTiledesc(dataView, offset);
|
|
301
|
+
}
|
|
302
|
+
else if (type === 'lineOrder') {
|
|
303
|
+
return parseLineOrder(dataView, offset);
|
|
304
|
+
}
|
|
305
|
+
else if (type === 'float') {
|
|
306
|
+
return parseFloat32(dataView, offset);
|
|
307
|
+
}
|
|
308
|
+
else if (type === 'v2f') {
|
|
309
|
+
return parseV2f(dataView, offset);
|
|
310
|
+
}
|
|
311
|
+
else if (type === 'v3f') {
|
|
312
|
+
return parseV3f(dataView, offset);
|
|
313
|
+
}
|
|
314
|
+
else if (type === 'int') {
|
|
315
|
+
return parseInt32(dataView, offset);
|
|
316
|
+
}
|
|
317
|
+
else if (type === 'rational') {
|
|
318
|
+
return parseRational(dataView, offset);
|
|
319
|
+
}
|
|
320
|
+
else if (type === 'timecode') {
|
|
321
|
+
return parseTimecode(dataView, offset);
|
|
322
|
+
}
|
|
323
|
+
else if (type === 'preview' || type === 'deepImageState' || type === 'idmanifest') {
|
|
324
|
+
// Known metadata-only types: silently skip, they carry no pixel data.
|
|
325
|
+
offset.value += size;
|
|
326
|
+
return 'skipped';
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
offset.value += size;
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function roundLog2(x, mode) {
|
|
334
|
+
const log2 = Math.log2(x);
|
|
335
|
+
return mode == 'ROUND_DOWN' ? Math.floor(log2) : Math.ceil(log2);
|
|
336
|
+
}
|
|
337
|
+
function calculateTileLevels(tiledesc, w, h) {
|
|
338
|
+
let num = 0;
|
|
339
|
+
switch (tiledesc.levelMode) {
|
|
340
|
+
case 'ONE_LEVEL':
|
|
341
|
+
num = 1;
|
|
342
|
+
break;
|
|
343
|
+
case 'MIPMAP_LEVELS':
|
|
344
|
+
num = roundLog2(Math.max(w, h), tiledesc.roundingMode) + 1;
|
|
345
|
+
break;
|
|
346
|
+
case 'RIPMAP_LEVELS':
|
|
347
|
+
throw new Error('EXRLoader: RIPMAP_LEVELS tiles currently unsupported.');
|
|
348
|
+
}
|
|
349
|
+
return num;
|
|
350
|
+
}
|
|
351
|
+
function calculateTiles(count, dataSize, size, roundingMode) {
|
|
352
|
+
const tiles = new Array(count);
|
|
353
|
+
for (let i = 0; i < count; i++) {
|
|
354
|
+
const b = (1 << i);
|
|
355
|
+
let s = (dataSize / b) | 0;
|
|
356
|
+
if (roundingMode == 'ROUND_UP' && s * b < dataSize)
|
|
357
|
+
s += 1;
|
|
358
|
+
const l = Math.max(s, 1);
|
|
359
|
+
tiles[i] = ((l + size - 1) / size) | 0;
|
|
360
|
+
}
|
|
361
|
+
return tiles;
|
|
362
|
+
}
|
|
363
|
+
async function parseTiles() {
|
|
364
|
+
const EXRDecoder = this;
|
|
365
|
+
const offset = EXRDecoder.offset;
|
|
366
|
+
const tmpOffset = { value: 0 };
|
|
367
|
+
for (let tile = 0; tile < EXRDecoder.tileCount; tile++) {
|
|
368
|
+
const tileX = parseInt32(EXRDecoder.viewer, offset);
|
|
369
|
+
const tileY = parseInt32(EXRDecoder.viewer, offset);
|
|
370
|
+
offset.value += 8; // skip levels - only parsing top-level
|
|
371
|
+
EXRDecoder.size = parseUint32(EXRDecoder.viewer, offset);
|
|
372
|
+
const startX = tileX * EXRDecoder.blockWidth;
|
|
373
|
+
const startY = tileY * EXRDecoder.blockHeight;
|
|
374
|
+
EXRDecoder.columns = (startX + EXRDecoder.blockWidth > EXRDecoder.width) ? EXRDecoder.width - startX : EXRDecoder.blockWidth;
|
|
375
|
+
EXRDecoder.lines = (startY + EXRDecoder.blockHeight > EXRDecoder.height) ? EXRDecoder.height - startY : EXRDecoder.blockHeight;
|
|
376
|
+
const bytesBlockLine = EXRDecoder.columns * EXRDecoder.totalBytes;
|
|
377
|
+
const isCompressed = EXRDecoder.size < EXRDecoder.lines * bytesBlockLine;
|
|
378
|
+
const viewer = isCompressed ? await EXRDecoder.uncompress(EXRDecoder) : uncompressRAW(EXRDecoder);
|
|
379
|
+
offset.value += EXRDecoder.size;
|
|
380
|
+
for (let line = 0; line < EXRDecoder.lines; line++) {
|
|
381
|
+
const lineOffset = line * EXRDecoder.columns * EXRDecoder.totalBytes;
|
|
382
|
+
for (let channelID = 0; channelID < EXRDecoder.inputChannels.length; channelID++) {
|
|
383
|
+
const name = EXRHeader.channels[channelID].name;
|
|
384
|
+
const lOff = EXRDecoder.channelByteOffsets[name] * EXRDecoder.columns;
|
|
385
|
+
const cOff = EXRDecoder.decodeChannels[name];
|
|
386
|
+
if (cOff === undefined)
|
|
387
|
+
continue;
|
|
388
|
+
tmpOffset.value = lineOffset + lOff;
|
|
389
|
+
const outLineOffset = (EXRDecoder.height - (1 + startY + line)) * EXRDecoder.outLineWidth;
|
|
390
|
+
for (let x = 0; x < EXRDecoder.columns; x++) {
|
|
391
|
+
const outIndex = outLineOffset + (x + startX) * EXRDecoder.outputChannels + cOff;
|
|
392
|
+
EXRDecoder.byteArray[outIndex] = EXRDecoder.getter(viewer, tmpOffset);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function reverseLutFromBitmap(bitmap, lut) {
|
|
399
|
+
let k = 0;
|
|
400
|
+
for (let i = 0; i < USHORT_RANGE; ++i) {
|
|
401
|
+
if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7)))) {
|
|
402
|
+
lut[k++] = i;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const n = k - 1;
|
|
406
|
+
while (k < USHORT_RANGE)
|
|
407
|
+
lut[k++] = 0;
|
|
408
|
+
return n;
|
|
409
|
+
}
|
|
410
|
+
function hufClearDecTable(hdec) {
|
|
411
|
+
for (let i = 0; i < HUF_DECSIZE; i++) {
|
|
412
|
+
hdec[i] = {};
|
|
413
|
+
hdec[i].len = 0;
|
|
414
|
+
hdec[i].lit = 0;
|
|
415
|
+
hdec[i].p = null;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
const getBitsReturn = { l: 0, c: 0, lc: 0 };
|
|
419
|
+
function getBits(nBits, c, lc, uInt8Array, inOffset) {
|
|
420
|
+
while (lc < nBits) {
|
|
421
|
+
c = (c << 8) | parseUint8Array(uInt8Array, inOffset);
|
|
422
|
+
lc += 8;
|
|
423
|
+
}
|
|
424
|
+
lc -= nBits;
|
|
425
|
+
getBitsReturn.l = (c >> lc) & ((1 << nBits) - 1);
|
|
426
|
+
getBitsReturn.c = c;
|
|
427
|
+
getBitsReturn.lc = lc;
|
|
428
|
+
}
|
|
429
|
+
const hufTableBuffer = new Array(59);
|
|
430
|
+
function hufCanonicalCodeTable(hcode) {
|
|
431
|
+
for (let i = 0; i <= 58; ++i)
|
|
432
|
+
hufTableBuffer[i] = 0;
|
|
433
|
+
for (let i = 0; i < HUF_ENCSIZE; ++i)
|
|
434
|
+
hufTableBuffer[hcode[i]] += 1;
|
|
435
|
+
let c = 0;
|
|
436
|
+
for (let i = 58; i > 0; --i) {
|
|
437
|
+
const nc = ((c + hufTableBuffer[i]) >> 1);
|
|
438
|
+
hufTableBuffer[i] = c;
|
|
439
|
+
c = nc;
|
|
440
|
+
}
|
|
441
|
+
for (let i = 0; i < HUF_ENCSIZE; ++i) {
|
|
442
|
+
const l = hcode[i];
|
|
443
|
+
if (l > 0)
|
|
444
|
+
hcode[i] = l | (hufTableBuffer[l]++ << 6);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
function hufUnpackEncTable(uInt8Array, inOffset, ni, im, iM, hcode) {
|
|
448
|
+
const p = inOffset;
|
|
449
|
+
let c = 0;
|
|
450
|
+
let lc = 0;
|
|
451
|
+
for (; im <= iM; im++) {
|
|
452
|
+
if (p.value - inOffset.value > ni)
|
|
453
|
+
return false;
|
|
454
|
+
getBits(6, c, lc, uInt8Array, p);
|
|
455
|
+
const l = getBitsReturn.l;
|
|
456
|
+
c = getBitsReturn.c;
|
|
457
|
+
lc = getBitsReturn.lc;
|
|
458
|
+
hcode[im] = l;
|
|
459
|
+
if (l == LONG_ZEROCODE_RUN) {
|
|
460
|
+
if (p.value - inOffset.value > ni) {
|
|
461
|
+
throw new Error('EXRLoader: Something wrong with hufUnpackEncTable');
|
|
462
|
+
}
|
|
463
|
+
getBits(8, c, lc, uInt8Array, p);
|
|
464
|
+
let zerun = getBitsReturn.l + SHORTEST_LONG_RUN;
|
|
465
|
+
c = getBitsReturn.c;
|
|
466
|
+
lc = getBitsReturn.lc;
|
|
467
|
+
if (im + zerun > iM + 1) {
|
|
468
|
+
throw new Error('EXRLoader: Something wrong with hufUnpackEncTable');
|
|
469
|
+
}
|
|
470
|
+
while (zerun--)
|
|
471
|
+
hcode[im++] = 0;
|
|
472
|
+
im--;
|
|
473
|
+
}
|
|
474
|
+
else if (l >= SHORT_ZEROCODE_RUN) {
|
|
475
|
+
let zerun = l - SHORT_ZEROCODE_RUN + 2;
|
|
476
|
+
if (im + zerun > iM + 1) {
|
|
477
|
+
throw new Error('EXRLoader: Something wrong with hufUnpackEncTable');
|
|
478
|
+
}
|
|
479
|
+
while (zerun--)
|
|
480
|
+
hcode[im++] = 0;
|
|
481
|
+
im--;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
hufCanonicalCodeTable(hcode);
|
|
485
|
+
}
|
|
486
|
+
function hufLength(code) {
|
|
487
|
+
return code & 63;
|
|
488
|
+
}
|
|
489
|
+
function hufCode(code) {
|
|
490
|
+
return code >> 6;
|
|
491
|
+
}
|
|
492
|
+
function hufBuildDecTable(hcode, im, iM, hdecod) {
|
|
493
|
+
for (; im <= iM; im++) {
|
|
494
|
+
const c = hufCode(hcode[im]);
|
|
495
|
+
const l = hufLength(hcode[im]);
|
|
496
|
+
if (c >> l) {
|
|
497
|
+
throw new Error('EXRLoader: Invalid table entry');
|
|
498
|
+
}
|
|
499
|
+
if (l > HUF_DECBITS) {
|
|
500
|
+
const pl = hdecod[(c >> (l - HUF_DECBITS))];
|
|
501
|
+
if (pl.len) {
|
|
502
|
+
throw new Error('EXRLoader: Invalid table entry');
|
|
503
|
+
}
|
|
504
|
+
pl.lit++;
|
|
505
|
+
if (pl.p) {
|
|
506
|
+
const p = pl.p;
|
|
507
|
+
pl.p = new Array(pl.lit);
|
|
508
|
+
for (let i = 0; i < pl.lit - 1; ++i) {
|
|
509
|
+
pl.p[i] = p[i];
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
pl.p = new Array(1);
|
|
514
|
+
}
|
|
515
|
+
pl.p[pl.lit - 1] = im;
|
|
516
|
+
}
|
|
517
|
+
else if (l) {
|
|
518
|
+
let plOffset = 0;
|
|
519
|
+
for (let i = 1 << (HUF_DECBITS - l); i > 0; i--) {
|
|
520
|
+
const pl = hdecod[(c << (HUF_DECBITS - l)) + plOffset];
|
|
521
|
+
if (pl.len || pl.p) {
|
|
522
|
+
throw new Error('EXRLoader: Invalid table entry');
|
|
523
|
+
}
|
|
524
|
+
pl.len = l;
|
|
525
|
+
pl.lit = im;
|
|
526
|
+
plOffset++;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
const getCharReturn = { c: 0, lc: 0 };
|
|
533
|
+
function getChar(c, lc, uInt8Array, inOffset) {
|
|
534
|
+
c = (c << 8) | parseUint8Array(uInt8Array, inOffset);
|
|
535
|
+
lc += 8;
|
|
536
|
+
getCharReturn.c = c;
|
|
537
|
+
getCharReturn.lc = lc;
|
|
538
|
+
}
|
|
539
|
+
const getCodeReturn = { c: 0, lc: 0 };
|
|
540
|
+
function getCode(po, rlc, c, lc, uInt8Array, inOffset, outBuffer, outBufferOffset, outBufferEndOffset) {
|
|
541
|
+
if (po == rlc) {
|
|
542
|
+
if (lc < 8) {
|
|
543
|
+
getChar(c, lc, uInt8Array, inOffset);
|
|
544
|
+
c = getCharReturn.c;
|
|
545
|
+
lc = getCharReturn.lc;
|
|
546
|
+
}
|
|
547
|
+
lc -= 8;
|
|
548
|
+
let cs = (c >> lc);
|
|
549
|
+
cs = new Uint8Array([cs])[0];
|
|
550
|
+
if (outBufferOffset.value + cs > outBufferEndOffset) {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
const s = outBuffer[outBufferOffset.value - 1];
|
|
554
|
+
while (cs-- > 0) {
|
|
555
|
+
outBuffer[outBufferOffset.value++] = s;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
else if (outBufferOffset.value < outBufferEndOffset) {
|
|
559
|
+
outBuffer[outBufferOffset.value++] = po;
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
getCodeReturn.c = c;
|
|
565
|
+
getCodeReturn.lc = lc;
|
|
566
|
+
}
|
|
567
|
+
function UInt16(value) {
|
|
568
|
+
return (value & 0xFFFF);
|
|
569
|
+
}
|
|
570
|
+
function Int16(value) {
|
|
571
|
+
const ref = UInt16(value);
|
|
572
|
+
return (ref > 0x7FFF) ? ref - 0x10000 : ref;
|
|
573
|
+
}
|
|
574
|
+
const wdec14Return = { a: 0, b: 0 };
|
|
575
|
+
function wdec14(l, h) {
|
|
576
|
+
const ls = Int16(l);
|
|
577
|
+
const hs = Int16(h);
|
|
578
|
+
const hi = hs;
|
|
579
|
+
const ai = ls + (hi & 1) + (hi >> 1);
|
|
580
|
+
const as = ai;
|
|
581
|
+
const bs = ai - hi;
|
|
582
|
+
wdec14Return.a = as;
|
|
583
|
+
wdec14Return.b = bs;
|
|
584
|
+
}
|
|
585
|
+
function wdec16(l, h) {
|
|
586
|
+
const m = UInt16(l);
|
|
587
|
+
const d = UInt16(h);
|
|
588
|
+
const bb = (m - (d >> 1)) & MOD_MASK;
|
|
589
|
+
const aa = (d + bb - A_OFFSET) & MOD_MASK;
|
|
590
|
+
wdec14Return.a = aa;
|
|
591
|
+
wdec14Return.b = bb;
|
|
592
|
+
}
|
|
593
|
+
function wav2Decode(buffer, j, nx, ox, ny, oy, mx) {
|
|
594
|
+
const w14 = mx < (1 << 14);
|
|
595
|
+
const n = (nx > ny) ? ny : nx;
|
|
596
|
+
let p = 1;
|
|
597
|
+
let p2;
|
|
598
|
+
let py;
|
|
599
|
+
while (p <= n)
|
|
600
|
+
p <<= 1;
|
|
601
|
+
p >>= 1;
|
|
602
|
+
p2 = p;
|
|
603
|
+
p >>= 1;
|
|
604
|
+
while (p >= 1) {
|
|
605
|
+
py = 0;
|
|
606
|
+
const ey = py + oy * (ny - p2);
|
|
607
|
+
const oy1 = oy * p;
|
|
608
|
+
const oy2 = oy * p2;
|
|
609
|
+
const ox1 = ox * p;
|
|
610
|
+
const ox2 = ox * p2;
|
|
611
|
+
let i00, i01, i10, i11;
|
|
612
|
+
for (; py <= ey; py += oy2) {
|
|
613
|
+
let px = py;
|
|
614
|
+
const ex = py + ox * (nx - p2);
|
|
615
|
+
for (; px <= ex; px += ox2) {
|
|
616
|
+
const p01 = px + ox1;
|
|
617
|
+
const p10 = px + oy1;
|
|
618
|
+
const p11 = p10 + ox1;
|
|
619
|
+
if (w14) {
|
|
620
|
+
wdec14(buffer[px + j], buffer[p10 + j]);
|
|
621
|
+
i00 = wdec14Return.a;
|
|
622
|
+
i10 = wdec14Return.b;
|
|
623
|
+
wdec14(buffer[p01 + j], buffer[p11 + j]);
|
|
624
|
+
i01 = wdec14Return.a;
|
|
625
|
+
i11 = wdec14Return.b;
|
|
626
|
+
wdec14(i00, i01);
|
|
627
|
+
buffer[px + j] = wdec14Return.a;
|
|
628
|
+
buffer[p01 + j] = wdec14Return.b;
|
|
629
|
+
wdec14(i10, i11);
|
|
630
|
+
buffer[p10 + j] = wdec14Return.a;
|
|
631
|
+
buffer[p11 + j] = wdec14Return.b;
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
wdec16(buffer[px + j], buffer[p10 + j]);
|
|
635
|
+
i00 = wdec14Return.a;
|
|
636
|
+
i10 = wdec14Return.b;
|
|
637
|
+
wdec16(buffer[p01 + j], buffer[p11 + j]);
|
|
638
|
+
i01 = wdec14Return.a;
|
|
639
|
+
i11 = wdec14Return.b;
|
|
640
|
+
wdec16(i00, i01);
|
|
641
|
+
buffer[px + j] = wdec14Return.a;
|
|
642
|
+
buffer[p01 + j] = wdec14Return.b;
|
|
643
|
+
wdec16(i10, i11);
|
|
644
|
+
buffer[p10 + j] = wdec14Return.a;
|
|
645
|
+
buffer[p11 + j] = wdec14Return.b;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (nx & p) {
|
|
649
|
+
const p10 = px + oy1;
|
|
650
|
+
if (w14)
|
|
651
|
+
wdec14(buffer[px + j], buffer[p10 + j]);
|
|
652
|
+
else
|
|
653
|
+
wdec16(buffer[px + j], buffer[p10 + j]);
|
|
654
|
+
i00 = wdec14Return.a;
|
|
655
|
+
buffer[p10 + j] = wdec14Return.b;
|
|
656
|
+
buffer[px + j] = i00;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
if (ny & p) {
|
|
660
|
+
let px = py;
|
|
661
|
+
const ex = py + ox * (nx - p2);
|
|
662
|
+
for (; px <= ex; px += ox2) {
|
|
663
|
+
const p01 = px + ox1;
|
|
664
|
+
if (w14)
|
|
665
|
+
wdec14(buffer[px + j], buffer[p01 + j]);
|
|
666
|
+
else
|
|
667
|
+
wdec16(buffer[px + j], buffer[p01 + j]);
|
|
668
|
+
i00 = wdec14Return.a;
|
|
669
|
+
buffer[p01 + j] = wdec14Return.b;
|
|
670
|
+
buffer[px + j] = i00;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
p2 = p;
|
|
674
|
+
p >>= 1;
|
|
675
|
+
}
|
|
676
|
+
return py;
|
|
677
|
+
}
|
|
678
|
+
function hufDecode(encodingTable, decodingTable, uInt8Array, inOffset, ni, rlc, no, outBuffer, outOffset) {
|
|
679
|
+
let c = 0;
|
|
680
|
+
let lc = 0;
|
|
681
|
+
const outBufferEndOffset = no;
|
|
682
|
+
const inOffsetEnd = Math.trunc(inOffset.value + (ni + 7) / 8);
|
|
683
|
+
while (inOffset.value < inOffsetEnd) {
|
|
684
|
+
getChar(c, lc, uInt8Array, inOffset);
|
|
685
|
+
c = getCharReturn.c;
|
|
686
|
+
lc = getCharReturn.lc;
|
|
687
|
+
while (lc >= HUF_DECBITS) {
|
|
688
|
+
const index = (c >> (lc - HUF_DECBITS)) & HUF_DECMASK;
|
|
689
|
+
const pl = decodingTable[index];
|
|
690
|
+
if (pl.len) {
|
|
691
|
+
lc -= pl.len;
|
|
692
|
+
getCode(pl.lit, rlc, c, lc, uInt8Array, inOffset, outBuffer, outOffset, outBufferEndOffset);
|
|
693
|
+
c = getCodeReturn.c;
|
|
694
|
+
lc = getCodeReturn.lc;
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
if (!pl.p) {
|
|
698
|
+
throw new Error('EXRLoader: hufDecode issues');
|
|
699
|
+
}
|
|
700
|
+
let j;
|
|
701
|
+
for (j = 0; j < pl.lit; j++) {
|
|
702
|
+
const l = hufLength(encodingTable[pl.p[j]]);
|
|
703
|
+
while (lc < l && inOffset.value < inOffsetEnd) {
|
|
704
|
+
getChar(c, lc, uInt8Array, inOffset);
|
|
705
|
+
c = getCharReturn.c;
|
|
706
|
+
lc = getCharReturn.lc;
|
|
707
|
+
}
|
|
708
|
+
if (lc >= l) {
|
|
709
|
+
if (hufCode(encodingTable[pl.p[j]]) == ((c >> (lc - l)) & ((1 << l) - 1))) {
|
|
710
|
+
lc -= l;
|
|
711
|
+
getCode(pl.p[j], rlc, c, lc, uInt8Array, inOffset, outBuffer, outOffset, outBufferEndOffset);
|
|
712
|
+
c = getCodeReturn.c;
|
|
713
|
+
lc = getCodeReturn.lc;
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (j == pl.lit) {
|
|
719
|
+
throw new Error('EXRLoader: hufDecode issues');
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
const i = (8 - ni) & 7;
|
|
725
|
+
c >>= i;
|
|
726
|
+
lc -= i;
|
|
727
|
+
while (lc > 0) {
|
|
728
|
+
const pl = decodingTable[(c << (HUF_DECBITS - lc)) & HUF_DECMASK];
|
|
729
|
+
if (pl.len) {
|
|
730
|
+
lc -= pl.len;
|
|
731
|
+
getCode(pl.lit, rlc, c, lc, uInt8Array, inOffset, outBuffer, outOffset, outBufferEndOffset);
|
|
732
|
+
c = getCodeReturn.c;
|
|
733
|
+
lc = getCodeReturn.lc;
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
throw new Error('EXRLoader: hufDecode issues');
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
function hufUncompress(uInt8Array, inDataView, inOffset, nCompressed, outBuffer, nRaw) {
|
|
742
|
+
const outOffset = { value: 0 };
|
|
743
|
+
const initialInOffset = inOffset.value;
|
|
744
|
+
const im = parseUint32(inDataView, inOffset);
|
|
745
|
+
const iM = parseUint32(inDataView, inOffset);
|
|
746
|
+
inOffset.value += 4;
|
|
747
|
+
const nBits = parseUint32(inDataView, inOffset);
|
|
748
|
+
inOffset.value += 4;
|
|
749
|
+
if (im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE) {
|
|
750
|
+
throw new Error('EXRLoader: Something wrong with HUF_ENCSIZE');
|
|
751
|
+
}
|
|
752
|
+
const freq = new Array(HUF_ENCSIZE);
|
|
753
|
+
const hdec = new Array(HUF_DECSIZE);
|
|
754
|
+
hufClearDecTable(hdec);
|
|
755
|
+
const ni = nCompressed - (inOffset.value - initialInOffset);
|
|
756
|
+
hufUnpackEncTable(uInt8Array, inOffset, ni, im, iM, freq);
|
|
757
|
+
if (nBits > 8 * (nCompressed - (inOffset.value - initialInOffset))) {
|
|
758
|
+
throw new Error('EXRLoader: Something wrong with hufUncompress');
|
|
759
|
+
}
|
|
760
|
+
hufBuildDecTable(freq, im, iM, hdec);
|
|
761
|
+
hufDecode(freq, hdec, uInt8Array, inOffset, nBits, iM, nRaw, outBuffer, outOffset);
|
|
762
|
+
}
|
|
763
|
+
function applyLut(lut, data, nData) {
|
|
764
|
+
for (let i = 0; i < nData; ++i) {
|
|
765
|
+
data[i] = lut[data[i]];
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
function predictor(source) {
|
|
769
|
+
for (let t = 1; t < source.length; t++) {
|
|
770
|
+
const d = source[t - 1] + source[t] - 128;
|
|
771
|
+
source[t] = d;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function interleaveScalar(source, out) {
|
|
775
|
+
let t1 = 0;
|
|
776
|
+
let t2 = Math.floor((source.length + 1) / 2);
|
|
777
|
+
let s = 0;
|
|
778
|
+
const stop = source.length - 1;
|
|
779
|
+
while (true) {
|
|
780
|
+
if (s > stop)
|
|
781
|
+
break;
|
|
782
|
+
out[s++] = source[t1++];
|
|
783
|
+
if (s > stop)
|
|
784
|
+
break;
|
|
785
|
+
out[s++] = source[t2++];
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
function decodeRunLength(source) {
|
|
789
|
+
let size = source.byteLength;
|
|
790
|
+
const out = new Array();
|
|
791
|
+
let p = 0;
|
|
792
|
+
const reader = new DataView(source);
|
|
793
|
+
while (size > 0) {
|
|
794
|
+
const l = reader.getInt8(p++);
|
|
795
|
+
if (l < 0) {
|
|
796
|
+
const count = -l;
|
|
797
|
+
size -= count + 1;
|
|
798
|
+
for (let i = 0; i < count; i++) {
|
|
799
|
+
out.push(reader.getUint8(p++));
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
const count = l;
|
|
804
|
+
size -= 2;
|
|
805
|
+
const value = reader.getUint8(p++);
|
|
806
|
+
for (let i = 0; i < count + 1; i++) {
|
|
807
|
+
out.push(value);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
return out;
|
|
812
|
+
}
|
|
813
|
+
function uncompressRAW(info) {
|
|
814
|
+
return new DataView(info.array.buffer, info.offset.value, info.size);
|
|
815
|
+
}
|
|
816
|
+
function uncompressRLE(info) {
|
|
817
|
+
const compressed = info.viewer.buffer.slice(info.offset.value, info.offset.value + info.size);
|
|
818
|
+
const rawBuffer = new Uint8Array(decodeRunLength(compressed));
|
|
819
|
+
const tmpBuffer = new Uint8Array(rawBuffer.length);
|
|
820
|
+
predictor(rawBuffer); // revert predictor
|
|
821
|
+
interleaveScalar(rawBuffer, tmpBuffer); // interleave pixels
|
|
822
|
+
return new DataView(tmpBuffer.buffer);
|
|
823
|
+
}
|
|
824
|
+
async function uncompressZIP(info) {
|
|
825
|
+
const compressed = info.array.slice(info.offset.value, info.offset.value + info.size);
|
|
826
|
+
const rawBuffer = await inflateZlib(compressed);
|
|
827
|
+
const tmpBuffer = new Uint8Array(rawBuffer.length);
|
|
828
|
+
predictor(rawBuffer); // revert predictor
|
|
829
|
+
interleaveScalar(rawBuffer, tmpBuffer); // interleave pixels
|
|
830
|
+
return new DataView(tmpBuffer.buffer);
|
|
831
|
+
}
|
|
832
|
+
function uncompressPIZ(info) {
|
|
833
|
+
const inDataView = info.viewer;
|
|
834
|
+
const inOffset = { value: info.offset.value };
|
|
835
|
+
const outBuffer = new Uint16Array(info.columns * info.lines * (info.inputChannels.length * info.type));
|
|
836
|
+
const bitmap = new Uint8Array(BITMAP_SIZE);
|
|
837
|
+
// Setup channel info
|
|
838
|
+
let outBufferEnd = 0;
|
|
839
|
+
const pizChannelData = new Array(info.inputChannels.length);
|
|
840
|
+
for (let i = 0, il = info.inputChannels.length; i < il; i++) {
|
|
841
|
+
pizChannelData[i] = {};
|
|
842
|
+
pizChannelData[i]['start'] = outBufferEnd;
|
|
843
|
+
pizChannelData[i]['end'] = pizChannelData[i]['start'];
|
|
844
|
+
pizChannelData[i]['nx'] = info.columns;
|
|
845
|
+
pizChannelData[i]['ny'] = info.lines;
|
|
846
|
+
pizChannelData[i]['size'] = info.type;
|
|
847
|
+
outBufferEnd += pizChannelData[i].nx * pizChannelData[i].ny * pizChannelData[i].size;
|
|
848
|
+
}
|
|
849
|
+
// Read range compression data
|
|
850
|
+
const minNonZero = parseUint16(inDataView, inOffset);
|
|
851
|
+
const maxNonZero = parseUint16(inDataView, inOffset);
|
|
852
|
+
if (maxNonZero >= BITMAP_SIZE) {
|
|
853
|
+
throw new Error('EXRLoader: Something is wrong with PIZ_COMPRESSION BITMAP_SIZE');
|
|
854
|
+
}
|
|
855
|
+
if (minNonZero <= maxNonZero) {
|
|
856
|
+
for (let i = 0; i < maxNonZero - minNonZero + 1; i++) {
|
|
857
|
+
bitmap[i + minNonZero] = parseUint8(inDataView, inOffset);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
// Reverse LUT
|
|
861
|
+
const lut = new Uint16Array(USHORT_RANGE);
|
|
862
|
+
const maxValue = reverseLutFromBitmap(bitmap, lut);
|
|
863
|
+
const length = parseUint32(inDataView, inOffset);
|
|
864
|
+
// Huffman decoding
|
|
865
|
+
hufUncompress(info.array, inDataView, inOffset, length, outBuffer, outBufferEnd);
|
|
866
|
+
// Wavelet decoding
|
|
867
|
+
for (let i = 0; i < info.inputChannels.length; ++i) {
|
|
868
|
+
const cd = pizChannelData[i];
|
|
869
|
+
for (let j = 0; j < pizChannelData[i].size; ++j) {
|
|
870
|
+
wav2Decode(outBuffer, cd.start + j, cd.nx, cd.size, cd.ny, cd.nx * cd.size, maxValue);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
// Expand the pixel data to their original range
|
|
874
|
+
applyLut(lut, outBuffer, outBufferEnd);
|
|
875
|
+
// Rearrange the pixel data into the format expected by the caller.
|
|
876
|
+
let tmpOffset = 0;
|
|
877
|
+
const tmpBuffer = new Uint8Array(outBuffer.buffer.byteLength);
|
|
878
|
+
for (let y = 0; y < info.lines; y++) {
|
|
879
|
+
for (let c = 0; c < info.inputChannels.length; c++) {
|
|
880
|
+
const cd = pizChannelData[c];
|
|
881
|
+
const n = cd.nx * cd.size;
|
|
882
|
+
const cp = new Uint8Array(outBuffer.buffer, cd.end * INT16_SIZE, n * INT16_SIZE);
|
|
883
|
+
tmpBuffer.set(cp, tmpOffset);
|
|
884
|
+
tmpOffset += n * INT16_SIZE;
|
|
885
|
+
cd.end += n;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return new DataView(tmpBuffer.buffer);
|
|
889
|
+
}
|
|
890
|
+
async function parseScanline() {
|
|
891
|
+
const EXRDecoder = this;
|
|
892
|
+
const offset = EXRDecoder.offset;
|
|
893
|
+
const tmpOffset = { value: 0 };
|
|
894
|
+
for (let scanlineBlockIdx = 0; scanlineBlockIdx < EXRDecoder.height / EXRDecoder.blockHeight; scanlineBlockIdx++) {
|
|
895
|
+
const line = parseInt32(EXRDecoder.viewer, offset) - EXRHeader.dataWindow.yMin; // line_no
|
|
896
|
+
EXRDecoder.size = parseUint32(EXRDecoder.viewer, offset); // data_len
|
|
897
|
+
EXRDecoder.lines = ((line + EXRDecoder.blockHeight > EXRDecoder.height) ? (EXRDecoder.height - line) : EXRDecoder.blockHeight);
|
|
898
|
+
const bytesPerLine = EXRDecoder.columns * EXRDecoder.totalBytes;
|
|
899
|
+
const isCompressed = EXRDecoder.size < EXRDecoder.lines * bytesPerLine;
|
|
900
|
+
const viewer = isCompressed ? await EXRDecoder.uncompress(EXRDecoder) : uncompressRAW(EXRDecoder);
|
|
901
|
+
offset.value += EXRDecoder.size;
|
|
902
|
+
for (let line_y = 0; line_y < EXRDecoder.lines; line_y++) {
|
|
903
|
+
const true_y = line + line_y;
|
|
904
|
+
const lineOffset = line_y * bytesPerLine;
|
|
905
|
+
const outLineOffset = (EXRDecoder.height - 1 - true_y) * EXRDecoder.outLineWidth;
|
|
906
|
+
for (let channelID = 0; channelID < EXRDecoder.inputChannels.length; channelID++) {
|
|
907
|
+
const name = EXRHeader.channels[channelID].name;
|
|
908
|
+
const lOff = EXRDecoder.channelByteOffsets[name] * EXRDecoder.columns;
|
|
909
|
+
const cOff = EXRDecoder.decodeChannels[name];
|
|
910
|
+
if (cOff === undefined)
|
|
911
|
+
continue;
|
|
912
|
+
tmpOffset.value = lineOffset + lOff;
|
|
913
|
+
for (let x = 0; x < EXRDecoder.columns; x++) {
|
|
914
|
+
const outIndex = outLineOffset + x * EXRDecoder.outputChannels + cOff;
|
|
915
|
+
EXRDecoder.byteArray[outIndex] = EXRDecoder.getter(viewer, tmpOffset);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
async function parseMultiPartScanline() {
|
|
922
|
+
const EXRDecoder = this;
|
|
923
|
+
const chunkOffsets = EXRDecoder.chunkOffsets;
|
|
924
|
+
const tmpOffset = { value: 0 };
|
|
925
|
+
for (let chunkIdx = 0; chunkIdx < chunkOffsets.length; chunkIdx++) {
|
|
926
|
+
const offset = { value: chunkOffsets[chunkIdx] };
|
|
927
|
+
offset.value += INT32_SIZE; // skip part number
|
|
928
|
+
const line = parseInt32(EXRDecoder.viewer, offset) - EXRHeader.dataWindow.yMin;
|
|
929
|
+
EXRDecoder.size = parseUint32(EXRDecoder.viewer, offset);
|
|
930
|
+
EXRDecoder.lines = ((line + EXRDecoder.blockHeight > EXRDecoder.height) ? (EXRDecoder.height - line) : EXRDecoder.blockHeight);
|
|
931
|
+
const bytesPerLine = EXRDecoder.columns * EXRDecoder.totalBytes;
|
|
932
|
+
const isCompressed = EXRDecoder.size < EXRDecoder.lines * bytesPerLine;
|
|
933
|
+
const savedOffset = EXRDecoder.offset;
|
|
934
|
+
EXRDecoder.offset = offset;
|
|
935
|
+
const viewer = isCompressed ? await EXRDecoder.uncompress(EXRDecoder) : uncompressRAW(EXRDecoder);
|
|
936
|
+
EXRDecoder.offset = savedOffset;
|
|
937
|
+
for (let line_y = 0; line_y < EXRDecoder.lines; line_y++) {
|
|
938
|
+
const true_y = line + line_y;
|
|
939
|
+
const lineOffset = line_y * bytesPerLine;
|
|
940
|
+
const outLineOffset = (EXRDecoder.height - 1 - true_y) * EXRDecoder.outLineWidth;
|
|
941
|
+
for (let channelID = 0; channelID < EXRDecoder.inputChannels.length; channelID++) {
|
|
942
|
+
const name = EXRHeader.channels[channelID].name;
|
|
943
|
+
const lOff = EXRDecoder.channelByteOffsets[name] * EXRDecoder.columns;
|
|
944
|
+
const cOff = EXRDecoder.decodeChannels[name];
|
|
945
|
+
if (cOff === undefined)
|
|
946
|
+
continue;
|
|
947
|
+
tmpOffset.value = lineOffset + lOff;
|
|
948
|
+
for (let x = 0; x < EXRDecoder.columns; x++) {
|
|
949
|
+
const outIndex = outLineOffset + x * EXRDecoder.outputChannels + cOff;
|
|
950
|
+
EXRDecoder.byteArray[outIndex] = EXRDecoder.getter(viewer, tmpOffset);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
function parsePartHeader(dataView, buffer, offset) {
|
|
957
|
+
const header = {};
|
|
958
|
+
let hasAttributes = false;
|
|
959
|
+
while (true) {
|
|
960
|
+
const attributeName = parseNullTerminatedString(buffer, offset);
|
|
961
|
+
if (attributeName === '')
|
|
962
|
+
break;
|
|
963
|
+
hasAttributes = true;
|
|
964
|
+
const attributeType = parseNullTerminatedString(buffer, offset);
|
|
965
|
+
const attributeSize = parseUint32(dataView, offset);
|
|
966
|
+
const attributeValue = parseValue(dataView, buffer, offset, attributeType, attributeSize);
|
|
967
|
+
if (attributeValue === undefined) {
|
|
968
|
+
warn(`EXRLoader: Skipped unknown header attribute type '${attributeType}'.`);
|
|
969
|
+
}
|
|
970
|
+
else {
|
|
971
|
+
header[attributeName] = attributeValue;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
return hasAttributes ? header : null;
|
|
975
|
+
}
|
|
976
|
+
function parseHeader(dataView, buffer, offset) {
|
|
977
|
+
if (dataView.getUint32(0, true) != 20000630) {
|
|
978
|
+
throw new AssetLoadError("EXRLoader: Provided file doesn't appear to be in OpenEXR format.", "exr");
|
|
979
|
+
}
|
|
980
|
+
const version = dataView.getUint8(4);
|
|
981
|
+
const spec = dataView.getUint8(5);
|
|
982
|
+
const flags = {
|
|
983
|
+
singleTile: !!(spec & 2),
|
|
984
|
+
longName: !!(spec & 4),
|
|
985
|
+
deepFormat: !!(spec & 8),
|
|
986
|
+
multiPart: !!(spec & 16),
|
|
987
|
+
};
|
|
988
|
+
// start of header
|
|
989
|
+
offset.value = 8; // start at 8 - after pre-amble
|
|
990
|
+
const headers = [];
|
|
991
|
+
if (flags.multiPart) {
|
|
992
|
+
while (true) {
|
|
993
|
+
const header = parsePartHeader(dataView, buffer, offset);
|
|
994
|
+
if (header === null)
|
|
995
|
+
break;
|
|
996
|
+
header.version = version;
|
|
997
|
+
header.spec = flags;
|
|
998
|
+
headers.push(header);
|
|
999
|
+
}
|
|
1000
|
+
if (headers.length === 0) {
|
|
1001
|
+
throw new AssetLoadError("EXRLoader: No valid part headers found.", "exr");
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
const header = parsePartHeader(dataView, buffer, offset);
|
|
1006
|
+
if (header === null) {
|
|
1007
|
+
throw new AssetLoadError("EXRLoader: Missing EXR header attributes.", "exr");
|
|
1008
|
+
}
|
|
1009
|
+
header.version = version;
|
|
1010
|
+
header.spec = flags;
|
|
1011
|
+
headers.push(header);
|
|
1012
|
+
}
|
|
1013
|
+
return headers;
|
|
1014
|
+
}
|
|
1015
|
+
function setupDecoder(EXRHeader, dataView, uInt8Array, offset) {
|
|
1016
|
+
const EXRDecoder = {
|
|
1017
|
+
size: 0,
|
|
1018
|
+
viewer: dataView,
|
|
1019
|
+
array: uInt8Array,
|
|
1020
|
+
offset: offset,
|
|
1021
|
+
width: EXRHeader.dataWindow.xMax - EXRHeader.dataWindow.xMin + 1,
|
|
1022
|
+
height: EXRHeader.dataWindow.yMax - EXRHeader.dataWindow.yMin + 1,
|
|
1023
|
+
inputChannels: EXRHeader.channels,
|
|
1024
|
+
channelByteOffsets: {},
|
|
1025
|
+
shouldExpand: false,
|
|
1026
|
+
yCbCr: false,
|
|
1027
|
+
totalBytes: null,
|
|
1028
|
+
columns: null,
|
|
1029
|
+
lines: null,
|
|
1030
|
+
type: null,
|
|
1031
|
+
uncompress: null,
|
|
1032
|
+
getter: null,
|
|
1033
|
+
format: null,
|
|
1034
|
+
};
|
|
1035
|
+
switch (EXRHeader.compression) {
|
|
1036
|
+
case 'NO_COMPRESSION':
|
|
1037
|
+
EXRDecoder.blockHeight = 1;
|
|
1038
|
+
EXRDecoder.uncompress = uncompressRAW;
|
|
1039
|
+
break;
|
|
1040
|
+
case 'RLE_COMPRESSION':
|
|
1041
|
+
EXRDecoder.blockHeight = 1;
|
|
1042
|
+
EXRDecoder.uncompress = uncompressRLE;
|
|
1043
|
+
break;
|
|
1044
|
+
case 'ZIPS_COMPRESSION':
|
|
1045
|
+
EXRDecoder.blockHeight = 1;
|
|
1046
|
+
EXRDecoder.uncompress = uncompressZIP;
|
|
1047
|
+
break;
|
|
1048
|
+
case 'ZIP_COMPRESSION':
|
|
1049
|
+
EXRDecoder.blockHeight = 16;
|
|
1050
|
+
EXRDecoder.uncompress = uncompressZIP;
|
|
1051
|
+
break;
|
|
1052
|
+
case 'PIZ_COMPRESSION':
|
|
1053
|
+
EXRDecoder.blockHeight = 32;
|
|
1054
|
+
EXRDecoder.uncompress = uncompressPIZ;
|
|
1055
|
+
break;
|
|
1056
|
+
default:
|
|
1057
|
+
// AD-03 scope: PXR24, B44/A, DWA/B and deep data are out of the lane's
|
|
1058
|
+
// required compression family — fail loudly rather than mis-decoding.
|
|
1059
|
+
throw new AssetLoadError(`EXRLoader: ${EXRHeader.compression} is unsupported (supported: none, RLE, ZIPS, ZIP, PIZ).`, "exr");
|
|
1060
|
+
}
|
|
1061
|
+
const channels = {};
|
|
1062
|
+
for (const channel of EXRHeader.channels) {
|
|
1063
|
+
switch (channel.name) {
|
|
1064
|
+
case 'Y':
|
|
1065
|
+
case 'R':
|
|
1066
|
+
case 'G':
|
|
1067
|
+
case 'B':
|
|
1068
|
+
case 'A':
|
|
1069
|
+
channels[channel.name] = true;
|
|
1070
|
+
EXRDecoder.type = channel.pixelType;
|
|
1071
|
+
break;
|
|
1072
|
+
case 'RY':
|
|
1073
|
+
case 'BY':
|
|
1074
|
+
channels[channel.name] = true;
|
|
1075
|
+
EXRDecoder.yCbCr = true;
|
|
1076
|
+
EXRDecoder.type = channel.pixelType;
|
|
1077
|
+
break;
|
|
1078
|
+
default:
|
|
1079
|
+
EXRDecoder.type = channel.pixelType;
|
|
1080
|
+
warn(`EXRLoader: unsupported channel ${channel.name}; decoding anyway (pixelType ${channel.pixelType}).`);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
if (!('A' in channels) && !('R' in channels) && !('Y' in channels)) {
|
|
1084
|
+
throw new AssetLoadError('EXRLoader: no recognizable channels found (expected R/G/B/A, Y or RY/BY).', "exr");
|
|
1085
|
+
}
|
|
1086
|
+
let fillAlpha = false;
|
|
1087
|
+
const channelCount = EXRDecoder.inputChannels.length;
|
|
1088
|
+
let invalidOutput = false;
|
|
1089
|
+
switch (channelCount) {
|
|
1090
|
+
case 4:
|
|
1091
|
+
if (outputFormat === "rgba") {
|
|
1092
|
+
EXRDecoder.format = "rgba";
|
|
1093
|
+
EXRDecoder.outputChannels = 4;
|
|
1094
|
+
EXRDecoder.decodeChannels = { R: 0, G: 1, B: 2, A: 3 };
|
|
1095
|
+
}
|
|
1096
|
+
else if (outputFormat === "rg") {
|
|
1097
|
+
EXRDecoder.format = "rg";
|
|
1098
|
+
EXRDecoder.outputChannels = 2;
|
|
1099
|
+
EXRDecoder.decodeChannels = { R: 0, G: 1 };
|
|
1100
|
+
}
|
|
1101
|
+
else if (outputFormat === "r") {
|
|
1102
|
+
EXRDecoder.format = "r";
|
|
1103
|
+
EXRDecoder.outputChannels = 1;
|
|
1104
|
+
EXRDecoder.decodeChannels = { R: 0 };
|
|
1105
|
+
}
|
|
1106
|
+
else
|
|
1107
|
+
invalidOutput = true;
|
|
1108
|
+
break;
|
|
1109
|
+
case 3:
|
|
1110
|
+
if (outputFormat === "rgba") {
|
|
1111
|
+
fillAlpha = !channels.A;
|
|
1112
|
+
EXRDecoder.format = "rgba";
|
|
1113
|
+
EXRDecoder.outputChannels = 4;
|
|
1114
|
+
EXRDecoder.decodeChannels = { R: 0, G: 1, B: 2, A: 3 };
|
|
1115
|
+
}
|
|
1116
|
+
else if (outputFormat === "rg") {
|
|
1117
|
+
EXRDecoder.format = "rg";
|
|
1118
|
+
EXRDecoder.outputChannels = 2;
|
|
1119
|
+
EXRDecoder.decodeChannels = { R: 0, G: 1 };
|
|
1120
|
+
}
|
|
1121
|
+
else if (outputFormat === "r") {
|
|
1122
|
+
EXRDecoder.format = "r";
|
|
1123
|
+
EXRDecoder.outputChannels = 1;
|
|
1124
|
+
EXRDecoder.decodeChannels = { R: 0 };
|
|
1125
|
+
}
|
|
1126
|
+
else
|
|
1127
|
+
invalidOutput = true;
|
|
1128
|
+
break;
|
|
1129
|
+
case 2:
|
|
1130
|
+
if (outputFormat === "rgba") {
|
|
1131
|
+
fillAlpha = !channels.A;
|
|
1132
|
+
EXRDecoder.format = "rgba";
|
|
1133
|
+
EXRDecoder.outputChannels = 4;
|
|
1134
|
+
EXRDecoder.decodeChannels = { R: 0, G: 1, B: 2, A: 3 };
|
|
1135
|
+
}
|
|
1136
|
+
else if (outputFormat === "rg") {
|
|
1137
|
+
EXRDecoder.format = "rg";
|
|
1138
|
+
EXRDecoder.outputChannels = 2;
|
|
1139
|
+
EXRDecoder.decodeChannels = { R: 0, G: 1 };
|
|
1140
|
+
}
|
|
1141
|
+
else if (outputFormat === "r") {
|
|
1142
|
+
EXRDecoder.format = "r";
|
|
1143
|
+
EXRDecoder.outputChannels = 1;
|
|
1144
|
+
EXRDecoder.decodeChannels = { R: 0 };
|
|
1145
|
+
}
|
|
1146
|
+
else
|
|
1147
|
+
invalidOutput = true;
|
|
1148
|
+
break;
|
|
1149
|
+
case 1:
|
|
1150
|
+
if (outputFormat === "rgba") {
|
|
1151
|
+
fillAlpha = true;
|
|
1152
|
+
EXRDecoder.format = "rgba";
|
|
1153
|
+
EXRDecoder.outputChannels = 4;
|
|
1154
|
+
EXRDecoder.shouldExpand = true;
|
|
1155
|
+
EXRDecoder.decodeChannels = { Y: 0 };
|
|
1156
|
+
}
|
|
1157
|
+
else if (outputFormat === "rg") {
|
|
1158
|
+
EXRDecoder.format = "rg";
|
|
1159
|
+
EXRDecoder.outputChannels = 2;
|
|
1160
|
+
EXRDecoder.shouldExpand = true;
|
|
1161
|
+
EXRDecoder.decodeChannels = { Y: 0 };
|
|
1162
|
+
}
|
|
1163
|
+
else if (outputFormat === "r") {
|
|
1164
|
+
EXRDecoder.format = "r";
|
|
1165
|
+
EXRDecoder.outputChannels = 1;
|
|
1166
|
+
EXRDecoder.decodeChannels = { Y: 0 };
|
|
1167
|
+
}
|
|
1168
|
+
else
|
|
1169
|
+
invalidOutput = true;
|
|
1170
|
+
break;
|
|
1171
|
+
default:
|
|
1172
|
+
invalidOutput = true;
|
|
1173
|
+
}
|
|
1174
|
+
if (invalidOutput) {
|
|
1175
|
+
throw new AssetLoadError('EXRLoader: invalid output format for specified file.', "exr");
|
|
1176
|
+
}
|
|
1177
|
+
// Luminance/chroma images always decode to RGBA.
|
|
1178
|
+
if (EXRDecoder.yCbCr) {
|
|
1179
|
+
EXRDecoder.format = "rgba";
|
|
1180
|
+
EXRDecoder.outputChannels = 4;
|
|
1181
|
+
EXRDecoder.decodeChannels = { Y: 0, RY: 1, BY: 2 };
|
|
1182
|
+
fillAlpha = true;
|
|
1183
|
+
}
|
|
1184
|
+
if (EXRDecoder.type == 1) {
|
|
1185
|
+
// half
|
|
1186
|
+
EXRDecoder.getter = outputType === "float" ? parseFloat16 : parseUint16;
|
|
1187
|
+
}
|
|
1188
|
+
else if (EXRDecoder.type == 2) {
|
|
1189
|
+
// float
|
|
1190
|
+
EXRDecoder.getter = outputType === "float" ? parseFloat32 : decodeFloat32;
|
|
1191
|
+
}
|
|
1192
|
+
else {
|
|
1193
|
+
throw new AssetLoadError(`EXRLoader: unsupported pixelType ${EXRDecoder.type} for ${EXRHeader.compression}.`, "exr");
|
|
1194
|
+
}
|
|
1195
|
+
EXRDecoder.columns = EXRDecoder.width;
|
|
1196
|
+
const size = EXRDecoder.width * EXRDecoder.height * EXRDecoder.outputChannels;
|
|
1197
|
+
if (outputType === "float") {
|
|
1198
|
+
EXRDecoder.byteArray = new Float32Array(size);
|
|
1199
|
+
if (fillAlpha)
|
|
1200
|
+
EXRDecoder.byteArray.fill(1, 0, size);
|
|
1201
|
+
}
|
|
1202
|
+
else {
|
|
1203
|
+
EXRDecoder.byteArray = new Uint16Array(size);
|
|
1204
|
+
if (fillAlpha)
|
|
1205
|
+
EXRDecoder.byteArray.fill(0x3C00, 0, size); // half 1.0
|
|
1206
|
+
}
|
|
1207
|
+
let byteOffset = 0;
|
|
1208
|
+
for (const channel of EXRHeader.channels) {
|
|
1209
|
+
if (EXRDecoder.decodeChannels[channel.name] !== undefined) {
|
|
1210
|
+
EXRDecoder.channelByteOffsets[channel.name] = byteOffset;
|
|
1211
|
+
}
|
|
1212
|
+
byteOffset += channel.pixelType * 2;
|
|
1213
|
+
}
|
|
1214
|
+
EXRDecoder.totalBytes = byteOffset;
|
|
1215
|
+
EXRDecoder.outLineWidth = EXRDecoder.width * EXRDecoder.outputChannels;
|
|
1216
|
+
if (EXRHeader.spec.deepFormat) {
|
|
1217
|
+
throw new AssetLoadError('EXRLoader: deep image data is unsupported.', "exr");
|
|
1218
|
+
}
|
|
1219
|
+
else if (EXRHeader.spec.singleTile) {
|
|
1220
|
+
EXRDecoder.blockHeight = EXRHeader.tiles.ySize;
|
|
1221
|
+
EXRDecoder.blockWidth = EXRHeader.tiles.xSize;
|
|
1222
|
+
const numXLevels = calculateTileLevels(EXRHeader.tiles, EXRDecoder.width, EXRDecoder.height);
|
|
1223
|
+
const numXTiles = calculateTiles(numXLevels, EXRDecoder.width, EXRHeader.tiles.xSize, EXRHeader.tiles.roundingMode);
|
|
1224
|
+
const numYTiles = calculateTiles(numXLevels, EXRDecoder.height, EXRHeader.tiles.ySize, EXRHeader.tiles.roundingMode);
|
|
1225
|
+
EXRDecoder.tileCount = numXTiles[0] * numYTiles[0];
|
|
1226
|
+
for (let l = 0; l < numXLevels; l++)
|
|
1227
|
+
for (let y = 0; y < numYTiles[l]; y++)
|
|
1228
|
+
for (let x = 0; x < numXTiles[l]; x++)
|
|
1229
|
+
parseInt64(dataView, offset); // tileOffset
|
|
1230
|
+
EXRDecoder.decode = parseTiles.bind(EXRDecoder);
|
|
1231
|
+
}
|
|
1232
|
+
else if (EXRHeader.spec.multiPart) {
|
|
1233
|
+
EXRDecoder.blockWidth = EXRDecoder.width;
|
|
1234
|
+
EXRDecoder.chunkOffsets = EXRHeader._chunkOffsets;
|
|
1235
|
+
EXRDecoder.decode = parseMultiPartScanline.bind(EXRDecoder);
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
EXRDecoder.blockWidth = EXRDecoder.width;
|
|
1239
|
+
const blockCount = Math.ceil(EXRDecoder.height / EXRDecoder.blockHeight);
|
|
1240
|
+
for (let i = 0; i < blockCount; i++)
|
|
1241
|
+
parseInt64(dataView, offset); // scanlineOffset
|
|
1242
|
+
EXRDecoder.decode = parseScanline.bind(EXRDecoder);
|
|
1243
|
+
}
|
|
1244
|
+
return EXRDecoder;
|
|
1245
|
+
}
|
|
1246
|
+
// start parsing file
|
|
1247
|
+
const offset = { value: 0 };
|
|
1248
|
+
const bufferDataView = new DataView(buffer);
|
|
1249
|
+
const uInt8Array = new Uint8Array(buffer);
|
|
1250
|
+
const EXRHeaders = parseHeader(bufferDataView, buffer, offset);
|
|
1251
|
+
const EXRHeader = EXRHeaders[Math.min(partIndex, EXRHeaders.length - 1)];
|
|
1252
|
+
if (!EXRHeader) {
|
|
1253
|
+
throw new AssetLoadError("EXRLoader: No EXR headers found.", "exr");
|
|
1254
|
+
}
|
|
1255
|
+
// Multi-part files interleave all parts' chunk-offset tables before the
|
|
1256
|
+
// pixel data; read the selected part's table and skip the rest.
|
|
1257
|
+
if (EXRHeader.spec.multiPart || EXRHeader.spec.deepFormat) {
|
|
1258
|
+
for (let p = 0; p < EXRHeaders.length; p++) {
|
|
1259
|
+
const chunkCount = EXRHeaders[p].chunkCount;
|
|
1260
|
+
if (p === Math.min(partIndex, EXRHeaders.length - 1)) {
|
|
1261
|
+
EXRHeader._chunkOffsets = [];
|
|
1262
|
+
for (let i = 0; i < chunkCount; i++)
|
|
1263
|
+
EXRHeader._chunkOffsets.push(parseInt64(bufferDataView, offset));
|
|
1264
|
+
}
|
|
1265
|
+
else {
|
|
1266
|
+
for (let i = 0; i < chunkCount; i++)
|
|
1267
|
+
parseInt64(bufferDataView, offset);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
const EXRDecoder = setupDecoder(EXRHeader, bufferDataView, uInt8Array, offset);
|
|
1272
|
+
// parse input data
|
|
1273
|
+
await EXRDecoder.decode();
|
|
1274
|
+
// output texture post-processing
|
|
1275
|
+
if (EXRDecoder.shouldExpand) {
|
|
1276
|
+
const byteArray = EXRDecoder.byteArray;
|
|
1277
|
+
if (outputFormat === "rgba") {
|
|
1278
|
+
for (let i = 0; i < byteArray.length; i += 4)
|
|
1279
|
+
byteArray[i + 2] = (byteArray[i + 1] = byteArray[i]);
|
|
1280
|
+
}
|
|
1281
|
+
else if (outputFormat === "rg") {
|
|
1282
|
+
for (let i = 0; i < byteArray.length; i += 2)
|
|
1283
|
+
byteArray[i + 1] = byteArray[i];
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
// Luminance/chroma → RGB (Rec.709): R = (1+RY)·Y, B = (1+BY)·Y,
|
|
1287
|
+
// G = (Y − R·0.2126 − B·0.0722) / 0.7152 — same as three's second pass.
|
|
1288
|
+
if (EXRDecoder.yCbCr) {
|
|
1289
|
+
const byteArray = EXRDecoder.byteArray;
|
|
1290
|
+
const nPixels = EXRDecoder.width * EXRDecoder.height;
|
|
1291
|
+
if (outputType === "half") {
|
|
1292
|
+
for (let i = 0; i < nPixels; i++) {
|
|
1293
|
+
const base = i * 4;
|
|
1294
|
+
const Y = decodeFloat16(byteArray[base]);
|
|
1295
|
+
const RY = decodeFloat16(byteArray[base + 1]);
|
|
1296
|
+
const BY = decodeFloat16(byteArray[base + 2]);
|
|
1297
|
+
const R = (1 + RY) * Y;
|
|
1298
|
+
const B = (1 + BY) * Y;
|
|
1299
|
+
const G = (Y - R * 0.2126 - B * 0.0722) / 0.7152;
|
|
1300
|
+
byteArray[base] = toHalfFloat(Math.max(0, R));
|
|
1301
|
+
byteArray[base + 1] = toHalfFloat(Math.max(0, G));
|
|
1302
|
+
byteArray[base + 2] = toHalfFloat(Math.max(0, B));
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
else {
|
|
1306
|
+
for (let i = 0; i < nPixels; i++) {
|
|
1307
|
+
const base = i * 4;
|
|
1308
|
+
const Y = byteArray[base];
|
|
1309
|
+
const RY = byteArray[base + 1];
|
|
1310
|
+
const BY = byteArray[base + 2];
|
|
1311
|
+
const R = (1 + RY) * Y;
|
|
1312
|
+
const B = (1 + BY) * Y;
|
|
1313
|
+
byteArray[base] = Math.max(0, R);
|
|
1314
|
+
byteArray[base + 1] = Math.max(0, (Y - R * 0.2126 - B * 0.0722) / 0.7152);
|
|
1315
|
+
byteArray[base + 2] = Math.max(0, B);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return {
|
|
1320
|
+
width: EXRDecoder.width,
|
|
1321
|
+
height: EXRDecoder.height,
|
|
1322
|
+
data: EXRDecoder.byteArray,
|
|
1323
|
+
channels: EXRDecoder.outputChannels,
|
|
1324
|
+
type: outputType,
|
|
1325
|
+
format: EXRDecoder.format,
|
|
1326
|
+
colorSpace: "linear",
|
|
1327
|
+
flipY: false,
|
|
1328
|
+
generateMipmaps: false,
|
|
1329
|
+
header: EXRHeader,
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* `new THREE.EXRLoader()` — real async decode. Works standalone
|
|
1334
|
+
* (`load(url, onLoad?, onProgress?, onError?)` → `Promise<AuraEXRTextureData>`)
|
|
1335
|
+
* and as an `AssetManager` loader (`type: "exr"`).
|
|
1336
|
+
*
|
|
1337
|
+
* `type`/`outputFormat`/`part` mirror three's `setDataType`/`setOutputFormat`/
|
|
1338
|
+
* `setPart` builder methods.
|
|
1339
|
+
*/
|
|
1340
|
+
export class EXRLoader {
|
|
1341
|
+
type = "exr";
|
|
1342
|
+
outputType = "half";
|
|
1343
|
+
outputFormat = "rgba";
|
|
1344
|
+
part = 0;
|
|
1345
|
+
setDataType(value) {
|
|
1346
|
+
this.outputType = value;
|
|
1347
|
+
return this;
|
|
1348
|
+
}
|
|
1349
|
+
setOutputFormat(value) {
|
|
1350
|
+
this.outputFormat = value;
|
|
1351
|
+
return this;
|
|
1352
|
+
}
|
|
1353
|
+
setPart(value) {
|
|
1354
|
+
this.part = value;
|
|
1355
|
+
return this;
|
|
1356
|
+
}
|
|
1357
|
+
canLoad(request) {
|
|
1358
|
+
return (request.type === this.type || request.type === undefined) && /\.exr(?:\?.*)?$/i.test(request.url);
|
|
1359
|
+
}
|
|
1360
|
+
load(requestOrUrl, contextOrOnLoad, onProgress, onError) {
|
|
1361
|
+
if (typeof requestOrUrl === "string") {
|
|
1362
|
+
const url = requestOrUrl;
|
|
1363
|
+
const onLoad = typeof contextOrOnLoad === "function" ? contextOrOnLoad : undefined;
|
|
1364
|
+
const pending = this.loadBytes(url)
|
|
1365
|
+
.then((data) => { onLoad?.(data); return data; });
|
|
1366
|
+
if (onError)
|
|
1367
|
+
pending.catch(onError);
|
|
1368
|
+
return pending;
|
|
1369
|
+
}
|
|
1370
|
+
const request = requestOrUrl;
|
|
1371
|
+
const context = contextOrOnLoad;
|
|
1372
|
+
context.throwIfAborted(request.url);
|
|
1373
|
+
return this.loadBytes(request.url, request.signal);
|
|
1374
|
+
}
|
|
1375
|
+
/** `parse(buffer)` — three's synchronous-looking parse; async here for zlib. */
|
|
1376
|
+
parse(buffer) {
|
|
1377
|
+
return parseEXR(buffer, { type: this.outputType, outputFormat: this.outputFormat, part: this.part });
|
|
1378
|
+
}
|
|
1379
|
+
async loadBytes(url, signal) {
|
|
1380
|
+
if (typeof fetch !== "function") {
|
|
1381
|
+
throw new AssetLoadError("EXRLoader requires fetch for non-buffer input", url);
|
|
1382
|
+
}
|
|
1383
|
+
const response = await fetch(url, { signal });
|
|
1384
|
+
if (!response.ok) {
|
|
1385
|
+
throw new AssetLoadError(`EXR request failed with ${response.status}`, url);
|
|
1386
|
+
}
|
|
1387
|
+
const buffer = await response.arrayBuffer();
|
|
1388
|
+
try {
|
|
1389
|
+
return await parseEXR(buffer, { type: this.outputType, outputFormat: this.outputFormat, part: this.part });
|
|
1390
|
+
}
|
|
1391
|
+
catch (error) {
|
|
1392
|
+
throw new AssetLoadError(error instanceof Error ? error.message : String(error), url, [], error);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
/** Alias matching three's export name where an explicit class is wanted. */
|
|
1397
|
+
export { EXRLoader as EXRLoaderThreeCompat };
|
|
1398
|
+
//# sourceMappingURL=EXRLoader.js.map
|