pub mod cdl;
pub mod ctf;
pub mod file_transform;
pub mod format_3dl;
pub mod format_csp;
pub mod format_discreet1dl;
pub mod format_hdl;
pub mod format_icc;
pub mod format_iridas_cube;
pub mod format_iridas_itx;
pub mod format_iridas_look;
pub mod format_pandora;
pub mod format_resolve_cube;
pub mod format_spi1d;
pub mod format_spi3d;
pub mod format_spimtx;
pub mod format_truelight;
pub mod format_vf;
pub mod utils;
use crate::baker::Baker;
use crate::config::Config;
use crate::context::Context;
use crate::error::{Error, Result};
use crate::transforms::GroupTransform;
use crate::types::Interpolation;
use std::sync::OnceLock;
pub const FILEFORMAT_CLF: &str = "Academy/ASC Common LUT Format";
pub const FILEFORMAT_CTF: &str = "Color Transform Format";
pub const FILEFORMAT_COLOR_CORRECTION: &str = "ColorCorrection";
pub const FILEFORMAT_COLOR_CORRECTION_COLLECTION: &str = "ColorCorrectionCollection";
pub const FILEFORMAT_COLOR_DECISION_LIST: &str = "ColorDecisionList";
pub mod capability {
pub const NONE: u32 = 0;
pub const READ: u32 = 1;
pub const BAKE: u32 = 2;
pub const WRITE: u32 = 4;
}
pub mod bake_capability {
pub const NONE: u32 = 0;
pub const LUT3D: u32 = 1;
pub const LUT1D: u32 = 2;
pub const LUT1D_3D: u32 = 4;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FormatInfo {
pub name: &'static str,
pub extension: &'static str,
pub capabilities: u32,
pub bake_capabilities: u32,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct CachedFile {
pub group: GroupTransform,
pub is_cdl_collection: bool,
}
impl CachedFile {
pub fn new(group: GroupTransform) -> Self {
Self {
group,
is_cdl_collection: false,
}
}
}
pub trait FileFormat: Send + Sync {
fn format_info(&self) -> Vec<FormatInfo>;
fn read(
&self,
data: &[u8],
original_file_name: &str,
interp: Interpolation,
) -> Result<CachedFile>;
fn bake(&self, _baker: &Baker, format_name: &str) -> Result<Vec<u8>> {
Err(Error::msg(format!(
"Format {format_name} does not support baking."
)))
}
fn write(
&self,
_config: &Config,
_context: &Context,
_group: &GroupTransform,
format_name: &str,
) -> Result<String> {
Err(Error::msg(format!(
"Format {format_name} does not support writing."
)))
}
fn is_binary(&self) -> bool {
false
}
fn name(&self) -> &'static str {
self.format_info().first().map(|i| i.name).unwrap_or("")
}
}
pub struct FormatRegistry {
formats: Vec<Box<dyn FileFormat>>,
}
impl FormatRegistry {
fn new() -> Self {
let formats: Vec<Box<dyn FileFormat>> = vec![
format_3dl::create(),
cdl::create_cc(),
cdl::create_ccc(),
cdl::create_cdl(),
ctf::create(),
format_csp::create(),
format_discreet1dl::create(),
format_hdl::create(),
format_icc::create(),
format_iridas_cube::create(),
format_iridas_itx::create(),
format_iridas_look::create(),
format_pandora::create(),
format_resolve_cube::create(),
format_spi1d::create(),
format_spi3d::create(),
format_spimtx::create(),
format_truelight::create(),
format_vf::create(),
];
Self { formats }
}
pub fn instance() -> &'static FormatRegistry {
static REG: OnceLock<FormatRegistry> = OnceLock::new();
REG.get_or_init(FormatRegistry::new)
}
pub fn formats(&self) -> &[Box<dyn FileFormat>] {
&self.formats
}
pub fn format_by_name(&self, name: &str) -> Option<&dyn FileFormat> {
let lname = name.to_ascii_lowercase();
self.formats
.iter()
.find(|f| {
f.format_info()
.iter()
.any(|i| i.name.to_ascii_lowercase() == lname)
})
.map(|b| b.as_ref())
}
pub fn formats_for_extension(&self, ext: &str) -> Vec<&dyn FileFormat> {
let ext = ext.to_ascii_lowercase();
let mut res = Vec::new();
for f in &self.formats {
for info in f.format_info() {
if info.extension == ext {
res.push(f.as_ref());
}
}
}
res
}
pub fn format_infos(&self, cap: u32) -> Vec<FormatInfo> {
self.formats
.iter()
.flat_map(|f| f.format_info())
.filter(|i| i.capabilities & cap != 0)
.collect()
}
pub fn num_formats(&self, cap: u32) -> usize {
self.format_infos(cap).len()
}
pub fn is_format_extension_supported(&self, ext: &str) -> bool {
let ext = ext.trim_start_matches('.').to_ascii_lowercase();
self.format_infos(capability::READ)
.iter()
.any(|i| i.extension == ext)
}
}