A scan is a run of MCUs, each holding several blocks interleaved by component. Today you decode a whole MCU, keeping each component's DC predictor independent.
Decode all blocks of one MCU in component-interleaved order, with a separate DC predictor per component.
A scan is a sequence of MCUs, and each MCU is decoded by walking the components in scan order and, for each, decoding its H*V blocks with decodeBlock. For 4:2:0 that is four luma blocks, then one Cb block, then one Cr block - six blocks, in that exact interleaved order. The blocks come out still in zig-zag coefficient form; assembling them into pixels is a later chapter’s job.
The subtlety is that each component carries its own DC predictor. Luma’s predictor threads through all four of its blocks - differences +5, +2, 0, 0 give DCs 5, 7, 7, 7 - while Cb and Cr each have a separate predictor that started at 0, so a Cb difference of +4 yields a DC of 4 regardless of what luma did. Sharing one predictor across components is a bug that tints the whole image; the spec pins the independence. Looping decodeMCU across the MCU grid you computed in the frame chapter walks the entire scan.
// a per-component decode view (distinct from the scan header's ScanComp):// type mcuComp struct{ blocks int; dc, ac *HuffTable; pred int }// for each component in scan order, for each of its `blocks` blocks:// block := decodeBlock(r, comp.dc, comp.ac, &comp.pred)// each component owns its own pred; luma decodes 4 blocks, chroma 1 each.func decodeMCU(r *BitReader, comps []mcuComp) [][64]int { }