use crate::error::{Jpeg2000Error, Result};
use crate::metadata::EnumeratedColorSpace;
pub struct ColorConverter {
source: EnumeratedColorSpace,
num_components: usize,
}
impl ColorConverter {
pub fn new(source: EnumeratedColorSpace, num_components: usize) -> Self {
Self {
source,
num_components,
}
}
pub fn to_rgb(&self, components: &[Vec<u8>]) -> Result<Vec<u8>> {
if components.len() != self.num_components {
return Err(Jpeg2000Error::ColorError(format!(
"Expected {} components, got {}",
self.num_components,
components.len()
)));
}
match self.source {
EnumeratedColorSpace::Srgb => self.srgb_to_rgb(components),
EnumeratedColorSpace::Grayscale => self.grayscale_to_rgb(components),
EnumeratedColorSpace::Sycc => self.sycc_to_rgb(components),
EnumeratedColorSpace::Custom(_) => Err(Jpeg2000Error::UnsupportedFeature(
"Custom color spaces not yet supported".to_string(),
)),
}
}
fn srgb_to_rgb(&self, components: &[Vec<u8>]) -> Result<Vec<u8>> {
if components.len() < 3 {
return Err(Jpeg2000Error::ColorError(
"sRGB requires at least 3 components".to_string(),
));
}
let num_pixels = components[0].len();
let mut rgb = Vec::with_capacity(num_pixels * 3);
for ((&r, &g), &b) in components[0].iter().zip(&components[1]).zip(&components[2]) {
rgb.push(r); rgb.push(g); rgb.push(b); }
Ok(rgb)
}
fn grayscale_to_rgb(&self, components: &[Vec<u8>]) -> Result<Vec<u8>> {
if components.is_empty() {
return Err(Jpeg2000Error::ColorError(
"Grayscale requires at least 1 component".to_string(),
));
}
let num_pixels = components[0].len();
let mut rgb = Vec::with_capacity(num_pixels * 3);
for &gray in &components[0] {
rgb.push(gray); rgb.push(gray); rgb.push(gray); }
Ok(rgb)
}
fn sycc_to_rgb(&self, components: &[Vec<u8>]) -> Result<Vec<u8>> {
if components.len() < 3 {
return Err(Jpeg2000Error::ColorError(
"sYCC requires at least 3 components".to_string(),
));
}
let num_pixels = components[0].len();
let mut rgb = Vec::with_capacity(num_pixels * 3);
for ((&y_val, &cb_val), &cr_val) in
components[0].iter().zip(&components[1]).zip(&components[2])
{
let y = f32::from(y_val);
let cb = f32::from(cb_val) - 128.0;
let cr = f32::from(cr_val) - 128.0;
let r = y + 1.402 * cr;
let g = y - 0.344136 * cb - 0.714136 * cr;
let b = y + 1.772 * cb;
rgb.push(r.clamp(0.0, 255.0) as u8);
rgb.push(g.clamp(0.0, 255.0) as u8);
rgb.push(b.clamp(0.0, 255.0) as u8);
}
Ok(rgb)
}
pub fn to_rgba(&self, components: &[Vec<u8>]) -> Result<Vec<u8>> {
let rgb = self.to_rgb(components)?;
let num_pixels = rgb.len() / 3;
let mut rgba = Vec::with_capacity(num_pixels * 4);
let has_alpha = components.len() >= 4;
for i in 0..num_pixels {
rgba.push(rgb[i * 3]); rgba.push(rgb[i * 3 + 1]); rgba.push(rgb[i * 3 + 2]);
if has_alpha && i < components[3].len() {
rgba.push(components[3][i]); } else {
rgba.push(255); }
}
Ok(rgba)
}
pub fn apply_mct(components: &mut [Vec<i32>]) -> Result<()> {
if components.len() < 3 {
return Err(Jpeg2000Error::ColorError(
"MCT requires at least 3 components".to_string(),
));
}
let (first, rest) = components.split_at_mut(1);
let (second, third) = rest.split_at_mut(1);
for ((r, g), b) in first[0]
.iter_mut()
.zip(second[0].iter_mut())
.zip(third[0].iter_mut())
{
let y = *r;
let cb = *g;
let cr = *b;
let r_new = y + ((1.402 * f64::from(cr)) as i32);
let g_new = y - ((0.34413 * f64::from(cb)) as i32) - ((0.71414 * f64::from(cr)) as i32);
let b_new = y + ((1.772 * f64::from(cb)) as i32);
*r = r_new;
*g = g_new;
*b = b_new;
}
Ok(())
}
pub fn apply_rct(components: &mut [Vec<i32>]) -> Result<()> {
if components.len() < 3 {
return Err(Jpeg2000Error::ColorError(
"RCT requires at least 3 components".to_string(),
));
}
let (first, rest) = components.split_at_mut(1);
let (second, third) = rest.split_at_mut(1);
for ((r, g), b) in first[0]
.iter_mut()
.zip(second[0].iter_mut())
.zip(third[0].iter_mut())
{
let y = *r;
let cb = *g;
let cr = *b;
let g_new = y - ((cb + cr) >> 2);
let r_new = cr + g_new;
let b_new = cb + g_new;
*r = r_new;
*g = g_new;
*b = b_new;
}
Ok(())
}
}
pub fn level_shift(data: &[i32], precision: u8, is_signed: bool) -> Vec<u8> {
let shift = if is_signed { 1 << (precision - 1) } else { 0 };
let max_val = (1 << precision) - 1;
data.iter()
.map(|&val| {
let shifted = val + shift;
shifted.clamp(0, max_val) as u8
})
.collect()
}
pub fn dequantize(coefficients: &[i32], step_size: f32) -> Vec<f32> {
coefficients
.iter()
.map(|&c| (c as f32) * step_size)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grayscale_to_rgb() {
let converter = ColorConverter::new(EnumeratedColorSpace::Grayscale, 1);
let gray = vec![vec![128u8, 64, 192]];
let rgb = converter.to_rgb(&gray).expect("conversion failed");
assert_eq!(rgb.len(), 9); assert_eq!(rgb[0], 128);
assert_eq!(rgb[1], 128);
assert_eq!(rgb[2], 128);
}
#[test]
fn test_srgb_to_rgb() {
let converter = ColorConverter::new(EnumeratedColorSpace::Srgb, 3);
let components = vec![
vec![255u8, 128], vec![0, 64], vec![127, 192], ];
let rgb = converter.to_rgb(&components).expect("conversion failed");
assert_eq!(rgb.len(), 6); assert_eq!(rgb[0], 255);
assert_eq!(rgb[1], 0);
assert_eq!(rgb[2], 127);
}
#[test]
fn test_level_shift() {
let data = vec![-128, 0, 127];
let result = level_shift(&data, 8, true);
assert_eq!(result[0], 0);
assert_eq!(result[1], 128);
assert_eq!(result[2], 255);
}
#[test]
fn test_dequantize() {
let coefficients = vec![10, 20, 30];
let step_size = 0.5;
let result = dequantize(&coefficients, step_size);
assert_eq!(result[0], 5.0);
assert_eq!(result[1], 10.0);
assert_eq!(result[2], 15.0);
}
#[test]
fn test_to_rgba() {
let converter = ColorConverter::new(EnumeratedColorSpace::Srgb, 4);
let components = vec![
vec![255u8], vec![0], vec![127], vec![200], ];
let rgba = converter.to_rgba(&components).expect("conversion failed");
assert_eq!(rgba.len(), 4);
assert_eq!(rgba[0], 255); assert_eq!(rgba[1], 0); assert_eq!(rgba[2], 127); assert_eq!(rgba[3], 200); }
}