decodeShBandRecord function
Decodes one SH Band Stream record's content: a u8 band followed by an
ordinary attribute stream.
expectedBand and expectedCount are what the chunk index promised. Both
are checked, because the index is the only thing that said this byte range
held band N: point a band-1 range at a band-2 record and the opcode still
matches, the bytes still decode, and assembly then strides fifteen
coefficients as nine — mixing one gaussian's colour into the next with
nothing raised anywhere.
Coefficients are returned as the bytes the producer stored, undecoded. What
those bytes mean is a rendering decision and does not belong to a container.
fileOffset is where content starts in the file, so a stream-codec
refusal can name the SH Band Stream's exact header byte. Zero is truthful
for callers holding detached record content.
Implementation
Uint8List? decodeShBandRecord(
Uint8List content, {
required int expectedBand,
required int expectedCount,
int fileOffset = 0,
}) {
final cursor = FourdgsCursor(content);
final band = cursor.u8();
if (band != expectedBand) {
throw FourdgsMalformedFile(
'the index points band $expectedBand at a record carrying band $band',
);
}
if (cursor.remaining < streamHeaderBytes) {
// A well-framed band record with no stream in it. Returning null here would
// present it to the assembler as a band the file simply does not have, and
// the coefficients would render as zeros — wrong, and quietly so.
throw FourdgsTruncatedFile('band $band has a record but no stream in it');
}
final streamOffset = fileOffset + cursor.pos;
final header = readStreamHeader(cursor);
final channels = shBandChannels[band];
if (channels == null) {
throw FourdgsMalformedFile(
'band $band is outside the 1-3 this version defines',
);
}
if (header.count != expectedCount) {
throw FourdgsMalformedFile(
'band $band carries ${header.count} gaussians, the chunk holds $expectedCount',
);
}
if (header.channels != channels) {
throw FourdgsMalformedFile(
'band $band declares ${header.channels} coefficients per gaussian, expected $channels',
);
}
final stream = decodeAttributeStreamBody(
cursor,
header,
streamOffset: streamOffset,
);
final out = Uint8List(stream.count * stream.channels);
for (int i = 0; i < out.length; i++) {
out[i] = stream.values[i] & 0xFF;
}
return out;
}