Skip to main content

phasm_core/codec/jpeg/
zigzag.rs

1// Copyright (c) 2026 Christoph Gaffga
2// SPDX-License-Identifier: GPL-3.0-only
3// https://github.com/cgaffga/phasmcore
4
5//! Zigzag scan order mapping between JPEG coefficient order and natural order.
6
7/// Maps zigzag index (0–63) to natural row-major index (0–63).
8///
9/// JPEG stores DCT coefficients in zigzag order. This table converts
10/// a zigzag position to the corresponding (row * 8 + col) position.
11pub const ZIGZAG_TO_NATURAL: [usize; 64] = [
12     0,  1,  8, 16,  9,  2,  3, 10,
13    17, 24, 32, 25, 18, 11,  4,  5,
14    12, 19, 26, 33, 40, 48, 41, 34,
15    27, 20, 13,  6,  7, 14, 21, 28,
16    35, 42, 49, 56, 57, 50, 43, 36,
17    29, 22, 15, 23, 30, 37, 44, 51,
18    58, 59, 52, 45, 38, 31, 39, 46,
19    53, 60, 61, 54, 47, 55, 62, 63,
20];
21
22/// Maps natural row-major index (0–63) to zigzag index (0–63).
23///
24/// Inverse of [`ZIGZAG_TO_NATURAL`].
25pub const NATURAL_TO_ZIGZAG: [usize; 64] = {
26    let mut table = [0usize; 64];
27    let mut i = 0;
28    while i < 64 {
29        table[ZIGZAG_TO_NATURAL[i]] = i;
30        i += 1;
31    }
32    table
33};
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn round_trip() {
41        for i in 0..64 {
42            assert_eq!(NATURAL_TO_ZIGZAG[ZIGZAG_TO_NATURAL[i]], i);
43            assert_eq!(ZIGZAG_TO_NATURAL[NATURAL_TO_ZIGZAG[i]], i);
44        }
45    }
46
47    #[test]
48    fn known_positions() {
49        // DC coefficient: zigzag 0 → natural 0 (top-left)
50        assert_eq!(ZIGZAG_TO_NATURAL[0], 0);
51        // Zigzag 1 → natural 1 (row 0, col 1)
52        assert_eq!(ZIGZAG_TO_NATURAL[1], 1);
53        // Zigzag 2 → natural 8 (row 1, col 0)
54        assert_eq!(ZIGZAG_TO_NATURAL[2], 8);
55        // Last zigzag position → natural 63 (bottom-right)
56        assert_eq!(ZIGZAG_TO_NATURAL[63], 63);
57    }
58
59    #[test]
60    fn all_indices_covered() {
61        let mut seen = [false; 64];
62        for &idx in &ZIGZAG_TO_NATURAL {
63            assert!(!seen[idx], "duplicate natural index {idx}");
64            seen[idx] = true;
65        }
66        assert!(seen.iter().all(|&s| s));
67    }
68}