388 lines
12 KiB
Rust
388 lines
12 KiB
Rust
#![allow(dead_code)]
|
|
use crate::sections::image_data_section::ChannelBytes;
|
|
use crate::sections::PsdCursor;
|
|
use thiserror::Error;
|
|
|
|
pub trait IntoRgba {
|
|
/// Given an index of a pixel in the current rectangle
|
|
/// (top left is 0.. to the right of that is 1.. etc) return the index of that pixel in the
|
|
/// RGBA image that will be generated.
|
|
///
|
|
/// If the final image or layer is the size of the PSD then this will return the same idx,
|
|
/// otherwise it will get transformed.
|
|
///
|
|
/// index could be `None` if layer's top or left is negative.
|
|
///
|
|
/// index could be bigger than the size of the image if layer's bottom, right, width, height is bigger than image.
|
|
fn rgba_idx(&self, idx: usize) -> Option<usize>;
|
|
|
|
/// The first channel
|
|
fn red(&self) -> &ChannelBytes;
|
|
|
|
/// The second channel
|
|
fn green(&self) -> Option<&ChannelBytes>;
|
|
|
|
/// The third channel
|
|
fn blue(&self) -> Option<&ChannelBytes>;
|
|
|
|
/// The fourth channel
|
|
fn alpha(&self) -> Option<&ChannelBytes>;
|
|
|
|
/// The width of the PSD
|
|
fn psd_width(&self) -> u32;
|
|
|
|
/// The height of the PSD
|
|
fn psd_height(&self) -> u32;
|
|
|
|
fn generate_rgba(&self) -> Vec<u8> {
|
|
let rgba_len = (self.psd_width() * self.psd_height() * 4) as usize;
|
|
|
|
let red = self.red();
|
|
let green = self.green();
|
|
let blue = self.blue();
|
|
let alpha = self.alpha();
|
|
|
|
// TODO: We're assuming that if we only see two channels it is a 16 bit grayscale
|
|
// PSD. Instead we should just check the Psd's color mode and depth to see if
|
|
// they are grayscale and sixteen. As we run into more cases we'll clean things like
|
|
// this up over time.
|
|
// if green.is_some() && blue.is_none() && alpha.is_none() {
|
|
// return self.generate_16_bit_grayscale_rgba();
|
|
// }
|
|
|
|
let mut rgba = vec![0; rgba_len];
|
|
|
|
use crate::psd_channel::PsdChannelKind::*;
|
|
|
|
self.insert_channel_bytes(&mut rgba, Red, red);
|
|
|
|
// If there is a green channel we use it, otherwise we use the red channel since this is
|
|
// a single channel grey image (such as a heightmap).
|
|
if let Some(green) = green {
|
|
self.insert_channel_bytes(&mut rgba, Green, green);
|
|
} else {
|
|
self.insert_channel_bytes(&mut rgba, Green, red);
|
|
}
|
|
|
|
// If there is a blue channel we use it, otherwise we use the red channel since this is
|
|
// a single channel grey image (such as a heightmap).
|
|
if let Some(blue) = blue {
|
|
self.insert_channel_bytes(&mut rgba, Blue, blue);
|
|
} else {
|
|
self.insert_channel_bytes(&mut rgba, Blue, red);
|
|
}
|
|
|
|
if let Some(alpha_channel) = alpha {
|
|
self.insert_channel_bytes(&mut rgba, TransparencyMask, alpha_channel);
|
|
} else {
|
|
// If there is no transparency data then the image is opaque
|
|
for idx in 0..rgba_len / 4 {
|
|
rgba[idx * 4 + 3] = 255;
|
|
}
|
|
}
|
|
|
|
rgba
|
|
}
|
|
|
|
/// Generate an RGBA Vec<u8> from a composite image or layer that uses 16 bits per
|
|
/// pixel. We do this by mapping the 16 bits back down to 8 bits.
|
|
///
|
|
/// The 16 bits are stored across the red and green channels (first and second).
|
|
fn generate_16_bit_grayscale_rgba(&self) -> Vec<u8> {
|
|
match self.red() {
|
|
ChannelBytes::RawData(red) => match self.green().unwrap() {
|
|
ChannelBytes::RawData(green) => sixteen_to_eight_rgba(red, green),
|
|
ChannelBytes::RleCompressed(green) => {
|
|
let green = &rle_decompress(green);
|
|
|
|
sixteen_to_eight_rgba(red, green)
|
|
}
|
|
},
|
|
ChannelBytes::RleCompressed(red) => {
|
|
let red = &rle_decompress(red);
|
|
|
|
match self.green().unwrap() {
|
|
ChannelBytes::RawData(green) => sixteen_to_eight_rgba(red, green),
|
|
ChannelBytes::RleCompressed(green) => {
|
|
let green = &rle_decompress(green);
|
|
sixteen_to_eight_rgba(red, green)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Given some vector of bytes, insert the bytes from the given channel into the vector.
|
|
///
|
|
/// Doing it this way allows us to allocate for one vector and insert all 4 (RGBA) channels into
|
|
/// it.
|
|
fn insert_channel_bytes(
|
|
&self,
|
|
rgba: &mut Vec<u8>,
|
|
channel_kind: PsdChannelKind,
|
|
channel_bytes: &ChannelBytes,
|
|
) {
|
|
match channel_bytes {
|
|
ChannelBytes::RawData(channel_bytes) => {
|
|
let offset = channel_kind.rgba_offset().unwrap();
|
|
|
|
for (idx, byte) in channel_bytes.iter().enumerate() {
|
|
if let Some(rgba_idx) = self.rgba_idx(idx) {
|
|
rgba[rgba_idx * 4 + offset] = *byte;
|
|
}
|
|
}
|
|
}
|
|
// https://en.wikipedia.org/wiki/PackBits
|
|
ChannelBytes::RleCompressed(channel_bytes) => {
|
|
self.insert_rle_channel(rgba, channel_kind, &channel_bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// rle decompress a channel (R,G,B or A) and insert it into a vector of RGBA pixels.
|
|
///
|
|
/// We use the channels offset to know where to put it.. So red would go in 0, 4, 8..
|
|
/// blue would go in 1, 5, 9.. etc
|
|
///
|
|
/// https://en.wikipedia.org/wiki/PackBits - algorithm used for decompression
|
|
fn insert_rle_channel(
|
|
&self,
|
|
rgba: &mut Vec<u8>,
|
|
channel_kind: PsdChannelKind,
|
|
channel_bytes: &[u8],
|
|
) {
|
|
let mut cursor = PsdCursor::new(&channel_bytes[..]);
|
|
|
|
let mut idx = 0;
|
|
let offset = channel_kind.rgba_offset().unwrap();
|
|
let len = cursor.get_ref().len() as u64;
|
|
|
|
while cursor.position() < len {
|
|
let header = cursor.read_i8() as i16;
|
|
|
|
if header == -128 {
|
|
continue;
|
|
} else if header >= 0 {
|
|
let bytes_to_read = 1 + header;
|
|
if cursor.position() + bytes_to_read as u64 > len {
|
|
break;
|
|
}
|
|
for byte in cursor.read(bytes_to_read as u32) {
|
|
if let Some(rgba_idx) = self.rgba_idx(idx) {
|
|
if let Some(buffer) = rgba.get_mut(rgba_idx * 4 + offset) {
|
|
*buffer = *byte;
|
|
}
|
|
}
|
|
|
|
idx += 1;
|
|
}
|
|
} else {
|
|
let repeat = 1 - header;
|
|
|
|
if cursor.position() + 1 > len {
|
|
break;
|
|
}
|
|
let byte = cursor.read_1()[0];
|
|
for _ in 0..repeat {
|
|
if let Some(rgba_idx) = self.rgba_idx(idx) {
|
|
if let Some(buffer) = rgba.get_mut(rgba_idx * 4 + offset) {
|
|
*buffer = byte;
|
|
}
|
|
}
|
|
|
|
idx += 1;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Rle decompress a channel
|
|
fn rle_decompress(bytes: &[u8]) -> Vec<u8> {
|
|
let mut cursor = PsdCursor::new(&bytes[..]);
|
|
|
|
let mut decompressed = vec![];
|
|
|
|
while cursor.position() != cursor.get_ref().len() as u64 {
|
|
let header = cursor.read_i8() as i16;
|
|
|
|
if header == -128 {
|
|
continue;
|
|
} else if header >= 0 {
|
|
let bytes_to_read = 1 + header;
|
|
for byte in cursor.read(bytes_to_read as u32) {
|
|
decompressed.push(*byte);
|
|
}
|
|
} else {
|
|
let repeat = 1 - header;
|
|
let byte = cursor.read_1()[0];
|
|
for _ in 0..repeat {
|
|
decompressed.push(byte);
|
|
}
|
|
};
|
|
}
|
|
|
|
decompressed
|
|
}
|
|
|
|
/// Take two 8 bit channels that together represent a 16 bit channel and convert them down
|
|
/// into an 8 bit channel.
|
|
///
|
|
/// We store the final bytes in the first channel (overwriting the old bytes)
|
|
fn sixteen_to_eight_rgba(channel1: &[u8], channel2: &[u8]) -> Vec<u8> {
|
|
let mut eight = Vec::with_capacity(channel1.len());
|
|
|
|
for idx in 0..channel1.len() {
|
|
if idx % 2 == 1 {
|
|
continue;
|
|
}
|
|
|
|
let sixteen_bit = [channel1[idx], channel1[idx + 1]];
|
|
let sixteen_bit = u16::from_be_bytes(sixteen_bit);
|
|
|
|
let eight_bit = (sixteen_bit / 256) as u8;
|
|
|
|
eight.push(eight_bit);
|
|
eight.push(eight_bit);
|
|
eight.push(eight_bit);
|
|
eight.push(255);
|
|
}
|
|
|
|
for idx in 0..channel2.len() {
|
|
if idx % 2 == 1 {
|
|
continue;
|
|
}
|
|
|
|
let sixteen_bit = [channel2[idx], channel2[idx + 1]];
|
|
let sixteen_bit = u16::from_be_bytes(sixteen_bit);
|
|
|
|
let eight_bit = (sixteen_bit / 256) as u8;
|
|
|
|
eight.push(eight_bit);
|
|
eight.push(eight_bit);
|
|
eight.push(eight_bit);
|
|
eight.push(255);
|
|
}
|
|
|
|
eight
|
|
}
|
|
|
|
/// Indicates how a channe'sl data is compressed
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
#[allow(missing_docs)]
|
|
pub enum PsdChannelCompression {
|
|
/// Not compressed
|
|
RawData = 0,
|
|
/// Compressed using [PackBits RLE compression](https://en.wikipedia.org/wiki/PackBits)
|
|
RleCompressed = 1,
|
|
/// Currently unsupported
|
|
ZipWithoutPrediction = 2,
|
|
/// Currently unsupported
|
|
ZipWithPrediction = 3,
|
|
}
|
|
|
|
impl PsdChannelCompression {
|
|
/// Create a new PsdLayerChannelCompression
|
|
pub fn new(compression: u16) -> Option<PsdChannelCompression> {
|
|
match compression {
|
|
0 => Some(PsdChannelCompression::RawData),
|
|
1 => Some(PsdChannelCompression::RleCompressed),
|
|
2 => Some(PsdChannelCompression::ZipWithoutPrediction),
|
|
3 => Some(PsdChannelCompression::ZipWithPrediction),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The different kinds of channels in a layer (red, green, blue, ...).
|
|
#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
|
|
#[allow(missing_docs)]
|
|
pub enum PsdChannelKind {
|
|
Red = 0,
|
|
Green = 1,
|
|
Blue = 2,
|
|
TransparencyMask = -1,
|
|
UserSuppliedLayerMask = -2,
|
|
RealUserSuppliedLayerMask = -3,
|
|
}
|
|
|
|
/// Represents an invalid channel
|
|
#[derive(Debug, Error)]
|
|
pub enum PsdChannelError {
|
|
#[error("Channel {channel:#?} not present")]
|
|
ChannelNotFound { channel: PsdChannelKind },
|
|
}
|
|
|
|
impl PsdChannelKind {
|
|
/// Create a new PsdLayerChannel
|
|
pub fn new(channel_id: i16) -> Option<PsdChannelKind> {
|
|
match channel_id {
|
|
0 => Some(PsdChannelKind::Red),
|
|
1 => Some(PsdChannelKind::Green),
|
|
2 => Some(PsdChannelKind::Blue),
|
|
-1 => Some(PsdChannelKind::TransparencyMask),
|
|
-2 => Some(PsdChannelKind::UserSuppliedLayerMask),
|
|
-3 => Some(PsdChannelKind::RealUserSuppliedLayerMask),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// R -> 0
|
|
/// G -> 1
|
|
/// B -> 2
|
|
/// A -> 3
|
|
pub fn rgba_offset(self) -> Result<usize, String> {
|
|
match self {
|
|
PsdChannelKind::Red => Ok(0),
|
|
PsdChannelKind::Green => Ok(1),
|
|
PsdChannelKind::Blue => Ok(2),
|
|
PsdChannelKind::TransparencyMask => Ok(3),
|
|
_ => Err(format!("{:#?} is not an RGBA channel", &self)),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::sections::layer_and_mask_information_section::layer::{
|
|
BlendMode, LayerChannels, LayerProperties,
|
|
};
|
|
use crate::PsdLayer;
|
|
|
|
use super::*;
|
|
|
|
/// Verify that when inserting an RLE channel's bytes into an RGBA byte vec we do not attempt to
|
|
/// read beyond the channel's length.
|
|
#[test]
|
|
fn does_not_read_beyond_rle_channels_bytes() {
|
|
let layer_properties = LayerProperties {
|
|
name: "".into(),
|
|
layer_top: 0,
|
|
layer_left: 0,
|
|
layer_bottom: 0,
|
|
layer_right: 0,
|
|
visible: true,
|
|
opacity: 0,
|
|
clipping_mask: false,
|
|
psd_width: 1,
|
|
psd_height: 1,
|
|
blend_mode: BlendMode::Normal,
|
|
group_id: None,
|
|
};
|
|
|
|
let layer = PsdLayer {
|
|
channels: LayerChannels::from([(
|
|
PsdChannelKind::Red,
|
|
ChannelBytes::RleCompressed(vec![0, 0, 0]),
|
|
)]),
|
|
layer_properties,
|
|
};
|
|
|
|
let mut rgba = vec![0; (layer.width() * layer.height() * 4) as usize];
|
|
|
|
layer.insert_channel_bytes(&mut rgba, PsdChannelKind::Red, layer.red());
|
|
|
|
assert_eq!(rgba, vec![0; 4]);
|
|
}
|
|
}
|