use core::ops::Deref;
use std::ffi::CStr;
#[derive(Debug, Clone, Default)]
pub struct Layer {
name: String,
depth: LayerDepth,
offset: usize,
}
impl Layer {
#[inline]
pub fn name(&self) -> &str {
self.name.as_str()
}
#[inline]
pub fn depth(&self) -> LayerDepth {
self.depth
}
#[inline]
pub fn offset(&self) -> usize {
self.offset
}
#[inline]
pub fn channels(&self) -> usize {
self.depth.channels()
}
#[inline]
pub fn has_alpha(&self) -> bool {
self.depth.has_alpha()
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub enum LayerDepth {
#[default]
OneChannel,
OneChannelAndAlpha,
Color,
ColorAndAlpha,
Vector,
VectorAndAlpha,
FourChannels,
FourChannelsAndAlpha,
}
impl LayerDepth {
pub fn channels(&self) -> usize {
match self {
LayerDepth::OneChannel => 1,
LayerDepth::OneChannelAndAlpha => 2,
LayerDepth::Color => 3,
LayerDepth::Vector => 3,
LayerDepth::ColorAndAlpha => 4,
LayerDepth::VectorAndAlpha => 4,
LayerDepth::FourChannels => 4,
LayerDepth::FourChannelsAndAlpha => 5,
}
}
pub fn has_alpha(&self) -> bool {
[
LayerDepth::OneChannelAndAlpha,
LayerDepth::ColorAndAlpha,
LayerDepth::VectorAndAlpha,
LayerDepth::FourChannelsAndAlpha,
]
.contains(self)
}
}
#[derive(Debug, Default)]
pub struct PixelFormat(Vec<Layer>);
impl PixelFormat {
#[inline]
pub(crate) fn new(format: &[ndspy_sys::PtDspyDevFormat]) -> Self {
let (mut previous_layer_name, mut previous_channel_id) =
Self::split_into_layer_name_and_channel_id(
unsafe { CStr::from_ptr(format[0].name) }.to_str().unwrap(),
);
let mut depth = LayerDepth::OneChannel;
let mut offset = 0;
PixelFormat(
format
.iter()
.enumerate()
.cycle()
.take(format.len() + 1)
.filter_map(|format| {
let name = unsafe { CStr::from_ptr(format.1.name) }
.to_str()
.unwrap();
let (layer_name, channel_id) =
Self::split_into_layer_name_and_channel_id(name);
if ["b", "z", "s", "a"].contains(&previous_channel_id)
&& ["r", "x", "s"].contains(&channel_id)
{
let tmp_layer_name = if previous_layer_name.is_empty() {
"Ci"
} else {
previous_layer_name
};
previous_layer_name = layer_name;
previous_channel_id = channel_id;
let tmp_depth = depth;
depth = LayerDepth::OneChannel;
let tmp_offset = offset;
offset = format.0;
Some(Layer {
name: tmp_layer_name.to_string(),
depth: tmp_depth,
offset: tmp_offset,
})
} else {
if layer_name.is_empty() && "a" == channel_id {
depth = match &depth {
LayerDepth::OneChannel => {
LayerDepth::OneChannelAndAlpha
}
LayerDepth::Color => LayerDepth::ColorAndAlpha,
LayerDepth::Vector => {
LayerDepth::VectorAndAlpha
}
LayerDepth::FourChannels => {
LayerDepth::FourChannelsAndAlpha
}
_ => unreachable!(),
};
}
else if layer_name == previous_layer_name {
match channel_id {
"r" | "g" | "b" => depth = LayerDepth::Color,
"x" | "y" | "z" => depth = LayerDepth::Vector,
"a" => {
if layer_name.is_empty() {
depth = match &depth {
LayerDepth::OneChannel => {
LayerDepth::OneChannelAndAlpha
}
LayerDepth::Color => {
LayerDepth::ColorAndAlpha
}
LayerDepth::Vector => {
LayerDepth::VectorAndAlpha
}
_ => unreachable!(),
};
} else {
depth = LayerDepth::FourChannels;
}
}
_ => (),
}
previous_layer_name = layer_name;
} else {
previous_layer_name = layer_name;
}
previous_channel_id = channel_id;
None
}
})
.collect::<Vec<_>>(),
)
}
fn split_into_layer_name_and_channel_id(name: &str) -> (&str, &str) {
let mut split = name.rsplitn(3, '.');
let mut postfix = split.next().unwrap();
if "000" == postfix {
postfix = "s";
split = name.rsplitn(2, '.');
}
if split.next().is_some() {
(split.next().unwrap(), postfix)
} else {
("", postfix)
}
}
#[inline]
pub fn channels(&self) -> usize {
self.0
.iter()
.fold(0, |total, layer| total + layer.channels())
}
}
impl Deref for PixelFormat {
type Target = Vec<Layer>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<Vec<Layer>> for PixelFormat {
fn as_ref(&self) -> &Vec<Layer> {
&self.0
}
}