code wiki / (root) / nx_av1_intra.nx

nx_av1_intra.nx source

↩ module page · 240 lines · 7304 B

1// nx_av1_intra.nx -- AV1/AV2 non-directional intra prediction. 2// 3// Part of the residual path: before any coefficient is added, a block is 4// PREDICTED from its already-decoded above row and left column. These are the 5// modes that need no angle -- DC, vertical, horizontal, Paeth and the three 6// smooth variants -- and together they cover most real intra blocks. 7// 8// AVAILABILITY CHANGES THE FORMULA, NOT JUST THE INPUT. DC has FOUR distinct 9// cases: both edges present, only above, only left, and neither. With neither 10// it is not zero and not an average of nothing -- it is the mid-grey 11// 1 << (bitdepth - 1). A decoder that treats a missing edge as zeros predicts 12// black along the top and left of every frame, which looks like a vignette 13// rather than a bug. 14// 15// THE SMOOTH WEIGHTS SUM TO 256, NOT 255. Each smooth predictor blends a 16// weight w against (256 - w) and rounds with +128 >> 8 (or +256 >> 9 for the 17// two-axis form). Using 255 as the complement makes constant input predict 18// slightly-wrong constant output -- a fraction of a level per pixel, which 19// accumulates across a frame of intra blocks and shows as a gradient. 20// 21// PAETH PICKS, IT DOES NOT AVERAGE. It selects whichever of left, above or 22// above-left is nearest the linear estimate. The tie order matters: left 23// beats above, and above beats above-left. 24// 25// EVERY LOCAL IS DECLARED ONCE. nx_cc uses FLAT FUNCTION SCOPE -- re-declaring 26// a name inside a sibling branch desyncs its parser (proven here 2026-07-31: 27// a `let below` in two exclusive branches produced 'unexpected operator token 28// at expression start'). Params also travel in an array rather than as ten 29// positional arguments. 30// 31// genealogy_id: av1_spec_7_11_2_intra_prediction 32// lineage_id: nx_av1_intra_v1 33// license_tier: ORIGINAL 34 35import "nx_syscalls.nx" 36 37const NX_INTRA_DC: i64 = 0 38const NX_INTRA_V: i64 = 1 39const NX_INTRA_H: i64 = 2 40const NX_INTRA_PAETH: i64 = 3 41const NX_INTRA_SMOOTH: i64 = 4 42const NX_INTRA_SMOOTH_V: i64 = 5 43const NX_INTRA_SMOOTH_H: i64 = 6 44 45const NX_INTRA_P_ABOVELEFT: i64 = 0 46const NX_INTRA_P_W: i64 = 1 47const NX_INTRA_P_H: i64 = 2 48const NX_INTRA_P_HAVE_A: i64 = 3 49const NX_INTRA_P_HAVE_L: i64 = 4 50const NX_INTRA_P_BITDEPTH: i64 = 5 51 52func nx_intra_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 53 54func nx_intra_sm_weight(size: i64, i: i64) -> i64 { 55 if i < 0 { return 0 - 1 } 56 if i >= size { return 0 - 1 } 57 if size == 4 { 58 if i == 0 { return 255 } 59 if i == 1 { return 149 } 60 if i == 2 { return 85 } 61 return 64 62 } 63 if size == 8 { 64 if i == 0 { return 255 } 65 if i == 1 { return 197 } 66 if i == 2 { return 146 } 67 if i == 3 { return 105 } 68 if i == 4 { return 73 } 69 if i == 5 { return 50 } 70 if i == 6 { return 37 } 71 return 32 72 } 73 if size == 16 { 74 if i == 0 { return 255 } 75 if i == 1 { return 225 } 76 if i == 2 { return 196 } 77 if i == 3 { return 170 } 78 if i == 4 { return 145 } 79 if i == 5 { return 123 } 80 if i == 6 { return 102 } 81 if i == 7 { return 84 } 82 if i == 8 { return 68 } 83 if i == 9 { return 54 } 84 if i == 10 { return 43 } 85 if i == 11 { return 33 } 86 if i == 12 { return 26 } 87 if i == 13 { return 20 } 88 if i == 14 { return 17 } 89 return 16 90 } 91 return 0 - 1 92} 93 94func nx_intra_dc_value(above: *i64, left: *i64, p: *i64) -> i64 { 95 let w: i64 = p[NX_INTRA_P_W] 96 let h: i64 = p[NX_INTRA_P_H] 97 let have_a: i64 = p[NX_INTRA_P_HAVE_A] 98 let have_l: i64 = p[NX_INTRA_P_HAVE_L] 99 let bd: i64 = p[NX_INTRA_P_BITDEPTH] 100 if w <= 0 { return 0 - 1 } 101 if h <= 0 { return 0 - 1 } 102 if bd < 8 { return 0 - 1 } 103 104 var sum: i64 = 0 105 var i: i64 = 0 106 107 if have_a == 1 { 108 if have_l == 1 { 109 while i < w { sum = sum + above[i]; i = i + 1 } 110 i = 0 111 while i < h { sum = sum + left[i]; i = i + 1 } 112 return (sum + ((w + h) >> 1)) / (w + h) 113 } 114 while i < w { sum = sum + above[i]; i = i + 1 } 115 return (sum + (w >> 1)) / w 116 } 117 if have_l == 1 { 118 while i < h { sum = sum + left[i]; i = i + 1 } 119 return (sum + (h >> 1)) / h 120 } 121 return 1 << (bd - 1) 122} 123 124func nx_intra_paeth_pick(left: i64, above: i64, aboveleft: i64) -> i64 { 125 let base: i64 = left + above - aboveleft 126 let p_left: i64 = nx_intra_abs(base - left) 127 let p_above: i64 = nx_intra_abs(base - above) 128 let p_al: i64 = nx_intra_abs(base - aboveleft) 129 if p_left <= p_above { 130 if p_left <= p_al { return left } 131 } 132 if p_above <= p_al { return above } 133 return aboveleft 134} 135 136func nx_intra_predict(mode: i64, out: *i64, above: *i64, left: *i64, p: *i64) -> i64 { 137 let w: i64 = p[NX_INTRA_P_W] 138 let h: i64 = p[NX_INTRA_P_H] 139 let aboveleft: i64 = p[NX_INTRA_P_ABOVELEFT] 140 if w <= 0 { return 0 } 141 if h <= 0 { return 0 } 142 if mode < 0 { return 0 } 143 if mode > NX_INTRA_SMOOTH_H { return 0 } 144 145 var r: i64 = 0 146 var c: i64 = 0 147 var wh: i64 = 0 148 var ww: i64 = 0 149 var acc: i64 = 0 150 var dc: i64 = 0 151 let below: i64 = left[h - 1] 152 let right: i64 = above[w - 1] 153 154 if mode == NX_INTRA_DC { 155 dc = nx_intra_dc_value(above, left, p) 156 if dc < 0 { return 0 } 157 r = 0 158 while r < w * h { out[r] = dc; r = r + 1 } 159 return 1 160 } 161 162 if mode == NX_INTRA_V { 163 r = 0 164 while r < h { 165 c = 0 166 while c < w { out[r * w + c] = above[c]; c = c + 1 } 167 r = r + 1 168 } 169 return 1 170 } 171 172 if mode == NX_INTRA_H { 173 r = 0 174 while r < h { 175 c = 0 176 while c < w { out[r * w + c] = left[r]; c = c + 1 } 177 r = r + 1 178 } 179 return 1 180 } 181 182 if mode == NX_INTRA_PAETH { 183 r = 0 184 while r < h { 185 c = 0 186 while c < w { 187 out[r * w + c] = nx_intra_paeth_pick(left[r], above[c], aboveleft) 188 c = c + 1 189 } 190 r = r + 1 191 } 192 return 1 193 } 194 195 if mode == NX_INTRA_SMOOTH_V { 196 r = 0 197 while r < h { 198 wh = nx_intra_sm_weight(h, r) 199 if wh < 0 { return 0 } 200 c = 0 201 while c < w { 202 out[r * w + c] = (wh * above[c] + (256 - wh) * below + 128) >> 8 203 c = c + 1 204 } 205 r = r + 1 206 } 207 return 1 208 } 209 210 if mode == NX_INTRA_SMOOTH_H { 211 r = 0 212 while r < h { 213 c = 0 214 while c < w { 215 ww = nx_intra_sm_weight(w, c) 216 if ww < 0 { return 0 } 217 out[r * w + c] = (ww * left[r] + (256 - ww) * right + 128) >> 8 218 c = c + 1 219 } 220 r = r + 1 221 } 222 return 1 223 } 224 225 r = 0 226 while r < h { 227 wh = nx_intra_sm_weight(h, r) 228 if wh < 0 { return 0 } 229 c = 0 230 while c < w { 231 ww = nx_intra_sm_weight(w, c) 232 if ww < 0 { return 0 } 233 acc = wh * above[c] + (256 - wh) * below + ww * left[r] + (256 - ww) * right 234 out[r * w + c] = (acc + 256) >> 9 235 c = c + 1 236 } 237 r = r + 1 238 } 239 return 1 240}