///|
/// Deterministic benchmark workload for GIF encoding, decoding, and quantization.
pub(all) struct BenchmarkWorkload {
width : Int
height : Int
frames : Int
palette_colors : Int
delay_cs : Int
} derive(Debug)
///|
pub fn BenchmarkWorkload::default() -> BenchmarkWorkload {
{ width: 128, height: 96, frames: 12, palette_colors: 64, delay_cs: 6 }
}
///|
pub fn BenchmarkWorkload::size(
self : BenchmarkWorkload,
width : Int,
height : Int,
) -> BenchmarkWorkload {
{
..self,
width: if width < 1 {
1
} else {
width
},
height: if height < 1 {
1
} else {
height
},
}
}
///|
pub fn BenchmarkWorkload::frame_count(
self : BenchmarkWorkload,
count : Int,
) -> BenchmarkWorkload {
{ ..self, frames: if count < 1 { 1 } else { count } }
}
///|
pub fn BenchmarkWorkload::colors(
self : BenchmarkWorkload,
count : Int,
) -> BenchmarkWorkload {
{ ..self, palette_colors: clamp_palette_size(count) }
}
///|
pub(all) struct BenchmarkResult {
workload : BenchmarkWorkload
encoded_bytes : Int
estimated_bytes : Int
decoded_frames : Int
source_pixels : Int
roundtrip_equal : Bool
palette_error : Int
lzw_saving_per_mille : Int
} derive(Debug)
///|
pub fn BenchmarkResult::workload(self : BenchmarkResult) -> BenchmarkWorkload {
self.workload
}
///|
pub fn BenchmarkResult::encoded_bytes(self : BenchmarkResult) -> Int {
self.encoded_bytes
}
///|
pub fn BenchmarkResult::estimated_bytes(self : BenchmarkResult) -> Int {
self.estimated_bytes
}
///|
pub fn BenchmarkResult::decoded_frames(self : BenchmarkResult) -> Int {
self.decoded_frames
}
///|
pub fn BenchmarkResult::source_pixels(self : BenchmarkResult) -> Int {
self.source_pixels
}
///|
pub fn BenchmarkResult::roundtrip_equal(self : BenchmarkResult) -> Bool {
self.roundtrip_equal
}
///|
pub fn BenchmarkResult::palette_error(self : BenchmarkResult) -> Int {
self.palette_error
}
///|
pub fn BenchmarkResult::lzw_saving_per_mille(self : BenchmarkResult) -> Int {
self.lzw_saving_per_mille
}
///|
fn benchmark_canvas(width : Int, height : Int, frame_index : Int) -> Canvas {
let canvas = Canvas::new(width~, height~, fill=rgba(12, 18, 30, 255))
let center_x = (frame_index * 7 + width / 3) % width
let center_y = (frame_index * 5 + height / 2) % height
for y in 0.. 12 { 255 } else { 0 }
canvas.pixels[x + y * width] = rgba(
((wave + ring) % 256).to_byte(),
((wave * 2 + frame_index * 11) % 256).to_byte(),
((255 - wave + ring / 2) % 256).to_byte(),
if (x + y + frame_index) % 29 == 0 {
160
} else {
255
},
)
}
}
canvas
}
///|
/// Generate the shared benchmark workload without randomness or external files.
pub fn benchmark_animation(workload : BenchmarkWorkload) -> Animation {
let safe_width = if workload.width < 1 { 1 } else { workload.width }
let safe_height = if workload.height < 1 { 1 } else { workload.height }
let safe_frames = if workload.frames < 1 { 1 } else { workload.frames }
let safe_delay = if workload.delay_cs < 0 { 0 } else { workload.delay_cs }
let result = Animation::new(width=safe_width, height=safe_height)
for index in 0.. BenchmarkResult raise GifError {
let animation = benchmark_animation(workload)
let options = EncodeOptions::new(
max_colors=workload.palette_colors,
default_delay_cs=workload.delay_cs,
)
let encoded = encode_with_options(animation, options)
let decoded = decode(encoded)
let estimates = estimate_size(animation, max_colors=workload.palette_colors)
let quantized = quantize_animation(
animation,
PalettePlan::new(LocalMedianCut, workload.palette_colors),
)
let mut error_total = 0
for i in 0.. String {
"frames=\{self.workload.frames} size=\{self.workload.width}x\{self.workload.height} " +
"encoded=\{self.encoded_bytes} estimated=\{self.estimated_bytes} " +
"decoded=\{self.decoded_frames} pixels=\{self.source_pixels} " +
"roundtrip=\{self.roundtrip_equal} palette_error=\{self.palette_error}"
}
///|
/// Compare two benchmark runs by encoded size and quality error.
pub fn benchmark_delta(
left : BenchmarkResult,
right : BenchmarkResult,
) -> (Int, Int) {
(
right.encoded_bytes - left.encoded_bytes,
right.palette_error - left.palette_error,
)
}
///|
/// Return a workload scaled for a quick local smoke check.
pub fn quick_benchmark_workload() -> BenchmarkWorkload {
BenchmarkWorkload::default().size(32, 24).frame_count(4).colors(32)
}
///|
/// Return a workload that exercises the full export path.
pub fn acceptance_benchmark_workload() -> BenchmarkWorkload {
BenchmarkWorkload::default()
}