@woosh/meep-engine 3.63.3 → 3.64.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/package.json +1 -1
- package/src/core/color/xyz/m3_chromatic_adaptation_bradford.d.ts +14 -0
- package/src/core/color/xyz/m3_chromatic_adaptation_bradford.d.ts.map +1 -0
- package/src/core/color/xyz/m3_chromatic_adaptation_bradford.js +51 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.d.ts +2 -1
- package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.d.ts.map +1 -1
- package/src/core/math/linalg/eigen/matrix_symmetric_3x3_eigen.js +51 -70
- package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.d.ts.map +1 -1
- package/src/core/math/linalg/eigen/matrix_symmetric_4x4_eigen.js +8 -64
- package/src/core/math/linalg/eigen/matrix_symmetric_jacobi_rotate_in_place.d.ts +17 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_jacobi_rotate_in_place.d.ts.map +1 -0
- package/src/core/math/linalg/eigen/matrix_symmetric_jacobi_rotate_in_place.js +64 -0
- package/src/core/math/linalg/solve_box_constrained_qp.d.ts +30 -0
- package/src/core/math/linalg/solve_box_constrained_qp.d.ts.map +1 -0
- package/src/core/math/linalg/solve_box_constrained_qp.js +205 -0
- package/src/engine/graphics/texture/sampler/exr_to_sampler2d.d.ts +27 -0
- package/src/engine/graphics/texture/sampler/exr_to_sampler2d.d.ts.map +1 -0
- package/src/engine/graphics/texture/sampler/exr_to_sampler2d.js +91 -0
- package/src/engine/graphics/texture/sampler/sampler2d_to_exr.d.ts +23 -0
- package/src/engine/graphics/texture/sampler/sampler2d_to_exr.d.ts.map +1 -0
- package/src/engine/graphics/texture/sampler/sampler2d_to_exr.js +64 -0
- package/src/engine/physics/constraint/soft_spring_coefficients.d.ts +24 -0
- package/src/engine/physics/constraint/soft_spring_coefficients.d.ts.map +1 -0
- package/src/engine/physics/constraint/soft_spring_coefficients.js +38 -0
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +16 -8
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +54 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +192 -7
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +3 -5
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
- package/src/engine/physics/inertia/world_inverse_inertia.js +3 -5
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts +183 -12
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
- package/src/engine/physics/vehicle/RaycastVehicle.js +1046 -121
- package/src/format/image/exr/ExrCompression.d.ts +22 -0
- package/src/format/image/exr/ExrCompression.d.ts.map +1 -0
- package/src/format/image/exr/ExrCompression.js +27 -0
- package/src/format/image/exr/ExrImage.d.ts +32 -0
- package/src/format/image/exr/ExrImage.d.ts.map +1 -0
- package/src/format/image/exr/ExrImage.js +32 -0
- package/src/format/image/exr/ExrPixelType.d.ts +13 -0
- package/src/format/image/exr/ExrPixelType.d.ts.map +1 -0
- package/src/format/image/exr/ExrPixelType.js +18 -0
- package/src/format/image/exr/exr_compare_names.d.ts +16 -0
- package/src/format/image/exr/exr_compare_names.d.ts.map +1 -0
- package/src/format/image/exr/exr_compare_names.js +25 -0
- package/src/format/image/exr/exr_constants.d.ts +49 -0
- package/src/format/image/exr/exr_constants.d.ts.map +1 -0
- package/src/format/image/exr/exr_constants.js +56 -0
- package/src/format/image/exr/exr_read.d.ts +32 -0
- package/src/format/image/exr/exr_read.d.ts.map +1 -0
- package/src/format/image/exr/exr_read.js +531 -0
- package/src/format/image/exr/exr_sample_bits.d.ts +16 -0
- package/src/format/image/exr/exr_sample_bits.d.ts.map +1 -0
- package/src/format/image/exr/exr_sample_bits.js +21 -0
- package/src/format/image/exr/exr_scanlines_per_chunk.d.ts +17 -0
- package/src/format/image/exr/exr_scanlines_per_chunk.d.ts.map +1 -0
- package/src/format/image/exr/exr_scanlines_per_chunk.js +31 -0
- package/src/format/image/exr/exr_write.d.ts +27 -0
- package/src/format/image/exr/exr_write.d.ts.map +1 -0
- package/src/format/image/exr/exr_write.js +378 -0
- package/src/shade/playground/probe_clipmap/index.html +5 -0
- package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
- package/src/shade/playground/probe_clipmap/main.js +34 -1
- package/src/shade/playground/tonemap_sdr/README.md +11 -8
- package/src/shade/playground/tonemap_sdr/ToneMapCompare.js +2 -2
- package/src/shade/renderer/Renderer.d.ts +5 -6
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +15 -88
- package/src/shade/renderer/animation/chunk_animation_curve_evaluate_keys.d.ts +14 -0
- package/src/shade/renderer/animation/chunk_animation_curve_evaluate_keys.d.ts.map +1 -0
- package/src/shade/renderer/animation/chunk_animation_curve_evaluate_keys.js +62 -0
- package/src/shade/renderer/deferred/tonemap/ColorGrade.d.ts +158 -0
- package/src/shade/renderer/deferred/tonemap/ColorGrade.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/ColorGrade.js +337 -0
- package/src/shade/renderer/deferred/tonemap/SAMPLER_TONEMAP_LUT.d.ts +9 -0
- package/src/shade/renderer/deferred/tonemap/SAMPLER_TONEMAP_LUT.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/SAMPLER_TONEMAP_LUT.js +15 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_ACES.d.ts +6 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_ACES.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_ACES.js +84 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_GT7.d.ts +10 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_GT7.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_GT7.js +25 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_NONE.d.ts +9 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_NONE.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_NONE.js +20 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_PBR_NEUTRAL.d.ts +14 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_PBR_NEUTRAL.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/TONE_MAP_OPERATOR_PBR_NEUTRAL.js +30 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapOperator.d.ts +54 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapOperator.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapOperator.js +63 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapper.d.ts +106 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapper.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/ToneMapper.js +474 -0
- package/src/shade/renderer/deferred/tonemap/TonemapAces2Tables.d.ts +5 -13
- package/src/shade/renderer/deferred/tonemap/TonemapAces2Tables.d.ts.map +1 -1
- package/src/shade/renderer/deferred/tonemap/TonemapAces2Tables.js +5 -44
- package/src/shade/renderer/deferred/tonemap/TonemapDisplaySettings.d.ts +7 -25
- package/src/shade/renderer/deferred/tonemap/TonemapDisplaySettings.d.ts.map +1 -1
- package/src/shade/renderer/deferred/tonemap/TonemapDisplaySettings.js +11 -51
- package/src/shade/renderer/deferred/tonemap/m3_white_balance_rec709.d.ts +20 -0
- package/src/shade/renderer/deferred/tonemap/m3_white_balance_rec709.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/m3_white_balance_rec709.js +103 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_lut.d.ts +9 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_lut.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/{shader_tonemap_HDR_none.js → shader_tonemap_HDR_lut.js} +22 -18
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_lut.d.ts +9 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_lut.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/{shader_tonemap_LDR_aces.js → shader_tonemap_LDR_lut.js} +24 -24
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_bake.d.ts +23 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_bake.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_bake.js +92 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_sdr_from_hdr.d.ts +15 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_sdr_from_hdr.d.ts.map +1 -0
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_lut_sdr_from_hdr.js +73 -0
- package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
- package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
- package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
- package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +3 -15
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +4 -17
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +3 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +15 -13
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +7 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +28 -17
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts +18 -18
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.js +44 -48
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.d.ts +22 -0
- package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/conversion/chunk_CRM_REC709_AP1.js +62 -0
- package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/grade/chunk_color_grade.js +140 -0
- package/src/shade/renderer/shader/chunk/color/oklab/chunk_oklab_compute_max_saturation.d.ts +11 -0
- package/src/shade/renderer/shader/chunk/color/oklab/chunk_oklab_compute_max_saturation.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/oklab/chunk_oklab_compute_max_saturation.js +65 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_bt2390_eetf.d.ts +14 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_bt2390_eetf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_bt2390_eetf.js +38 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_lut.d.ts +16 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_lut.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_lut.js +41 -0
- package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts +8 -0
- package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.js +21 -3
- package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +8 -8
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +2 -0
- package/src/shade/renderer/ShadeToneMappingMode.d.ts +0 -15
- package/src/shade/renderer/ShadeToneMappingMode.d.ts.map +0 -1
- package/src/shade/renderer/ShadeToneMappingMode.js +0 -41
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR.d.ts +0 -9
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR.js +0 -79
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_aces.d.ts +0 -17
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_aces.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_aces.js +0 -101
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_none.d.ts +0 -9
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_none.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_pbr_neutral.d.ts +0 -10
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_pbr_neutral.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_HDR_pbr_neutral.js +0 -80
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR.d.ts +0 -9
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR.js +0 -80
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_aces.d.ts +0 -10
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_aces.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_none.d.ts +0 -9
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_none.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_none.js +0 -71
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_pbr_neutral.d.ts +0 -10
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_pbr_neutral.d.ts.map +0 -1
- package/src/shade/renderer/deferred/tonemap/shader_tonemap_LDR_pbr_neutral.js +0 -76
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_hdr_bt2390.d.ts +0 -3
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_hdr_bt2390.d.ts.map +0 -1
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_hdr_bt2390.js +0 -44
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js";
|
|
2
|
+
import { decompress_bytes_into } from "../../../core/binary/compression/decompress_bytes_into.js";
|
|
3
|
+
import { deflate_decompress_bound } from "../../../core/binary/compression/deflate_decompress_bound.js";
|
|
4
|
+
import { EndianType } from "../../../core/binary/EndianType.js";
|
|
5
|
+
import { utf8_decode } from "../../../core/binary/utf8/utf8_decode.js";
|
|
6
|
+
import {
|
|
7
|
+
EXR_FLAG_LONG_NAMES,
|
|
8
|
+
EXR_FLAG_MULTI_PART,
|
|
9
|
+
EXR_FLAG_NON_IMAGE,
|
|
10
|
+
EXR_FLAG_TILED,
|
|
11
|
+
EXR_LONG_NAME_BYTES,
|
|
12
|
+
EXR_MAGIC,
|
|
13
|
+
EXR_VERSION
|
|
14
|
+
} from "./exr_constants.js";
|
|
15
|
+
import { exr_compare_names } from "./exr_compare_names.js";
|
|
16
|
+
import { exr_sample_bits } from "./exr_sample_bits.js";
|
|
17
|
+
import { exr_scanlines_per_chunk } from "./exr_scanlines_per_chunk.js";
|
|
18
|
+
import { ExrCompression } from "./ExrCompression.js";
|
|
19
|
+
import { ExrPixelType } from "./ExrPixelType.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Read a scanline OpenEXR file.
|
|
23
|
+
*
|
|
24
|
+
* **What it reads**: a single-part scanline image with full-resolution channels, compressed with
|
|
25
|
+
* {@link ExrCompression.None}, {@link ExrCompression.ZIPS} or {@link ExrCompression.ZIP}, in any of
|
|
26
|
+
* OpenEXR's three pixel types, mixed freely between channels. Samples come back as they are stored — a
|
|
27
|
+
* HALF channel is a `Float16Array` holding the file's exact bits — so reading converts nothing. Chunks are
|
|
28
|
+
* found through the offset table, so the file's line order does not matter.
|
|
29
|
+
*
|
|
30
|
+
* Of the header, only what decoding depends on is read: the channel list, the compression method and the
|
|
31
|
+
* data window. OpenEXR requires five more attributes — display window, line order, pixel aspect ratio,
|
|
32
|
+
* screen window centre and width — and its own reader fills in a default for any a file leaves out;
|
|
33
|
+
* nothing here would use them, so they are skipped like every other attribute.
|
|
34
|
+
*
|
|
35
|
+
* **What it refuses**, by name: tiled, multi-part and deep files; every other compression method;
|
|
36
|
+
* subsampled channels; a header without the channel list, compression or data window, or with one of
|
|
37
|
+
* them of the wrong type; and a file whose chunks do not add up — an offset outside the file, a chunk
|
|
38
|
+
* holding other scanlines than its place in the table says, a chunk whose stored or inflated size
|
|
39
|
+
* disagrees with the scanlines it holds. Every chunk is checked before anything is allocated for the
|
|
40
|
+
* pixels — against its place, its scanline, and what deflate could inflate its stored bytes to — so a file
|
|
41
|
+
* cannot claim more pixel data than its own chunks could hold.
|
|
42
|
+
*
|
|
43
|
+
* @param {ArrayBuffer} bytes the whole file; keep unchanged until the promise settles
|
|
44
|
+
* @returns {Promise<ExrImage>}
|
|
45
|
+
* @throws {Error} naming what is unsupported or malformed
|
|
46
|
+
*
|
|
47
|
+
* @see https://openexr.com/en/latest/OpenEXRFileLayout.html
|
|
48
|
+
* @author Alex Goldring
|
|
49
|
+
* @copyright Company Named Limited (c) 2026
|
|
50
|
+
*/
|
|
51
|
+
export async function exr_read(bytes) {
|
|
52
|
+
const buffer = BinaryBuffer.fromArrayBuffer(bytes);
|
|
53
|
+
|
|
54
|
+
// OpenEXR is little-endian throughout
|
|
55
|
+
buffer.endianness = EndianType.LittleEndian;
|
|
56
|
+
|
|
57
|
+
const file_bytes = bytes.byteLength;
|
|
58
|
+
|
|
59
|
+
if (file_bytes < 8 || buffer.readUint32() !== EXR_MAGIC) {
|
|
60
|
+
throw new Error("EXR: not an OpenEXR file");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
read_version(buffer);
|
|
64
|
+
|
|
65
|
+
const header = read_header(buffer);
|
|
66
|
+
|
|
67
|
+
const width = header.x_max - header.x_min + 1;
|
|
68
|
+
const height = header.y_max - header.y_min + 1;
|
|
69
|
+
|
|
70
|
+
if (width < 1 || height < 1) {
|
|
71
|
+
throw new Error(`EXR: the data window is ${width}x${height}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const scanlines_per_chunk = exr_scanlines_per_chunk(header.compression);
|
|
75
|
+
|
|
76
|
+
const deflated = header.compression !== ExrCompression.None;
|
|
77
|
+
|
|
78
|
+
const channels = header.channels;
|
|
79
|
+
const channel_count = channels.length;
|
|
80
|
+
|
|
81
|
+
let pixel_bytes = 0;
|
|
82
|
+
|
|
83
|
+
for (let c = 0; c < channel_count; c++) {
|
|
84
|
+
pixel_bytes += channels[c].pixel_type === ExrPixelType.HALF ? 2 : 4;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const line_bytes = width * pixel_bytes;
|
|
88
|
+
|
|
89
|
+
// the header is a claim; refuse one that no file of this length could back before allocating for it
|
|
90
|
+
const file_capacity = deflated ? deflate_decompress_bound(file_bytes) : file_bytes;
|
|
91
|
+
|
|
92
|
+
if (line_bytes * height > file_capacity) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`EXR: the header claims a ${width}x${height} image of ${pixel_bytes} bytes a pixel,`
|
|
95
|
+
+ ` which a ${file_bytes} byte file cannot hold`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const chunk_count = Math.ceil(height / scanlines_per_chunk);
|
|
100
|
+
|
|
101
|
+
const table_offset = buffer.position;
|
|
102
|
+
const pixel_data_offset = table_offset + chunk_count * 8;
|
|
103
|
+
|
|
104
|
+
if (pixel_data_offset > file_bytes) {
|
|
105
|
+
throw new Error(`EXR: the file ends inside its offset table of ${chunk_count} chunks`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Seek to a chunk's pixel data, refusing a chunk outside the file, holding other scanlines than its
|
|
110
|
+
* place in the table says, or of a stored size its scanlines rule out — too small among them, when
|
|
111
|
+
* deflate could not inflate it to them.
|
|
112
|
+
*
|
|
113
|
+
* @param {number} i
|
|
114
|
+
* @returns {number} the chunk's stored size
|
|
115
|
+
*/
|
|
116
|
+
const seek_chunk = (i) => {
|
|
117
|
+
const first_line = i * scanlines_per_chunk;
|
|
118
|
+
const unpacked_size = Math.min(scanlines_per_chunk, height - first_line) * line_bytes;
|
|
119
|
+
|
|
120
|
+
buffer.position = table_offset + i * 8;
|
|
121
|
+
|
|
122
|
+
const offset = Number(buffer.readUint64());
|
|
123
|
+
|
|
124
|
+
if (offset < pixel_data_offset || offset > file_bytes - 8) {
|
|
125
|
+
throw new Error(`EXR: chunk ${i} is at offset ${offset}, outside the file's pixel data`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
buffer.position = offset;
|
|
129
|
+
|
|
130
|
+
const y = buffer.readInt32();
|
|
131
|
+
const packed_size = buffer.readInt32();
|
|
132
|
+
|
|
133
|
+
if (y !== header.y_min + first_line) {
|
|
134
|
+
throw new Error(`EXR: chunk ${i} holds scanline ${y}, where scanline ${header.y_min + first_line} belongs`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (packed_size < 0 || packed_size > file_bytes - buffer.position) {
|
|
138
|
+
throw new Error(`EXR: chunk ${i} claims ${packed_size} bytes, past the end of the file`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (packed_size > unpacked_size || (!deflated && packed_size !== unpacked_size)) {
|
|
142
|
+
throw new Error(`EXR: chunk ${i} is ${packed_size} bytes, but its scanlines are ${unpacked_size}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (packed_size < unpacked_size && unpacked_size > deflate_decompress_bound(packed_size)) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`EXR: chunk ${i} is ${packed_size} bytes, which cannot inflate to the ${unpacked_size} bytes of its scanlines`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return packed_size;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// every chunk is checked before anything is allocated for the pixels or any chunk is inflated, so
|
|
155
|
+
// what is allocated is bounded by what the chunks themselves could inflate to
|
|
156
|
+
for (let i = 0; i < chunk_count; i++) {
|
|
157
|
+
seek_chunk(i);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const planes = new Array(channel_count);
|
|
161
|
+
const plane_bits = new Array(channel_count);
|
|
162
|
+
|
|
163
|
+
for (let c = 0; c < channel_count; c++) {
|
|
164
|
+
planes[c] = create_plane(channels[c].pixel_type, width * height);
|
|
165
|
+
plane_bits[c] = exr_sample_bits(planes[c]);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const chunk_bytes = Math.min(scanlines_per_chunk, height) * line_bytes;
|
|
169
|
+
|
|
170
|
+
const inflated = deflated ? new Uint8Array(chunk_bytes) : null;
|
|
171
|
+
const reconstructed = deflated ? BinaryBuffer.fromArrayBuffer(new ArrayBuffer(chunk_bytes)) : null;
|
|
172
|
+
|
|
173
|
+
if (deflated) {
|
|
174
|
+
reconstructed.endianness = EndianType.LittleEndian;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
for (let i = 0; i < chunk_count; i++) {
|
|
178
|
+
const packed_size = seek_chunk(i);
|
|
179
|
+
|
|
180
|
+
const first_line = i * scanlines_per_chunk;
|
|
181
|
+
const line_count = Math.min(scanlines_per_chunk, height - first_line);
|
|
182
|
+
const unpacked_size = line_count * line_bytes;
|
|
183
|
+
|
|
184
|
+
let source = buffer;
|
|
185
|
+
|
|
186
|
+
if (packed_size < unpacked_size) {
|
|
187
|
+
let written;
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
written = await decompress_bytes_into(
|
|
191
|
+
inflated, 0, unpacked_size,
|
|
192
|
+
[new Uint8Array(bytes, buffer.position, packed_size)],
|
|
193
|
+
"deflate"
|
|
194
|
+
);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
// the platform's stream errors can carry their reason only in the cause
|
|
197
|
+
throw new Error(
|
|
198
|
+
`EXR: chunk ${i} does not inflate: ${error?.message || error?.cause?.message || "not a zlib stream"}`,
|
|
199
|
+
{ cause: error }
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (written !== unpacked_size) {
|
|
204
|
+
throw new Error(`EXR: chunk ${i} inflates to ${written} bytes, but its scanlines are ${unpacked_size}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
zip_reconstruct(inflated, reconstructed.raw_bytes, unpacked_size);
|
|
208
|
+
|
|
209
|
+
reconstructed.position = 0;
|
|
210
|
+
|
|
211
|
+
source = reconstructed;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// a chunk that deflate could not shrink is stored as it is, and is read from the file directly
|
|
215
|
+
read_scanlines(source, plane_bits, width, first_line, line_count);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const result = new Array(channel_count);
|
|
219
|
+
|
|
220
|
+
for (let c = 0; c < channel_count; c++) {
|
|
221
|
+
result[c] = { name: channels[c].name, data: planes[c] };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return { width, height, channels: result };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Read the version field and refuse every kind of file other than a single-part scanline image.
|
|
229
|
+
*
|
|
230
|
+
* @param {BinaryBuffer} buffer
|
|
231
|
+
*/
|
|
232
|
+
function read_version(buffer) {
|
|
233
|
+
const version = buffer.readUint32();
|
|
234
|
+
|
|
235
|
+
if ((version & 0xFF) !== EXR_VERSION) {
|
|
236
|
+
throw new Error(`EXR: format version ${version & 0xFF} is not supported; ${EXR_VERSION} is`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if ((version & EXR_FLAG_TILED) !== 0) {
|
|
240
|
+
throw new Error("EXR: tiled files are not supported; scanline files are");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if ((version & EXR_FLAG_MULTI_PART) !== 0) {
|
|
244
|
+
throw new Error("EXR: multi-part files are not supported; single-part files are");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if ((version & EXR_FLAG_NON_IMAGE) !== 0) {
|
|
248
|
+
throw new Error("EXR: deep data is not supported; flat images are");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const unknown = (version & ~(0xFF | EXR_FLAG_LONG_NAMES)) >>> 0;
|
|
252
|
+
|
|
253
|
+
if (unknown !== 0) {
|
|
254
|
+
throw new Error(`EXR: the version field sets unknown flags 0x${unknown.toString(16)}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Read the header's attributes up to its terminator, keeping the three that decoding the pixels needs.
|
|
260
|
+
*
|
|
261
|
+
* @param {BinaryBuffer} buffer
|
|
262
|
+
* @returns {{channels: {name: string, pixel_type: ExrPixelType}[], compression: number, x_min: number, y_min: number, x_max: number, y_max: number}}
|
|
263
|
+
*/
|
|
264
|
+
function read_header(buffer) {
|
|
265
|
+
let channels = null;
|
|
266
|
+
let compression = -1;
|
|
267
|
+
let has_data_window = false;
|
|
268
|
+
let x_min = 0, y_min = 0, x_max = 0, y_max = 0;
|
|
269
|
+
|
|
270
|
+
const file_end = buffer.capacity;
|
|
271
|
+
|
|
272
|
+
for (; ;) {
|
|
273
|
+
const name = read_name(buffer, file_end, "attribute name");
|
|
274
|
+
|
|
275
|
+
if (name === "") {
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const type = read_name(buffer, file_end, `type of attribute '${name}'`);
|
|
280
|
+
|
|
281
|
+
if (buffer.position + 4 > file_end) {
|
|
282
|
+
throw new Error(`EXR: the file ends inside the size of attribute '${name}'`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const size = buffer.readInt32();
|
|
286
|
+
const end = buffer.position + size;
|
|
287
|
+
|
|
288
|
+
if (size < 0 || end > buffer.capacity) {
|
|
289
|
+
throw new Error(`EXR: attribute '${name}' claims ${size} bytes, past the end of the file`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
switch (name) {
|
|
293
|
+
case "channels":
|
|
294
|
+
check_attribute(name, type, size, "chlist", size);
|
|
295
|
+
channels = read_channel_list(buffer, end);
|
|
296
|
+
break;
|
|
297
|
+
case "compression":
|
|
298
|
+
check_attribute(name, type, size, "compression", 1);
|
|
299
|
+
compression = buffer.readUint8();
|
|
300
|
+
break;
|
|
301
|
+
case "dataWindow":
|
|
302
|
+
check_attribute(name, type, size, "box2i", 16);
|
|
303
|
+
x_min = buffer.readInt32();
|
|
304
|
+
y_min = buffer.readInt32();
|
|
305
|
+
x_max = buffer.readInt32();
|
|
306
|
+
y_max = buffer.readInt32();
|
|
307
|
+
has_data_window = true;
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
buffer.position = end;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (channels === null || compression === -1 || !has_data_window) {
|
|
315
|
+
const missing = [];
|
|
316
|
+
|
|
317
|
+
if (channels === null) missing.push("channels");
|
|
318
|
+
if (compression === -1) missing.push("compression");
|
|
319
|
+
if (!has_data_window) missing.push("dataWindow");
|
|
320
|
+
|
|
321
|
+
throw new Error(`EXR: the header has no ${missing.join(", ")} attribute`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return { channels, compression, x_min, y_min, x_max, y_max };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* @param {string} name
|
|
329
|
+
* @param {string} type as the file gives it
|
|
330
|
+
* @param {number} size as the file gives it
|
|
331
|
+
* @param {string} expected_type
|
|
332
|
+
* @param {number} expected_size
|
|
333
|
+
*/
|
|
334
|
+
function check_attribute(name, type, size, expected_type, expected_size) {
|
|
335
|
+
if (type !== expected_type || size !== expected_size) {
|
|
336
|
+
throw new Error(
|
|
337
|
+
`EXR: attribute '${name}' is a ${size} byte '${type}', where a ${expected_size} byte '${expected_type}' belongs`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Read a `chlist` attribute's value.
|
|
344
|
+
*
|
|
345
|
+
* OpenEXR keeps a channel list in name order, by {@link exr_compare_names}, whatever order a writer listed
|
|
346
|
+
* it in, and the pixel data follows that order — so the list is sorted here, and two channels of one name
|
|
347
|
+
* are refused as OpenEXR refuses them.
|
|
348
|
+
*
|
|
349
|
+
* @param {BinaryBuffer} buffer at the start of the value
|
|
350
|
+
* @param {number} end where the value ends
|
|
351
|
+
* @returns {{name: string, pixel_type: ExrPixelType}[]} in the pixel data's order
|
|
352
|
+
*/
|
|
353
|
+
function read_channel_list(buffer, end) {
|
|
354
|
+
const channels = [];
|
|
355
|
+
|
|
356
|
+
for (; ;) {
|
|
357
|
+
const name = read_name_bytes(buffer, end, "channel name");
|
|
358
|
+
|
|
359
|
+
if (name.length === 0) {
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// pixel type; pLinear and three reserved bytes; x and y sampling
|
|
364
|
+
if (buffer.position + 16 > end) {
|
|
365
|
+
throw new Error("EXR: the channel list's contents disagree with its size");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const pixel_type = buffer.readInt32();
|
|
369
|
+
|
|
370
|
+
// pLinear, then three reserved bytes
|
|
371
|
+
buffer.skip(4);
|
|
372
|
+
|
|
373
|
+
const x_sampling = buffer.readInt32();
|
|
374
|
+
const y_sampling = buffer.readInt32();
|
|
375
|
+
|
|
376
|
+
if (pixel_type !== ExrPixelType.UINT && pixel_type !== ExrPixelType.HALF && pixel_type !== ExrPixelType.FLOAT) {
|
|
377
|
+
throw new Error(`EXR: channel '${utf8_decode(name)}' has pixel type ${pixel_type}, which OpenEXR does not define`);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (x_sampling !== 1 || y_sampling !== 1) {
|
|
381
|
+
throw new Error(
|
|
382
|
+
`EXR: channel '${utf8_decode(name)}' is subsampled ${x_sampling}x${y_sampling};`
|
|
383
|
+
+ ` only full-resolution channels are supported`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
channels.push({ name, pixel_type });
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (buffer.position !== end) {
|
|
391
|
+
throw new Error("EXR: the channel list's contents disagree with its size");
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (channels.length === 0) {
|
|
395
|
+
throw new Error("EXR: the image has no channels");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
channels.sort((a, b) => exr_compare_names(a.name, b.name));
|
|
399
|
+
|
|
400
|
+
const result = new Array(channels.length);
|
|
401
|
+
|
|
402
|
+
for (let c = 0; c < channels.length; c++) {
|
|
403
|
+
const name = utf8_decode(channels[c].name);
|
|
404
|
+
|
|
405
|
+
if (c > 0 && exr_compare_names(channels[c - 1].name, channels[c].name) === 0) {
|
|
406
|
+
throw new Error(`EXR: there are two channels named '${name}'`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
result[c] = { name, pixel_type: channels[c].pixel_type };
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Read a null-terminated name: an attribute's, its type's, or a channel's.
|
|
417
|
+
*
|
|
418
|
+
* @param {BinaryBuffer} buffer
|
|
419
|
+
* @param {number} limit where the name's terminator must come by: the end of the file, or of the channel list
|
|
420
|
+
* @param {string} what named in the error
|
|
421
|
+
* @returns {Uint8Array} the name's bytes, a view onto the file; empty for the terminator that ends a header
|
|
422
|
+
* or a channel list
|
|
423
|
+
*/
|
|
424
|
+
function read_name_bytes(buffer, limit, what) {
|
|
425
|
+
const bytes = buffer.raw_bytes;
|
|
426
|
+
const start = buffer.position;
|
|
427
|
+
|
|
428
|
+
const scan_end = Math.min(start + EXR_LONG_NAME_BYTES + 1, limit);
|
|
429
|
+
|
|
430
|
+
const length = bytes.subarray(start, scan_end).indexOf(0);
|
|
431
|
+
|
|
432
|
+
if (length === -1) {
|
|
433
|
+
throw new Error(
|
|
434
|
+
scan_end - start > EXR_LONG_NAME_BYTES
|
|
435
|
+
? `EXR: the ${what} at offset ${start} is longer than ${EXR_LONG_NAME_BYTES} bytes`
|
|
436
|
+
: `EXR: the ${what} at offset ${start} is cut off at offset ${scan_end}`
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
buffer.position = start + length + 1;
|
|
441
|
+
|
|
442
|
+
return bytes.subarray(start, start + length);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* {@link read_name_bytes}, decoded.
|
|
447
|
+
*
|
|
448
|
+
* @param {BinaryBuffer} buffer
|
|
449
|
+
* @param {number} limit
|
|
450
|
+
* @param {string} what
|
|
451
|
+
* @returns {string}
|
|
452
|
+
*/
|
|
453
|
+
function read_name(buffer, limit, what) {
|
|
454
|
+
return utf8_decode(read_name_bytes(buffer, limit, what));
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* @param {ExrPixelType} pixel_type
|
|
459
|
+
* @param {number} length
|
|
460
|
+
* @returns {Uint32Array|Float16Array|Float32Array}
|
|
461
|
+
*/
|
|
462
|
+
function create_plane(pixel_type, length) {
|
|
463
|
+
switch (pixel_type) {
|
|
464
|
+
case ExrPixelType.UINT:
|
|
465
|
+
return new Uint32Array(length);
|
|
466
|
+
case ExrPixelType.HALF:
|
|
467
|
+
return new Float16Array(length);
|
|
468
|
+
default:
|
|
469
|
+
return new Float32Array(length);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Scatter a chunk's scanlines into the channel planes. Within a scanline the channels follow one another,
|
|
475
|
+
* each a full row of samples, in the order of the channel list.
|
|
476
|
+
*
|
|
477
|
+
* @param {BinaryBuffer} source at the first byte of the chunk's pixel data
|
|
478
|
+
* @param {(Uint16Array|Uint32Array)[]} plane_bits
|
|
479
|
+
* @param {number} width
|
|
480
|
+
* @param {number} first_line row of the image the chunk starts at
|
|
481
|
+
* @param {number} line_count
|
|
482
|
+
*/
|
|
483
|
+
function read_scanlines(source, plane_bits, width, first_line, line_count) {
|
|
484
|
+
const channel_count = plane_bits.length;
|
|
485
|
+
|
|
486
|
+
for (let line = first_line; line < first_line + line_count; line++) {
|
|
487
|
+
const row = line * width;
|
|
488
|
+
|
|
489
|
+
for (let c = 0; c < channel_count; c++) {
|
|
490
|
+
const bits = plane_bits[c];
|
|
491
|
+
|
|
492
|
+
if (bits.BYTES_PER_ELEMENT === 2) {
|
|
493
|
+
source.readUint16Array(bits, row, width);
|
|
494
|
+
} else {
|
|
495
|
+
source.readUint32Array(bits, row, width);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Undo what ZIP compression does to a chunk before deflating it — the inverse of `zip_prepare` in
|
|
503
|
+
* `exr_write`.
|
|
504
|
+
*
|
|
505
|
+
* The chunk's bytes were split into two runs, the bytes at even positions followed by those at odd ones,
|
|
506
|
+
* and the runs then delta-coded byte by byte with a bias of 128. Decoding keeps a running sum over the
|
|
507
|
+
* runs and deals each byte back to its position.
|
|
508
|
+
*
|
|
509
|
+
* @param {Uint8Array} source inflated bytes
|
|
510
|
+
* @param {Uint8Array} destination the chunk's pixel data
|
|
511
|
+
* @param {number} length bytes, at least one
|
|
512
|
+
*/
|
|
513
|
+
function zip_reconstruct(source, destination, length) {
|
|
514
|
+
const half = (length + 1) >> 1;
|
|
515
|
+
|
|
516
|
+
let value = source[0];
|
|
517
|
+
|
|
518
|
+
destination[0] = value;
|
|
519
|
+
|
|
520
|
+
for (let i = 1; i < half; i++) {
|
|
521
|
+
value = (value + source[i] - 128) & 0xFF;
|
|
522
|
+
|
|
523
|
+
destination[2 * i] = value;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
for (let i = half; i < length; i++) {
|
|
527
|
+
value = (value + source[i] - 128) & 0xFF;
|
|
528
|
+
|
|
529
|
+
destination[2 * (i - half) + 1] = value;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View a channel's samples as the unsigned integers that hold them: 16 bits for a `Float16Array`, 32 for a
|
|
3
|
+
* `Float32Array` or a `Uint32Array`.
|
|
4
|
+
*
|
|
5
|
+
* Samples move between an {@link ExrImage} and a file through this view, so they move as bits: nothing is
|
|
6
|
+
* converted, a NaN keeps its payload, and the byte order is {@link BinaryBuffer}'s business rather than the
|
|
7
|
+
* host's.
|
|
8
|
+
*
|
|
9
|
+
* @param {Float16Array|Float32Array|Uint32Array} data
|
|
10
|
+
* @returns {Uint16Array|Uint32Array} over the same memory
|
|
11
|
+
*
|
|
12
|
+
* @author Alex Goldring
|
|
13
|
+
* @copyright Company Named Limited (c) 2026
|
|
14
|
+
*/
|
|
15
|
+
export function exr_sample_bits(data: Float16Array | Float32Array | Uint32Array): Uint16Array | Uint32Array;
|
|
16
|
+
//# sourceMappingURL=exr_sample_bits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exr_sample_bits.d.ts","sourceRoot":"","sources":["../../../../../src/format/image/exr/exr_sample_bits.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,sCANW,eAAa,YAAY,GAAC,WAAW,GACnC,WAAW,GAAC,WAAW,CAWnC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View a channel's samples as the unsigned integers that hold them: 16 bits for a `Float16Array`, 32 for a
|
|
3
|
+
* `Float32Array` or a `Uint32Array`.
|
|
4
|
+
*
|
|
5
|
+
* Samples move between an {@link ExrImage} and a file through this view, so they move as bits: nothing is
|
|
6
|
+
* converted, a NaN keeps its payload, and the byte order is {@link BinaryBuffer}'s business rather than the
|
|
7
|
+
* host's.
|
|
8
|
+
*
|
|
9
|
+
* @param {Float16Array|Float32Array|Uint32Array} data
|
|
10
|
+
* @returns {Uint16Array|Uint32Array} over the same memory
|
|
11
|
+
*
|
|
12
|
+
* @author Alex Goldring
|
|
13
|
+
* @copyright Company Named Limited (c) 2026
|
|
14
|
+
*/
|
|
15
|
+
export function exr_sample_bits(data) {
|
|
16
|
+
if (data.BYTES_PER_ELEMENT === 2) {
|
|
17
|
+
return new Uint16Array(data.buffer, data.byteOffset, data.length);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return new Uint32Array(data.buffer, data.byteOffset, data.length);
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How many scanlines a chunk of pixel data holds under a compression method — every chunk but the
|
|
3
|
+
* last, which holds whatever remains of the image.
|
|
4
|
+
*
|
|
5
|
+
* This is also where an unsupported method is refused, by name, for reading and writing alike.
|
|
6
|
+
*
|
|
7
|
+
* @param {ExrCompression|number} compression
|
|
8
|
+
* @returns {number}
|
|
9
|
+
* @throws {Error} for a method other than {@link ExrCompression.None}, {@link ExrCompression.ZIPS} and
|
|
10
|
+
* {@link ExrCompression.ZIP}
|
|
11
|
+
*
|
|
12
|
+
* @author Alex Goldring
|
|
13
|
+
* @copyright Company Named Limited (c) 2026
|
|
14
|
+
*/
|
|
15
|
+
export function exr_scanlines_per_chunk(compression: ExrCompression | number): number;
|
|
16
|
+
import { ExrCompression } from "./ExrCompression.js";
|
|
17
|
+
//# sourceMappingURL=exr_scanlines_per_chunk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exr_scanlines_per_chunk.d.ts","sourceRoot":"","sources":["../../../../../src/format/image/exr/exr_scanlines_per_chunk.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AACH,qDARW,cAAc,GAAC,MAAM,GACnB,MAAM,CAoBlB;+BA7B8B,qBAAqB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { objectKeyByValue } from "../../../core/model/object/objectKeyByValue.js";
|
|
2
|
+
import { ExrCompression } from "./ExrCompression.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* How many scanlines a chunk of pixel data holds under a compression method — every chunk but the
|
|
6
|
+
* last, which holds whatever remains of the image.
|
|
7
|
+
*
|
|
8
|
+
* This is also where an unsupported method is refused, by name, for reading and writing alike.
|
|
9
|
+
*
|
|
10
|
+
* @param {ExrCompression|number} compression
|
|
11
|
+
* @returns {number}
|
|
12
|
+
* @throws {Error} for a method other than {@link ExrCompression.None}, {@link ExrCompression.ZIPS} and
|
|
13
|
+
* {@link ExrCompression.ZIP}
|
|
14
|
+
*
|
|
15
|
+
* @author Alex Goldring
|
|
16
|
+
* @copyright Company Named Limited (c) 2026
|
|
17
|
+
*/
|
|
18
|
+
export function exr_scanlines_per_chunk(compression) {
|
|
19
|
+
switch (compression) {
|
|
20
|
+
case ExrCompression.None:
|
|
21
|
+
case ExrCompression.ZIPS:
|
|
22
|
+
return 1;
|
|
23
|
+
case ExrCompression.ZIP:
|
|
24
|
+
return 16;
|
|
25
|
+
default:
|
|
26
|
+
throw new Error(
|
|
27
|
+
`EXR: ${objectKeyByValue(ExrCompression, compression) ?? `unknown (${compression})`}`
|
|
28
|
+
+ ` compression is not supported; NONE, ZIPS and ZIP are`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write an image as a scanline OpenEXR file.
|
|
3
|
+
*
|
|
4
|
+
* Each channel is written in the pixel type its array holds — a `Float16Array` as HALF, a `Float32Array`
|
|
5
|
+
* as FLOAT, a `Uint32Array` as UINT — and its samples go into the file as the bits they are. The header
|
|
6
|
+
* carries OpenEXR's required attributes and nothing else: both windows at the origin exactly covering the
|
|
7
|
+
* pixels, increasing line order, square pixels and the default screen window.
|
|
8
|
+
*
|
|
9
|
+
* Under {@link ExrCompression.ZIP} and {@link ExrCompression.ZIPS} a chunk that deflate does not make
|
|
10
|
+
* smaller is stored as it is, as OpenEXR's own writer does and every reader expects.
|
|
11
|
+
*
|
|
12
|
+
* The channels' arrays are read chunk by chunk as earlier chunks are compressed, so keep them unchanged
|
|
13
|
+
* until the promise settles.
|
|
14
|
+
*
|
|
15
|
+
* @param {ExrImage} image channels in any order, with distinct names of 1 to 255 bytes in UTF-8
|
|
16
|
+
* @param {ExrCompression} compression {@link ExrCompression.None}, {@link ExrCompression.ZIPS} or
|
|
17
|
+
* {@link ExrCompression.ZIP}
|
|
18
|
+
* @returns {Promise<ArrayBuffer>} the whole file
|
|
19
|
+
* @throws {Error} naming what about the image or the compression method cannot be written
|
|
20
|
+
*
|
|
21
|
+
* @see https://openexr.com/en/latest/OpenEXRFileLayout.html
|
|
22
|
+
* @author Alex Goldring
|
|
23
|
+
* @copyright Company Named Limited (c) 2026
|
|
24
|
+
*/
|
|
25
|
+
export function exr_write(image: ExrImage, compression: ExrCompression): Promise<ArrayBuffer>;
|
|
26
|
+
import { ExrCompression } from "./ExrCompression.js";
|
|
27
|
+
//# sourceMappingURL=exr_write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exr_write.d.ts","sourceRoot":"","sources":["../../../../../src/format/image/exr/exr_write.js"],"names":[],"mappings":"AA0BA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,iCAVW,QAAQ,eACR,cAAc,GAEZ,QAAQ,WAAW,CAAC,CA+GhC;+BA3I8B,qBAAqB"}
|