jxl_color/
ycbcr.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
use jxl_grid::AlignedGrid;

/// Applies transform from YCbCr to RGB.
///
/// Channels are expected to be in CbYCr order.
pub fn ycbcr_to_rgb(fb_cbycr: [&mut AlignedGrid<f32>; 3]) {
    let [cb, y, cr] = fb_cbycr;
    let cb = cb.buf_mut();
    let y = y.buf_mut();
    let cr = cr.buf_mut();

    #[cfg(target_arch = "x86_64")]
    {
        if is_x86_feature_detected!("avx") && is_x86_feature_detected!("fma") {
            // SAFETY: Feature set is checked above.
            return unsafe { run_x86_64_avx2([cb, y, cr]) };
        }
    }

    #[cfg(target_arch = "aarch64")]
    {
        if std::arch::is_aarch64_feature_detected!("neon") {
            // SAFETY: Feature set is checked above.
            return unsafe { run_aarch64_neon([cb, y, cr]) };
        }
    }

    run_generic([cb, y, cr])
}

#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
#[target_feature(enable = "fma")]
unsafe fn run_x86_64_avx2(buf_cbycr: [&mut [f32]; 3]) {
    run_generic(buf_cbycr)
}

#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
unsafe fn run_aarch64_neon(buf_cbycr: [&mut [f32]; 3]) {
    run_generic(buf_cbycr)
}

#[inline(always)]
fn run_generic([cb, y, cr]: [&mut [f32]; 3]) {
    if cb.len() != y.len() || y.len() != cr.len() {
        panic!("Grid size mismatch");
    }
    for ((r, g), b) in cb.iter_mut().zip(y).zip(cr) {
        let cb = *r;
        let y = *g + 128.0 / 255.0;
        let cr = *b;

        *r = cr.mul_add(1.402, y);
        *g = cb.mul_add(
            -0.114 * 1.772 / 0.587,
            cr.mul_add(-0.299 * 1.402 / 0.587, y),
        );
        *b = cb.mul_add(1.772, y);
    }
}