#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![allow(
clippy::needless_range_loop, // explicit `for i in 0..n { arr[i] }` is clearer in codec code
clippy::too_many_arguments, // codec functions pass many buffers/params
clippy::manual_div_ceil, // `(x + y - 1) / y` is the standard idiom
clippy::manual_is_multiple_of, // `x % y == 0` is clearer than `.is_multiple_of()`
clippy::manual_repeat_n, // `repeat().take()` vs `repeat_n` — not available on MSRV
)]
#![cfg_attr(all(test, feature = "_benchmarks"), feature(test))]
extern crate alloc;
whereat::define_at_crate_info!();
#[cfg(all(test, feature = "_benchmarks"))]
extern crate test;
pub(crate) mod common;
pub mod decoder;
pub mod detect;
pub mod encoder;
mod exif_orientation;
pub mod mux;
mod slice_reader;
#[cfg(feature = "pixel-types")]
pub mod pixel;
pub mod heuristics;
pub mod oneshot;
pub use decoder::{
DecodeConfig, DecodeError, DecodeRequest, DecodeResult, ImageInfo, Limits, StreamingDecoder,
WebPDecoder,
};
pub use zenpixels::Orientation;
pub use encoder::{
CostModel, EncodeError, EncodeRequest, EncodeResult, EncoderConfig, ImageMetadata,
LosslessConfig, LossyConfig, PixelLayout, Preset, ValidationError, ZensimEncodeMetrics,
ZensimTarget,
};
#[cfg(feature = "__expert")]
pub use encoder::{InternalParams, SharpYuvSetting};
#[cfg(feature = "ablation")]
pub use encoder::{AblationToggles, set_ablation_toggles};
pub use zenyuv::SharpYuvConfig;
#[cfg(feature = "zencodec")]
mod codec;
#[cfg(feature = "zencodec")]
pub mod zencodec {
pub use crate::codec::{
WebpAnimationFrameDecoder, WebpAnimationFrameEncoder, WebpDecodeJob, WebpDecoder,
WebpDecoderConfig, WebpEncodeJob, WebpEncoder, WebpEncoderConfig, WebpStreamingDecoder,
};
}
pub mod metadata;
#[doc(hidden)]
pub mod __test_helpers {
pub fn convert_image_yuv_rgb(
image_data: &[u8],
width: u16,
height: u16,
stride: usize,
) -> (
alloc::vec::Vec<u8>,
alloc::vec::Vec<u8>,
alloc::vec::Vec<u8>,
) {
crate::decoder::yuv::convert_image_yuv::<3>(image_data, width, height, stride)
}
pub fn convert_image_yuv_rgb_fast(
image_data: &[u8],
width: u16,
height: u16,
stride: usize,
) -> (
alloc::vec::Vec<u8>,
alloc::vec::Vec<u8>,
alloc::vec::Vec<u8>,
) {
crate::decoder::yuv::convert_image_yuv_fast(
image_data,
crate::encoder::PixelLayout::Rgb8,
width,
height,
stride,
)
}
pub fn convert_image_y_l8(
image_data: &[u8],
width: u16,
height: u16,
stride: usize,
) -> (
alloc::vec::Vec<u8>,
alloc::vec::Vec<u8>,
alloc::vec::Vec<u8>,
) {
crate::decoder::yuv::convert_image_y::<1>(image_data, width, height, stride)
}
pub fn sse4x4_dispatch(src: &[u8; 16], pred: &[u8; 16]) -> u32 {
crate::encoder::vp8::mode_selection::__test_only_sse4x4_dispatch(src, pred)
}
pub fn ftransform_from_u8_4x4_dispatch(src: &[u8; 16], ref_: &[u8; 16]) -> [i32; 16] {
crate::common::transform::ftransform_from_u8_4x4(src, ref_)
}
pub fn ftransform_from_u8_4x4_scalar(src: &[u8; 16], ref_: &[u8; 16]) -> [i32; 16] {
crate::common::transform::ftransform_from_u8_4x4_scalar(src, ref_)
}
pub fn idct4x4_dispatch(block: &mut [i32; 16]) {
crate::common::transform::idct4x4(block);
}
pub fn idct4x4_scalar(block: &mut [i32; 16]) {
crate::common::transform::idct4x4_scalar(block);
}
pub fn dct4x4_dispatch(block: &mut [i32; 16]) {
crate::common::transform::dct4x4(block);
}
pub fn dct4x4_scalar(block: &mut [i32; 16]) {
crate::common::transform::dct4x4_scalar(block);
}
pub fn make_test_matrix(q_dc: u16, q_ac: u16) -> crate::encoder::quantize::VP8Matrix {
crate::encoder::quantize::VP8Matrix::new(
q_dc,
q_ac,
crate::encoder::quantize::MatrixType::Y1,
)
}
pub fn quantize_dequantize_block_dispatch(
coeffs: &[i32; 16],
matrix: &crate::encoder::quantize::VP8Matrix,
use_sharpen: bool,
quantized: &mut [i32; 16],
dequantized: &mut [i32; 16],
) -> bool {
crate::encoder::quantize::quantize_dequantize_block_simd(
coeffs,
matrix,
use_sharpen,
quantized,
dequantized,
)
}
pub fn quantize_dequantize_block_scalar(
coeffs: &[i32; 16],
matrix: &crate::encoder::quantize::VP8Matrix,
quantized: &mut [i32; 16],
dequantized: &mut [i32; 16],
) -> bool {
crate::encoder::quantize::quantize_dequantize_block_scalar(
coeffs,
matrix,
quantized,
dequantized,
)
}
pub fn quantize_block_dispatch(
coeffs: &mut [i32; 16],
matrix: &crate::encoder::quantize::VP8Matrix,
use_sharpen: bool,
) -> bool {
crate::encoder::quantize::quantize_block_simd(coeffs, matrix, use_sharpen)
}
pub fn quantize_block_scalar(
coeffs: &mut [i32; 16],
matrix: &crate::encoder::quantize::VP8Matrix,
) -> bool {
let mut has_nz = false;
for pos in 0..16 {
coeffs[pos] = matrix.quantize_coeff(coeffs[pos], pos);
if coeffs[pos] != 0 {
has_nz = true;
}
}
has_nz
}
pub fn dequantize_block_dispatch(
matrix: &crate::encoder::quantize::VP8Matrix,
coeffs: &mut [i32; 16],
) {
matrix.dequantize_block(coeffs);
}
pub fn dequantize_block_scalar(
matrix: &crate::encoder::quantize::VP8Matrix,
coeffs: &mut [i32; 16],
) {
for i in 0..16 {
coeffs[i] *= matrix.q[i] as i32;
}
}
pub fn is_flat_coeffs_dispatch(levels: &[i16], num_blocks: usize, thresh: i32) -> bool {
crate::encoder::cost::distortion::is_flat_coeffs(levels, num_blocks, thresh)
}
pub fn is_flat_coeffs_scalar(levels: &[i16], num_blocks: usize, thresh: i32) -> bool {
let mut score = 0i32;
for block in 0..num_blocks {
for i in 1..16 {
if levels[block * 16 + i] != 0 {
score += 1;
if score > thresh {
return false;
}
}
}
}
true
}
pub fn sse4x4_with_residual_dispatch(
src: &[u8; 16],
pred: &[u8; 16],
dequantized: &[i32; 16],
) -> u32 {
crate::encoder::vp8::mode_selection::__test_only_sse4x4_with_residual_dispatch(
src,
pred,
dequantized,
)
}
pub fn sse4x4_with_residual_scalar(
src: &[u8; 16],
pred: &[u8; 16],
dequantized: &[i32; 16],
) -> u32 {
let mut sum = 0u32;
for i in 0..16 {
let rec = (i32::from(pred[i]) + dequantized[i]).clamp(0, 255) as u8;
let d = (src[i] as i32 - rec as i32).unsigned_abs();
sum += d * d;
}
sum
}
pub const LUMA_BLOCK_SIZE: usize = crate::common::prediction::LUMA_BLOCK_SIZE;
pub const CHROMA_BLOCK_SIZE: usize = crate::common::prediction::CHROMA_BLOCK_SIZE;
pub const LUMA_STRIDE: usize = crate::common::prediction::LUMA_STRIDE;
pub const CHROMA_STRIDE: usize = crate::common::prediction::CHROMA_STRIDE;
pub fn sse_16x16_luma_dispatch(
src_y: &[u8],
src_width: usize,
mbx: usize,
mby: usize,
pred: &[u8; LUMA_BLOCK_SIZE],
) -> u32 {
crate::encoder::vp8::sse_16x16_luma(src_y, src_width, mbx, mby, pred)
}
pub fn sse_16x16_luma_scalar(
src_y: &[u8],
src_width: usize,
mbx: usize,
mby: usize,
pred: &[u8; LUMA_BLOCK_SIZE],
) -> u32 {
let mut sse = 0u32;
let src_base = mby * 16 * src_width + mbx * 16;
for y in 0..16 {
let src_row = src_base + y * src_width;
let pred_row = (y + 1) * LUMA_STRIDE + 1;
for x in 0..16 {
let diff = i32::from(src_y[src_row + x]) - i32::from(pred[pred_row + x]);
sse += (diff * diff) as u32;
}
}
sse
}
pub fn sse_8x8_chroma_dispatch(
src_uv: &[u8],
src_width: usize,
mbx: usize,
mby: usize,
pred: &[u8; CHROMA_BLOCK_SIZE],
) -> u32 {
crate::encoder::vp8::sse_8x8_chroma(src_uv, src_width, mbx, mby, pred)
}
pub fn sse_8x8_chroma_scalar(
src_uv: &[u8],
src_width: usize,
mbx: usize,
mby: usize,
pred: &[u8; CHROMA_BLOCK_SIZE],
) -> u32 {
let mut sse = 0u32;
let src_base = mby * 8 * src_width + mbx * 8;
for y in 0..8 {
let src_row = src_base + y * src_width;
let pred_row = (y + 1) * CHROMA_STRIDE + 1;
for x in 0..8 {
let diff = i32::from(src_uv[src_row + x]) - i32::from(pred[pred_row + x]);
sse += (diff * diff) as u32;
}
}
sse
}
pub fn gamma_to_linear_tab() -> &'static [u16; 256] {
&crate::decoder::yuv::GAMMA_TO_LINEAR_TAB
}
pub fn linear_to_gamma_tab() -> &'static [u8; 33] {
&crate::decoder::yuv::LINEAR_TO_GAMMA_TAB
}
}