decodeRadianceHdr function
Decodes a Radiance .hdr (RGBE, flat or run-length encoded, -Y w +X h
orientation) into linear RGB floats.
Implementation
HdrImage decodeRadianceHdr(Uint8List bytes) {
var at = 0;
String line() {
final start = at;
while (at < bytes.length && bytes[at] != 0x0A) {
at++;
}
final text = String.fromCharCodes(bytes, start, at);
at++;
return text;
}
final magic = line();
if (!magic.startsWith('#?')) {
throw const FormatException('Not a Radiance HDR file.');
}
var format = '';
while (true) {
final l = line();
if (l.isEmpty) break;
if (l.startsWith('FORMAT=')) format = l.substring(7);
if (at >= bytes.length) {
throw const FormatException('The HDR header never ends.');
}
}
if (format.isNotEmpty && format != '32-bit_rle_rgbe') {
throw FormatException('Unsupported HDR format "$format".');
}
final size = RegExp(r'^-Y (\d+) \+X (\d+)$').firstMatch(line().trim());
if (size == null) {
throw const FormatException('Only "-Y h +X w" HDR images are read.');
}
final height = int.parse(size.group(1)!), width = int.parse(size.group(2)!);
final rgbe = Uint8List(width * height * 4);
final scan = Uint8List(width * 4);
for (var y = 0; y < height; y++) {
final rle =
width >= 8 &&
width < 32768 &&
at + 4 <= bytes.length &&
bytes[at] == 2 &&
bytes[at + 1] == 2 &&
(bytes[at + 2] & 0x80) == 0;
if (rle) {
at += 4;
for (var c = 0; c < 4; c++) {
var x = 0;
while (x < width) {
var count = bytes[at++];
if (count > 128) {
count -= 128;
final value = bytes[at++];
for (var i = 0; i < count; i++) {
scan[(x++) * 4 + c] = value;
}
} else {
for (var i = 0; i < count; i++) {
scan[(x++) * 4 + c] = bytes[at++];
}
}
}
}
} else {
for (var i = 0; i < width * 4; i++) {
scan[i] = bytes[at++];
}
}
rgbe.setRange(y * width * 4, (y + 1) * width * 4, scan);
}
final rgb = Float32List(width * height * 3);
for (var i = 0; i < width * height; i++) {
final e = rgbe[i * 4 + 3];
if (e == 0) continue;
final f = math.pow(2, e - 136).toDouble();
rgb[i * 3] = rgbe[i * 4] * f;
rgb[i * 3 + 1] = rgbe[i * 4 + 1] * f;
rgb[i * 3 + 2] = rgbe[i * 4 + 2] * f;
}
return HdrImage(width, height, rgb);
}