#![allow(dead_code)]
use crate::foundation::consts::DCT_BLOCK_SIZE;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
#[repr(u8)]
pub enum ColorSpace {
#[default]
Unknown = 0,
Grayscale = 1,
Rgb = 2,
YCbCr = 3,
Cmyk = 4,
Ycck = 5,
Xyb = 6,
}
impl ColorSpace {
#[must_use]
pub const fn num_components(self) -> usize {
match self {
Self::Unknown => 0,
Self::Grayscale => 1,
Self::Rgb | Self::YCbCr | Self::Xyb => 3,
Self::Cmyk | Self::Ycck => 4,
}
}
#[must_use]
pub const fn default_subsampling(self) -> bool {
matches!(self, Self::YCbCr | Self::Ycck)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum PixelFormat {
Gray,
#[default]
Rgb,
Rgba,
Bgr,
Bgra,
Bgrx,
Gray16,
Rgb16,
Rgba16,
GrayF32,
RgbF32,
RgbaF32,
Cmyk,
}
impl PixelFormat {
#[must_use]
pub const fn bytes_per_pixel(self) -> usize {
match self {
Self::Gray => 1,
Self::Gray16 => 2,
Self::Rgb | Self::Bgr => 3,
Self::Rgba | Self::Bgra | Self::Bgrx | Self::Cmyk | Self::GrayF32 => 4,
Self::Rgb16 => 6,
Self::Rgba16 => 8,
Self::RgbF32 => 12,
Self::RgbaF32 => 16,
}
}
#[must_use]
pub const fn num_channels(self) -> usize {
match self {
Self::Gray | Self::Gray16 | Self::GrayF32 => 1,
Self::Rgb
| Self::Bgr
| Self::Rgba
| Self::Bgra
| Self::Bgrx
| Self::Rgb16
| Self::Rgba16
| Self::RgbF32
| Self::RgbaF32 => 3,
Self::Cmyk => 4,
}
}
#[must_use]
pub const fn color_space(self) -> ColorSpace {
match self {
Self::Gray | Self::Gray16 | Self::GrayF32 => ColorSpace::Grayscale,
Self::Rgb
| Self::Rgba
| Self::Bgr
| Self::Bgra
| Self::Bgrx
| Self::Rgb16
| Self::Rgba16
| Self::RgbF32
| Self::RgbaF32 => ColorSpace::Rgb,
Self::Cmyk => ColorSpace::Cmyk,
}
}
#[must_use]
pub const fn is_grayscale(self) -> bool {
matches!(self, Self::Gray | Self::Gray16 | Self::GrayF32)
}
#[must_use]
pub const fn is_fast_path(self) -> bool {
matches!(
self,
Self::Rgb
| Self::Bgr
| Self::Bgrx
| Self::Rgb16
| Self::RgbF32
| Self::Gray
| Self::Gray16
| Self::GrayF32
)
}
#[must_use]
pub const fn bit_depth(self) -> u8 {
match self {
Self::Gray
| Self::Rgb
| Self::Rgba
| Self::Bgr
| Self::Bgra
| Self::Bgrx
| Self::Cmyk => 8,
Self::Gray16 | Self::Rgb16 | Self::Rgba16 => 16,
Self::GrayF32 | Self::RgbF32 | Self::RgbaF32 => 32,
}
}
#[must_use]
pub const fn is_float(self) -> bool {
matches!(self, Self::GrayF32 | Self::RgbF32 | Self::RgbaF32)
}
#[must_use]
pub const fn has_alpha_or_padding(self) -> bool {
matches!(
self,
Self::Rgba | Self::Bgra | Self::Bgrx | Self::Rgba16 | Self::RgbaF32
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum Subsampling {
#[default]
S444,
S422,
S420,
S440,
}
impl Subsampling {
#[must_use]
pub const fn h_samp_factor_luma(self) -> u8 {
match self {
Self::S444 | Self::S440 => 1,
Self::S422 | Self::S420 => 2,
}
}
#[must_use]
pub const fn v_samp_factor_luma(self) -> u8 {
match self {
Self::S444 | Self::S422 => 1,
Self::S420 | Self::S440 => 2,
}
}
#[must_use]
pub const fn mcu_size(self) -> usize {
match self {
Self::S444 => 8,
Self::S420 | Self::S422 | Self::S440 => 16,
}
}
}
impl From<crate::encode::encoder_types::ChromaSubsampling> for Subsampling {
fn from(cs: crate::encode::encoder_types::ChromaSubsampling) -> Self {
use crate::encode::encoder_types::ChromaSubsampling;
match cs {
ChromaSubsampling::None => Self::S444,
ChromaSubsampling::HalfHorizontal => Self::S422,
ChromaSubsampling::Quarter => Self::S420,
ChromaSubsampling::HalfVertical => Self::S440,
}
}
}
impl From<Subsampling> for crate::encode::encoder_types::ChromaSubsampling {
fn from(s: Subsampling) -> Self {
match s {
Subsampling::S444 => Self::None,
Subsampling::S422 => Self::HalfHorizontal,
Subsampling::S420 => Self::Quarter,
Subsampling::S440 => Self::HalfVertical,
}
}
}
impl From<crate::encode::encoder_types::PixelLayout> for PixelFormat {
fn from(layout: crate::encode::encoder_types::PixelLayout) -> Self {
use crate::encode::encoder_types::PixelLayout;
match layout {
PixelLayout::Rgb8Srgb => Self::Rgb,
PixelLayout::Bgr8Srgb => Self::Bgr,
PixelLayout::Rgbx8Srgb | PixelLayout::Rgba8Srgb => Self::Rgba,
PixelLayout::Bgrx8Srgb | PixelLayout::Bgra8Srgb => Self::Bgrx,
PixelLayout::Gray8Srgb => Self::Gray,
PixelLayout::Rgb16Linear => Self::Rgb16,
PixelLayout::Rgbx16Linear | PixelLayout::Rgba16Linear => Self::Rgba16,
PixelLayout::Gray16Linear => Self::Gray16,
PixelLayout::RgbF32Linear => Self::RgbF32,
PixelLayout::RgbxF32Linear | PixelLayout::RgbaF32Linear => Self::RgbaF32,
PixelLayout::GrayF32Linear => Self::GrayF32,
PixelLayout::YCbCr8 | PixelLayout::YCbCrF32 => Self::Rgb,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum EdgePadding {
#[default]
Replicate,
Mirror,
Wrap,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EdgePaddingConfig {
pub luma: EdgePadding,
pub chroma: EdgePadding,
}
impl EdgePaddingConfig {
#[must_use]
pub const fn uniform(strategy: EdgePadding) -> Self {
Self {
luma: strategy,
chroma: strategy,
}
}
#[must_use]
pub const fn recommended() -> Self {
Self {
luma: EdgePadding::Mirror,
chroma: EdgePadding::Replicate,
}
}
#[must_use]
pub const fn cpp_compat() -> Self {
Self::uniform(EdgePadding::Replicate)
}
}
impl Default for EdgePaddingConfig {
fn default() -> Self {
Self::cpp_compat()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum JpegMode {
#[default]
Baseline,
Extended,
Progressive,
Lossless,
ArithmeticSequential,
ArithmeticProgressive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum HuffmanMethod {
#[default]
JpegliCreateTree,
MozjpegClassic,
}
#[derive(Debug, Clone)]
pub struct Component {
pub id: u8,
pub h_samp_factor: u8,
pub v_samp_factor: u8,
pub quant_table_idx: u8,
pub dc_huffman_idx: u8,
pub ac_huffman_idx: u8,
}
impl Default for Component {
fn default() -> Self {
Self {
id: 0,
h_samp_factor: 1,
v_samp_factor: 1,
quant_table_idx: 0,
dc_huffman_idx: 0,
ac_huffman_idx: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct QuantTable {
pub values: [u16; DCT_BLOCK_SIZE],
pub precision: u8,
}
impl Default for QuantTable {
fn default() -> Self {
Self {
values: [16; DCT_BLOCK_SIZE], precision: 0,
}
}
}
impl QuantTable {
#[must_use]
pub fn from_natural_order(values: &[u16; DCT_BLOCK_SIZE]) -> Self {
let mut zigzag = [0u16; DCT_BLOCK_SIZE];
for (i, &v) in values.iter().enumerate() {
let zi = crate::foundation::consts::JPEG_ZIGZAG_ORDER[i] as usize;
zigzag[zi] = v;
}
Self {
values: zigzag,
precision: if values.iter().any(|&v| v > 255) {
1
} else {
0
},
}
}
#[must_use]
pub fn to_natural_order(&self) -> [u16; DCT_BLOCK_SIZE] {
let mut natural = [0u16; DCT_BLOCK_SIZE];
for (i, &zi) in crate::foundation::consts::JPEG_NATURAL_ORDER[..DCT_BLOCK_SIZE]
.iter()
.enumerate()
{
natural[zi as usize] = self.values[i];
}
natural
}
#[must_use]
pub fn clamp_to_baseline(self) -> Self {
let mut values = self.values;
for v in &mut values {
*v = (*v).clamp(1, 255);
}
Self {
values,
precision: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct HuffmanTable {
pub bits: [u8; 16],
pub values: Vec<u8>,
pub is_dc: bool,
}
impl Default for HuffmanTable {
fn default() -> Self {
Self {
bits: [0; 16],
values: Vec::new(),
is_dc: true,
}
}
}
pub type Coeff = i16;
pub type CoeffBlock = [Coeff; DCT_BLOCK_SIZE];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Dimensions {
pub width: u32,
pub height: u32,
}
impl Dimensions {
#[must_use]
pub const fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
#[must_use]
pub const fn width_in_blocks(self) -> u32 {
(self.width + 7) / 8
}
#[must_use]
pub const fn height_in_blocks(self) -> u32 {
(self.height + 7) / 8
}
#[must_use]
pub const fn num_pixels(self) -> u64 {
self.width as u64 * self.height as u64
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Limits {
pub max_pixels: Option<u64>,
pub max_memory: Option<u64>,
pub max_output: Option<u64>,
}
impl Limits {
#[must_use]
pub fn max_pixels(mut self, pixels: u64) -> Self {
self.max_pixels = Some(pixels);
self
}
#[must_use]
pub fn max_memory(mut self, bytes: u64) -> Self {
self.max_memory = Some(bytes);
self
}
#[must_use]
pub fn max_output(mut self, bytes: u64) -> Self {
self.max_output = Some(bytes);
self
}
#[must_use]
pub fn effective_max_pixels(&self) -> u64 {
self.max_pixels.unwrap_or(u64::MAX)
}
#[must_use]
pub fn effective_max_memory(&self) -> u64 {
self.max_memory.unwrap_or(u64::MAX)
}
#[must_use]
pub fn effective_max_output(&self) -> u64 {
self.max_output.unwrap_or(u64::MAX)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_space_components() {
assert_eq!(ColorSpace::Grayscale.num_components(), 1);
assert_eq!(ColorSpace::Rgb.num_components(), 3);
assert_eq!(ColorSpace::YCbCr.num_components(), 3);
assert_eq!(ColorSpace::Cmyk.num_components(), 4);
}
#[test]
fn test_pixel_format_bytes() {
assert_eq!(PixelFormat::Gray.bytes_per_pixel(), 1);
assert_eq!(PixelFormat::Rgb.bytes_per_pixel(), 3);
assert_eq!(PixelFormat::Rgba.bytes_per_pixel(), 4);
}
#[test]
fn test_quant_table_order_conversion() {
let natural = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
];
let table = QuantTable::from_natural_order(&natural);
let recovered = table.to_natural_order();
assert_eq!(natural, recovered);
}
}