imageEncodingProblem function

String? imageEncodingProblem(
  1. ImageInfo image,
  2. StoreImageRules rules
)

What rules refuse about image's encoding, as a sentence to follow the file's name, or null when nothing does.

Returned rather than thrown because the two callers report differently — the App Store loader throws on the first problem, the Play checker collects them — and a check that picked one could only be used by that one.

Implementation

String? imageEncodingProblem(ImageInfo image, StoreImageRules rules) {
  if (image.hasAlpha && !rules.allowsAlpha) {
    return 'has an alpha channel; ${rules.store} refuses transparency — '
        '${rules.alphaRule}.\n'
        '  Remove it, and change nothing else about the image:\n'
        '    cux_ship screenshots flatten <path>\n'
        '  `sips` cannot do this while staying PNG — it writes RGBA whatever '
        'flags it\n'
        '  is given, so a round trip through it changes nothing.';
  }

  // **`> 8` rather than `!= 8`, and the difference is whether this is checking
  // a rule or inventing one.** Play asks for eight bits per channel by name —
  // "24-bit PNG" — and Apple, publishing no depth at all, has been observed
  // refusing sixteen at ingestion. Fewer than eight is
  // a greyscale or palettised PNG, whose palette entries are eight bits each —
  // so "24-bit" is arguably what it already is, no store has been seen to
  // refuse one, and failing it would be this package enforcing a rule with no
  // failure under it. That is the mistake the aspect-ratio note in
  // play_metadata.dart exists to refuse to make.
  //
  // **PNG only, and for the same reason.** Every justification under this
  // check is PNG's: Play states a depth for PNG and none for JPEG — "24-bit"
  // in "JPEG or 24-bit PNG (no alpha)" modifies the PNG, not the JPEG beside
  // it — the set Apple was observed refusing was a PNG, and `screenshots
  // flatten` decodes PNG and nothing else. Applied to a JPEG this quoted Play
  // for a rule Play does not state, offered a PNG refusal as evidence about a
  // JPEG, and named a remedy that throws (the CLI walks `.png`, so it would
  // skip the file, exit 0, and leave verify still refusing it) — a loop, and
  // the same shape as the greyscale-with-alpha gate found in review.
  //
  // A >8-bit JPEG is legal and essentially unproducible: baseline SOF0 is
  // 8-bit by definition, 12 needs an extended sequential or progressive frame,
  // and reading one needs the 12-bit decoder entry points, which Blink and
  // everything else on libjpeg's 8-bit API do not call. So it is a rule with
  // no observed failure and no working remedy. Measured and argued in
  // docs/design/store-image-rules.md.
  if (image.format == ImageFormat.png && image.bitDepth > 8) {
    return 'is ${image.bitDepth} bits per channel; ${rules.store} takes 8 — '
        '${rules.depthRule}.\n'
        '  Reduce it, and change nothing else about the image:\n'
        '    cux_ship screenshots flatten <path>\n'
        '  It rescales rather than truncating, so the picture survives. A '
        'macOS\n'
        '  `--no-chrome` capture is the usual source of a 16-bit screenshot.';
  }
  return null;
}