use crate::color::Cicp;
use crate::encoder::{
encode_lossy_gray_obu, encode_still_lossy, encode_still_lossy_420, encode_still_lossy_422,
encode_yuv420_obu, encode_yuv422_obu, encode_yuv444_obu,
};
use crate::err::EncodeError;
use crate::metadata::{ContentLightLevel, Metadata, Orientation};
use crate::{BitDepth, PlanarImage, encode_lossless_gray_obu, isobmff};
const MIN_DIM: u32 = 1;
const MAX_DIM: u32 = 16_383;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum ChromaFormat {
#[default]
Yuv420,
Yuv422,
Yuv444,
Monochrome,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Speed {
#[default]
Slow,
Medium,
Fast,
}
impl Speed {
pub(crate) fn per_candidate_rdoq(self) -> bool {
matches!(self, Speed::Slow)
}
pub(crate) fn try_adst(self) -> bool {
true
}
pub(crate) fn try_angle_deltas(self) -> bool {
matches!(self, Speed::Slow)
}
pub(crate) fn reduced_modes(self) -> bool {
matches!(self, Speed::Fast)
}
pub(crate) fn try_chroma_directional(self) -> bool {
!matches!(self, Speed::Fast)
}
}
#[derive(Clone, Debug)]
pub struct EncodeConfig {
pub quality: u8,
pub chroma: ChromaFormat,
pub color_encoding: Option<Cicp>,
pub icc: Option<Vec<u8>>,
pub metadata: Metadata,
pub threads: usize,
pub speed: Speed,
pub adaptive_quant: bool,
pub variance_boost: bool,
pub cdef: bool,
pub wiener: bool,
}
impl Default for EncodeConfig {
fn default() -> Self {
EncodeConfig {
quality: 80,
chroma: ChromaFormat::Yuv420,
color_encoding: Some(Cicp::srgb_ycbcr()),
icc: None,
metadata: Metadata::default(),
threads: 1,
speed: Speed::Slow,
adaptive_quant: true,
variance_boost: true,
cdef: false,
wiener: false,
}
}
}
impl EncodeConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_quality(mut self, quality: u8) -> Self {
self.quality = quality;
self
}
pub fn with_chroma(mut self, chroma: ChromaFormat) -> Self {
self.chroma = chroma;
self
}
pub fn with_cicp(mut self, color: Cicp) -> Self {
self.color_encoding = Some(color);
self
}
pub fn with_icc_profile(mut self, icc: Vec<u8>) -> Self {
self.icc = Some(icc);
self
}
pub fn with_metadata(mut self, metadata: Metadata) -> Self {
self.metadata = metadata;
self
}
pub fn with_orientation(mut self, o: Orientation) -> Self {
self.metadata.orientation = o;
self
}
pub fn with_content_light_level(mut self, cll: ContentLightLevel) -> Self {
self.metadata.content_light_level = Some(cll);
self
}
pub fn with_exif(mut self, exif: Vec<u8>) -> Self {
self.metadata.exif = Some(exif);
self
}
pub fn with_threads(mut self, threads: usize) -> Self {
self.threads = threads;
self
}
pub fn with_speed(mut self, speed: Speed) -> Self {
self.speed = speed;
self
}
pub fn with_adaptive_quant(mut self, v: bool) -> Self {
self.adaptive_quant = v;
self
}
pub fn with_variance_boost(mut self, v: bool) -> Self {
self.variance_boost = v;
self
}
pub fn with_cdef(mut self, v: bool) -> Self {
self.cdef = v;
self
}
pub fn with_wiener(mut self, v: bool) -> Self {
self.wiener = v;
self
}
pub(crate) fn vb(&self) -> crate::av1_coder::VarianceBoost {
if self.variance_boost {
crate::av1_coder::VarianceBoost::on()
} else {
crate::av1_coder::VarianceBoost::off()
}
}
pub(crate) fn validate(&self) -> Result<(), EncodeError> {
validate_quality(self.quality)
}
}
pub(crate) fn validate_dims(width: u32, height: u32) -> Result<(), EncodeError> {
if width < MIN_DIM || height < MIN_DIM || width > MAX_DIM || height > MAX_DIM {
return Err(EncodeError::InvalidDimensions { width, height });
}
Ok(())
}
fn validate_quality(quality: u8) -> Result<(), EncodeError> {
if quality == 0 || quality > 100 {
return Err(EncodeError::InvalidQuality);
}
Ok(())
}
pub(crate) fn validate_buf<T>(buf: &[T], w: u32, h: u32, ch: usize) -> Result<(), EncodeError> {
let needed = checked_buffer_size::<T>(w as usize, h as usize, ch)?;
if buf.len() != needed {
return Err(EncodeError::InvalidInput);
}
Ok(())
}
pub(crate) fn checked_buffer_size<T>(w: usize, h: usize, ch: usize) -> Result<usize, EncodeError> {
_ = w
.checked_mul(h)
.and_then(|n| n.checked_mul(ch))
.and_then(|n| n.checked_mul(size_of::<T>()))
.and_then(|n| isize::try_from(n).ok())
.ok_or(EncodeError::DimensionTooLarge {
width: w,
height: h,
})?;
w.checked_mul(h)
.and_then(|n| n.checked_mul(ch))
.ok_or(EncodeError::DimensionTooLarge {
width: w,
height: h,
})
}
fn quality_to_q(quality: u8) -> u8 {
(1u32 + (100 - quality as u32) * 254 / 99).clamp(1, 255) as u8
}
fn av1_profile(bit_depth: u8, chroma: ChromaFormat) -> u8 {
match chroma {
_ if bit_depth == 12 => 2,
ChromaFormat::Yuv422 => 2,
ChromaFormat::Yuv444 => 1,
ChromaFormat::Yuv420 | ChromaFormat::Monochrome => 0,
}
}
pub(crate) fn make_av1c(
_obu: &[u8],
bit_depth: u8,
width: u32,
height: u32,
chroma: ChromaFormat,
) -> isobmff::Av1cParams {
let (sub_x, sub_y) = match chroma {
ChromaFormat::Yuv420 => (true, true),
ChromaFormat::Yuv422 => (true, false),
ChromaFormat::Yuv444 | ChromaFormat::Monochrome => (false, false),
};
isobmff::Av1cParams {
seq_profile: av1_profile(bit_depth, chroma),
seq_level_idx: isobmff::level_for(width, height),
high_bitdepth: bit_depth > 8,
twelve_bit: bit_depth == 12,
monochrome: matches!(chroma, ChromaFormat::Monochrome),
chroma_sub_x: sub_x,
chroma_sub_y: sub_y,
seq_header_obu: vec![],
}
}
pub(crate) fn finalize_color(
av1_obu: Vec<u8>,
width: u32,
height: u32,
bit_depth: u8,
chroma: ChromaFormat,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
let av1c = make_av1c(&av1_obu, bit_depth, width, height, chroma);
let channels: u8 = if matches!(chroma, ChromaFormat::Monochrome) {
1
} else {
3
};
isobmff::wrap_av1_image(
&av1_obu,
width,
height,
bit_depth,
channels,
&av1c,
cfg.color_encoding.as_ref(),
cfg.icc.as_deref(),
&cfg.metadata,
)
}
pub(crate) fn finalize_with_alpha(
color_obu: Vec<u8>,
alpha_obu: Vec<u8>,
width: u32,
height: u32,
bit_depth: u8,
chroma: ChromaFormat,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
let av1c_color = make_av1c(&color_obu, bit_depth, width, height, chroma);
let av1c_alpha = make_av1c(
&alpha_obu,
bit_depth,
width,
height,
ChromaFormat::Monochrome,
);
isobmff::wrap_av1_image_with_alpha(
&color_obu,
&alpha_obu,
width,
height,
bit_depth,
&av1c_color,
&av1c_alpha,
cfg.color_encoding.as_ref(),
cfg.icc.as_deref(),
&cfg.metadata,
)
}
#[allow(clippy::too_many_arguments)]
fn dispatch_lossy<T: crate::Pixel>(
img: &PlanarImage<T>,
q: u8,
chroma: ChromaFormat,
color: Option<&Cicp>,
threads: usize,
speed: Speed,
aq: bool,
vb: crate::av1_coder::VarianceBoost,
cdef: bool,
wiener: bool,
) -> Vec<u8> {
match chroma {
ChromaFormat::Yuv420 | ChromaFormat::Monochrome => {
encode_still_lossy_420(img, q, color, threads, speed, aq, vb, cdef, wiener)
}
ChromaFormat::Yuv422 => {
encode_still_lossy_422(img, q, color, threads, speed, aq, vb, cdef, wiener)
}
ChromaFormat::Yuv444 => {
encode_still_lossy(img, q, color, threads, speed, aq, vb, cdef, wiener)
}
}
}
pub fn encode_rgb8(img: &PlanarImage<u8>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Eight {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[2], img.width as u32, img.height as u32, 1)?;
let obu = dispatch_lossy(
&img.packed_3(),
quality_to_q(cfg.quality),
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
);
finalize_color(obu, img.width as u32, img.height as u32, 8, cfg.chroma, cfg)
}
pub fn encode_rgba8(img: &PlanarImage<u8>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Eight {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[2], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[3], img.width as u32, img.height as u32, 1)?;
let obu = dispatch_lossy(
&img.packed_3(),
quality_to_q(cfg.quality),
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
);
finalize_color(obu, img.width as u32, img.height as u32, 8, cfg.chroma, cfg)
}
pub fn encode_rgba8_with_alpha(
img: &PlanarImage<u8>,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Eight {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[2], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[3], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let color_obu = dispatch_lossy(
&img.packed_3(),
q,
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
);
let alpha_obu = encode_lossless_gray_obu(&img.packed_alpha_4(), true, cfg.threads)?;
finalize_with_alpha(
color_obu,
alpha_obu,
img.width as u32,
img.height as u32,
8,
cfg.chroma,
cfg,
)
}
pub fn encode_rgb10(img: &PlanarImage<u16>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Ten {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[2], img.width as u32, img.height as u32, 1)?;
let obu = dispatch_lossy(
&img.packed_3(),
quality_to_q(cfg.quality),
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
);
finalize_color(
obu,
img.width as u32,
img.height as u32,
10,
cfg.chroma,
cfg,
)
}
pub fn encode_rgba10(img: &PlanarImage<u16>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Ten {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[2], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[3], img.width as u32, img.height as u32, 1)?;
let obu = dispatch_lossy(
&img.packed_3(),
quality_to_q(cfg.quality),
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
);
finalize_color(
obu,
img.width as u32,
img.height as u32,
10,
cfg.chroma,
cfg,
)
}
pub fn encode_rgba10_with_alpha(
img: &PlanarImage<u16>,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Ten {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[2], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[3], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let color_obu = dispatch_lossy(
&img.packed_3(),
q,
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
);
let alpha_obu = encode_lossless_gray_obu(&img.packed_alpha_4(), true, cfg.threads)?;
finalize_with_alpha(
color_obu,
alpha_obu,
img.width as u32,
img.height as u32,
10,
cfg.chroma,
cfg,
)
}
pub fn encode_rgb12(img: &PlanarImage<u16>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Twelve {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[2], img.width as u32, img.height as u32, 1)?;
let obu = dispatch_lossy(
&img.packed_3(),
quality_to_q(cfg.quality),
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
);
finalize_color(
obu,
img.width as u32,
img.height as u32,
12,
cfg.chroma,
cfg,
)
}
pub fn encode_rgba12(img: &PlanarImage<u16>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Twelve {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[2], img.width as u32, img.height as u32, 1)?;
let obu = dispatch_lossy(
&img.packed_3(),
quality_to_q(cfg.quality),
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
);
finalize_color(
obu,
img.width as u32,
img.height as u32,
12,
cfg.chroma,
cfg,
)
}
pub fn encode_rgba12_with_alpha(
img: &PlanarImage<u16>,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Twelve {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[2], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[3], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let color_obu = dispatch_lossy(
&img.packed_3(),
q,
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
);
let alpha_obu = encode_lossless_gray_obu(&img.packed_alpha_4(), true, cfg.threads)?;
finalize_with_alpha(
color_obu,
alpha_obu,
img.width as u32,
img.height as u32,
12,
cfg.chroma,
cfg,
)
}
pub fn encode_gray8(img: &PlanarImage<u8>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Eight {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let obu = encode_lossy_gray_obu(
&img.packed_1(),
BitDepth::Eight,
q,
true,
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
finalize_color(
obu,
img.width as u32,
img.height as u32,
8,
ChromaFormat::Monochrome,
cfg,
)
}
pub fn encode_gray10(img: &PlanarImage<u16>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Ten {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let obu = encode_lossy_gray_obu(
&img.packed_1(),
BitDepth::Ten,
q,
true,
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
finalize_color(
obu,
img.width as u32,
img.height as u32,
10,
ChromaFormat::Monochrome,
cfg,
)
}
pub fn encode_gray12(img: &PlanarImage<u16>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Twelve {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let obu = encode_lossy_gray_obu(
&img.packed_1(),
BitDepth::Twelve,
q,
true,
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
finalize_color(
obu,
img.width as u32,
img.height as u32,
12,
ChromaFormat::Monochrome,
cfg,
)
}
pub fn encode_yuv8(img: &PlanarImage<u8>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Eight {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
img.validate_with(cfg.chroma)?;
let q = quality_to_q(cfg.quality);
let obu = dispatch_yuv_u8(
img,
BitDepth::Eight,
q,
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
finalize_color(obu, img.width as u32, img.height as u32, 8, cfg.chroma, cfg)
}
pub fn encode_yuv10(img: &PlanarImage<u16>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Ten {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
img.validate_with(cfg.chroma)?;
let q = quality_to_q(cfg.quality);
let obu = dispatch_yuv_u16(
img,
BitDepth::Ten,
q,
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
finalize_color(
obu,
img.width as u32,
img.height as u32,
10,
cfg.chroma,
cfg,
)
}
pub fn encode_yuv12(img: &PlanarImage<u16>, cfg: &EncodeConfig) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Twelve {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
img.validate_with(cfg.chroma)?;
let q = quality_to_q(cfg.quality);
let obu = dispatch_yuv_u16(
img,
BitDepth::Twelve,
q,
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
finalize_color(
obu,
img.width as u32,
img.height as u32,
12,
cfg.chroma,
cfg,
)
}
pub fn encode_yuva8_with_alpha(
img: &PlanarImage<u8>,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Eight {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
img.validate_with(cfg.chroma)?;
validate_buf(&img.planes[3], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let color_obu = dispatch_yuv_u8(
&img.packed_3(),
BitDepth::Eight,
q,
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
let alpha_obu = encode_lossless_gray_obu(&img.packed_alpha_4(), true, cfg.threads)?;
finalize_with_alpha(
color_obu,
alpha_obu,
img.width as u32,
img.height as u32,
8,
cfg.chroma,
cfg,
)
}
pub fn encode_yuva10_with_alpha(
img: &PlanarImage<u16>,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Ten {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
img.validate_with(cfg.chroma)?;
validate_buf(&img.planes[3], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let color_obu = dispatch_yuv_u16(
&img.packed_3(),
BitDepth::Ten,
q,
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
let alpha_obu = encode_lossless_gray_obu(&img.packed_alpha_4(), true, cfg.threads)?;
finalize_with_alpha(
color_obu,
alpha_obu,
img.width as u32,
img.height as u32,
10,
cfg.chroma,
cfg,
)
}
pub fn encode_yuva12_with_alpha(
img: &PlanarImage<u16>,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Twelve {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
img.validate_with(cfg.chroma)?;
validate_buf(&img.planes[3], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let color_obu = dispatch_yuv_u16(
&img.packed_3(),
BitDepth::Twelve,
q,
cfg.chroma,
cfg.color_encoding.as_ref(),
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
let alpha_obu = encode_lossless_gray_obu(&img.packed_alpha_4(), true, cfg.threads)?;
finalize_with_alpha(
color_obu,
alpha_obu,
img.width as u32,
img.height as u32,
12,
cfg.chroma,
cfg,
)
}
pub fn encode_gray_alpha8(
img: &PlanarImage<u8>,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Eight {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let color_obu = encode_lossy_gray_obu(
&img.packed_1(),
BitDepth::Eight,
q,
true,
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
let alpha_obu = encode_lossless_gray_obu(&img.packed_alpha_2(), true, cfg.threads)?;
finalize_with_alpha(
color_obu,
alpha_obu,
img.width as u32,
img.height as u32,
8,
ChromaFormat::Monochrome,
cfg,
)
}
pub fn encode_gray_alpha10(
img: &PlanarImage<u16>,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Ten {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let color_obu = encode_lossy_gray_obu(
&img.packed_1(),
BitDepth::Ten,
q,
true,
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
let alpha_obu = encode_lossless_gray_obu(&img.packed_alpha_2(), true, cfg.threads)?;
finalize_with_alpha(
color_obu,
alpha_obu,
img.width as u32,
img.height as u32,
10,
ChromaFormat::Monochrome,
cfg,
)
}
pub fn encode_gray_alpha12(
img: &PlanarImage<u16>,
cfg: &EncodeConfig,
) -> Result<Vec<u8>, EncodeError> {
if img.bit_depth != BitDepth::Twelve {
return Err(EncodeError::UnsupportedChromaBitDepth(img.bit_depth));
}
validate_dims(img.width as u32, img.height as u32)?;
cfg.validate()?;
validate_buf(&img.planes[0], img.width as u32, img.height as u32, 1)?;
validate_buf(&img.planes[1], img.width as u32, img.height as u32, 1)?;
let q = quality_to_q(cfg.quality);
let color_obu = encode_lossy_gray_obu(
&img.packed_1(),
BitDepth::Twelve,
q,
true,
cfg.threads,
cfg.speed,
cfg.adaptive_quant,
cfg.vb(),
cfg.cdef,
cfg.wiener,
)?;
let alpha_obu = encode_lossless_gray_obu(&img.packed_alpha_2(), true, cfg.threads)?;
finalize_with_alpha(
color_obu,
alpha_obu,
img.width as u32,
img.height as u32,
12,
ChromaFormat::Monochrome,
cfg,
)
}
#[allow(clippy::too_many_arguments)]
fn dispatch_yuv_u8(
planar_image: &PlanarImage<u8>,
bd: BitDepth,
q: u8,
chroma: ChromaFormat,
color: Option<&Cicp>,
threads: usize,
speed: Speed,
aq: bool,
vb: crate::av1_coder::VarianceBoost,
cdef: bool,
wiener: bool,
) -> Result<Vec<u8>, EncodeError> {
planar_image.validate_with(chroma)?;
match chroma {
ChromaFormat::Yuv420 => encode_yuv420_obu(
planar_image,
bd,
q,
color,
threads,
speed,
aq,
vb,
cdef,
wiener,
),
ChromaFormat::Yuv422 => encode_yuv422_obu(
planar_image,
bd,
q,
color,
threads,
speed,
aq,
vb,
cdef,
wiener,
),
ChromaFormat::Yuv444 | ChromaFormat::Monochrome => encode_yuv444_obu(
planar_image,
bd,
q,
color,
threads,
speed,
aq,
vb,
cdef,
wiener,
),
}
}
#[allow(clippy::too_many_arguments)]
fn dispatch_yuv_u16(
planar_image: &PlanarImage<u16>,
bd: BitDepth,
q: u8,
chroma: ChromaFormat,
color: Option<&Cicp>,
threads: usize,
speed: Speed,
aq: bool,
vb: crate::av1_coder::VarianceBoost,
cdef: bool,
wiener: bool,
) -> Result<Vec<u8>, EncodeError> {
planar_image.validate_with(chroma)?;
match chroma {
ChromaFormat::Yuv420 => encode_yuv420_obu(
planar_image,
bd,
q,
color,
threads,
speed,
aq,
vb,
cdef,
wiener,
),
ChromaFormat::Yuv422 => encode_yuv422_obu(
planar_image,
bd,
q,
color,
threads,
speed,
aq,
vb,
cdef,
wiener,
),
ChromaFormat::Yuv444 | ChromaFormat::Monochrome => encode_yuv444_obu(
planar_image,
bd,
q,
color,
threads,
speed,
aq,
vb,
cdef,
wiener,
),
}
}