zune_jpeg/color_convert/
scalar.rs

1/*
2 * Copyright (c) 2023.
3 *
4 * This software is free software;
5 *
6 * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license
7 */
8
9use core::convert::TryInto;
10
11// Bt.601 Full Range inverse coefficients computed with 14 bits of precision with MPFR.
12// This is important to keep them in i16.
13// In most cases LLVM will detect what we're doing i16 widening to i32 math and will use
14// appropriate optimizations.
15pub(crate) const Y_CF: i16 = 16384;
16pub(crate) const CR_CF: i16 = 22970;
17pub(crate) const CB_CF: i16 = 29032;
18pub(crate) const C_G_CR_COEF_1: i16 = -11700;
19pub(crate) const C_G_CB_COEF_2: i16 = -5638;
20pub(crate) const YUV_PREC: i16 = 14;
21// Rounding const for YUV -> RGB conversion: floating equivalent 0.499(9).
22pub(crate) const YUV_RND: i16 = (1 << (YUV_PREC - 1)) - 1;
23
24/// Limit values to 0 and 255
25#[inline]
26#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, dead_code)]
27fn clamp(a: i32) -> u8 {
28    a.clamp(0, 255) as u8
29}
30
31/// YCbCr to RGBA color conversion
32
33/// Convert YCbCr to RGB/BGR
34///
35/// Converts to RGB if const BGRA is false
36///
37/// Converts to BGR if const BGRA is true
38pub fn ycbcr_to_rgba_inner_16_scalar<const BGRA: bool>(
39    y: &[i16; 16], cb: &[i16; 16], cr: &[i16; 16], output: &mut [u8], pos: &mut usize
40) {
41    let (_, output_position) = output.split_at_mut(*pos);
42
43    // Convert into a slice with 64 elements for Rust to see we won't go out of bounds.
44    let opt: &mut [u8; 64] = output_position
45        .get_mut(0..64)
46        .expect("Slice to small cannot write")
47        .try_into()
48        .unwrap();
49    for ((&y, (cb, cr)), out) in y
50        .iter()
51        .zip(cb.iter().zip(cr.iter()))
52        .zip(opt.chunks_exact_mut(4))
53    {
54        let cr = cr - 128;
55        let cb = cb - 128;
56
57        let y0 = i32::from(y) * i32::from(Y_CF) + i32::from(YUV_RND);
58
59        let r = (y0 + i32::from(cr) * i32::from(CR_CF)) >> YUV_PREC;
60        let g = (y0
61            + i32::from(cr) * i32::from(C_G_CR_COEF_1)
62            + i32::from(cb) * i32::from(C_G_CB_COEF_2))
63            >> YUV_PREC;
64        let b = (y0 + i32::from(cb) * i32::from(CB_CF)) >> YUV_PREC;
65
66        if BGRA {
67            out[0] = clamp(b);
68            out[1] = clamp(g);
69            out[2] = clamp(r);
70            out[3] = 255;
71        } else {
72            out[0] = clamp(r);
73            out[1] = clamp(g);
74            out[2] = clamp(b);
75            out[3] = 255;
76        }
77    }
78    *pos += 64;
79}
80
81/// Convert YCbCr to RGB/BGR
82///
83/// Converts to RGB if const BGRA is false
84///
85/// Converts to BGR if const BGRA is true
86pub fn ycbcr_to_rgb_inner_16_scalar<const BGRA: bool>(
87    y: &[i16; 16], cb: &[i16; 16], cr: &[i16; 16], output: &mut [u8], pos: &mut usize
88) {
89    let (_, output_position) = output.split_at_mut(*pos);
90
91    // Convert into a slice with 48 elements
92    let opt: &mut [u8; 48] = output_position
93        .get_mut(0..48)
94        .expect("Slice to small cannot write")
95        .try_into()
96        .unwrap();
97
98    for ((&y, (cb, cr)), out) in y
99        .iter()
100        .zip(cb.iter().zip(cr.iter()))
101        .zip(opt.chunks_exact_mut(3))
102    {
103        let cr = cr - 128;
104        let cb = cb - 128;
105
106        let y0 = i32::from(y) * i32::from(Y_CF) + i32::from(YUV_RND);
107
108        let r = (y0 + i32::from(cr) * i32::from(CR_CF)) >> YUV_PREC;
109        let g = (y0
110            + i32::from(cr) * i32::from(C_G_CR_COEF_1)
111            + i32::from(cb) * i32::from(C_G_CB_COEF_2))
112            >> YUV_PREC;
113        let b = (y0 + i32::from(cb) * i32::from(CB_CF)) >> YUV_PREC;
114
115        if BGRA {
116            out[0] = clamp(b);
117            out[1] = clamp(g);
118            out[2] = clamp(r);
119        } else {
120            out[0] = clamp(r);
121            out[1] = clamp(g);
122            out[2] = clamp(b);
123        }
124    }
125
126    // Increment pos
127    *pos += 48;
128}
129
130pub fn ycbcr_to_grayscale(y: &[i16], width: usize, padded_width: usize, output: &mut [u8]) {
131    for (y_in, out) in y
132        .chunks_exact(padded_width)
133        .zip(output.chunks_exact_mut(width))
134    {
135        for (y, out) in y_in.iter().zip(out.iter_mut()) {
136            *out = *y as u8;
137        }
138    }
139}