use crate::{JxrError, JxrErrorKind};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ChromaSampling {
Cs420,
Cs422,
Cs444,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ColorFormat {
Luma,
Yuv(ChromaSampling),
Rgb,
Cmyk,
CmykDirect,
YuvK,
Rgbe,
NComponent(u16),
}
impl ColorFormat {
#[must_use]
pub const fn component_count(self) -> Option<u16> {
match self {
Self::Luma => Some(1),
Self::Yuv(_) | Self::Rgb | Self::Rgbe => Some(3),
Self::Cmyk | Self::CmykDirect | Self::YuvK => Some(4),
Self::NComponent(0) => None,
Self::NComponent(count) => Some(count),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SampleFormat {
Bit1,
Unsigned { bits: u8 },
Signed { bits: u8 },
FixedPoint { bits: u8, fractional_bits: u8 },
Float16,
Float32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ChannelLayout {
Luma,
LumaAlpha,
Yuv(ChromaSampling),
Yuva(ChromaSampling),
Rgb,
Rgbx,
Rgba,
Bgr,
Bgrx,
Bgra,
Cmyk,
Cmyka,
NComponent(u16),
NComponentAlpha(u16),
}
impl ChannelLayout {
#[must_use]
pub const fn channel_count(self) -> u16 {
match self {
Self::Luma => 1,
Self::LumaAlpha => 2,
Self::Yuv(_) | Self::Rgb | Self::Bgr => 3,
Self::Yuva(_) | Self::Rgbx | Self::Rgba | Self::Bgrx | Self::Bgra | Self::Cmyk => 4,
Self::Cmyka => 5,
Self::NComponent(count) => count,
Self::NComponentAlpha(count) => count.saturating_add(1),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StorageKind {
BitPacked,
U8,
U16,
I16,
I32,
F16Bits,
F32,
PackedU16,
PackedU32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PixelFormat {
BitPacked(ChannelLayout),
U8(ChannelLayout),
U16(ChannelLayout),
I16(ChannelLayout),
I32(ChannelLayout),
F16(ChannelLayout),
F32(ChannelLayout),
Rgb555,
Rgb565,
Rgb101010,
Rgbe,
}
impl PixelFormat {
#[must_use]
pub const fn channel_count(self) -> u16 {
match self {
Self::BitPacked(layout)
| Self::U8(layout)
| Self::U16(layout)
| Self::I16(layout)
| Self::I32(layout)
| Self::F16(layout)
| Self::F32(layout) => layout.channel_count(),
Self::Rgb555 | Self::Rgb565 | Self::Rgb101010 | Self::Rgbe => 3,
}
}
#[must_use]
pub const fn storage_kind(self) -> StorageKind {
match self {
Self::BitPacked(_) => StorageKind::BitPacked,
Self::U8(_) => StorageKind::U8,
Self::U16(_) => StorageKind::U16,
Self::I16(_) => StorageKind::I16,
Self::I32(_) => StorageKind::I32,
Self::F16(_) => StorageKind::F16Bits,
Self::F32(_) => StorageKind::F32,
Self::Rgb555 | Self::Rgb565 => StorageKind::PackedU16,
Self::Rgb101010 | Self::Rgbe => StorageKind::PackedU32,
}
}
pub fn row_bytes(self, width: u32) -> Result<usize, JxrError> {
self.row_bytes_for_channels(width, self.channel_count())
}
pub fn row_bytes_for_channels(self, width: u32, channels: u16) -> Result<usize, JxrError> {
let channels = usize::from(channels);
if channels == 0 {
return Err(JxrError::new(
JxrErrorKind::InvalidRequest,
"output channel layout",
));
}
let width = usize::try_from(width).map_err(|_| JxrError::arithmetic("output row width"))?;
let elements = width
.checked_mul(channels)
.ok_or_else(|| JxrError::arithmetic("output row elements"))?;
match self.storage_kind() {
StorageKind::BitPacked => elements
.checked_add(7)
.map(|bits| bits / 8)
.ok_or_else(|| JxrError::arithmetic("bit-packed row size")),
StorageKind::U8 => Ok(elements),
StorageKind::U16 | StorageKind::I16 | StorageKind::F16Bits => elements
.checked_mul(2)
.ok_or_else(|| JxrError::arithmetic("16-bit row size")),
StorageKind::I32 | StorageKind::F32 => elements
.checked_mul(4)
.ok_or_else(|| JxrError::arithmetic("32-bit row size")),
StorageKind::PackedU16 => width
.checked_mul(2)
.ok_or_else(|| JxrError::arithmetic("packed 16-bit row size")),
StorageKind::PackedU32 => width
.checked_mul(4)
.ok_or_else(|| JxrError::arithmetic("packed 32-bit row size")),
}
}
}
#[cfg(test)]
mod tests {
use super::{ChannelLayout, ChromaSampling, PixelFormat};
#[test]
fn planar_row_size_uses_plane_channel_count() {
let format = PixelFormat::U16(ChannelLayout::Yuv(ChromaSampling::Cs420));
assert_eq!(format.row_bytes(7).unwrap(), 42);
assert_eq!(format.row_bytes_for_channels(4, 1).unwrap(), 8);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AlphaMode {
None,
Integrated,
Separate,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Profile {
SubBaseline,
Baseline,
Main,
Advanced,
Unknown(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Level(pub u8);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Orientation {
#[default]
Identity,
MirrorHorizontal,
Rotate180,
MirrorVertical,
Transpose,
Rotate90,
Transverse,
Rotate270,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BitstreamMode {
Spatial,
Frequency,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BandPresence {
DcOnly,
NoHighPass,
NoFlexbits,
All,
}
impl BandPresence {
#[must_use]
pub const fn has_low_pass(self) -> bool {
!matches!(self, Self::DcOnly)
}
#[must_use]
pub const fn has_high_pass(self) -> bool {
matches!(self, Self::NoFlexbits | Self::All)
}
#[must_use]
pub const fn has_flexbits(self) -> bool {
matches!(self, Self::All)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum OverlapMode {
None = 0,
One = 1,
Two = 2,
}