#![allow(dead_code)]
pub const DEFAULT_SCAN_4X4: [u16; 16] =
[0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
pub const DEFAULT_SCAN_8X8: [u16; 64] = generate_zigzag_8x8();
pub const DEFAULT_SCAN_16X16: [u16; 256] = generate_zigzag_16x16();
pub fn default_scan(log2_size: usize) -> &'static [u16] {
match log2_size {
2 => &DEFAULT_SCAN_4X4,
3 => &DEFAULT_SCAN_8X8,
4 => &DEFAULT_SCAN_16X16,
_ => panic!("default_scan: unsupported log2_size {log2_size} (must be 2, 3, or 4)"),
}
}
pub fn zigzag_scan(size: usize) -> Vec<u16> {
debug_assert!(
(4..=32).contains(&size) && size.is_power_of_two(),
"zigzag_scan: size must be 4, 8, 16, or 32; got {size}"
);
let n = size * size;
let mut out = Vec::with_capacity(n);
for d in 0..(2 * size - 1) {
let row_start = if d < size { 0 } else { d + 1 - size };
let row_end = if d < size { d } else { size - 1 };
if d.is_multiple_of(2) {
let mut row = row_end;
loop {
let col = d - row;
out.push((row * size + col) as u16);
if row == row_start {
break;
}
row -= 1;
}
} else {
for row in row_start..=row_end {
let col = d - row;
out.push((row * size + col) as u16);
}
}
}
debug_assert_eq!(out.len(), n);
out
}
const fn generate_zigzag_8x8() -> [u16; 64] {
let size: usize = 8;
let mut out = [0u16; 64];
let mut idx: usize = 0;
let mut d: usize = 0;
while d < 2 * size - 1 {
let row_start: usize = if d < size { 0 } else { d + 1 - size };
let row_end: usize = if d < size { d } else { size - 1 };
if d.is_multiple_of(2) {
let mut row = row_end;
loop {
let col = d - row;
out[idx] = (row * size + col) as u16;
idx += 1;
if row == row_start { break; }
row -= 1;
}
} else {
let mut row = row_start;
while row <= row_end {
let col = d - row;
out[idx] = (row * size + col) as u16;
idx += 1;
row += 1;
}
}
d += 1;
}
out
}
const fn generate_zigzag_16x16() -> [u16; 256] {
let size: usize = 16;
let mut out = [0u16; 256];
let mut idx: usize = 0;
let mut d: usize = 0;
while d < 2 * size - 1 {
let row_start: usize = if d < size { 0 } else { d + 1 - size };
let row_end: usize = if d < size { d } else { size - 1 };
if d.is_multiple_of(2) {
let mut row = row_end;
loop {
let col = d - row;
out[idx] = (row * size + col) as u16;
idx += 1;
if row == row_start { break; }
row -= 1;
}
} else {
let mut row = row_start;
while row <= row_end {
let col = d - row;
out[idx] = (row * size + col) as u16;
idx += 1;
row += 1;
}
}
d += 1;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
fn validate_scan(scan: &[u16], size: usize) {
let n = size * size;
assert_eq!(scan.len(), n, "scan length should be {n} for size={size}");
let mut seen = vec![false; n];
for &idx in scan {
let i = idx as usize;
assert!(i < n, "index {i} out of range for size={size}");
assert!(!seen[i], "duplicate index {i} in scan for size={size}");
seen[i] = true;
}
for (i, &s) in seen.iter().enumerate() {
assert!(s, "index {i} missing from scan for size={size}");
}
}
#[test]
fn test_default_scan_4x4_spec_values() {
assert_eq!(
DEFAULT_SCAN_4X4,
[0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15]
);
validate_scan(&DEFAULT_SCAN_4X4, 4);
}
#[test]
fn test_default_scan_8x8_valid() {
validate_scan(&DEFAULT_SCAN_8X8, 8);
}
#[test]
fn test_default_scan_16x16_valid() {
validate_scan(&DEFAULT_SCAN_16X16, 16);
}
#[test]
fn test_zigzag_scan_4x4_matches_spec() {
let scan = zigzag_scan(4);
assert_eq!(scan.as_slice(), &DEFAULT_SCAN_4X4[..]);
}
#[test]
fn test_zigzag_scan_8x8_valid() {
let scan = zigzag_scan(8);
validate_scan(&scan, 8);
assert_eq!(scan.as_slice(), &DEFAULT_SCAN_8X8[..]);
}
#[test]
fn test_zigzag_scan_16x16_valid() {
let scan = zigzag_scan(16);
validate_scan(&scan, 16);
assert_eq!(scan.as_slice(), &DEFAULT_SCAN_16X16[..]);
}
#[test]
fn test_zigzag_scan_32x32_valid() {
let scan = zigzag_scan(32);
validate_scan(&scan, 32);
}
#[test]
fn test_default_scan_dispatch() {
assert_eq!(default_scan(2).len(), 16);
assert_eq!(default_scan(3).len(), 64);
assert_eq!(default_scan(4).len(), 256);
}
#[test]
#[should_panic]
fn test_default_scan_invalid_log2() {
let _ = default_scan(5);
}
#[test]
fn test_zigzag_first_and_last() {
for size in [4usize, 8, 16, 32] {
let scan = zigzag_scan(size);
assert_eq!(scan[0], 0, "DC not first for size={size}");
assert_eq!(
*scan.last().unwrap(),
(size * size - 1) as u16,
"last coefficient wrong for size={size}"
);
}
}
}