You can encode high dynamic range images into a single JPEG with an embedded gain map using the encoding utilities. This is useful for creating compatible HDR files from .exr or .hdr sources.
Workflow:
- Load the HDR image (e.g., using Three.js
EXRLoader). - Use
findTextureMinMax to find the RAW RGB max value. - Call
encode() to generate the SDR and Gain map buffers. - Compress the buffers using
compress(). - Use
encodeJPEGMetadata() to combine the compressed images and metadata into a single Uint8Array JPEG.
Note: You must manually call .dispose() on the encodingResult.gainMap and encodingResult.sdr objects to prevent memory leaks.
import { compress, encode, findTextureMinMax } from '@monogrid/gainmap-js/encode'
import { encodeJPEGMetadata } from '@monogrid/gainmap-js/libultrahdr'
import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js'
// load an HDR file
const loader = new EXRLoader()
const image = await loader.loadAsync('image.exr')
// find RAW RGB Max value of a texture
const textureMax = findTextureMinMax(image)
// Encode the gainmap
const encodingResult = encode({
image,
// this will encode the full HDR range
maxContentBoost: Math.max.apply(this, textureMax)
})
// obtain the RAW RGBA SDR buffer and create an ImageData
const sdrImageData = new ImageData(encodingResult.sdr.toArray(), encodingResult.sdr.width, encodingResult.sdr.height)
// obtain the RAW RGBA Gain map buffer and create an ImageData
const gainMapImageData = new ImageData(encodingResult.gainMap.toArray(), encodingResult.gainMap.width, encodingResult.gainMap.height)
// parallel compress the RAW buffers into the specified mimeType
const mimeType = 'image/jpeg'
const quality = 0.9
const [sdr, gainMap] = await Promise.all([
compress({
source: sdrImageData,
mimeType,
quality,
flipY: true // output needs to be flipped
}),
compress({
source: gainMapImageData,
mimeType,
quality,
flipY: true // output needs to be flipped
})
])
// obtain the metadata which will be embedded into
// and XMP tag inside the final JPEG file
const metadata = encodingResult.getMetadata()
// embed the compressed images + metadata into a single
// JPEG file
const jpeg = encodeJPEGMetadata({
...encodingResult,
...metadata,
sdr,
gainMap
})
// `jpeg` will be an `Uint8Array` which can be saved somewhere
// encoder must be manually disposed
// when no longer needed
encodingResult.gainMap.dispose()
encodingResult.sdr.dispose()