h264: Intra_8x8 luma (High profile) — pre-filter + 3 modes (V/H/DC) #21

Merged
marfrit merged 1 commits from noether/h264-intra-pred-8x8-luma into main 2026-05-25 07:51:58 +00:00

1 Commits

Author SHA1 Message Date
claude-noether 8bc6d27ea7 h264: Intra_8x8 luma prediction (High profile) — pre-filter + 3 modes
Adds the High-profile Intra_8x8 luma primitive set.  Per H.264
§8.3.2.1, this is distinct from Intra_4x4 in two ways:

  1. REFERENCE SAMPLE PRE-FILTER (§8.3.2.1.1).  The 25 raw neighbour
     samples are smoothed with a 1-2-1 filter BEFORE prediction.
     Spec-defined boundary handling at corners and the right edge:
       - top-left filt'd: (top[0] + 2*tl + left[0] + 2) >> 2
       - top[0] filt'd:   (tl + 2*t[0] + t[1] + 2) >> 2
       - top[i] for 1..14: (t[i-1] + 2*t[i] + t[i+1] + 2) >> 2
       - top[15] filt'd:  (t[14] + 3*t[15] + 2) >> 2  ← 3× boundary
       - left analogous, with l[7] using 3× boundary.

  2. SCALE.  All 9 prediction modes operate at 8x8 on the filtered
     samples (Intra_4x4 is 4x4 on raw samples).

This PR ships the pre-filter + the 3 simple modes (V, H, DC):

  - Mode 0 Vertical (§8.3.2.1.2): pred[r,c] = filt_top[c]
  - Mode 1 Horizontal (§8.3.2.1.3): pred[r,c] = filt_left[r]
  - Mode 2 DC (§8.3.2.1.4): ((sum_filt_top[0..7] + sum_filt_left[0..7]
                              + 8) >> 4) broadcast

The 6 directional modes (DDL, DDR, VR, HD, VL, HU at 8x8 per
§8.3.2.1.5..§8.3.2.1.10) follow in a separate PR.  They use the
same filtered samples; only the per-cell formula differs.

Test design (tests/test_intra_pred_8x8_luma.c):

  - 3 uniform-context tests, one per mode (sanity).
  - 2 gradient tests that exercise the pre-filter's interior +
    boundary cases:
      * Vertical with top = 0..15: spec arithmetic gives filtered
        top[c] = c for c in 0..7 (gradient input → identity through
        the 1-2-1 filter on the interior; boundaries arithmetically
        verify too).  Test expects pred[r,c] = c.
      * Horizontal with left = 0..7: same arithmetic chain on the
        left col.  Test expects pred[r,c] = r.

Verified on hertz:

  $ ./build/test_intra_pred_8x8_luma
    Vertical (mode 0, uniform top) PASS
    Horizontal (mode 1, uniform left) PASS
    DC (mode 2, uniform)           PASS
    Vertical (mode 0, gradient)    PASS (filtered gradient)
    Horizontal (mode 1, gradient)  PASS (filtered gradient)

  ALL Intra_8x8 luma PASS (3 modes — V, H, DC)

The pre-filter being right first try is meaningful — the boundary
samples use a 3× weight rather than 2× (filt[top 15] = (t[14] +
3*t[15] + 2) >> 2), which is easy to forget when transcribing.  The
gradient test would have surfaced any boundary mistake immediately.

Combined intra-prediction primitive coverage after this PR:
  Intra_4x4 luma   ✓ (9 modes, PR #12)
  Intra_16x16 luma ✓ (4 modes, PR #13)
  Intra_8x8 chroma ✓ (4 modes, PR #14)
  Intra_8x8 luma   △ (3 of 9 modes — V, H, DC ✓; DDL/DDR/VR/HD/VL/HU pending)

The 6 remaining Intra_8x8 luma directional modes are spec-mechanical
follow-ups; each is a ~30-line formula per §8.3.2.1.5+.
2026-05-25 09:35:49 +02:00