extractChunks function

List<Map<String, dynamic>> extractChunks(
  1. Uint8List data
)

Implementation

List<Map<String, dynamic>> extractChunks(Uint8List data) {
  if (data[0] != 0x89) throw ArgumentError('Invalid .png file header');
  if (data[1] != 0x50) throw ArgumentError('Invalid .png file header');
  if (data[2] != 0x4E) throw ArgumentError('Invalid .png file header');
  if (data[3] != 0x47) throw ArgumentError('Invalid .png file header');
  if (data[4] != 0x0D) {
    throw ArgumentError(
        'Invalid .png file header: possibly caused by DOS-Unix line ending conversion?');
  }
  if (data[5] != 0x0A) {
    throw ArgumentError(
        'Invalid .png file header: possibly caused by DOS-Unix line ending conversion?');
  }
  if (data[6] != 0x1A) throw ArgumentError('Invalid .png file header');
  if (data[7] != 0x0A) {
    throw ArgumentError(
        'Invalid .png file header: possibly caused by DOS-Unix line ending conversion?');
  }

  var ended = false;
  var chunks = <Map<String, dynamic>>[];
  var idx = 8;

  while (idx < data.length) {
    // Read the length of the current chunk,
    // which is stored as a Uint32.
    uint8[3] = data[idx++];
    uint8[2] = data[idx++];
    uint8[1] = data[idx++];
    uint8[0] = data[idx++];

    // Chunk includes name/type for CRC check (see below).
    var length = uint32[0] + 4;
    var chunk = Uint8List(length);
    chunk[0] = data[idx++];
    chunk[1] = data[idx++];
    chunk[2] = data[idx++];
    chunk[3] = data[idx++];

    // Get the name in ASCII for identification.
    var name = (String.fromCharCode(chunk[0]) +
        String.fromCharCode(chunk[1]) +
        String.fromCharCode(chunk[2]) +
        String.fromCharCode(chunk[3]));

    // The IHDR header MUST come first.
    if (chunks.isEmpty && name != 'IHDR') {
      throw UnsupportedError('IHDR header missing');
    }

    // The IEND header marks the end of the file,
    // so on discovering it break out of the loop.
    if (name == 'IEND') {
      ended = true;
      chunks.add({
        'name': name,
        'data': Uint8List(0),
      });

      break;
    }

    // Read the contents of the chunk out of the main buffer.
    for (var i = 4; i < length; i++) {
      chunk[i] = data[idx++];
    }

    // Read out the CRC value for comparison.
    // It's stored as an Int32.
    uint8[3] = data[idx++];
    uint8[2] = data[idx++];
    uint8[1] = data[idx++];
    uint8[0] = data[idx++];

    var crcActual = int32[0];
    var crcExpect = Crc32.getCrc32(chunk);
    if (crcExpect != crcActual) {
      throw UnsupportedError(
          'CRC values for $name header do not match, PNG file is likely corrupted');
    }

    // The chunk data is now copied to remove the 4 preceding
    // bytes used for the chunk name/type.

    var chunkData = Uint8List.fromList(chunk.sublist(4));

    chunks.add({'name': name, 'data': chunkData});
  }

  if (!ended) {
    throw UnsupportedError(
        '.png file ended prematurely: no IEND header was found');
  }

  return chunks;
}