open-ultrahdr 0.1.0 → 0.1.2
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/index.d.mts +77 -1
- package/dist/index.d.ts +77 -1
- package/dist/index.js +42 -10
- package/dist/index.mjs +40 -10
- package/package.json +7 -6
- package/src/index.ts +118 -18
- package/src/types.ts +38 -0
package/dist/index.d.mts
CHANGED
|
@@ -28,6 +28,34 @@ interface GainMapMetadata {
|
|
|
28
28
|
/** Maximum HDR capacity (log2 scale) for full HDR output */
|
|
29
29
|
hdrCapacityMax: number;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Result of probing an image to check if it's UltraHDR.
|
|
33
|
+
*
|
|
34
|
+
* Provides detailed information about what components were found
|
|
35
|
+
* without fully decoding the image. Useful for batch processing and filtering.
|
|
36
|
+
*/
|
|
37
|
+
interface UltraHdrProbeResult {
|
|
38
|
+
/** Whether the image is a valid UltraHDR image (has all required components) */
|
|
39
|
+
isValid: boolean;
|
|
40
|
+
/** Whether a primary JPEG image was found */
|
|
41
|
+
hasPrimaryImage: boolean;
|
|
42
|
+
/** Whether a gain map image was found */
|
|
43
|
+
hasGainMap: boolean;
|
|
44
|
+
/** Whether gain map metadata (XMP) was found */
|
|
45
|
+
hasMetadata: boolean;
|
|
46
|
+
/** Primary image width in pixels (0 if not found) */
|
|
47
|
+
width: number;
|
|
48
|
+
/** Primary image height in pixels (0 if not found) */
|
|
49
|
+
height: number;
|
|
50
|
+
/** Gain map width in pixels (0 if not found) */
|
|
51
|
+
gainMapWidth: number;
|
|
52
|
+
/** Gain map height in pixels (0 if not found) */
|
|
53
|
+
gainMapHeight: number;
|
|
54
|
+
/** HDR capacity (max additional stops of dynamic range), 0 if not found */
|
|
55
|
+
hdrCapacity: number;
|
|
56
|
+
/** Metadata version string (empty if not found) */
|
|
57
|
+
metadataVersion: string;
|
|
58
|
+
}
|
|
31
59
|
/**
|
|
32
60
|
* Result of decoding an UltraHDR image.
|
|
33
61
|
*/
|
|
@@ -139,6 +167,23 @@ declare const smallSizeEncodeOptions: UltraHdrEncodeOptions;
|
|
|
139
167
|
* ```
|
|
140
168
|
*/
|
|
141
169
|
declare function setLocation(newLocation: string): void;
|
|
170
|
+
/**
|
|
171
|
+
* Sets a direct URL (including data URLs) for the WASM module.
|
|
172
|
+
*
|
|
173
|
+
* Use this when the WASM binary is inlined as a base64 data URL
|
|
174
|
+
* at build time. Unlike `setLocation`, this passes the URL directly
|
|
175
|
+
* to the WASM init function without appending a filename.
|
|
176
|
+
*
|
|
177
|
+
* @param url - Direct URL or data URL for the WASM binary.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* // With a build tool that inlines WASM as base64 data URLs:
|
|
182
|
+
* import wasmDataUrl from 'open-ultrahdr-wasm/pkg/open_ultrahdr_bg.wasm';
|
|
183
|
+
* setWasmUrl(wasmDataUrl);
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
declare function setWasmUrl(url: string): void;
|
|
142
187
|
/**
|
|
143
188
|
* Checks if a buffer contains an UltraHDR image.
|
|
144
189
|
*
|
|
@@ -157,6 +202,37 @@ declare function setLocation(newLocation: string): void;
|
|
|
157
202
|
* ```
|
|
158
203
|
*/
|
|
159
204
|
declare function isUltraHdr(buffer: ArrayBuffer): Promise<boolean>;
|
|
205
|
+
/**
|
|
206
|
+
* Probes an image to check if it's UltraHDR and extracts component information.
|
|
207
|
+
*
|
|
208
|
+
* This function efficiently validates if an image is UltraHDR by checking for
|
|
209
|
+
* required components (primary image, gain map, metadata) without full decoding.
|
|
210
|
+
* Returns structured results useful for batch processing and filtering.
|
|
211
|
+
*
|
|
212
|
+
* Unlike `isUltraHdr`, this function provides detailed information about what
|
|
213
|
+
* was found, making it useful for diagnostics and filtering workflows.
|
|
214
|
+
*
|
|
215
|
+
* @param buffer - Image file contents.
|
|
216
|
+
* @return Probe result with detailed component information.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```typescript
|
|
220
|
+
* const buffer = await file.arrayBuffer();
|
|
221
|
+
* const result = await probeUltraHdr(buffer);
|
|
222
|
+
*
|
|
223
|
+
* if (result.isValid) {
|
|
224
|
+
* console.log('UltraHDR image:', result.width, 'x', result.height);
|
|
225
|
+
* console.log('HDR capacity:', result.hdrCapacity, 'stops');
|
|
226
|
+
* console.log('Gain map:', result.gainMapWidth, 'x', result.gainMapHeight);
|
|
227
|
+
* } else {
|
|
228
|
+
* // Diagnose why it's not a valid UltraHDR
|
|
229
|
+
* if (!result.hasPrimaryImage) console.log('Not a valid JPEG');
|
|
230
|
+
* if (!result.hasGainMap) console.log('Missing gain map');
|
|
231
|
+
* if (!result.hasMetadata) console.log('Missing HDR metadata');
|
|
232
|
+
* }
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
declare function probeUltraHdr(buffer: ArrayBuffer): Promise<UltraHdrProbeResult>;
|
|
160
236
|
/**
|
|
161
237
|
* Decodes an UltraHDR image, extracting all components.
|
|
162
238
|
*
|
|
@@ -263,4 +339,4 @@ declare function estimateHdrHeadroom(metadata: GainMapMetadata): Promise<number>
|
|
|
263
339
|
*/
|
|
264
340
|
declare function isMeaningfulHdr(metadata: GainMapMetadata): Promise<boolean>;
|
|
265
341
|
|
|
266
|
-
export { ColorGamut, type GainMapMetadata, type ItemId, TransferFunction, type UltraHdrDecodeResult, type UltraHdrEncodeOptions, decodeUltraHdr, defaultEncodeOptions, encodeUltraHdr, estimateHdrHeadroom, extractSdrBase, getMetadata, highQualityEncodeOptions, isMeaningfulHdr, isUltraHdr, setLocation, smallSizeEncodeOptions, validateMetadata };
|
|
342
|
+
export { ColorGamut, type GainMapMetadata, type ItemId, TransferFunction, type UltraHdrDecodeResult, type UltraHdrEncodeOptions, type UltraHdrProbeResult, decodeUltraHdr, defaultEncodeOptions, encodeUltraHdr, estimateHdrHeadroom, extractSdrBase, getMetadata, highQualityEncodeOptions, isMeaningfulHdr, isUltraHdr, probeUltraHdr, setLocation, setWasmUrl, smallSizeEncodeOptions, validateMetadata };
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,34 @@ interface GainMapMetadata {
|
|
|
28
28
|
/** Maximum HDR capacity (log2 scale) for full HDR output */
|
|
29
29
|
hdrCapacityMax: number;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Result of probing an image to check if it's UltraHDR.
|
|
33
|
+
*
|
|
34
|
+
* Provides detailed information about what components were found
|
|
35
|
+
* without fully decoding the image. Useful for batch processing and filtering.
|
|
36
|
+
*/
|
|
37
|
+
interface UltraHdrProbeResult {
|
|
38
|
+
/** Whether the image is a valid UltraHDR image (has all required components) */
|
|
39
|
+
isValid: boolean;
|
|
40
|
+
/** Whether a primary JPEG image was found */
|
|
41
|
+
hasPrimaryImage: boolean;
|
|
42
|
+
/** Whether a gain map image was found */
|
|
43
|
+
hasGainMap: boolean;
|
|
44
|
+
/** Whether gain map metadata (XMP) was found */
|
|
45
|
+
hasMetadata: boolean;
|
|
46
|
+
/** Primary image width in pixels (0 if not found) */
|
|
47
|
+
width: number;
|
|
48
|
+
/** Primary image height in pixels (0 if not found) */
|
|
49
|
+
height: number;
|
|
50
|
+
/** Gain map width in pixels (0 if not found) */
|
|
51
|
+
gainMapWidth: number;
|
|
52
|
+
/** Gain map height in pixels (0 if not found) */
|
|
53
|
+
gainMapHeight: number;
|
|
54
|
+
/** HDR capacity (max additional stops of dynamic range), 0 if not found */
|
|
55
|
+
hdrCapacity: number;
|
|
56
|
+
/** Metadata version string (empty if not found) */
|
|
57
|
+
metadataVersion: string;
|
|
58
|
+
}
|
|
31
59
|
/**
|
|
32
60
|
* Result of decoding an UltraHDR image.
|
|
33
61
|
*/
|
|
@@ -139,6 +167,23 @@ declare const smallSizeEncodeOptions: UltraHdrEncodeOptions;
|
|
|
139
167
|
* ```
|
|
140
168
|
*/
|
|
141
169
|
declare function setLocation(newLocation: string): void;
|
|
170
|
+
/**
|
|
171
|
+
* Sets a direct URL (including data URLs) for the WASM module.
|
|
172
|
+
*
|
|
173
|
+
* Use this when the WASM binary is inlined as a base64 data URL
|
|
174
|
+
* at build time. Unlike `setLocation`, this passes the URL directly
|
|
175
|
+
* to the WASM init function without appending a filename.
|
|
176
|
+
*
|
|
177
|
+
* @param url - Direct URL or data URL for the WASM binary.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* // With a build tool that inlines WASM as base64 data URLs:
|
|
182
|
+
* import wasmDataUrl from 'open-ultrahdr-wasm/pkg/open_ultrahdr_bg.wasm';
|
|
183
|
+
* setWasmUrl(wasmDataUrl);
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
declare function setWasmUrl(url: string): void;
|
|
142
187
|
/**
|
|
143
188
|
* Checks if a buffer contains an UltraHDR image.
|
|
144
189
|
*
|
|
@@ -157,6 +202,37 @@ declare function setLocation(newLocation: string): void;
|
|
|
157
202
|
* ```
|
|
158
203
|
*/
|
|
159
204
|
declare function isUltraHdr(buffer: ArrayBuffer): Promise<boolean>;
|
|
205
|
+
/**
|
|
206
|
+
* Probes an image to check if it's UltraHDR and extracts component information.
|
|
207
|
+
*
|
|
208
|
+
* This function efficiently validates if an image is UltraHDR by checking for
|
|
209
|
+
* required components (primary image, gain map, metadata) without full decoding.
|
|
210
|
+
* Returns structured results useful for batch processing and filtering.
|
|
211
|
+
*
|
|
212
|
+
* Unlike `isUltraHdr`, this function provides detailed information about what
|
|
213
|
+
* was found, making it useful for diagnostics and filtering workflows.
|
|
214
|
+
*
|
|
215
|
+
* @param buffer - Image file contents.
|
|
216
|
+
* @return Probe result with detailed component information.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```typescript
|
|
220
|
+
* const buffer = await file.arrayBuffer();
|
|
221
|
+
* const result = await probeUltraHdr(buffer);
|
|
222
|
+
*
|
|
223
|
+
* if (result.isValid) {
|
|
224
|
+
* console.log('UltraHDR image:', result.width, 'x', result.height);
|
|
225
|
+
* console.log('HDR capacity:', result.hdrCapacity, 'stops');
|
|
226
|
+
* console.log('Gain map:', result.gainMapWidth, 'x', result.gainMapHeight);
|
|
227
|
+
* } else {
|
|
228
|
+
* // Diagnose why it's not a valid UltraHDR
|
|
229
|
+
* if (!result.hasPrimaryImage) console.log('Not a valid JPEG');
|
|
230
|
+
* if (!result.hasGainMap) console.log('Missing gain map');
|
|
231
|
+
* if (!result.hasMetadata) console.log('Missing HDR metadata');
|
|
232
|
+
* }
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
declare function probeUltraHdr(buffer: ArrayBuffer): Promise<UltraHdrProbeResult>;
|
|
160
236
|
/**
|
|
161
237
|
* Decodes an UltraHDR image, extracting all components.
|
|
162
238
|
*
|
|
@@ -263,4 +339,4 @@ declare function estimateHdrHeadroom(metadata: GainMapMetadata): Promise<number>
|
|
|
263
339
|
*/
|
|
264
340
|
declare function isMeaningfulHdr(metadata: GainMapMetadata): Promise<boolean>;
|
|
265
341
|
|
|
266
|
-
export { ColorGamut, type GainMapMetadata, type ItemId, TransferFunction, type UltraHdrDecodeResult, type UltraHdrEncodeOptions, decodeUltraHdr, defaultEncodeOptions, encodeUltraHdr, estimateHdrHeadroom, extractSdrBase, getMetadata, highQualityEncodeOptions, isMeaningfulHdr, isUltraHdr, setLocation, smallSizeEncodeOptions, validateMetadata };
|
|
342
|
+
export { ColorGamut, type GainMapMetadata, type ItemId, TransferFunction, type UltraHdrDecodeResult, type UltraHdrEncodeOptions, type UltraHdrProbeResult, decodeUltraHdr, defaultEncodeOptions, encodeUltraHdr, estimateHdrHeadroom, extractSdrBase, getMetadata, highQualityEncodeOptions, isMeaningfulHdr, isUltraHdr, probeUltraHdr, setLocation, setWasmUrl, smallSizeEncodeOptions, validateMetadata };
|
package/dist/index.js
CHANGED
|
@@ -41,7 +41,9 @@ __export(index_exports, {
|
|
|
41
41
|
highQualityEncodeOptions: () => highQualityEncodeOptions,
|
|
42
42
|
isMeaningfulHdr: () => isMeaningfulHdr,
|
|
43
43
|
isUltraHdr: () => isUltraHdr,
|
|
44
|
+
probeUltraHdr: () => probeUltraHdr,
|
|
44
45
|
setLocation: () => setLocation,
|
|
46
|
+
setWasmUrl: () => setWasmUrl,
|
|
45
47
|
smallSizeEncodeOptions: () => smallSizeEncodeOptions,
|
|
46
48
|
validateMetadata: () => validateMetadata
|
|
47
49
|
});
|
|
@@ -88,11 +90,15 @@ var smallSizeEncodeOptions = {
|
|
|
88
90
|
|
|
89
91
|
// src/index.ts
|
|
90
92
|
var location = "";
|
|
93
|
+
var wasmUrl = "";
|
|
91
94
|
var wasmInstance = null;
|
|
92
95
|
var initPromise = null;
|
|
93
96
|
function setLocation(newLocation) {
|
|
94
97
|
location = newLocation;
|
|
95
98
|
}
|
|
99
|
+
function setWasmUrl(url) {
|
|
100
|
+
wasmUrl = url;
|
|
101
|
+
}
|
|
96
102
|
function isWasmMetadataInstance(metadata) {
|
|
97
103
|
return typeof metadata === "object" && metadata !== null && "__wbg_ptr" in metadata;
|
|
98
104
|
}
|
|
@@ -121,15 +127,29 @@ async function getWasm() {
|
|
|
121
127
|
return initPromise;
|
|
122
128
|
}
|
|
123
129
|
initPromise = (async () => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
try {
|
|
131
|
+
const UltraHdrWasm = await import("open-ultrahdr-wasm");
|
|
132
|
+
if (wasmUrl) {
|
|
133
|
+
await UltraHdrWasm.default(wasmUrl);
|
|
134
|
+
} else if (location) {
|
|
135
|
+
const base = location.endsWith("/") ? location : `${location}/`;
|
|
136
|
+
const wasmPath = base + "open_ultrahdr_bg.wasm";
|
|
137
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
138
|
+
const fs = await import("fs");
|
|
139
|
+
const wasmBytes = await fs.promises.readFile(wasmPath);
|
|
140
|
+
await UltraHdrWasm.default(wasmBytes);
|
|
141
|
+
} else {
|
|
142
|
+
await UltraHdrWasm.default(wasmPath);
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
await UltraHdrWasm.default();
|
|
146
|
+
}
|
|
147
|
+
wasmInstance = UltraHdrWasm;
|
|
148
|
+
return wasmInstance;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
initPromise = null;
|
|
151
|
+
throw err;
|
|
130
152
|
}
|
|
131
|
-
wasmInstance = UltraHdrWasm;
|
|
132
|
-
return wasmInstance;
|
|
133
153
|
})();
|
|
134
154
|
return initPromise;
|
|
135
155
|
}
|
|
@@ -137,6 +157,10 @@ async function isUltraHdr(buffer) {
|
|
|
137
157
|
const wasm = await getWasm();
|
|
138
158
|
return wasm.isUltraHdr(new Uint8Array(buffer));
|
|
139
159
|
}
|
|
160
|
+
async function probeUltraHdr(buffer) {
|
|
161
|
+
const wasm = await getWasm();
|
|
162
|
+
return wasm.probeUltraHdr(new Uint8Array(buffer));
|
|
163
|
+
}
|
|
140
164
|
async function decodeUltraHdr(id, buffer) {
|
|
141
165
|
const wasm = await getWasm();
|
|
142
166
|
return wasm.decodeUltraHdr(new Uint8Array(buffer));
|
|
@@ -156,12 +180,18 @@ async function encodeUltraHdr(id, sdrBuffer, hdrBuffer, options) {
|
|
|
156
180
|
new Float32Array(hdrBuffer),
|
|
157
181
|
wasmOpts
|
|
158
182
|
);
|
|
159
|
-
return result.buffer.slice(
|
|
183
|
+
return result.buffer.slice(
|
|
184
|
+
result.byteOffset,
|
|
185
|
+
result.byteOffset + result.byteLength
|
|
186
|
+
);
|
|
160
187
|
}
|
|
161
188
|
async function extractSdrBase(buffer) {
|
|
162
189
|
const wasm = await getWasm();
|
|
163
190
|
const result = wasm.extractSdrBase(new Uint8Array(buffer));
|
|
164
|
-
return result.buffer.slice(
|
|
191
|
+
return result.buffer.slice(
|
|
192
|
+
result.byteOffset,
|
|
193
|
+
result.byteOffset + result.byteLength
|
|
194
|
+
);
|
|
165
195
|
}
|
|
166
196
|
async function getMetadata(buffer) {
|
|
167
197
|
const wasm = await getWasm();
|
|
@@ -195,7 +225,9 @@ async function isMeaningfulHdr(metadata) {
|
|
|
195
225
|
highQualityEncodeOptions,
|
|
196
226
|
isMeaningfulHdr,
|
|
197
227
|
isUltraHdr,
|
|
228
|
+
probeUltraHdr,
|
|
198
229
|
setLocation,
|
|
230
|
+
setWasmUrl,
|
|
199
231
|
smallSizeEncodeOptions,
|
|
200
232
|
validateMetadata
|
|
201
233
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -39,11 +39,15 @@ var smallSizeEncodeOptions = {
|
|
|
39
39
|
|
|
40
40
|
// src/index.ts
|
|
41
41
|
var location = "";
|
|
42
|
+
var wasmUrl = "";
|
|
42
43
|
var wasmInstance = null;
|
|
43
44
|
var initPromise = null;
|
|
44
45
|
function setLocation(newLocation) {
|
|
45
46
|
location = newLocation;
|
|
46
47
|
}
|
|
48
|
+
function setWasmUrl(url) {
|
|
49
|
+
wasmUrl = url;
|
|
50
|
+
}
|
|
47
51
|
function isWasmMetadataInstance(metadata) {
|
|
48
52
|
return typeof metadata === "object" && metadata !== null && "__wbg_ptr" in metadata;
|
|
49
53
|
}
|
|
@@ -72,15 +76,29 @@ async function getWasm() {
|
|
|
72
76
|
return initPromise;
|
|
73
77
|
}
|
|
74
78
|
initPromise = (async () => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
try {
|
|
80
|
+
const UltraHdrWasm = await import("open-ultrahdr-wasm");
|
|
81
|
+
if (wasmUrl) {
|
|
82
|
+
await UltraHdrWasm.default(wasmUrl);
|
|
83
|
+
} else if (location) {
|
|
84
|
+
const base = location.endsWith("/") ? location : `${location}/`;
|
|
85
|
+
const wasmPath = base + "open_ultrahdr_bg.wasm";
|
|
86
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
87
|
+
const fs = await import("fs");
|
|
88
|
+
const wasmBytes = await fs.promises.readFile(wasmPath);
|
|
89
|
+
await UltraHdrWasm.default(wasmBytes);
|
|
90
|
+
} else {
|
|
91
|
+
await UltraHdrWasm.default(wasmPath);
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
await UltraHdrWasm.default();
|
|
95
|
+
}
|
|
96
|
+
wasmInstance = UltraHdrWasm;
|
|
97
|
+
return wasmInstance;
|
|
98
|
+
} catch (err) {
|
|
99
|
+
initPromise = null;
|
|
100
|
+
throw err;
|
|
81
101
|
}
|
|
82
|
-
wasmInstance = UltraHdrWasm;
|
|
83
|
-
return wasmInstance;
|
|
84
102
|
})();
|
|
85
103
|
return initPromise;
|
|
86
104
|
}
|
|
@@ -88,6 +106,10 @@ async function isUltraHdr(buffer) {
|
|
|
88
106
|
const wasm = await getWasm();
|
|
89
107
|
return wasm.isUltraHdr(new Uint8Array(buffer));
|
|
90
108
|
}
|
|
109
|
+
async function probeUltraHdr(buffer) {
|
|
110
|
+
const wasm = await getWasm();
|
|
111
|
+
return wasm.probeUltraHdr(new Uint8Array(buffer));
|
|
112
|
+
}
|
|
91
113
|
async function decodeUltraHdr(id, buffer) {
|
|
92
114
|
const wasm = await getWasm();
|
|
93
115
|
return wasm.decodeUltraHdr(new Uint8Array(buffer));
|
|
@@ -107,12 +129,18 @@ async function encodeUltraHdr(id, sdrBuffer, hdrBuffer, options) {
|
|
|
107
129
|
new Float32Array(hdrBuffer),
|
|
108
130
|
wasmOpts
|
|
109
131
|
);
|
|
110
|
-
return result.buffer.slice(
|
|
132
|
+
return result.buffer.slice(
|
|
133
|
+
result.byteOffset,
|
|
134
|
+
result.byteOffset + result.byteLength
|
|
135
|
+
);
|
|
111
136
|
}
|
|
112
137
|
async function extractSdrBase(buffer) {
|
|
113
138
|
const wasm = await getWasm();
|
|
114
139
|
const result = wasm.extractSdrBase(new Uint8Array(buffer));
|
|
115
|
-
return result.buffer.slice(
|
|
140
|
+
return result.buffer.slice(
|
|
141
|
+
result.byteOffset,
|
|
142
|
+
result.byteOffset + result.byteLength
|
|
143
|
+
);
|
|
116
144
|
}
|
|
117
145
|
async function getMetadata(buffer) {
|
|
118
146
|
const wasm = await getWasm();
|
|
@@ -145,7 +173,9 @@ export {
|
|
|
145
173
|
highQualityEncodeOptions,
|
|
146
174
|
isMeaningfulHdr,
|
|
147
175
|
isUltraHdr,
|
|
176
|
+
probeUltraHdr,
|
|
148
177
|
setLocation,
|
|
178
|
+
setWasmUrl,
|
|
149
179
|
smallSizeEncodeOptions,
|
|
150
180
|
validateMetadata
|
|
151
181
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-ultrahdr",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "UltraHDR (ISO 21496-1) gain map support for JavaScript/TypeScript",
|
|
5
5
|
"author": "Adam Silverstein",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
"homepage": "https://github.com/adamsilverstein/lib-open-ultrahdr",
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
|
-
"url": "https://github.com/adamsilverstein/lib-open-ultrahdr.git",
|
|
18
|
+
"url": "git+https://github.com/adamsilverstein/lib-open-ultrahdr.git",
|
|
19
19
|
"directory": "js"
|
|
20
20
|
},
|
|
21
21
|
"bugs": {
|
|
22
22
|
"url": "https://github.com/adamsilverstein/lib-open-ultrahdr/issues"
|
|
23
23
|
},
|
|
24
24
|
"engines": {
|
|
25
|
-
"node": ">=
|
|
25
|
+
"node": ">=20.0.0"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"src",
|
|
@@ -52,11 +52,12 @@
|
|
|
52
52
|
"lint:fix": "eslint src __tests__ --fix"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"open-ultrahdr-wasm": "^0.1.
|
|
55
|
+
"open-ultrahdr-wasm": "^0.1.2"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@eslint/js": "^9.0.0",
|
|
59
|
-
"@
|
|
59
|
+
"@types/node": "^25.1.0",
|
|
60
|
+
"@vitest/coverage-v8": "^4.0.0",
|
|
60
61
|
"eslint": "^9.0.0",
|
|
61
62
|
"eslint-config-prettier": "^9.0.0",
|
|
62
63
|
"tsup": "^8.0.0",
|
|
@@ -64,7 +65,7 @@
|
|
|
64
65
|
"typescript-eslint": "^8.0.0",
|
|
65
66
|
"vite-plugin-top-level-await": "^1.6.0",
|
|
66
67
|
"vite-plugin-wasm": "^3.5.0",
|
|
67
|
-
"vitest": "^
|
|
68
|
+
"vitest": "^4.0.0"
|
|
68
69
|
},
|
|
69
70
|
"publishConfig": {
|
|
70
71
|
"access": "public"
|
package/src/index.ts
CHANGED
|
@@ -22,7 +22,13 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
// Re-export types
|
|
25
|
-
export type {
|
|
25
|
+
export type {
|
|
26
|
+
ItemId,
|
|
27
|
+
GainMapMetadata,
|
|
28
|
+
UltraHdrDecodeResult,
|
|
29
|
+
UltraHdrEncodeOptions,
|
|
30
|
+
UltraHdrProbeResult,
|
|
31
|
+
} from './types';
|
|
26
32
|
|
|
27
33
|
export {
|
|
28
34
|
ColorGamut,
|
|
@@ -32,7 +38,13 @@ export {
|
|
|
32
38
|
smallSizeEncodeOptions,
|
|
33
39
|
} from './types';
|
|
34
40
|
|
|
35
|
-
import type {
|
|
41
|
+
import type {
|
|
42
|
+
ItemId,
|
|
43
|
+
GainMapMetadata,
|
|
44
|
+
UltraHdrDecodeResult,
|
|
45
|
+
UltraHdrEncodeOptions,
|
|
46
|
+
UltraHdrProbeResult,
|
|
47
|
+
} from './types';
|
|
36
48
|
|
|
37
49
|
import { defaultEncodeOptions } from './types';
|
|
38
50
|
|
|
@@ -62,6 +74,7 @@ interface WasmGainMapMetadata {
|
|
|
62
74
|
interface UltraHdrWasmModule {
|
|
63
75
|
default: (moduleOrPath?: string | URL | Response | BufferSource) => Promise<unknown>;
|
|
64
76
|
isUltraHdr: (buffer: Uint8Array) => boolean;
|
|
77
|
+
probeUltraHdr: (buffer: Uint8Array) => UltraHdrProbeResult;
|
|
65
78
|
decodeUltraHdr: (buffer: Uint8Array) => UltraHdrDecodeResult;
|
|
66
79
|
encodeUltraHdr: (
|
|
67
80
|
sdrBuffer: Uint8Array,
|
|
@@ -85,6 +98,13 @@ interface UltraHdrWasmModule {
|
|
|
85
98
|
*/
|
|
86
99
|
let location = '';
|
|
87
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Direct URL or data URL for the WASM module.
|
|
103
|
+
* When set, this is passed directly to the WASM init function,
|
|
104
|
+
* bypassing the location prefix + filename construction.
|
|
105
|
+
*/
|
|
106
|
+
let wasmUrl = '';
|
|
107
|
+
|
|
88
108
|
/**
|
|
89
109
|
* Cached WASM module instance.
|
|
90
110
|
*/
|
|
@@ -113,6 +133,26 @@ export function setLocation(newLocation: string): void {
|
|
|
113
133
|
location = newLocation;
|
|
114
134
|
}
|
|
115
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Sets a direct URL (including data URLs) for the WASM module.
|
|
138
|
+
*
|
|
139
|
+
* Use this when the WASM binary is inlined as a base64 data URL
|
|
140
|
+
* at build time. Unlike `setLocation`, this passes the URL directly
|
|
141
|
+
* to the WASM init function without appending a filename.
|
|
142
|
+
*
|
|
143
|
+
* @param url - Direct URL or data URL for the WASM binary.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* // With a build tool that inlines WASM as base64 data URLs:
|
|
148
|
+
* import wasmDataUrl from 'open-ultrahdr-wasm/pkg/open_ultrahdr_bg.wasm';
|
|
149
|
+
* setWasmUrl(wasmDataUrl);
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export function setWasmUrl(url: string): void {
|
|
153
|
+
wasmUrl = url;
|
|
154
|
+
}
|
|
155
|
+
|
|
116
156
|
/**
|
|
117
157
|
* Checks if metadata is a WASM class instance (has __wbg_ptr property).
|
|
118
158
|
*/
|
|
@@ -159,21 +199,40 @@ async function getWasm(): Promise<UltraHdrWasmModule> {
|
|
|
159
199
|
}
|
|
160
200
|
|
|
161
201
|
initPromise = (async () => {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
202
|
+
try {
|
|
203
|
+
// Dynamic import of the WASM package
|
|
204
|
+
const UltraHdrWasm = (await import('open-ultrahdr-wasm')) as unknown as UltraHdrWasmModule;
|
|
205
|
+
|
|
206
|
+
// Initialize WASM module.
|
|
207
|
+
// Priority: wasmUrl (direct URL/data URL) > location (path prefix) > default
|
|
208
|
+
if (wasmUrl) {
|
|
209
|
+
// Direct URL (e.g. inlined base64 data URL from build tool)
|
|
210
|
+
await UltraHdrWasm.default(wasmUrl);
|
|
211
|
+
} else if (location) {
|
|
212
|
+
const base = location.endsWith('/') ? location : `${location}/`;
|
|
213
|
+
const wasmPath = base + 'open_ultrahdr_bg.wasm';
|
|
214
|
+
|
|
215
|
+
// In Node.js, load the WASM file from the filesystem
|
|
216
|
+
// instead of using fetch which doesn't work with file:// URLs
|
|
217
|
+
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
|
|
218
|
+
const fs = await import('fs');
|
|
219
|
+
const wasmBytes = await fs.promises.readFile(wasmPath);
|
|
220
|
+
await UltraHdrWasm.default(wasmBytes);
|
|
221
|
+
} else {
|
|
222
|
+
// In browser, use the URL directly
|
|
223
|
+
await UltraHdrWasm.default(wasmPath);
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
// Let the WASM module use its default URL resolution (import.meta.url)
|
|
227
|
+
await UltraHdrWasm.default();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
wasmInstance = UltraHdrWasm as unknown as UltraHdrWasmModule;
|
|
231
|
+
return wasmInstance;
|
|
232
|
+
} catch (err) {
|
|
233
|
+
initPromise = null;
|
|
234
|
+
throw err;
|
|
173
235
|
}
|
|
174
|
-
|
|
175
|
-
wasmInstance = UltraHdrWasm as unknown as UltraHdrWasmModule;
|
|
176
|
-
return wasmInstance;
|
|
177
236
|
})();
|
|
178
237
|
|
|
179
238
|
return initPromise;
|
|
@@ -201,6 +260,41 @@ export async function isUltraHdr(buffer: ArrayBuffer): Promise<boolean> {
|
|
|
201
260
|
return wasm.isUltraHdr(new Uint8Array(buffer));
|
|
202
261
|
}
|
|
203
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Probes an image to check if it's UltraHDR and extracts component information.
|
|
265
|
+
*
|
|
266
|
+
* This function efficiently validates if an image is UltraHDR by checking for
|
|
267
|
+
* required components (primary image, gain map, metadata) without full decoding.
|
|
268
|
+
* Returns structured results useful for batch processing and filtering.
|
|
269
|
+
*
|
|
270
|
+
* Unlike `isUltraHdr`, this function provides detailed information about what
|
|
271
|
+
* was found, making it useful for diagnostics and filtering workflows.
|
|
272
|
+
*
|
|
273
|
+
* @param buffer - Image file contents.
|
|
274
|
+
* @return Probe result with detailed component information.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* const buffer = await file.arrayBuffer();
|
|
279
|
+
* const result = await probeUltraHdr(buffer);
|
|
280
|
+
*
|
|
281
|
+
* if (result.isValid) {
|
|
282
|
+
* console.log('UltraHDR image:', result.width, 'x', result.height);
|
|
283
|
+
* console.log('HDR capacity:', result.hdrCapacity, 'stops');
|
|
284
|
+
* console.log('Gain map:', result.gainMapWidth, 'x', result.gainMapHeight);
|
|
285
|
+
* } else {
|
|
286
|
+
* // Diagnose why it's not a valid UltraHDR
|
|
287
|
+
* if (!result.hasPrimaryImage) console.log('Not a valid JPEG');
|
|
288
|
+
* if (!result.hasGainMap) console.log('Missing gain map');
|
|
289
|
+
* if (!result.hasMetadata) console.log('Missing HDR metadata');
|
|
290
|
+
* }
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
export async function probeUltraHdr(buffer: ArrayBuffer): Promise<UltraHdrProbeResult> {
|
|
294
|
+
const wasm = await getWasm();
|
|
295
|
+
return wasm.probeUltraHdr(new Uint8Array(buffer));
|
|
296
|
+
}
|
|
297
|
+
|
|
204
298
|
/**
|
|
205
299
|
* Decodes an UltraHDR image, extracting all components.
|
|
206
300
|
*
|
|
@@ -281,7 +375,10 @@ export async function encodeUltraHdr(
|
|
|
281
375
|
);
|
|
282
376
|
|
|
283
377
|
// Ensure we return a proper ArrayBuffer (not SharedArrayBuffer)
|
|
284
|
-
return result.buffer.slice(
|
|
378
|
+
return result.buffer.slice(
|
|
379
|
+
result.byteOffset,
|
|
380
|
+
result.byteOffset + result.byteLength
|
|
381
|
+
) as ArrayBuffer;
|
|
285
382
|
}
|
|
286
383
|
|
|
287
384
|
/**
|
|
@@ -306,7 +403,10 @@ export async function extractSdrBase(buffer: ArrayBuffer): Promise<ArrayBuffer>
|
|
|
306
403
|
const wasm = await getWasm();
|
|
307
404
|
const result = wasm.extractSdrBase(new Uint8Array(buffer));
|
|
308
405
|
// Ensure we return a proper ArrayBuffer (not SharedArrayBuffer)
|
|
309
|
-
return result.buffer.slice(
|
|
406
|
+
return result.buffer.slice(
|
|
407
|
+
result.byteOffset,
|
|
408
|
+
result.byteOffset + result.byteLength
|
|
409
|
+
) as ArrayBuffer;
|
|
310
410
|
}
|
|
311
411
|
|
|
312
412
|
/**
|
package/src/types.ts
CHANGED
|
@@ -38,6 +38,44 @@ export interface GainMapMetadata {
|
|
|
38
38
|
hdrCapacityMax: number;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Result of probing an image to check if it's UltraHDR.
|
|
43
|
+
*
|
|
44
|
+
* Provides detailed information about what components were found
|
|
45
|
+
* without fully decoding the image. Useful for batch processing and filtering.
|
|
46
|
+
*/
|
|
47
|
+
export interface UltraHdrProbeResult {
|
|
48
|
+
/** Whether the image is a valid UltraHDR image (has all required components) */
|
|
49
|
+
isValid: boolean;
|
|
50
|
+
|
|
51
|
+
/** Whether a primary JPEG image was found */
|
|
52
|
+
hasPrimaryImage: boolean;
|
|
53
|
+
|
|
54
|
+
/** Whether a gain map image was found */
|
|
55
|
+
hasGainMap: boolean;
|
|
56
|
+
|
|
57
|
+
/** Whether gain map metadata (XMP) was found */
|
|
58
|
+
hasMetadata: boolean;
|
|
59
|
+
|
|
60
|
+
/** Primary image width in pixels (0 if not found) */
|
|
61
|
+
width: number;
|
|
62
|
+
|
|
63
|
+
/** Primary image height in pixels (0 if not found) */
|
|
64
|
+
height: number;
|
|
65
|
+
|
|
66
|
+
/** Gain map width in pixels (0 if not found) */
|
|
67
|
+
gainMapWidth: number;
|
|
68
|
+
|
|
69
|
+
/** Gain map height in pixels (0 if not found) */
|
|
70
|
+
gainMapHeight: number;
|
|
71
|
+
|
|
72
|
+
/** HDR capacity (max additional stops of dynamic range), 0 if not found */
|
|
73
|
+
hdrCapacity: number;
|
|
74
|
+
|
|
75
|
+
/** Metadata version string (empty if not found) */
|
|
76
|
+
metadataVersion: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
41
79
|
/**
|
|
42
80
|
* Result of decoding an UltraHDR image.
|
|
43
81
|
*/
|