oidn-webgpu 0.0.0-stage → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +182 -0
- package/LICENSE +21 -0
- package/README.md +89 -2
- package/dist/oidn.js +6140 -0
- package/dist/oidn.umd.cjs +1513 -0
- package/lib/UNet.d.ts +161 -0
- package/lib/UNet.js +536 -0
- package/lib/UNet.js.map +1 -0
- package/lib/WGPUComputePass.d.ts +62 -0
- package/lib/WGPUComputePass.js +278 -0
- package/lib/WGPUComputePass.js.map +1 -0
- package/lib/WGPUFullQuadPass.d.ts +50 -0
- package/lib/WGPUFullQuadPass.js +261 -0
- package/lib/WGPUFullQuadPass.js.map +1 -0
- package/lib/backend.d.ts +1 -0
- package/lib/backend.js +39 -0
- package/lib/backend.js.map +1 -0
- package/lib/finalRgbShader.d.ts +13 -0
- package/lib/finalRgbShader.js +160 -0
- package/lib/finalRgbShader.js.map +1 -0
- package/lib/gpuTypes.d.ts +158 -0
- package/lib/gpuTypes.js +27 -0
- package/lib/gpuTypes.js.map +1 -0
- package/lib/graphOptimizer.d.ts +54 -0
- package/lib/graphOptimizer.js +216 -0
- package/lib/graphOptimizer.js.map +1 -0
- package/lib/hdr.d.ts +31 -0
- package/lib/hdr.js +340 -0
- package/lib/hdr.js.map +1 -0
- package/lib/hdrTransfer.d.ts +22 -0
- package/lib/hdrTransfer.js +128 -0
- package/lib/hdrTransfer.js.map +1 -0
- package/lib/main.d.ts +57 -0
- package/lib/main.js +24 -0
- package/lib/main.js.map +1 -0
- package/lib/modelSpec.d.ts +80 -0
- package/lib/modelSpec.js +270 -0
- package/lib/modelSpec.js.map +1 -0
- package/lib/native/denoise.d.ts +25 -0
- package/lib/native/denoise.js +223 -0
- package/lib/native/denoise.js.map +1 -0
- package/lib/native/program.d.ts +53 -0
- package/lib/native/program.js +310 -0
- package/lib/native/program.js.map +1 -0
- package/lib/native/recordingDevice.d.ts +125 -0
- package/lib/native/recordingDevice.js +247 -0
- package/lib/native/recordingDevice.js.map +1 -0
- package/lib/nativeUNet.d.ts +25 -0
- package/lib/nativeUNet.js +184 -0
- package/lib/nativeUNet.js.map +1 -0
- package/lib/nativeUNetCore.d.ts +202 -0
- package/lib/nativeUNetCore.js +2945 -0
- package/lib/nativeUNetCore.js.map +1 -0
- package/lib/process.d.ts +37 -0
- package/lib/process.js +328 -0
- package/lib/process.js.map +1 -0
- package/lib/resourceTracker.d.ts +26 -0
- package/lib/resourceTracker.js +65 -0
- package/lib/resourceTracker.js.map +1 -0
- package/lib/sanitize.d.ts +15 -0
- package/lib/sanitize.js +28 -0
- package/lib/sanitize.js.map +1 -0
- package/lib/tileScheduler.d.ts +61 -0
- package/lib/tileScheduler.js +199 -0
- package/lib/tileScheduler.js.map +1 -0
- package/lib/tza.d.ts +15 -0
- package/lib/tza.js +114 -0
- package/lib/tza.js.map +1 -0
- package/lib/webnnUNet.d.ts +52 -0
- package/lib/webnnUNet.js +535 -0
- package/lib/webnnUNet.js.map +1 -0
- package/package.json +43 -4
- package/src/UNet.ts +861 -0
- package/src/WGPUComputePass.ts +382 -0
- package/src/WGPUFullQuadPass.ts +348 -0
- package/src/backend.ts +42 -0
- package/src/finalRgbShader.ts +186 -0
- package/src/gpuTypes.ts +179 -0
- package/src/graphOptimizer.ts +301 -0
- package/src/hdr.ts +398 -0
- package/src/hdrTransfer.ts +169 -0
- package/src/main.ts +126 -0
- package/src/modelSpec.ts +414 -0
- package/src/native/core-profile.json +40 -0
- package/src/native/denoise.ts +268 -0
- package/src/native/program.ts +326 -0
- package/src/native/recordingDevice.ts +341 -0
- package/src/nativeUNet.ts +269 -0
- package/src/nativeUNetCore.ts +3735 -0
- package/src/process.ts +368 -0
- package/src/resourceTracker.ts +94 -0
- package/src/sanitize.ts +29 -0
- package/src/tileScheduler.ts +330 -0
- package/src/tza.ts +136 -0
- package/src/webnnUNet.ts +812 -0
package/lib/gpuTypes.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WebGPU subset used by the native WGSL runtime, as structural types and
|
|
3
|
+
* spec-defined flag values. A browser GPUDevice implements these operations,
|
|
4
|
+
* but its branded TypeScript types need the conversion in nativeUNet.ts;
|
|
5
|
+
* they also let an embedder supply its own device (for example one that
|
|
6
|
+
* records the work for replay in a native wgpu host) without lib.dom or
|
|
7
|
+
* WebGPU globals.
|
|
8
|
+
*/
|
|
9
|
+
/** GPUBufferUsage flag values from the WebGPU specification. */
|
|
10
|
+
export const BufferUsage = {
|
|
11
|
+
MAP_READ: 0x0001,
|
|
12
|
+
MAP_WRITE: 0x0002,
|
|
13
|
+
COPY_SRC: 0x0004,
|
|
14
|
+
COPY_DST: 0x0008,
|
|
15
|
+
INDEX: 0x0010,
|
|
16
|
+
VERTEX: 0x0020,
|
|
17
|
+
UNIFORM: 0x0040,
|
|
18
|
+
STORAGE: 0x0080,
|
|
19
|
+
INDIRECT: 0x0100,
|
|
20
|
+
QUERY_RESOLVE: 0x0200
|
|
21
|
+
};
|
|
22
|
+
/** GPUMapMode flag values from the WebGPU specification. */
|
|
23
|
+
export const MapMode = {
|
|
24
|
+
READ: 0x0001,
|
|
25
|
+
WRITE: 0x0002
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=gpuTypes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gpuTypes.js","sourceRoot":"","sources":["../src/gpuTypes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gEAAgE;AAChE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,MAAM;IACjB,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,MAAM;IAChB,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,MAAM;IACf,QAAQ,EAAE,MAAM;IAChB,aAAa,EAAE,MAAM;CACb,CAAC;AAEX,4DAA4D;AAC5D,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,MAAM;CACL,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Conv2DNodeSpec, MaxPool2DNodeSpec, ModelNodeSpec, UNetModelSpec, Upsample2DNodeSpec, UNetModelGraph } from './modelSpec';
|
|
2
|
+
export interface FusedConvPoolNodeSpec {
|
|
3
|
+
op: 'fusedConvReluMaxPool2d';
|
|
4
|
+
id: string;
|
|
5
|
+
input: string;
|
|
6
|
+
conv: Conv2DNodeSpec;
|
|
7
|
+
pool: MaxPool2DNodeSpec;
|
|
8
|
+
}
|
|
9
|
+
export interface FusedUpsampleConcatConvNodeSpec {
|
|
10
|
+
op: 'fusedUpsampleConcatConv2d';
|
|
11
|
+
id: string;
|
|
12
|
+
/** Inputs stay in concat order because that order selects weight channels. */
|
|
13
|
+
inputs: readonly {
|
|
14
|
+
value: string;
|
|
15
|
+
upsample?: Upsample2DNodeSpec;
|
|
16
|
+
}[];
|
|
17
|
+
conv: Conv2DNodeSpec;
|
|
18
|
+
}
|
|
19
|
+
export type ExecutableModelNode = ModelNodeSpec | FusedConvPoolNodeSpec | FusedUpsampleConcatConvNodeSpec;
|
|
20
|
+
export interface GraphOptimizationOptions {
|
|
21
|
+
fuseConvPool?: boolean;
|
|
22
|
+
fuseUpsampleConcatConv?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface OptimizedModelGraph {
|
|
25
|
+
spec: UNetModelSpec;
|
|
26
|
+
nodes: readonly ExecutableModelNode[];
|
|
27
|
+
fusions: {
|
|
28
|
+
convPool: number;
|
|
29
|
+
upsampleConcatConv: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Applies topology-only fusions. It never depends on a particular OIDN model
|
|
34
|
+
* name, so new descriptors automatically benefit from known graph patterns.
|
|
35
|
+
*/
|
|
36
|
+
export declare function optimizeModelGraph(validated: UNetModelGraph, options?: GraphOptimizationOptions): OptimizedModelGraph;
|
|
37
|
+
export interface ModelValueShape {
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
channels: number;
|
|
41
|
+
}
|
|
42
|
+
export interface PlannedModelNode {
|
|
43
|
+
node: ExecutableModelNode;
|
|
44
|
+
outputShape: ModelValueShape;
|
|
45
|
+
/** Last planned node that reads the output; output itself uses nodes.length. */
|
|
46
|
+
lastUse: number;
|
|
47
|
+
}
|
|
48
|
+
export interface ModelExecutionPlan extends OptimizedModelGraph {
|
|
49
|
+
inputShape: ModelValueShape;
|
|
50
|
+
valueShapes: ReadonlyMap<string, ModelValueShape>;
|
|
51
|
+
plannedNodes: readonly PlannedModelNode[];
|
|
52
|
+
}
|
|
53
|
+
/** Resolve all runtime shapes and value lifetimes before allocating GPU data. */
|
|
54
|
+
export declare function planModelExecution(validated: UNetModelGraph, width: number, height: number, options?: GraphOptimizationOptions): ModelExecutionPlan;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
function nodeInputs(node) {
|
|
2
|
+
return node.op === 'concat' ? node.inputs : [node.input];
|
|
3
|
+
}
|
|
4
|
+
function buildConsumers(spec) {
|
|
5
|
+
const consumers = new Map();
|
|
6
|
+
for (const node of spec.nodes) {
|
|
7
|
+
for (const input of nodeInputs(node)) {
|
|
8
|
+
const list = consumers.get(input) ?? [];
|
|
9
|
+
list.push(node);
|
|
10
|
+
consumers.set(input, list);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return consumers;
|
|
14
|
+
}
|
|
15
|
+
// Callers narrow on `op`; node ids are unique, so they compare consumers by id.
|
|
16
|
+
function onlyConsumer(consumers, value, op) {
|
|
17
|
+
const list = consumers.get(value);
|
|
18
|
+
if (list === undefined || list.length !== 1 || list[0].op !== op)
|
|
19
|
+
return undefined;
|
|
20
|
+
return list[0];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Applies topology-only fusions. It never depends on a particular OIDN model
|
|
24
|
+
* name, so new descriptors automatically benefit from known graph patterns.
|
|
25
|
+
*/
|
|
26
|
+
export function optimizeModelGraph(validated, options = {}) {
|
|
27
|
+
const spec = validated.spec;
|
|
28
|
+
const consumers = buildConsumers(spec);
|
|
29
|
+
const nodesById = new Map(spec.nodes.map((node) => [node.id, node]));
|
|
30
|
+
const eliminated = new Set();
|
|
31
|
+
const fusedAt = new Map();
|
|
32
|
+
let convPool = 0;
|
|
33
|
+
let upsampleConcatConv = 0;
|
|
34
|
+
if (options.fuseConvPool !== false) {
|
|
35
|
+
for (const node of spec.nodes) {
|
|
36
|
+
if (node.op !== 'conv2d' || node.activation !== 'relu')
|
|
37
|
+
continue;
|
|
38
|
+
const pool = onlyConsumer(consumers, node.id, 'maxPool2d');
|
|
39
|
+
if (!pool || pool.op !== 'maxPool2d' || pool.size !== 2 || pool.stride !== 2)
|
|
40
|
+
continue;
|
|
41
|
+
eliminated.add(node.id);
|
|
42
|
+
fusedAt.set(pool.id, {
|
|
43
|
+
op: 'fusedConvReluMaxPool2d',
|
|
44
|
+
id: pool.id,
|
|
45
|
+
input: node.input,
|
|
46
|
+
conv: node,
|
|
47
|
+
pool
|
|
48
|
+
});
|
|
49
|
+
convPool++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (options.fuseUpsampleConcatConv !== false) {
|
|
53
|
+
for (const node of spec.nodes) {
|
|
54
|
+
if (node.op !== 'conv2d')
|
|
55
|
+
continue;
|
|
56
|
+
const concat = nodesById.get(node.input);
|
|
57
|
+
if (concat?.op !== 'concat' || concat.inputs.length !== 2)
|
|
58
|
+
continue;
|
|
59
|
+
if (onlyConsumer(consumers, concat.id, 'conv2d')?.id !== node.id)
|
|
60
|
+
continue;
|
|
61
|
+
// The native blocked layout can remove concat only when the source
|
|
62
|
+
// boundary is also a vec4 boundary. Other graphs keep the generic ops.
|
|
63
|
+
const firstInputChannels = validated.channelsByValue.get(concat.inputs[0]);
|
|
64
|
+
if (firstInputChannels === undefined || firstInputChannels % 4 !== 0) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const inputs = concat.inputs.map((value) => {
|
|
68
|
+
const candidate = nodesById.get(value);
|
|
69
|
+
if (candidate?.op === 'upsample2d' &&
|
|
70
|
+
candidate.scale === 2 &&
|
|
71
|
+
candidate.mode === 'nearest' &&
|
|
72
|
+
onlyConsumer(consumers, candidate.id, 'concat')?.id === concat.id) {
|
|
73
|
+
return { value: candidate.input, upsample: candidate };
|
|
74
|
+
}
|
|
75
|
+
return { value };
|
|
76
|
+
});
|
|
77
|
+
const upsampleCount = inputs.filter((input) => input.upsample).length;
|
|
78
|
+
if (upsampleCount !== 1)
|
|
79
|
+
continue;
|
|
80
|
+
eliminated.add(concat.id);
|
|
81
|
+
for (const value of concat.inputs) {
|
|
82
|
+
const candidate = nodesById.get(value);
|
|
83
|
+
if (candidate?.op === 'upsample2d')
|
|
84
|
+
eliminated.add(candidate.id);
|
|
85
|
+
}
|
|
86
|
+
fusedAt.set(node.id, {
|
|
87
|
+
op: 'fusedUpsampleConcatConv2d',
|
|
88
|
+
id: node.id,
|
|
89
|
+
inputs,
|
|
90
|
+
conv: node
|
|
91
|
+
});
|
|
92
|
+
upsampleConcatConv++;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const nodes = [];
|
|
96
|
+
for (const node of spec.nodes) {
|
|
97
|
+
const fused = fusedAt.get(node.id);
|
|
98
|
+
if (fused) {
|
|
99
|
+
nodes.push(fused);
|
|
100
|
+
}
|
|
101
|
+
else if (!eliminated.has(node.id)) {
|
|
102
|
+
nodes.push(node);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
spec,
|
|
107
|
+
nodes,
|
|
108
|
+
fusions: { convPool, upsampleConcatConv }
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function executableInputs(node) {
|
|
112
|
+
if (node.op === 'concat')
|
|
113
|
+
return node.inputs;
|
|
114
|
+
if (node.op === 'fusedUpsampleConcatConv2d') {
|
|
115
|
+
return node.inputs.map((input) => input.value);
|
|
116
|
+
}
|
|
117
|
+
return [node.input];
|
|
118
|
+
}
|
|
119
|
+
function sameSpatialShape(left, right) {
|
|
120
|
+
return left.width === right.width && left.height === right.height;
|
|
121
|
+
}
|
|
122
|
+
/** Resolve all runtime shapes and value lifetimes before allocating GPU data. */
|
|
123
|
+
export function planModelExecution(validated, width, height, options) {
|
|
124
|
+
if (!Number.isInteger(width) || width <= 0 || !Number.isInteger(height) || height <= 0) {
|
|
125
|
+
throw new Error(`Invalid model input size ${width}x${height}`);
|
|
126
|
+
}
|
|
127
|
+
const graph = optimizeModelGraph(validated, options);
|
|
128
|
+
const inputShape = { width, height, channels: validated.inputChannels };
|
|
129
|
+
const valueShapes = new Map([
|
|
130
|
+
[validated.spec.input, inputShape]
|
|
131
|
+
]);
|
|
132
|
+
const outputShapes = [];
|
|
133
|
+
const shapeOf = (value, nodeId) => {
|
|
134
|
+
const shape = valueShapes.get(value);
|
|
135
|
+
if (!shape)
|
|
136
|
+
throw new Error(`Planned node ${nodeId} reads missing value ${value}`);
|
|
137
|
+
return shape;
|
|
138
|
+
};
|
|
139
|
+
for (const node of graph.nodes) {
|
|
140
|
+
let outputShape;
|
|
141
|
+
if (node.op === 'conv2d') {
|
|
142
|
+
const input = shapeOf(node.input, node.id);
|
|
143
|
+
outputShape = {
|
|
144
|
+
width: input.width,
|
|
145
|
+
height: input.height,
|
|
146
|
+
channels: validated.convChannels.get(node.id).outputChannels
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
else if (node.op === 'maxPool2d') {
|
|
150
|
+
const input = shapeOf(node.input, node.id);
|
|
151
|
+
outputShape = {
|
|
152
|
+
width: Math.ceil(input.width / 2),
|
|
153
|
+
height: Math.ceil(input.height / 2),
|
|
154
|
+
channels: input.channels
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
else if (node.op === 'upsample2d') {
|
|
158
|
+
const input = shapeOf(node.input, node.id);
|
|
159
|
+
outputShape = {
|
|
160
|
+
width: input.width * 2,
|
|
161
|
+
height: input.height * 2,
|
|
162
|
+
channels: input.channels
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
else if (node.op === 'concat') {
|
|
166
|
+
const inputs = node.inputs.map((value) => shapeOf(value, node.id));
|
|
167
|
+
if (inputs.some((shape) => !sameSpatialShape(shape, inputs[0]))) {
|
|
168
|
+
throw new Error(`Concat ${node.id} has mismatched spatial shapes`);
|
|
169
|
+
}
|
|
170
|
+
outputShape = {
|
|
171
|
+
width: inputs[0].width,
|
|
172
|
+
height: inputs[0].height,
|
|
173
|
+
channels: inputs.reduce((sum, shape) => sum + shape.channels, 0)
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
else if (node.op === 'fusedConvReluMaxPool2d') {
|
|
177
|
+
const input = shapeOf(node.input, node.id);
|
|
178
|
+
outputShape = {
|
|
179
|
+
width: Math.ceil(input.width / 2),
|
|
180
|
+
height: Math.ceil(input.height / 2),
|
|
181
|
+
channels: validated.convChannels.get(node.conv.id).outputChannels
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
const inputs = node.inputs.map((input) => {
|
|
186
|
+
const source = shapeOf(input.value, node.id);
|
|
187
|
+
return input.upsample
|
|
188
|
+
? { ...source, width: source.width * 2, height: source.height * 2 }
|
|
189
|
+
: source;
|
|
190
|
+
});
|
|
191
|
+
if (inputs.some((shape) => !sameSpatialShape(shape, inputs[0]))) {
|
|
192
|
+
throw new Error(`Fused decoder ${node.id} has mismatched spatial shapes`);
|
|
193
|
+
}
|
|
194
|
+
outputShape = {
|
|
195
|
+
width: inputs[0].width,
|
|
196
|
+
height: inputs[0].height,
|
|
197
|
+
channels: validated.convChannels.get(node.conv.id).outputChannels
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
valueShapes.set(node.id, outputShape);
|
|
201
|
+
outputShapes.push(outputShape);
|
|
202
|
+
}
|
|
203
|
+
const lastUses = new Map();
|
|
204
|
+
graph.nodes.forEach((node, index) => {
|
|
205
|
+
for (const input of executableInputs(node))
|
|
206
|
+
lastUses.set(input, index);
|
|
207
|
+
});
|
|
208
|
+
lastUses.set(validated.spec.output, graph.nodes.length);
|
|
209
|
+
const plannedNodes = graph.nodes.map((node, index) => ({
|
|
210
|
+
node,
|
|
211
|
+
outputShape: outputShapes[index],
|
|
212
|
+
lastUse: lastUses.get(node.id) ?? index
|
|
213
|
+
}));
|
|
214
|
+
return { ...graph, inputShape, valueShapes, plannedNodes };
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=graphOptimizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphOptimizer.js","sourceRoot":"","sources":["../src/graphOptimizer.ts"],"names":[],"mappings":"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"}
|
package/lib/hdr.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert 4 byte uint8 buffer to 3 channels float data
|
|
3
|
+
* @param rgbe input uint8 buffer
|
|
4
|
+
* @param float output float data
|
|
5
|
+
*/
|
|
6
|
+
declare function rgbe2float(rgbe: Uint8Array, float: Float32Array): void;
|
|
7
|
+
/**
|
|
8
|
+
* Convert 3 channels float data to 4 byte uint8 buffer
|
|
9
|
+
* @param float input float data
|
|
10
|
+
* @param rgbe output uint8 buffer
|
|
11
|
+
*/
|
|
12
|
+
declare function float2rgbe(float: Float32Array, rgbe: Uint8Array): void;
|
|
13
|
+
/**
|
|
14
|
+
* Write float data to RGBE(.hdr) file buffer
|
|
15
|
+
* @param w image width
|
|
16
|
+
* @param h image height
|
|
17
|
+
* @param data float data, RGB 3 channels.
|
|
18
|
+
* @returns file buffer
|
|
19
|
+
*/
|
|
20
|
+
declare function write_hdr(w: number, h: number, data: Float32Array): Uint8Array;
|
|
21
|
+
/**
|
|
22
|
+
* Read a RGBE(.hdr) file from buffer
|
|
23
|
+
* @param uint8 RGBE(.hdr) file buffer
|
|
24
|
+
* @returns Failure reason or resolved data.
|
|
25
|
+
*/
|
|
26
|
+
declare function read_hdr(uint8: Uint8Array): string | {
|
|
27
|
+
rgbFloat: Float32Array;
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
};
|
|
31
|
+
export { read_hdr as readHDR, write_hdr as writeHDR, float2rgbe, rgbe2float };
|
package/lib/hdr.js
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
// https://github.com/bbbbx/hdr.js/blob/main/src/hdr.ts
|
|
2
|
+
function frexp(v) {
|
|
3
|
+
v = Number(v);
|
|
4
|
+
const result = { f: v, e: 0 };
|
|
5
|
+
if (v !== 0 && Number.isFinite(v)) {
|
|
6
|
+
const absV = Math.abs(v);
|
|
7
|
+
const log2 = Math.log2 ||
|
|
8
|
+
function log2(n) {
|
|
9
|
+
return Math.log(n) * Math.LOG2E;
|
|
10
|
+
};
|
|
11
|
+
// Math.pow(2, -exp) === Infinity when exp <= -1024
|
|
12
|
+
let e = Math.max(-1023, Math.floor(log2(absV)) + 1);
|
|
13
|
+
let f = absV * Math.pow(2.0, -e);
|
|
14
|
+
while (f >= 1.0) {
|
|
15
|
+
f *= 0.5;
|
|
16
|
+
e++;
|
|
17
|
+
}
|
|
18
|
+
while (f < 0.5) {
|
|
19
|
+
f *= 2;
|
|
20
|
+
e--;
|
|
21
|
+
}
|
|
22
|
+
if (v < 0) {
|
|
23
|
+
f = -f;
|
|
24
|
+
}
|
|
25
|
+
result.f = f;
|
|
26
|
+
result.e = e;
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
function ldexp(f, e) {
|
|
31
|
+
return f * Math.pow(2.0, e);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Convert 4 byte uint8 buffer to 3 channels float data
|
|
35
|
+
* @param rgbe input uint8 buffer
|
|
36
|
+
* @param float output float data
|
|
37
|
+
*/
|
|
38
|
+
function rgbe2float(rgbe, float) {
|
|
39
|
+
if (rgbe[3] !== 0) {
|
|
40
|
+
const f1 = ldexp(1.0, rgbe[3] - (128 + 8));
|
|
41
|
+
float[0] = rgbe[0] * f1;
|
|
42
|
+
float[1] = rgbe[1] * f1;
|
|
43
|
+
float[2] = rgbe[2] * f1;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
float[0] = float[1] = float[2] = 0.0;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Convert 3 channels float data to 4 byte uint8 buffer
|
|
51
|
+
* @param float input float data
|
|
52
|
+
* @param rgbe output uint8 buffer
|
|
53
|
+
*/
|
|
54
|
+
function float2rgbe(float, rgbe) {
|
|
55
|
+
const red = float[0];
|
|
56
|
+
const green = float[1];
|
|
57
|
+
const blue = float[2];
|
|
58
|
+
const v = Math.max(red, Math.max(green, blue));
|
|
59
|
+
if (v < 1e-32) {
|
|
60
|
+
rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const { f, e } = frexp(v);
|
|
64
|
+
const s = (f / v) * 256.0;
|
|
65
|
+
rgbe[0] = red * s;
|
|
66
|
+
rgbe[1] = green * s;
|
|
67
|
+
rgbe[2] = blue * s;
|
|
68
|
+
rgbe[3] = e + 128;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Write float data to RGBE(.hdr) file buffer
|
|
73
|
+
* @param w image width
|
|
74
|
+
* @param h image height
|
|
75
|
+
* @param data float data, RGB 3 channels.
|
|
76
|
+
* @returns file buffer
|
|
77
|
+
*/
|
|
78
|
+
function write_hdr(w, h, data) {
|
|
79
|
+
const s = [];
|
|
80
|
+
const comp = 3;
|
|
81
|
+
write_hdr_core(s, w, h, comp, data);
|
|
82
|
+
return new Uint8Array(s);
|
|
83
|
+
}
|
|
84
|
+
function write_hdr_core(s, x, y, comp, data) {
|
|
85
|
+
if (y <= 0 || x <= 0 || !data) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const header = '#?RADIANCE\n' +
|
|
89
|
+
'FORMAT=32-bit_rle_rgbe\n' +
|
|
90
|
+
'EXPOSURE=1.0\n' +
|
|
91
|
+
'\n' +
|
|
92
|
+
'-Y ' +
|
|
93
|
+
y +
|
|
94
|
+
' +X ' +
|
|
95
|
+
x +
|
|
96
|
+
'\n';
|
|
97
|
+
header.split('').forEach((c) => {
|
|
98
|
+
const charCode = c.charCodeAt(0);
|
|
99
|
+
s.push(charCode);
|
|
100
|
+
});
|
|
101
|
+
const scratch = new Uint8Array(x * 4);
|
|
102
|
+
const stbi__flip_vertically_on_write = false;
|
|
103
|
+
for (let i = 0; i < y; i++) {
|
|
104
|
+
write_hdr_scanline(s, x, comp, scratch, data.subarray(comp * x * (stbi__flip_vertically_on_write ? y - 1 - i : i)));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function write_hdr_scanline(s, width, ncomp, scratch, scanline) {
|
|
108
|
+
const scanlineheader = new Uint8Array([2, 2, 0, 0]);
|
|
109
|
+
const rgbe = new Uint8Array(4);
|
|
110
|
+
const linear = new Float32Array(3);
|
|
111
|
+
let x;
|
|
112
|
+
scanlineheader[2] = (width & 0xff00) >> 8;
|
|
113
|
+
scanlineheader[3] = width & 0x00ff;
|
|
114
|
+
/* skip RLE for images too small or large */
|
|
115
|
+
if (width < 8 || width >= 32768) {
|
|
116
|
+
for (x = 0; x < width; x++) {
|
|
117
|
+
switch (ncomp) {
|
|
118
|
+
case 4: /* fallthrough */
|
|
119
|
+
case 3:
|
|
120
|
+
linear[2] = scanline[x * ncomp + 2];
|
|
121
|
+
linear[1] = scanline[x * ncomp + 1];
|
|
122
|
+
linear[0] = scanline[x * ncomp + 0];
|
|
123
|
+
break;
|
|
124
|
+
default:
|
|
125
|
+
linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0];
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
float2rgbe(linear, rgbe);
|
|
129
|
+
s.push(rgbe[0], rgbe[1], rgbe[2], rgbe[3]);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
let c, r;
|
|
134
|
+
/* encode into scratch buffer */
|
|
135
|
+
for (x = 0; x < width; x++) {
|
|
136
|
+
switch (ncomp) {
|
|
137
|
+
case 4: /* fallthrough */
|
|
138
|
+
case 3:
|
|
139
|
+
linear[2] = scanline[x * ncomp + 2];
|
|
140
|
+
linear[1] = scanline[x * ncomp + 1];
|
|
141
|
+
linear[0] = scanline[x * ncomp + 0];
|
|
142
|
+
break;
|
|
143
|
+
default:
|
|
144
|
+
linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0];
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
float2rgbe(linear, rgbe);
|
|
148
|
+
scratch[x + width * 0] = rgbe[0];
|
|
149
|
+
scratch[x + width * 1] = rgbe[1];
|
|
150
|
+
scratch[x + width * 2] = rgbe[2];
|
|
151
|
+
scratch[x + width * 3] = rgbe[3];
|
|
152
|
+
}
|
|
153
|
+
s.push(scanlineheader[0], scanlineheader[1], scanlineheader[2], scanlineheader[3]);
|
|
154
|
+
/* RLE each component separately */
|
|
155
|
+
for (c = 0; c < 4; c++) {
|
|
156
|
+
const comp = scratch.subarray(width * c);
|
|
157
|
+
x = 0;
|
|
158
|
+
while (x < width) {
|
|
159
|
+
// find first run
|
|
160
|
+
r = x;
|
|
161
|
+
while (r + 2 < width) {
|
|
162
|
+
if (comp[r] === comp[r + 1] && comp[r] === comp[r + 2])
|
|
163
|
+
break;
|
|
164
|
+
++r;
|
|
165
|
+
}
|
|
166
|
+
if (r + 2 >= width)
|
|
167
|
+
r = width;
|
|
168
|
+
// dump up to first run
|
|
169
|
+
while (x < r) {
|
|
170
|
+
let len = r - x;
|
|
171
|
+
if (len > 128)
|
|
172
|
+
len = 128;
|
|
173
|
+
write_dump_data(s, len, comp.subarray(x));
|
|
174
|
+
x += len;
|
|
175
|
+
}
|
|
176
|
+
// if there's a run, output it
|
|
177
|
+
if (r + 2 < width) {
|
|
178
|
+
// same test as what we break out of in search loop, so only true if we break'd
|
|
179
|
+
// find next byte after run
|
|
180
|
+
while (r < width && comp[r] == comp[x])
|
|
181
|
+
++r;
|
|
182
|
+
// output run up to r
|
|
183
|
+
while (x < r) {
|
|
184
|
+
let len = r - x;
|
|
185
|
+
if (len > 127)
|
|
186
|
+
len = 127;
|
|
187
|
+
write_run_data(s, len, comp[x]);
|
|
188
|
+
x += len;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function write_dump_data(s, length, data) {
|
|
196
|
+
const lengthbyte = length & 0xff;
|
|
197
|
+
if (!(length <= 128))
|
|
198
|
+
throw new Error('length is greater than 128');
|
|
199
|
+
s.push(lengthbyte);
|
|
200
|
+
for (let i = 0; i < length; i++) {
|
|
201
|
+
s.push(data[i]);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function write_run_data(s, length, databyte) {
|
|
205
|
+
const lengthbyte = (length + 128) & 0xff;
|
|
206
|
+
if (!(length + 128 <= 255))
|
|
207
|
+
throw new Error('length is greater than 128');
|
|
208
|
+
s.push(lengthbyte, databyte);
|
|
209
|
+
}
|
|
210
|
+
function testMagic(uint8, signature) {
|
|
211
|
+
const l = signature.length;
|
|
212
|
+
for (let i = 0; i < l; i++) {
|
|
213
|
+
if (uint8[i] !== signature.charCodeAt(i)) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
function isHdr(uint8) {
|
|
220
|
+
let r = testMagic(uint8, '#?RADIANCE\n');
|
|
221
|
+
if (!r) {
|
|
222
|
+
r = testMagic(uint8, '#?RGBE\n');
|
|
223
|
+
}
|
|
224
|
+
return r;
|
|
225
|
+
}
|
|
226
|
+
const MAX_HEADER_LENGTH = 1024 * 10;
|
|
227
|
+
const MAX_DIMENSIONS = 1 << 24;
|
|
228
|
+
/**
|
|
229
|
+
* Read a RGBE(.hdr) file from buffer
|
|
230
|
+
* @param uint8 RGBE(.hdr) file buffer
|
|
231
|
+
* @returns Failure reason or resolved data.
|
|
232
|
+
*/
|
|
233
|
+
function read_hdr(uint8) {
|
|
234
|
+
let header = '';
|
|
235
|
+
let pos = 0;
|
|
236
|
+
if (!isHdr(uint8)) {
|
|
237
|
+
return 'Corrupt HDR image.';
|
|
238
|
+
}
|
|
239
|
+
// read header
|
|
240
|
+
while (!header.match(/\n\n[^\n]+\n/g) && pos < MAX_HEADER_LENGTH) {
|
|
241
|
+
header += String.fromCharCode(uint8[pos++]);
|
|
242
|
+
}
|
|
243
|
+
// check format
|
|
244
|
+
const format = header.match(/FORMAT=(.*)$/m)?.[1];
|
|
245
|
+
if (format !== '32-bit_rle_rgbe') {
|
|
246
|
+
return 'Unsupported HDR format: ' + format;
|
|
247
|
+
}
|
|
248
|
+
// parse resolution
|
|
249
|
+
const rez = header.split(/\n/).reverse()[1].split(' ');
|
|
250
|
+
if (rez[0] !== '-Y' || rez[2] !== '+X') {
|
|
251
|
+
return 'Unsupported HDR format';
|
|
252
|
+
}
|
|
253
|
+
const width = Number.parseFloat(rez[3]);
|
|
254
|
+
const height = Number.parseFloat(rez[1]);
|
|
255
|
+
if (width > MAX_DIMENSIONS || height > MAX_DIMENSIONS) {
|
|
256
|
+
return 'Very large image (corrupt?)';
|
|
257
|
+
}
|
|
258
|
+
let i, j;
|
|
259
|
+
let c1 = uint8[pos];
|
|
260
|
+
let c2 = uint8[pos + 1];
|
|
261
|
+
let len = uint8[pos + 2];
|
|
262
|
+
// not run-length encoded, so we have to actually use THIS data as a decoded
|
|
263
|
+
// pixel (note this can't be a valid pixel--one of RGB must be >= 128)
|
|
264
|
+
const notRLE = c1 !== 2 || c2 !== 2 || !!(len & 0x80); // not run-length encoded
|
|
265
|
+
const hdrData = new Float32Array(width * height * 3);
|
|
266
|
+
if (width < 8 || width >= 32768 || notRLE) {
|
|
267
|
+
// 32768: 2^15
|
|
268
|
+
// Read flat data
|
|
269
|
+
for (j = 0; j < height; ++j) {
|
|
270
|
+
for (i = 0; i < width; ++i) {
|
|
271
|
+
const rgbe = uint8.subarray(pos, pos + 4);
|
|
272
|
+
pos += 4;
|
|
273
|
+
const start = (j * width + i) * 3;
|
|
274
|
+
rgbe2float(rgbe, hdrData.subarray(start, start + 3));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
// Read RLE-encoded data
|
|
280
|
+
let scanline;
|
|
281
|
+
let c1;
|
|
282
|
+
let c2;
|
|
283
|
+
let len;
|
|
284
|
+
for (let j = 0; j < height; j++) {
|
|
285
|
+
c1 = uint8[pos++];
|
|
286
|
+
c2 = uint8[pos++];
|
|
287
|
+
len = uint8[pos++];
|
|
288
|
+
if (c1 !== 2 || c2 !== 2 || len & 0x80) {
|
|
289
|
+
return 'Invalid scanline';
|
|
290
|
+
}
|
|
291
|
+
len = len << 8;
|
|
292
|
+
len |= uint8[pos++];
|
|
293
|
+
if (len !== width) {
|
|
294
|
+
return 'invalid decoded scanline length';
|
|
295
|
+
}
|
|
296
|
+
if (!scanline) {
|
|
297
|
+
scanline = new Uint8Array(width * 4);
|
|
298
|
+
}
|
|
299
|
+
let count;
|
|
300
|
+
let value;
|
|
301
|
+
for (let k = 0; k < 4; k++) {
|
|
302
|
+
let nLeft;
|
|
303
|
+
i = 0;
|
|
304
|
+
while ((nLeft = width - i) > 0) {
|
|
305
|
+
count = uint8[pos++];
|
|
306
|
+
if (count > 128) {
|
|
307
|
+
// is RUN
|
|
308
|
+
value = uint8[pos++];
|
|
309
|
+
count -= 128;
|
|
310
|
+
if (count > nLeft) {
|
|
311
|
+
return 'bad RLE data in HDR';
|
|
312
|
+
}
|
|
313
|
+
for (let z = 0; z < count; z++) {
|
|
314
|
+
scanline[i++ * 4 + k] = value;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
// is DUMP
|
|
319
|
+
if (count > nLeft) {
|
|
320
|
+
return 'bad RLE data in HDR';
|
|
321
|
+
}
|
|
322
|
+
for (let z = 0; z < count; z++) {
|
|
323
|
+
scanline[i++ * 4 + k] = uint8[pos++];
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
for (let i = 0; i < width; i++) {
|
|
329
|
+
rgbe2float(scanline.subarray(i * 4), hdrData.subarray((j * width + i) * 3));
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
rgbFloat: hdrData,
|
|
335
|
+
width: width,
|
|
336
|
+
height: height
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
export { read_hdr as readHDR, write_hdr as writeHDR, float2rgbe, rgbe2float };
|
|
340
|
+
//# sourceMappingURL=hdr.js.map
|