use std::collections::HashMap;
use std::ops::{Deref, Range};
use thiserror::Error;
use crate::psd_channel::IntoRgba;
use crate::psd_channel::PsdChannelCompression;
use crate::psd_channel::PsdChannelError;
use crate::psd_channel::PsdChannelKind;
use crate::sections::image_data_section::ChannelBytes;
#[derive(Debug, Clone)]
pub struct LayerProperties {
pub(crate) name: String,
pub(crate) layer_top: i32,
pub(crate) layer_left: i32,
pub(crate) layer_bottom: i32,
pub(crate) layer_right: i32,
pub(crate) visible: bool,
pub(crate) opacity: u8,
pub(crate) clipping_mask: bool,
pub(crate) psd_width: u32,
pub(crate) psd_height: u32,
pub(crate) blend_mode: BlendMode,
pub(crate) group_id: Option<u32>,
}
impl LayerProperties {
pub fn from_layer_record(
name: String,
layer_record: &LayerRecord,
psd_width: u32,
psd_height: u32,
group_id: Option<u32>,
) -> Self {
LayerProperties {
name,
layer_top: layer_record.top,
layer_left: layer_record.left,
layer_bottom: layer_record.bottom,
layer_right: layer_record.right,
opacity: layer_record.opacity,
clipping_mask: layer_record.clipping_base,
visible: layer_record.visible,
blend_mode: layer_record.blend_mode,
psd_width,
psd_height,
group_id,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn width(&self) -> u16 {
(self.layer_right - self.layer_left) as u16 + 1
}
pub fn height(&self) -> u16 {
(self.layer_bottom - self.layer_top) as u16 + 1
}
pub fn visible(&self) -> bool {
self.visible
}
pub fn layer_top(&self) -> i32 {
self.layer_top
}
pub fn layer_left(&self) -> i32 {
self.layer_left
}
pub fn layer_bottom(&self) -> i32 {
self.layer_bottom
}
pub fn layer_right(&self) -> i32 {
self.layer_right
}
pub fn opacity(&self) -> u8 {
self.opacity
}
pub fn is_clipping_mask(&self) -> bool {
self.clipping_mask
}
pub fn blend_mode(&self) -> BlendMode {
self.blend_mode
}
pub fn parent_id(&self) -> Option<u32> {
self.group_id
}
}
#[derive(Debug, Clone)]
pub struct PsdGroup {
pub(crate) id: u32,
pub(crate) contained_layers: Range<usize>,
pub(crate) layer_properties: LayerProperties,
}
impl PsdGroup {
pub fn new(
name: String,
id: u32,
contained_layers: Range<usize>,
layer_record: &LayerRecord,
psd_width: u32,
psd_height: u32,
group_id: Option<u32>,
) -> Self {
let layer_properties =
LayerProperties::from_layer_record(name, layer_record, psd_width, psd_height, group_id);
PsdGroup {
id,
contained_layers,
layer_properties,
}
}
pub fn id(&self) -> u32 {
self.id
}
}
impl Deref for PsdGroup {
type Target = LayerProperties;
fn deref(&self) -> &Self::Target {
&self.layer_properties
}
}
pub type LayerChannels = HashMap<PsdChannelKind, ChannelBytes>;
#[derive(Debug, Clone)]
pub struct PsdLayer {
pub(crate) channels: LayerChannels,
pub(crate) layer_properties: LayerProperties,
}
#[derive(Debug, PartialEq, Error)]
pub enum PsdLayerError {
#[error(
r#"Could not combine Red, Green, Blue and Alpha.
This layer is missing channel: {channel:#?}"#
)]
MissingChannels { channel: PsdChannelKind },
#[error("{channel_id} is an invalid channel id, must be 0, 1, 2, -1, -2, or -3.")]
InvalidChannel { channel_id: i16 },
#[error(r#"Unknown blending mode: {mode:#?}"#)]
UnknownBlendingMode { mode: [u8; 4] },
#[error("{compression} is an invalid layer channel compression. Must be 0, 1, 2 or 3")]
InvalidCompression { compression: u16 },
}
impl PsdLayer {
pub fn new(
layer_record: &LayerRecord,
psd_width: u32,
psd_height: u32,
group_id: Option<u32>,
channels: LayerChannels,
) -> PsdLayer {
PsdLayer {
layer_properties: LayerProperties::from_layer_record(
layer_record.name.clone(),
layer_record,
psd_width,
psd_height,
group_id,
),
channels,
}
}
pub fn compression(
&self,
channel: PsdChannelKind,
) -> Result<PsdChannelCompression, PsdChannelError> {
match self.channels.get(&channel) {
Some(channel) => match channel {
ChannelBytes::RawData(_) => Ok(PsdChannelCompression::RawData),
ChannelBytes::RleCompressed(_) => Ok(PsdChannelCompression::RleCompressed),
},
None => Err(PsdChannelError::ChannelNotFound { channel }),
}
}
pub fn rgba(&self) -> Vec<u8> {
self.generate_rgba()
}
fn get_channel(&self, channel: PsdChannelKind) -> Option<&ChannelBytes> {
self.channels.get(&channel)
}
}
impl Deref for PsdLayer {
type Target = LayerProperties;
fn deref(&self) -> &Self::Target {
&self.layer_properties
}
}
#[derive(Debug, Clone)]
pub(super) enum GroupDivider {
Other = 0,
OpenFolder = 1,
CloseFolder = 2,
BoundingSection = 3,
}
impl GroupDivider {
pub(super) fn match_divider(divider: i32) -> Option<GroupDivider> {
match divider {
0 => Some(GroupDivider::Other),
1 => Some(GroupDivider::OpenFolder),
2 => Some(GroupDivider::CloseFolder),
3 => Some(GroupDivider::BoundingSection),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy)]
#[allow(missing_docs)]
pub enum BlendMode {
PassThrough = 0,
Normal = 1,
Dissolve = 2,
Darken = 3,
Multiply = 4,
ColorBurn = 5,
LinearBurn = 6,
DarkerColor = 7,
Lighten = 8,
Screen = 9,
ColorDodge = 10,
LinearDodge = 11,
LighterColor = 12,
Overlay = 13,
SoftLight = 14,
HardLight = 15,
VividLight = 16,
LinearLight = 17,
PinLight = 18,
HardMix = 19,
Difference = 20,
Exclusion = 21,
Subtract = 22,
Divide = 23,
Hue = 24,
Saturation = 25,
Color = 26,
Luminosity = 27,
}
impl BlendMode {
pub(super) fn match_mode(mode: [u8; 4]) -> Option<BlendMode> {
match &mode {
b"pass" => Some(BlendMode::PassThrough),
b"norm" => Some(BlendMode::Normal),
b"diss" => Some(BlendMode::Dissolve),
b"dark" => Some(BlendMode::Darken),
b"mul " => Some(BlendMode::Multiply),
b"idiv" => Some(BlendMode::ColorBurn),
b"lbrn" => Some(BlendMode::LinearBurn),
b"dkCl" => Some(BlendMode::DarkerColor),
b"lite" => Some(BlendMode::Lighten),
b"scrn" => Some(BlendMode::Screen),
b"div " => Some(BlendMode::ColorDodge),
b"lddg" => Some(BlendMode::LinearDodge),
b"lgCl" => Some(BlendMode::LighterColor),
b"over" => Some(BlendMode::Overlay),
b"sLit" => Some(BlendMode::SoftLight),
b"hLit" => Some(BlendMode::HardLight),
b"vLit" => Some(BlendMode::VividLight),
b"lLit" => Some(BlendMode::LinearLight),
b"pLit" => Some(BlendMode::PinLight),
b"hMix" => Some(BlendMode::HardMix),
b"diff" => Some(BlendMode::Difference),
b"smud" => Some(BlendMode::Exclusion),
b"fsub" => Some(BlendMode::Subtract),
b"fdiv" => Some(BlendMode::Divide),
b"hue " => Some(BlendMode::Hue),
b"sat " => Some(BlendMode::Saturation),
b"colr" => Some(BlendMode::Color),
b"lum " => Some(BlendMode::Luminosity),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct LayerRecord {
pub(super) name: String,
pub(super) channel_data_lengths: Vec<(PsdChannelKind, u32)>,
pub(super) top: i32,
pub(super) left: i32,
pub(super) bottom: i32,
pub(super) right: i32,
pub(super) visible: bool,
pub(super) opacity: u8,
pub(super) clipping_base: bool,
pub(super) blend_mode: BlendMode,
pub(super) divider_type: Option<GroupDivider>,
}
impl LayerRecord {
pub fn height(&self) -> i32 {
(self.bottom - self.top) + 1
}
}
impl IntoRgba for PsdLayer {
fn rgba_idx(&self, idx: usize) -> Option<usize> {
let left_in_layer = idx % self.width() as usize;
let left_in_psd = self.layer_properties.layer_left + left_in_layer as i32;
let top_in_layer = idx / self.width() as usize;
let top_in_psd = self.layer_properties.layer_top + top_in_layer as i32;
let idx = top_in_psd
.checked_mul(self.layer_properties.psd_width as i32)
.unwrap()
+ left_in_psd;
if idx < 0 {
None
} else {
Some(idx as usize)
}
}
fn red(&self) -> &ChannelBytes {
self.get_channel(PsdChannelKind::Red).unwrap()
}
fn green(&self) -> Option<&ChannelBytes> {
self.get_channel(PsdChannelKind::Green)
}
fn blue(&self) -> Option<&ChannelBytes> {
self.get_channel(PsdChannelKind::Blue)
}
fn alpha(&self) -> Option<&ChannelBytes> {
self.get_channel(PsdChannelKind::TransparencyMask)
}
fn psd_width(&self) -> u32 {
self.layer_properties.psd_width
}
fn psd_height(&self) -> u32 {
self.layer_properties.psd_height
}
}