readImageInfo function

ImageInfo? readImageInfo(
  1. List<int> bytes
)

Dimensions and transparency of a PNG or JPEG, or null if bytes is neither.

Hand-rolled rather than a dependency, on the same reasoning as the copy in cux_ship_play: this reads a handful of integers out of a header, and package:image is a decoder for a dozen formats. The check it enables is worth having because Apple validates screenshots at submission — after they have already been uploaded one at a time.

Implementation

ImageInfo? readImageInfo(List<int> bytes) {
  // PNG: IHDR is required to be the first chunk, so everything needed sits at
  // a fixed offset — 8 signature, 4 length, 4 type, width, height, bit depth,
  // colour type.
  if (bytes.length >= 26 &&
      bytes[0] == 0x89 &&
      bytes[1] == 0x50 &&
      bytes[2] == 0x4E &&
      bytes[3] == 0x47) {
    final colourType = bytes[25];
    // 4 is greyscale+alpha and 6 is RGBA. A tRNS chunk makes types 0 and 2
    // transparent too, so it counts as alpha even though the colour type
    // alone does not say so.
    final hasAlphaChannel = colourType == 4 || colourType == 6;
    return ImageInfo(
      width: _be32(bytes, 16),
      height: _be32(bytes, 20),
      hasAlpha: hasAlphaChannel || _hasTrnsChunk(bytes),
    );
  }

  // JPEG: walk the marker segments to the start-of-frame, the only one that
  // carries the dimensions. JPEG has no alpha channel at all.
  if (bytes.length >= 4 && bytes[0] == 0xFF && bytes[1] == 0xD8) {
    var i = 2;
    while (i + 9 < bytes.length) {
      if (bytes[i] != 0xFF) {
        i++;
        continue;
      }
      final marker = bytes[i + 1];
      // Padding, and the standalone markers that carry no length field.
      if (marker == 0xFF ||
          marker == 0x01 ||
          (marker >= 0xD0 && marker <= 0xD9)) {
        i += 2;
        continue;
      }
      // Every SOFn except the three that are not frame headers at all: DHT
      // (C4), JPG (C8) and DAC (CC).
      final isFrameHeader =
          marker >= 0xC0 &&
          marker <= 0xCF &&
          marker != 0xC4 &&
          marker != 0xC8 &&
          marker != 0xCC;
      if (isFrameHeader) {
        return ImageInfo(
          width: _be16(bytes, i + 7),
          height: _be16(bytes, i + 5),
          hasAlpha: false,
        );
      }
      final length = _be16(bytes, i + 2);
      // A segment shorter than its own length field means the file is corrupt;
      // stop rather than loop forever on it.
      if (length < 2) {
        return null;
      }
      i += 2 + length;
    }
  }
  return null;
}