oximedia_optimize/partition/
split.rs1use crate::OptimizerConfig;
4use oximedia_core::OxiResult;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum PartitionMode {
9 None,
11 Horizontal,
13 Vertical,
15 Split4,
17}
18
19#[derive(Debug, Clone, Copy)]
21pub struct PartitionDecision {
22 pub mode: PartitionMode,
24 pub cost: f64,
26 pub should_recurse: bool,
28}
29
30pub struct SplitOptimizer {
32 min_block_size: usize,
33 #[allow(dead_code)]
34 max_block_size: usize,
35 enable_asymmetric: bool,
36}
37
38impl SplitOptimizer {
39 pub fn new(config: &OptimizerConfig) -> OxiResult<Self> {
41 let (min_size, max_size, asymmetric) = match config.level {
42 crate::OptimizationLevel::Fast => (16, 64, false),
43 crate::OptimizationLevel::Medium => (8, 64, false),
44 crate::OptimizationLevel::Slow => (4, 128, true),
45 crate::OptimizationLevel::Placebo => (4, 128, true),
46 };
47
48 Ok(Self {
49 min_block_size: min_size,
50 max_block_size: max_size,
51 enable_asymmetric: asymmetric,
52 })
53 }
54
55 #[allow(dead_code)]
57 #[must_use]
58 pub fn decide(&self, pixels: &[u8], block_size: usize, complexity: f64) -> PartitionDecision {
59 if block_size <= self.min_block_size {
60 return PartitionDecision {
61 mode: PartitionMode::None,
62 cost: 0.0,
63 should_recurse: false,
64 };
65 }
66
67 let mut best_mode = PartitionMode::None;
68 let mut best_cost = self.evaluate_no_split(pixels, complexity);
69
70 if complexity > 100.0 && block_size > self.min_block_size {
72 let split_cost = self.evaluate_split4(pixels, complexity);
73 if split_cost < best_cost {
74 best_cost = split_cost;
75 best_mode = PartitionMode::Split4;
76 }
77
78 if self.enable_asymmetric {
79 let h_cost = self.evaluate_horizontal(pixels, complexity);
80 if h_cost < best_cost {
81 best_cost = h_cost;
82 best_mode = PartitionMode::Horizontal;
83 }
84
85 let v_cost = self.evaluate_vertical(pixels, complexity);
86 if v_cost < best_cost {
87 best_cost = v_cost;
88 best_mode = PartitionMode::Vertical;
89 }
90 }
91 }
92
93 PartitionDecision {
94 mode: best_mode,
95 cost: best_cost,
96 should_recurse: best_mode != PartitionMode::None,
97 }
98 }
99
100 fn evaluate_no_split(&self, pixels: &[u8], complexity: f64) -> f64 {
101 let variance = self.calculate_variance(pixels);
103 variance + complexity * 0.1
104 }
105
106 fn evaluate_split4(&self, pixels: &[u8], complexity: f64) -> f64 {
107 let variance = self.calculate_variance(pixels);
109 variance * 0.7 + complexity * 0.2 + 10.0 }
111
112 fn evaluate_horizontal(&self, pixels: &[u8], complexity: f64) -> f64 {
113 let variance = self.calculate_variance(pixels);
115 variance * 0.8 + complexity * 0.15 + 8.0
116 }
117
118 fn evaluate_vertical(&self, pixels: &[u8], complexity: f64) -> f64 {
119 let variance = self.calculate_variance(pixels);
121 variance * 0.8 + complexity * 0.15 + 8.0
122 }
123
124 fn calculate_variance(&self, pixels: &[u8]) -> f64 {
125 if pixels.is_empty() {
126 return 0.0;
127 }
128
129 let mean = pixels.iter().map(|&p| f64::from(p)).sum::<f64>() / pixels.len() as f64;
130 pixels
131 .iter()
132 .map(|&p| {
133 let diff = f64::from(p) - mean;
134 diff * diff
135 })
136 .sum::<f64>()
137 / pixels.len() as f64
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_split_optimizer_creation() {
147 let config = OptimizerConfig::default();
148 let optimizer =
149 SplitOptimizer::new(&config).expect("split optimizer creation should succeed");
150 assert_eq!(optimizer.min_block_size, 8);
151 }
152
153 #[test]
154 fn test_partition_modes() {
155 assert_ne!(PartitionMode::None, PartitionMode::Split4);
156 assert_eq!(PartitionMode::Horizontal, PartitionMode::Horizontal);
157 }
158
159 #[test]
160 fn test_min_block_size_no_split() {
161 let config = OptimizerConfig::default();
162 let optimizer =
163 SplitOptimizer::new(&config).expect("split optimizer creation should succeed");
164 let pixels = vec![128u8; 64];
165 let decision = optimizer.decide(&pixels, 8, 50.0);
166 assert_eq!(decision.mode, PartitionMode::None);
167 assert!(!decision.should_recurse);
168 }
169
170 #[test]
171 fn test_high_complexity_split() {
172 let config = OptimizerConfig::default();
173 let optimizer =
174 SplitOptimizer::new(&config).expect("split optimizer creation should succeed");
175 let pixels = vec![128u8; 256]; let decision = optimizer.decide(&pixels, 32, 500.0); assert!(decision.cost >= 0.0);
179 }
180}