decodeRadianceHdr function

HdrImage decodeRadianceHdr(
  1. Uint8List bytes
)

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);
}