use crate::psd_channel::PsdChannelCompression;
use crate::sections::PsdCursor;
use crate::PsdDepth;
use thiserror::Error;
#[derive(Debug, PartialEq, Error)]
pub enum ImageDataSectionError {
#[error(
r#"Only 8 and 16 bit depths are supported at the moment.
If you'd like to see 1 and 32 bit depths supported - please open an issue."#
)]
UnsupportedDepth,
#[error("{compression} is an invalid layer channel compression. Must be 0, 1, 2 or 3")]
InvalidCompression { compression: u16 },
}
#[derive(Debug)]
pub struct ImageDataSection {
pub(crate) compression: PsdChannelCompression,
pub(crate) red: ChannelBytes,
pub(crate) green: Option<ChannelBytes>,
pub(crate) blue: Option<ChannelBytes>,
pub(crate) alpha: Option<ChannelBytes>,
}
impl ImageDataSection {
pub fn from_bytes(
bytes: &[u8],
depth: PsdDepth,
psd_height: u32,
channel_count: u8,
) -> Result<ImageDataSection, ImageDataSectionError> {
let mut cursor = PsdCursor::new(bytes);
let channel_count = channel_count as usize;
let compression = cursor.read_u16();
let compression = PsdChannelCompression::new(compression)
.ok_or(ImageDataSectionError::InvalidCompression { compression })?;
let (red, green, blue, alpha) = match compression {
PsdChannelCompression::RawData => {
let channel_bytes = &bytes[2..];
let channel_byte_count = channel_bytes.len();
let bytes_per_channel = channel_byte_count / channel_count;
let mut red = channel_bytes[..bytes_per_channel].into();
let green = if channel_count >= 2 {
Some(ChannelBytes::RawData(
channel_bytes[bytes_per_channel..2 * bytes_per_channel].into(),
))
} else {
None
};
let blue = if channel_count >= 3 {
Some(ChannelBytes::RawData(
channel_bytes[2 * bytes_per_channel..3 * bytes_per_channel].into(),
))
} else {
None
};
let alpha = if channel_count == 4 {
Some(ChannelBytes::RawData(
channel_bytes[3 * bytes_per_channel..4 * bytes_per_channel].to_vec(),
))
} else {
None
};
match depth {
PsdDepth::Eight => (ChannelBytes::RawData(red), green, blue, alpha),
PsdDepth::Sixteen => {
for idx in 0..red.len() / 2 {
let bytes = [red[2 * idx], red[2 * idx + 1]];
let bits16 = u16::from_be_bytes(bytes);
red[idx] = (bits16 / 256) as u8;
}
red.truncate(red.len() / 2);
(ChannelBytes::RawData(red), green, blue, alpha)
}
_ => return Err(ImageDataSectionError::UnsupportedDepth),
}
}
PsdChannelCompression::RleCompressed => {
let mut red_byte_count = 0;
let mut green_byte_count = if channel_count >= 2 { Some(0) } else { None };
let mut blue_byte_count = if channel_count >= 3 { Some(0) } else { None };
let mut alpha_byte_count = if channel_count == 4 { Some(0) } else { None };
for _ in 0..psd_height {
red_byte_count += cursor.read_u16() as usize;
}
if let Some(ref mut green_byte_count) = green_byte_count {
for _ in 0..psd_height {
*green_byte_count += cursor.read_u16() as usize;
}
}
if let Some(ref mut blue_byte_count) = blue_byte_count {
for _ in 0..psd_height {
*blue_byte_count += cursor.read_u16() as usize;
}
}
if let Some(ref mut alpha_byte_count) = alpha_byte_count {
for _ in 0..psd_height {
*alpha_byte_count += cursor.read_u16() as usize;
}
}
let channel_data_start = 2 + (channel_count * psd_height as usize * 2);
let (red_start, red_end) =
(channel_data_start, channel_data_start + red_byte_count);
let red = bytes[red_start..red_end].into();
let green = match green_byte_count {
Some(green_byte_count) => {
let green_start = red_end;
let green_end = green_start + green_byte_count;
Some(ChannelBytes::RleCompressed(
bytes[green_start..green_end].into(),
))
}
None => None,
};
let blue = match blue_byte_count {
Some(blue_byte_count) => {
let blue_start = red_end + green_byte_count.unwrap();
let blue_end = blue_start + blue_byte_count;
Some(ChannelBytes::RleCompressed(
bytes[blue_start..blue_end].into(),
))
}
None => None,
};
let alpha = match alpha_byte_count {
Some(alpha_byte_count) => {
let alpha_start =
red_end + green_byte_count.unwrap() + blue_byte_count.unwrap();
let alpha_end = alpha_start + alpha_byte_count;
Some(ChannelBytes::RleCompressed(
bytes[alpha_start..alpha_end].into(),
))
}
None => None,
};
(ChannelBytes::RleCompressed(red), green, blue, alpha)
}
PsdChannelCompression::ZipWithoutPrediction => unimplemented!(
r#"Zip without prediction compression is currently unsupported.
Please open an issue"#
),
PsdChannelCompression::ZipWithPrediction => unimplemented!(
r#"Zip with prediction compression is currently unsupported.
Please open an issue"#
),
};
Ok(ImageDataSection {
compression,
red,
green,
blue,
alpha,
})
}
}
#[derive(Debug, Clone)]
pub enum ChannelBytes {
RawData(Vec<u8>),
RleCompressed(Vec<u8>),
}