| 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 | |
| 9 | use core::convert::TryInto; |
| 10 | |
| 11 | /// Limit values to 0 and 255 |
| 12 | #[inline ] |
| 13 | #[allow (clippy::cast_possible_truncation, clippy::cast_sign_loss, dead_code)] |
| 14 | fn clamp(a: i16) -> u8 { |
| 15 | a.clamp(min:0, max:255) as u8 |
| 16 | } |
| 17 | |
| 18 | /// YCbCr to RGBA color conversion |
| 19 | |
| 20 | /// Convert YCbCr to RGB/BGR |
| 21 | /// |
| 22 | /// Converts to RGB if const BGRA is false |
| 23 | /// |
| 24 | /// Converts to BGR if const BGRA is true |
| 25 | pub fn ycbcr_to_rgba_inner_16_scalar<const BGRA: bool>( |
| 26 | y: &[i16; 16], cb: &[i16; 16], cr: &[i16; 16], output: &mut [u8], pos: &mut usize |
| 27 | ) { |
| 28 | let (_, output_position) = output.split_at_mut(*pos); |
| 29 | |
| 30 | // Convert into a slice with 64 elements for Rust to see we won't go out of bounds. |
| 31 | let opt: &mut [u8; 64] = output_position |
| 32 | .get_mut(0..64) |
| 33 | .expect("Slice to small cannot write" ) |
| 34 | .try_into() |
| 35 | .unwrap(); |
| 36 | for ((y, (cb, cr)), out) in y |
| 37 | .iter() |
| 38 | .zip(cb.iter().zip(cr.iter())) |
| 39 | .zip(opt.chunks_exact_mut(4)) |
| 40 | { |
| 41 | let cr = cr - 128; |
| 42 | let cb = cb - 128; |
| 43 | |
| 44 | let r = y + ((45_i16.wrapping_mul(cr)) >> 5); |
| 45 | let g = y - ((11_i16.wrapping_mul(cb) + 23_i16.wrapping_mul(cr)) >> 5); |
| 46 | let b = y + ((113_i16.wrapping_mul(cb)) >> 6); |
| 47 | |
| 48 | if BGRA { |
| 49 | out[0] = clamp(b); |
| 50 | out[1] = clamp(g); |
| 51 | out[2] = clamp(r); |
| 52 | out[3] = 255; |
| 53 | } else { |
| 54 | out[0] = clamp(r); |
| 55 | out[1] = clamp(g); |
| 56 | out[2] = clamp(b); |
| 57 | out[3] = 255; |
| 58 | } |
| 59 | } |
| 60 | *pos += 64; |
| 61 | } |
| 62 | |
| 63 | /// Convert YCbCr to RGB/BGR |
| 64 | /// |
| 65 | /// Converts to RGB if const BGRA is false |
| 66 | /// |
| 67 | /// Converts to BGR if const BGRA is true |
| 68 | pub fn ycbcr_to_rgb_inner_16_scalar<const BGRA: bool>( |
| 69 | y: &[i16; 16], cb: &[i16; 16], cr: &[i16; 16], output: &mut [u8], pos: &mut usize |
| 70 | ) { |
| 71 | let (_, output_position) = output.split_at_mut(*pos); |
| 72 | |
| 73 | // Convert into a slice with 48 elements |
| 74 | let opt: &mut [u8; 48] = output_position |
| 75 | .get_mut(0..48) |
| 76 | .expect("Slice to small cannot write" ) |
| 77 | .try_into() |
| 78 | .unwrap(); |
| 79 | |
| 80 | for ((y, (cb, cr)), out) in y |
| 81 | .iter() |
| 82 | .zip(cb.iter().zip(cr.iter())) |
| 83 | .zip(opt.chunks_exact_mut(3)) |
| 84 | { |
| 85 | let cr = cr - 128; |
| 86 | let cb = cb - 128; |
| 87 | |
| 88 | let r = y + ((45_i16.wrapping_mul(cr)) >> 5); |
| 89 | let g = y - ((11_i16.wrapping_mul(cb) + 23_i16.wrapping_mul(cr)) >> 5); |
| 90 | let b = y + ((113_i16.wrapping_mul(cb)) >> 6); |
| 91 | |
| 92 | if BGRA { |
| 93 | out[0] = clamp(b); |
| 94 | out[1] = clamp(g); |
| 95 | out[2] = clamp(r); |
| 96 | } else { |
| 97 | out[0] = clamp(r); |
| 98 | out[1] = clamp(g); |
| 99 | out[2] = clamp(b); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | // Increment pos |
| 104 | *pos += 48; |
| 105 | } |
| 106 | |
| 107 | pub fn ycbcr_to_grayscale(y: &[i16], width: usize, padded_width: usize, output: &mut [u8]) { |
| 108 | for (y_in: &[i16], out: &mut [u8]) in yChunksExact<'_, i16> |
| 109 | .chunks_exact(chunk_size:padded_width) |
| 110 | .zip(output.chunks_exact_mut(chunk_size:width)) |
| 111 | { |
| 112 | for (y: &i16, out: &mut u8) in y_in.iter().zip(out.iter_mut()) { |
| 113 | *out = *y as u8; |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | |