use crate::encoder::EncoderConfig;
const LOSSY_FIXED_OVERHEAD: u64 = 200_000;
const LOSSY_BYTES_PER_PIXEL_LOW: f64 = 12.0;
const LOSSY_BYTES_PER_PIXEL_HIGH: f64 = 16.0;
const LOSSLESS_FIXED_OVERHEAD: u64 = 500_000;
const LOSSLESS_BYTES_PER_PIXEL_LOW: f64 = 20.0;
const LOSSLESS_BYTES_PER_PIXEL_HIGH: f64 = 32.0;
const DECODE_FIXED_OVERHEAD: u64 = 100_000;
const DECODE_BYTES_PER_PIXEL: f64 = 12.0;
const LOSSY_ENCODE_THROUGHPUT_MPIXELS: f64 = 14.5;
const LOSSLESS_ENCODE_THROUGHPUT_MPIXELS: f64 = 221.0;
const DECODE_THROUGHPUT_MPIXELS: f64 = 150.0;
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct EncodeEstimate {
pub peak_memory_bytes_min: u64,
pub peak_memory_bytes: u64,
pub peak_memory_bytes_max: u64,
pub time_ms: f32,
pub output_bytes: u64,
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct DecodeEstimate {
pub peak_memory_bytes: u64,
pub output_bytes: u64,
pub time_ms: f32,
}
#[must_use]
pub fn estimate_encode(width: u32, height: u32, bpp: u8, config: &EncoderConfig) -> EncodeEstimate {
let pixels = (width as u64) * (height as u64);
let input_bytes = pixels * (bpp as u64);
let method = config.get_method();
let is_lossless = config.is_lossless();
let (fixed, bpp_low, bpp_high) = if is_lossless {
(
LOSSLESS_FIXED_OVERHEAD,
LOSSLESS_BYTES_PER_PIXEL_LOW,
LOSSLESS_BYTES_PER_PIXEL_HIGH,
)
} else {
(
LOSSY_FIXED_OVERHEAD,
LOSSY_BYTES_PER_PIXEL_LOW,
LOSSY_BYTES_PER_PIXEL_HIGH,
)
};
let t = (method as f64) / 6.0;
let bpp_base = bpp_low + t * (bpp_high - bpp_low);
let base_memory = fixed + (pixels as f64 * bpp_base) as u64;
let peak_memory_bytes_min = (base_memory as f64 * 0.8) as u64;
let peak_memory_bytes = base_memory;
let peak_memory_bytes_max = (base_memory as f64 * 1.8) as u64;
let output_ratio = if is_lossless {
0.5 } else {
let q = config.get_quality() as f64;
0.02 + (q / 100.0) * 0.18
};
let output_bytes = (input_bytes as f64 * output_ratio) as u64;
let method_speed = match method {
0 => 25.7 / 14.5, 1 => 1.5, 2 => 16.7 / 14.5, 3 => 1.08, 4 => 1.0, 5 => 0.95, _ => 11.1 / 14.5, };
let throughput = if is_lossless {
LOSSLESS_ENCODE_THROUGHPUT_MPIXELS * method_speed
} else {
LOSSY_ENCODE_THROUGHPUT_MPIXELS * method_speed
};
let time_ms = (pixels as f64 / (throughput * 1000.0)) as f32;
EncodeEstimate {
peak_memory_bytes_min,
peak_memory_bytes,
peak_memory_bytes_max,
time_ms,
output_bytes,
}
}
#[must_use]
pub fn estimate_decode(width: u32, height: u32, output_bpp: u8) -> DecodeEstimate {
let pixels = (width as u64) * (height as u64);
let output_bytes = pixels * (output_bpp as u64);
let peak_memory_bytes = DECODE_FIXED_OVERHEAD + (pixels as f64 * DECODE_BYTES_PER_PIXEL) as u64;
let time_ms = (pixels as f64 / (DECODE_THROUGHPUT_MPIXELS * 1000.0)) as f32;
DecodeEstimate {
peak_memory_bytes,
output_bytes,
time_ms,
}
}
#[must_use]
pub fn estimate_animation_encode(
width: u32,
height: u32,
frame_count: u32,
config: &EncoderConfig,
) -> EncodeEstimate {
let single = estimate_encode(width, height, 4, config);
let canvas_bytes = (width as u64) * (height as u64) * 4;
let peak_memory_bytes = single.peak_memory_bytes + canvas_bytes;
EncodeEstimate {
peak_memory_bytes_min: single.peak_memory_bytes_min + canvas_bytes,
peak_memory_bytes,
peak_memory_bytes_max: single.peak_memory_bytes_max + canvas_bytes,
time_ms: single.time_ms * frame_count as f32,
output_bytes: single.output_bytes * frame_count as u64,
}
}
#[must_use]
pub fn estimate_animation_decode(width: u32, height: u32, frame_count: u32) -> DecodeEstimate {
let single = estimate_decode(width, height, 4);
let canvas_bytes = (width as u64) * (height as u64) * 4;
DecodeEstimate {
peak_memory_bytes: single.peak_memory_bytes + canvas_bytes,
output_bytes: single.output_bytes * frame_count as u64,
time_ms: single.time_ms * frame_count as f32,
}
}