#[derive(Debug, thiserror::Error)]
pub enum PixelFormatError {
#[error("unsupported or unknown fourcc: 0x{0:x}")]
UnsupportedFourCc(u32),
}
pub mod fourcc {
use super::{rgb_888, PixelFormatError};
use crate::rfb::{ColorFormat, ColorSpecification, PixelFormat};
pub const FOURCC_XR24: u32 = 0x34325258; pub const FOURCC_RX24: u32 = 0x34325852; pub const FOURCC_BX24: u32 = 0x34325842; pub const FOURCC_XB24: u32 = 0x34324258;
pub fn fourcc_to_pixel_format(fourcc: u32) -> Result<PixelFormat, PixelFormatError> {
match fourcc {
FOURCC_XR24 => Ok(PixelFormat {
bits_per_pixel: rgb_888::BITS_PER_PIXEL,
depth: rgb_888::DEPTH,
big_endian: false,
color_spec: ColorSpecification::ColorFormat(ColorFormat {
red_max: rgb_888::MAX_VALUE,
green_max: rgb_888::MAX_VALUE,
blue_max: rgb_888::MAX_VALUE,
red_shift: rgb_888::BITS_PER_COLOR * 2,
green_shift: rgb_888::BITS_PER_COLOR * 1,
blue_shift: rgb_888::BITS_PER_COLOR * 0,
}),
}),
FOURCC_RX24 => Ok(PixelFormat {
bits_per_pixel: rgb_888::BITS_PER_PIXEL,
depth: rgb_888::DEPTH,
big_endian: false,
color_spec: ColorSpecification::ColorFormat(ColorFormat {
red_max: rgb_888::MAX_VALUE,
green_max: rgb_888::MAX_VALUE,
blue_max: rgb_888::MAX_VALUE,
red_shift: rgb_888::BITS_PER_COLOR * 3,
green_shift: rgb_888::BITS_PER_COLOR * 2,
blue_shift: rgb_888::BITS_PER_COLOR * 1,
}),
}),
FOURCC_BX24 => Ok(PixelFormat {
bits_per_pixel: rgb_888::BITS_PER_PIXEL,
depth: rgb_888::DEPTH,
big_endian: false,
color_spec: ColorSpecification::ColorFormat(ColorFormat {
red_max: rgb_888::MAX_VALUE,
green_max: rgb_888::MAX_VALUE,
blue_max: rgb_888::MAX_VALUE,
red_shift: rgb_888::BITS_PER_COLOR * 1,
green_shift: rgb_888::BITS_PER_COLOR * 2,
blue_shift: rgb_888::BITS_PER_COLOR * 3,
}),
}),
FOURCC_XB24 => Ok(PixelFormat {
bits_per_pixel: rgb_888::BITS_PER_PIXEL,
depth: rgb_888::DEPTH,
big_endian: false,
color_spec: ColorSpecification::ColorFormat(ColorFormat {
red_max: rgb_888::MAX_VALUE,
green_max: rgb_888::MAX_VALUE,
blue_max: rgb_888::MAX_VALUE,
red_shift: rgb_888::BITS_PER_COLOR * 0,
green_shift: rgb_888::BITS_PER_COLOR * 1,
blue_shift: rgb_888::BITS_PER_COLOR * 2,
}),
}),
v => Err(PixelFormatError::UnsupportedFourCc(v)),
}
}
}
pub mod rgb_888 {
use crate::rfb::{ColorSpecification, PixelFormat};
pub const BYTES_PER_PIXEL: usize = 4;
pub const BITS_PER_PIXEL: u8 = 32;
pub const DEPTH: u8 = 24;
pub const BITS_PER_COLOR: u8 = 8;
pub const MAX_VALUE: u16 = 255;
pub fn valid_shift(shift: u8) -> bool {
shift == 0 || shift == 8 || shift == 16 || shift == 24
}
pub fn color_shift_to_index(shift: u8, big_endian: bool) -> usize {
assert!(valid_shift(shift));
if big_endian {
((DEPTH - shift) / BITS_PER_COLOR) as usize
} else {
(shift / BITS_PER_COLOR) as usize
}
}
pub fn unused_index(r: usize, g: usize, b: usize) -> usize {
(3 + 2 + 1) - r - g - b
}
pub fn rgbx_index(
red_shift: u8,
green_shift: u8,
blue_shift: u8,
big_endian: bool,
) -> (usize, usize, usize, usize) {
let r = color_shift_to_index(red_shift, big_endian);
let g = color_shift_to_index(green_shift, big_endian);
let b = color_shift_to_index(blue_shift, big_endian);
let x = unused_index(r, g, b);
(r, g, b, x)
}
pub fn transform(pixels: &Vec<u8>, input: &PixelFormat, output: &PixelFormat) -> Vec<u8> {
assert!(input.is_rgb_888());
assert!(output.is_rgb_888());
let mut buf = vec![0; pixels.len()];
let (ir, ig, ib, ix) = match &input.color_spec {
ColorSpecification::ColorFormat(cf) => rgbx_index(
cf.red_shift,
cf.green_shift,
cf.blue_shift,
input.big_endian,
),
ColorSpecification::ColorMap(_) => {
unreachable!();
}
};
let (or, og, ob, ox) = match &output.color_spec {
ColorSpecification::ColorFormat(cf) => rgbx_index(
cf.red_shift,
cf.green_shift,
cf.blue_shift,
output.big_endian,
),
ColorSpecification::ColorMap(_) => {
unreachable!();
}
};
let mut i = 0;
while i < pixels.len() {
let r = pixels[i + ir];
let g = pixels[i + ig];
let b = pixels[i + ib];
let x = pixels[i + ix];
buf[i + or] = r;
buf[i + og] = g;
buf[i + ob] = b;
buf[i + ox] = x;
i += 4;
}
buf
}
}
#[cfg(test)]
mod tests {
use crate::pixel_formats::rgb_888::{color_shift_to_index, rgbx_index};
use super::{fourcc, rgb_888::transform};
#[test]
fn test_color_shift_to_index() {
assert_eq!(color_shift_to_index(0, false), 0);
assert_eq!(color_shift_to_index(8, false), 1);
assert_eq!(color_shift_to_index(16, false), 2);
assert_eq!(color_shift_to_index(24, false), 3);
assert_eq!(color_shift_to_index(0, true), 3);
assert_eq!(color_shift_to_index(8, true), 2);
assert_eq!(color_shift_to_index(16, true), 1);
assert_eq!(color_shift_to_index(24, true), 0);
}
#[test]
fn test_rgbx_index() {
assert_eq!(rgbx_index(0, 8, 16, false), (0, 1, 2, 3));
assert_eq!(rgbx_index(0, 8, 16, true), (3, 2, 1, 0));
assert_eq!(rgbx_index(8, 16, 24, false), (1, 2, 3, 0));
assert_eq!(rgbx_index(8, 16, 24, true), (2, 1, 0, 3));
assert_eq!(rgbx_index(0, 16, 24, false), (0, 2, 3, 1));
assert_eq!(rgbx_index(0, 16, 24, true), (3, 1, 0, 2));
assert_eq!(rgbx_index(8, 16, 24, false), (1, 2, 3, 0));
assert_eq!(rgbx_index(8, 16, 24, true), (2, 1, 0, 3));
assert_eq!(rgbx_index(0, 24, 8, false), (0, 3, 1, 2));
assert_eq!(rgbx_index(0, 24, 8, true), (3, 0, 2, 1));
}
#[test]
fn test_rgb888_transform() {
let pixels = vec![0u8, 1u8, 2u8, 3u8];
let xrgb_le = fourcc::fourcc_to_pixel_format(fourcc::FOURCC_XR24).unwrap();
let rgbx_le = fourcc::fourcc_to_pixel_format(fourcc::FOURCC_RX24).unwrap();
let bgrx_le = fourcc::fourcc_to_pixel_format(fourcc::FOURCC_BX24).unwrap();
let xbgr_le = fourcc::fourcc_to_pixel_format(fourcc::FOURCC_XB24).unwrap();
assert_eq!(transform(&pixels, &xrgb_le, &xrgb_le), pixels);
assert_eq!(transform(&pixels, &rgbx_le, &rgbx_le), pixels);
assert_eq!(transform(&pixels, &bgrx_le, &bgrx_le), pixels);
assert_eq!(transform(&pixels, &xbgr_le, &xbgr_le), pixels);
let p2 = vec![3u8, 0u8, 1u8, 2u8];
assert_eq!(transform(&pixels, &xrgb_le, &rgbx_le), p2);
let p3 = vec![1u8, 2u8, 3u8, 0u8];
assert_eq!(transform(&pixels, &rgbx_le, &xrgb_le), p3);
let p4 = vec![3u8, 2u8, 1u8, 0u8];
assert_eq!(transform(&pixels, &xrgb_le, &bgrx_le), p4);
assert_eq!(transform(&pixels, &bgrx_le, &xrgb_le), p4);
let p5 = vec![1u8, 2u8, 3u8, 0u8];
assert_eq!(transform(&pixels, &bgrx_le, &xbgr_le), p5);
}
}