use gamut_core::{Error, Result};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Palette8 {
entries: [[u8; 3]; 256],
}
impl Palette8 {
pub fn from_rgb_triples(rgb: &[u8]) -> Result<Self> {
if rgb.len() != 256 * 3 {
return Err(Error::InvalidInput("TIFF: palette must be 256 RGB entries"));
}
let mut entries = [[0u8; 3]; 256];
for (i, e) in entries.iter_mut().enumerate() {
*e = [rgb[3 * i], rgb[3 * i + 1], rgb[3 * i + 2]];
}
Ok(Self { entries })
}
pub fn from_tiff_colormap(colormap: &[u32]) -> Result<Self> {
if colormap.len() != 3 * 256 {
return Err(Error::InvalidInput(
"TIFF: ColorMap must have 3*256 entries",
));
}
let mut entries = [[0u8; 3]; 256];
for (i, e) in entries.iter_mut().enumerate() {
*e = [
(colormap[i] >> 8) as u8,
(colormap[256 + i] >> 8) as u8,
(colormap[512 + i] >> 8) as u8,
];
}
Ok(Self { entries })
}
#[must_use]
pub fn to_tiff_colormap(&self) -> Vec<u16> {
let mut cm = vec![0u16; 3 * 256];
for (i, e) in self.entries.iter().enumerate() {
cm[i] = u16::from(e[0]) * 257;
cm[256 + i] = u16::from(e[1]) * 257;
cm[512 + i] = u16::from(e[2]) * 257;
}
cm
}
#[must_use]
pub fn entry(&self, idx: u8) -> [u8; 3] {
self.entries[idx as usize]
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ramp_triples() -> Vec<u8> {
let mut rgb = vec![0u8; 256 * 3];
for i in 0..256 {
rgb[3 * i] = i as u8;
rgb[3 * i + 1] = (255 - i) as u8;
rgb[3 * i + 2] = (i as u8) ^ 0x55;
}
rgb
}
#[test]
fn from_rgb_triples_validates_length() {
assert!(Palette8::from_rgb_triples(&[0u8; 767]).is_err());
assert!(Palette8::from_rgb_triples(&[0u8; 769]).is_err());
let p = Palette8::from_rgb_triples(&ramp_triples()).unwrap();
assert_eq!(p.entry(0), [0, 255, 0x55]);
assert_eq!(p.entry(255), [255, 0, 255 ^ 0x55]);
}
#[test]
fn from_tiff_colormap_validates_length() {
assert!(Palette8::from_tiff_colormap(&[0u32; 767]).is_err());
assert!(Palette8::from_tiff_colormap(&[0u32; 768]).is_ok());
}
#[test]
fn colormap_roundtrip_is_lossless() {
let p = Palette8::from_rgb_triples(&ramp_triples()).unwrap();
let cm: Vec<u32> = p.to_tiff_colormap().iter().map(|&v| u32::from(v)).collect();
let back = Palette8::from_tiff_colormap(&cm).unwrap();
assert_eq!(p, back);
}
#[test]
fn colormap_layout_is_planar_rgb() {
let p = Palette8::from_rgb_triples(&ramp_triples()).unwrap();
let cm = p.to_tiff_colormap();
assert_eq!(cm[1], 257);
assert_eq!(cm[256 + 1], 254 * 257);
assert_eq!(cm[512 + 1], u16::from(1u8 ^ 0x55) * 257);
}
}