Task 338: .JVX File Format
Task 338: .JVX File Format
File Format Specifications for .JXL
The .JXL file format is specified in ISO/IEC 18181-2:2024 for the file container and ISO/IEC 18181-1:2024 for the codestream. The container is based on ISO BMFF (box structure), and the codestream is a bitstream for the compressed image data. The format supports lossy and lossless compression, HDR, wide color gamut, animation, alpha channels, and metadata.
- List of all properties of this file format intrinsic to its file structure:
- Signature: Fixed bytes for identification (container: 00 00 00 0C 4A 58 4C 20 0D 0A 87 0A; codestream: FF 0A).
- Level: u8 value from 'jxll' box (default 5 if absent).
- Presence of Exif metadata: Boolean, from 'Exif' box with TIFF header offset (u32) and payload.
- Presence of XMP metadata: Boolean, from 'xml ' box with XML data.
- Presence of JUMBF metadata: Boolean, from 'jumb' box (superbox with description and content).
- Image width: u32 or u64 from codestream image header (SizeHeader bundle).
- Image height: u32 or u64 from codestream image header (SizeHeader bundle).
- Bit depth: u8 bits per sample from codestream image header.
- Floating point samples: Boolean, from codestream image header.
- Number of color channels: u32 from codestream image header (typically 1 for grayscale, 3 for RGB).
- Color space: Enum or ICC profile from color encoding bundle (e.g., XYB, sRGB, with primaries, white point, transfer function).
- Presence of alpha channel: Boolean, from codestream image header (extra channels).
- Premultiplied alpha: Boolean, from extra channel info.
- Orientation: u8 from codestream header (1-8, similar to Exif).
- Animation parameters: Boolean have_animation, number of frames, loop count, tick unit, frame durations (u32 multiples of tick).
- Intensity target: f32 for HDR tone mapping.
- Tone mapping parameters: Min/max lift, gain, offset for HDR.
- Preview presence: Boolean have_preview from header.
- Compression mode: VarDCT (lossy) or Modular (lossless) inferred from frame headers.
- Progressive levels: u8 number of LF levels for progressive decoding.
- Two direct download links for .JXL files:
- https://people.csail.mit.edu/ericchan/hdr/jxl_images/20140606_102418_IMGP0297.jxl
- https://raw.githubusercontent.com/libjxl/testdata/main/jxl/flower/flower.png.im_q85.jxl
- Ghost blog embedded HTML JavaScript for drag and drop .JXL file to dump properties:
Drag and Drop .JXL File Here
Note: The codestream parsing is simplified for demonstration; full decoding requires bit-level parsing of bundles as per ISO/IEC 18181-1.
- Python class for .JXL:
import struct
class JXLHandler:
def __init__(self, filename):
self.filename = filename
self.data = None
self.properties = {}
def open(self):
with open(self.filename, 'rb') as f:
self.data = f.read()
def decode_read(self):
if not self.data:
return
offset = 0
if self.data[offset:12] == b'\x00\x00\x00\x0cJXL \r\n\x87\n':
self.properties['Signature'] = 'Container (ISO BMFF)'
offset = 12
elif self.data[offset:2] == b'\xff\x0a':
self.properties['Signature'] = 'Naked Codestream'
offset = 2
else:
raise ValueError('Invalid JXL file')
while offset < len(self.data):
box_size, = struct.unpack('>I', self.data[offset:offset+4])
box_type = self.data[offset+4:offset+8].decode('ascii')
self.properties[f'Box {box_type} Size'] = box_size
if box_type == 'jxll':
level, = struct.unpack('>B', self.data[offset+8:offset+9])
self.properties['Level'] = level
elif box_type == 'Exif':
self.properties['Presence of Exif metadata'] = True
elif box_type == 'xml ':
self.properties['Presence of XMP metadata'] = True
elif box_type == 'jumb':
self.properties['Presence of JUMBF metadata'] = True
elif box_type in ('jxlc', 'jxlp'):
self.properties['Codestream found'] = True
cs_offset = offset + 8
cs_props = self.parse_codestream(self.data[cs_offset:])
self.properties.update(cs_props)
if box_size == 0:
break
offset += box_size
def parse_codestream(self, data):
props = {}
offset = 2 # Skip FF0A
# Simplified SizeHeader parsing (real is bit-packed)
# Assume: bool div8, u(3) ratio, if not div8 u(32) height, u(32) width
byte = data[offset]
div8 = byte & 1
ratio = (byte >> 1) & 7
offset += 1
height, = struct.unpack('>I', data[offset:offset+4])
width, = struct.unpack('>I', data[offset+4:offset+8])
props['Image height'] = height
props['Image width'] = width
# Add more parsing for bit depth, etc.
return props
def write(self, new_filename):
# Simplified: write back the same data
with open(new_filename, 'wb') as f:
f.write(self.data)
def print_properties(self):
for key, value in self.properties.items():
print(f'{key}: {value}')
# Example usage:
# handler = JXLHandler('example.jxl')
# handler.open()
# handler.decode_read()
# handler.print_properties()
# handler.write('output.jxl')
Note: Codestream parsing is simplified; full read/write requires complete bundle decoding.
- Java class for .JXL:
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class JXLHandler {
private String filename;
private byte[] data;
private java.util.Map<String, Object> properties = new java.util.HashMap<>();
public JXLHandler(String filename) {
this.filename = filename;
}
public void open() throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
data = fis.readAllBytes();
}
}
public void decodeRead() {
if (data == null) return;
int offset = 0;
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
if (bb.getInt(0) == 0x0000000C && bb.getInt(4) == 0x4A584C20 && bb.getInt(8) == 0x0D0A870A) {
properties.put("Signature", "Container (ISO BMFF)");
offset = 12;
} else if (bb.getShort(0) == (short)0xFF0A) {
properties.put("Signature", "Naked Codestream");
offset = 2;
} else {
throw new RuntimeException("Invalid JXL file");
}
while (offset < data.length) {
bb.position(offset);
int boxSize = bb.getInt();
String boxType = new String(data, offset + 4, 4);
properties.put("Box " + boxType + " Size", boxSize);
if (boxType.equals("jxll")) {
int level = data[offset + 8] & 0xFF;
properties.put("Level", level);
} else if (boxType.equals("Exif")) {
properties.put("Presence of Exif metadata", true);
} else if (boxType.equals("xml ")) {
properties.put("Presence of XMP metadata", true);
} else if (boxType.equals("jumb")) {
properties.put("Presence of JUMBF metadata", true);
} else if (boxType.equals("jxlc") || boxType.equals("jxlp")) {
properties.put("Codestream found", true);
java.util.Map<String, Object> csProps = parseCodestream(bb, offset + 8);
properties.putAll(csProps);
}
if (boxSize == 0) break;
offset += boxSize;
}
}
private java.util.Map<String, Object> parseCodestream(ByteBuffer bb, int start) {
java.util.Map<String, Object> props = new java.util.HashMap<>();
bb.position(start + 2); // Skip FF0A
// Simplified
int byteVal = bb.get() & 0xFF;
boolean div8 = (byteVal & 1) == 1;
int ratio = (byteVal >> 1) & 7;
int height = bb.getInt();
int width = bb.getInt();
props.put("Image height", height);
props.put("Image width", width);
return props;
}
public void write(String newFilename) throws IOException {
try (FileOutputStream fos = new FileOutputStream(newFilename)) {
fos.write(data);
}
}
public void printProperties() {
properties.forEach((k, v) -> System.out.println(k + ": " + v));
}
// Example usage:
// JXLHandler handler = new JXLHandler("example.jxl");
// handler.open();
// handler.decodeRead();
// handler.printProperties();
// handler.write("output.jxl");
}
Note: Codestream parsing simplified.
- JavaScript class for .JXL:
class JXLHandler {
constructor(filename) {
this.filename = filename;
this.data = null;
this.properties = {};
}
async open() {
// Assume node.js with fs
const fs = require('fs');
this.data = fs.readFileSync(this.filename);
}
decodeRead() {
if (!this.data) return;
let offset = 0;
if (this.data[0] === 0x00 && this.data[1] === 0x00 && this.data[2] === 0x00 && this.data[3] === 0x0C &&
this.data[4] === 0x4A && this.data[5] === 0x58 && this.data[6] === 0x4C && this.data[7] === 0x20) {
this.properties['Signature'] = 'Container (ISO BMFF)';
offset = 12;
} else if (this.data[0] === 0xFF && this.data[1] === 0x0A) {
this.properties['Signature'] = 'Naked Codestream';
offset = 2;
} else {
throw new Error('Invalid JXL file');
}
while (offset < this.data.length) {
let boxSize = (this.data[offset] << 24) | (this.data[offset+1] << 16) | (this.data[offset+2] << 8) | this.data[offset+3];
let boxType = String.fromCharCode(this.data[offset+4], this.data[offset+5], this.data[offset+6], this.data[offset+7]);
this.properties[`Box ${boxType} Size`] = boxSize;
if (boxType === 'jxll') {
this.properties['Level'] = this.data[offset+8];
} else if (boxType === 'Exif') {
this.properties['Presence of Exif metadata'] = true;
} else if (boxType === 'xml ') {
this.properties['Presence of XMP metadata'] = true;
} else if (boxType === 'jumb') {
this.properties['Presence of JUMBF metadata'] = true;
} else if (boxType === 'jxlc' || boxType === 'jxlp') {
this.properties['Codestream found'] = true;
let csProps = this.parseCodestream(this.data.slice(offset + 8));
Object.assign(this.properties, csProps);
}
if (boxSize === 0) break;
offset += boxSize;
}
}
parseCodestream(data) {
let props = {};
let offset = 2;
let byte = data[offset];
let div8 = byte & 1;
let ratio = (byte >> 1) & 7;
offset += 1;
let height = (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];
let width = (data[offset+4] << 24) | (data[offset+5] << 16) | (data[offset+6] << 8) | data[offset+7];
props['Image height'] = height;
props['Image width'] = width;
return props;
}
write(newFilename) {
const fs = require('fs');
fs.writeFileSync(newFilename, this.data);
}
printProperties() {
for (let [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
}
// Example:
// const handler = new JXLHandler('example.jxl');
// await handler.open();
// handler.decodeRead();
// handler.printProperties();
// handler.write('output.jxl');
Note: For node.js; codestream simplified.
- C class (using C++ for class):
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <string>
#include <cstdint>
class JXLHandler {
private:
std::string filename;
std::vector<uint8_t> data;
std::map<std::string, std::string> properties;
public:
JXLHandler(const std::string& fn) : filename(fn) {}
void open() {
std::ifstream file(filename, std::ios::binary);
if (file) {
file.seekg(0, std::ios::end);
size_t size = file.tellg();
file.seekg(0, std::ios::beg);
data.resize(size);
file.read(reinterpret_cast<char*>(data.data()), size);
}
}
void decodeRead() {
if (data.empty()) return;
size_t offset = 0;
if (data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00 && data[3] == 0x0C &&
data[4] == 0x4A && data[5] == 0x58 && data[6] == 0x4C && data[7] == 0x20) {
properties["Signature"] = "Container (ISO BMFF)";
offset = 12;
} else if (data[0] == 0xFF && data[1] == 0x0A) {
properties["Signature"] = "Naked Codestream";
offset = 2;
} else {
throw std::runtime_error("Invalid JXL file");
}
while (offset < data.size()) {
uint32_t boxSize = (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];
std::string boxType(1, data[offset+4]);
boxType += data[offset+5];
boxType += data[offset+6];
boxType += data[offset+7];
properties["Box " + boxType + " Size"] = std::to_string(boxSize);
if (boxType == "jxll") {
uint8_t level = data[offset+8];
properties["Level"] = std::to_string(level);
} else if (boxType == "Exif") {
properties["Presence of Exif metadata"] = "Yes";
} else if (boxType == "xml ") {
properties["Presence of XMP metadata"] = "Yes";
} else if (boxType == "jumb") {
properties["Presence of JUMBF metadata"] = "Yes";
} else if (boxType == "jxlc" || boxType == "jxlp") {
properties["Codestream found"] = "Yes";
auto csProps = parseCodestream(data, offset + 8);
properties.insert(csProps.begin(), csProps.end());
}
if (boxSize == 0) break;
offset += boxSize;
}
}
std::map<std::string, std::string> parseCodestream(const std::vector<uint8_t>& d, size_t start) {
std::map<std::string, std::string> props;
size_t offset = start + 2;
uint8_t byte = d[offset];
bool div8 = byte & 1;
int ratio = (byte >> 1) & 7;
offset += 1;
uint32_t height = (d[offset] << 24) | (d[offset+1] << 16) | (d[offset+2] << 8) | d[offset+3];
uint32_t width = (d[offset+4] << 24) | (d[offset+5] << 16) | (d[offset+6] << 8) | d[offset+7];
props["Image height"] = std::to_string(height);
props["Image width"] = std::to_string(width);
return props;
}
void write(const std::string& newFilename) {
std::ofstream file(newFilename, std::ios::binary);
file.write(reinterpret_cast<const char*>(data.data()), data.size());
}
void printProperties() {
for (const auto& p : properties) {
std::cout << p.first << ": " << p.second << std::endl;
}
}
};
// Example:
// JXLHandler handler("example.jxl");
// handler.open();
// handler.decodeRead();
// handler.printProperties();
// handler.write("output.jxl");
Note: Codestream parsing is simplified for illustration; full implementation requires handling bitstreams and all bundles per the spec.