code wiki / (root) / nx_jpeg_upsample.nx

nx_jpeg_upsample.nx source

↩ module page · 87 lines · 3575 B

1// nx_jpeg_upsample.nx -- chroma upsampling for JPEG decode. 2// 3// JPEG with sub-sampled chroma (4:2:2, 4:2:0) stores Cb/Cr at a 4// lower resolution than Y. Before YCbCr->RGB conversion the 5// chroma planes must be upsampled to match Y dimensions. 6// 7// This primitive implements nearest-neighbor (sample-replicate) 8// upsampling per T.81 Annex A.1.1: 9// 10// Each source pixel at (sx, sy) is written into a (max_h/Hi) x 11// (max_v/Vi) rectangle of the destination starting at 12// (sx * max_h/Hi, sy * max_v/Vi). 13// 14// Sampling combinations: 15// 4:4:4 (Hi=Vi=max_h=max_v=1) -> identity copy 16// 4:2:2 (Y H=2 V=1; Cb/Cr H=V=1, max_h=2 max_v=1) -> 2x1 replicate for chroma 17// 4:2:0 (Y H=V=2; Cb/Cr H=V=1, max_h=max_v=2) -> 2x2 replicate for chroma 18// 19// Substrate-honest MVP: nearest-neighbor only. Bilinear 20// upsampling per JFIF Annex F.1.1.3 is a sibling stone for 21// quality-critical decode paths; it adds smoother chroma edges 22// but costs ~3x more arithmetic per pixel. 23// 24// nx_safety_envelope: 25// intended_use: "Chroma-plane upsampling for JPEG decode." 26// sil_target: SIL1 27// evidence: [t81_annex_a_1_1_canonical_basis, 28// sample_replicate_no_arithmetic, 29// bounded_iteration] 30// hazard_register: [bug-tape-upsample-stride-overflow, 31// bug-tape-edge-clipping-wrong-on-non-multiple-dims] 32// residual_risk: "Nearest-neighbor produces blocky chroma 33// boundaries vs bilinear; acceptable for 34// substrate's first pass." 35// verdict: NOT_YET_EVALUATED 36 37import "nx_syscalls.nx" 38 39const NX_JPEG_UPSAMPLE_OK: i64 = 0 40const NX_JPEG_UPSAMPLE_BAD_FACTOR: i64 = 1 // Hi > max_h or Vi > max_v 41const NX_JPEG_UPSAMPLE_RESULT_N: i64 = 2 42 43// Replicate a sub-sampled plane to full image dimensions. 44// 45// src -- source plane (u8) 46// src_w, src_h -- source dimensions (Hi*8 * mcu_cols, Vi*8 * mcu_rows) 47// src_stride -- bytes per source row 48// hi, vi -- this component's H + V sampling factors 49// max_h, max_v -- frame's max H + V (from nx_jpeg_sof_parse) 50// dst -- destination plane (u8); must be sized for 51// max_h*8*mcu_cols x max_v*8*mcu_rows 52// dst_stride -- bytes per destination row 53// 54// The "replication factor" per axis is max_h/hi (h) and max_v/vi (v). 55// For 4:2:0 Cb: hi=vi=1, max_h=max_v=2, so each src pixel becomes a 56// 2x2 block in dst. 57func nx_jpeg_upsample_nearest(src: *u8, src_w: i64, src_h: i64, 58 src_stride: i64, 59 hi: i64, vi: i64, 60 max_h: i64, max_v: i64, 61 dst: *u8, dst_stride: i64) -> i64 { 62 if hi > max_h { return NX_JPEG_UPSAMPLE_BAD_FACTOR } 63 if vi > max_v { return NX_JPEG_UPSAMPLE_BAD_FACTOR } 64 let rep_h: i64 = max_h / hi 65 let rep_v: i64 = max_v / vi 66 var sy: i64 = 0 67 while sy < src_h { 68 var sx: i64 = 0 69 while sx < src_w { 70 let v: u8 = src[sy * src_stride + sx] 71 var dy: i64 = 0 72 while dy < rep_v { 73 var dx: i64 = 0 74 while dx < rep_h { 75 let dst_x: i64 = sx * rep_h + dx 76 let dst_y: i64 = sy * rep_v + dy 77 dst[dst_y * dst_stride + dst_x] = v 78 dx = dx + 1 79 } 80 dy = dy + 1 81 } 82 sx = sx + 1 83 } 84 sy = sy + 1 85 } 86 return NX_JPEG_UPSAMPLE_OK 87}