1#![forbid(unsafe_code)]
13#![allow(dead_code)]
14#![allow(clippy::similar_names)]
15#![allow(clippy::cast_possible_truncation)]
16#![allow(clippy::doc_markdown)]
17#![allow(clippy::needless_range_loop)]
18
19use super::{BlockDimensions, IntraPredContext, IntraPredictor};
20
21pub mod weights {
24 pub const SMOOTH_WEIGHTS_4: [u16; 4] = [255, 149, 85, 64];
26
27 pub const SMOOTH_WEIGHTS_8: [u16; 8] = [255, 197, 146, 105, 73, 50, 37, 32];
29
30 pub const SMOOTH_WEIGHTS_16: [u16; 16] = [
32 255, 225, 196, 170, 145, 123, 102, 84, 68, 54, 43, 33, 26, 20, 17, 16,
33 ];
34
35 pub const SMOOTH_WEIGHTS_32: [u16; 32] = [
37 255, 240, 225, 210, 196, 182, 169, 157, 145, 133, 122, 111, 101, 92, 83, 74, 66, 59, 52,
38 45, 39, 34, 29, 25, 21, 17, 14, 12, 10, 9, 8, 8,
39 ];
40
41 pub const SMOOTH_WEIGHTS_64: [u16; 64] = [
43 255, 248, 240, 233, 225, 218, 210, 203, 196, 189, 182, 176, 169, 163, 156, 150, 144, 138,
44 133, 127, 121, 116, 111, 106, 101, 96, 91, 86, 82, 77, 73, 69, 65, 61, 57, 54, 50, 47, 44,
45 41, 38, 35, 32, 29, 27, 25, 22, 20, 18, 16, 15, 13, 12, 10, 9, 8, 7, 6, 6, 5, 5, 4, 4, 4,
46 ];
47
48 #[must_use]
50 pub fn get_weights(size: usize) -> &'static [u16] {
51 match size {
52 4 => &SMOOTH_WEIGHTS_4,
53 8 => &SMOOTH_WEIGHTS_8,
54 16 => &SMOOTH_WEIGHTS_16,
55 32 => &SMOOTH_WEIGHTS_32,
56 64 => &SMOOTH_WEIGHTS_64,
57 _ => {
58 if size > 64 {
60 &SMOOTH_WEIGHTS_64
61 } else {
62 if size > 32 {
64 &SMOOTH_WEIGHTS_32
65 } else if size > 16 {
66 &SMOOTH_WEIGHTS_16
67 } else if size > 8 {
68 &SMOOTH_WEIGHTS_8
69 } else {
70 &SMOOTH_WEIGHTS_4
71 }
72 }
73 }
74 }
75 }
76
77 #[must_use]
79 pub fn interpolate_weight(size: usize, idx: usize) -> u16 {
80 let weights = get_weights(size);
81 let table_size = weights.len();
82
83 if size == table_size {
84 return weights[idx];
85 }
86
87 let scaled_idx = (idx * table_size) / size;
89 let frac = ((idx * table_size) % size) * 256 / size;
90
91 let w0 = weights[scaled_idx.min(table_size - 1)];
92 let w1 = weights[(scaled_idx + 1).min(table_size - 1)];
93
94 let w0_32 = u32::from(w0);
96 let w1_32 = u32::from(w1);
97 let result = (w0_32 * (256 - frac as u32) + w1_32 * frac as u32 + 128) / 256;
98 result as u16
99 }
100}
101
102#[derive(Clone, Copy, Debug, Default)]
104pub struct SmoothPredictor;
105
106impl SmoothPredictor {
107 #[must_use]
109 pub const fn new() -> Self {
110 Self
111 }
112
113 pub fn predict_smooth(
115 ctx: &IntraPredContext,
116 output: &mut [u16],
117 stride: usize,
118 dims: BlockDimensions,
119 ) {
120 let top = ctx.top_samples();
121 let left = ctx.left_samples();
122
123 let bottom_left = left[dims.height.saturating_sub(1)];
125 let top_right = top[dims.width.saturating_sub(1)];
126
127 let weights_x = weights::get_weights(dims.width);
128 let weights_y = weights::get_weights(dims.height);
129
130 for y in 0..dims.height {
131 let row_start = y * stride;
132 let weight_y = if y < weights_y.len() {
133 weights_y[y]
134 } else {
135 weights::interpolate_weight(dims.height, y)
136 };
137
138 for x in 0..dims.width {
139 let weight_x = if x < weights_x.len() {
140 weights_x[x]
141 } else {
142 weights::interpolate_weight(dims.width, x)
143 };
144
145 let top_sample = u32::from(top[x]);
149 let left_sample = u32::from(left[y]);
150 let bl = u32::from(bottom_left);
151 let tr = u32::from(top_right);
152
153 let wy = u32::from(weight_y);
154 let wx = u32::from(weight_x);
155
156 let vertical = wy * top_sample + (256 - wy) * bl;
157 let horizontal = wx * left_sample + (256 - wx) * tr;
158
159 let pred = (vertical + horizontal + 256) / 512;
160 output[row_start + x] = pred as u16;
161 }
162 }
163 }
164}
165
166impl IntraPredictor for SmoothPredictor {
167 fn predict(
168 &self,
169 ctx: &IntraPredContext,
170 output: &mut [u16],
171 stride: usize,
172 dims: BlockDimensions,
173 ) {
174 Self::predict_smooth(ctx, output, stride, dims);
175 }
176}
177
178#[derive(Clone, Copy, Debug, Default)]
180pub struct SmoothVPredictor;
181
182impl SmoothVPredictor {
183 #[must_use]
185 pub const fn new() -> Self {
186 Self
187 }
188
189 pub fn predict_smooth_v(
191 ctx: &IntraPredContext,
192 output: &mut [u16],
193 stride: usize,
194 dims: BlockDimensions,
195 ) {
196 let top = ctx.top_samples();
197 let left = ctx.left_samples();
198
199 let bottom_left = left[dims.height.saturating_sub(1)];
201
202 let weights_y = weights::get_weights(dims.height);
203
204 for y in 0..dims.height {
205 let row_start = y * stride;
206 let weight_y = if y < weights_y.len() {
207 weights_y[y]
208 } else {
209 weights::interpolate_weight(dims.height, y)
210 };
211
212 for x in 0..dims.width {
213 let top_sample = u32::from(top[x]);
216 let bl = u32::from(bottom_left);
217 let wy = u32::from(weight_y);
218
219 let pred = (wy * top_sample + (256 - wy) * bl + 128) / 256;
220 output[row_start + x] = pred as u16;
221 }
222 }
223 }
224}
225
226impl IntraPredictor for SmoothVPredictor {
227 fn predict(
228 &self,
229 ctx: &IntraPredContext,
230 output: &mut [u16],
231 stride: usize,
232 dims: BlockDimensions,
233 ) {
234 Self::predict_smooth_v(ctx, output, stride, dims);
235 }
236}
237
238#[derive(Clone, Copy, Debug, Default)]
240pub struct SmoothHPredictor;
241
242impl SmoothHPredictor {
243 #[must_use]
245 pub const fn new() -> Self {
246 Self
247 }
248
249 pub fn predict_smooth_h(
251 ctx: &IntraPredContext,
252 output: &mut [u16],
253 stride: usize,
254 dims: BlockDimensions,
255 ) {
256 let top = ctx.top_samples();
257 let left = ctx.left_samples();
258
259 let top_right = top[dims.width.saturating_sub(1)];
261
262 let weights_x = weights::get_weights(dims.width);
263
264 for y in 0..dims.height {
265 let row_start = y * stride;
266 let left_sample = u32::from(left[y]);
267
268 for x in 0..dims.width {
269 let weight_x = if x < weights_x.len() {
270 weights_x[x]
271 } else {
272 weights::interpolate_weight(dims.width, x)
273 };
274
275 let tr = u32::from(top_right);
278 let wx = u32::from(weight_x);
279
280 let pred = (wx * left_sample + (256 - wx) * tr + 128) / 256;
281 output[row_start + x] = pred as u16;
282 }
283 }
284 }
285}
286
287impl IntraPredictor for SmoothHPredictor {
288 fn predict(
289 &self,
290 ctx: &IntraPredContext,
291 output: &mut [u16],
292 stride: usize,
293 dims: BlockDimensions,
294 ) {
295 Self::predict_smooth_h(ctx, output, stride, dims);
296 }
297}
298
299#[inline]
301pub fn bilinear_interpolate(
302 top: u16,
303 left: u16,
304 bottom_left: u16,
305 top_right: u16,
306 weight_x: u16,
307 weight_y: u16,
308) -> u16 {
309 let t = u32::from(top);
310 let l = u32::from(left);
311 let bl = u32::from(bottom_left);
312 let tr = u32::from(top_right);
313 let wx = u32::from(weight_x);
314 let wy = u32::from(weight_y);
315
316 let vertical = wy * t + (256 - wy) * bl;
318 let horizontal = wx * l + (256 - wx) * tr;
319
320 let result = (vertical + horizontal + 256) / 512;
321 result as u16
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327 use crate::intra::context::IntraPredContext;
328 use crate::intra::BitDepth;
329
330 fn create_test_context() -> IntraPredContext {
331 let mut ctx = IntraPredContext::new(8, 8, BitDepth::Bits8);
332
333 for i in 0..16 {
335 ctx.set_top_sample(i, 200);
336 }
337
338 for i in 0..16 {
340 ctx.set_left_sample(i, 100);
341 }
342
343 ctx.set_top_left_sample(150);
344 ctx.set_availability(true, true);
345
346 ctx
347 }
348
349 #[test]
350 fn test_smooth_weights() {
351 let w4 = weights::get_weights(4);
353 assert_eq!(w4.len(), 4);
354 assert!(w4[0] > w4[3]);
355
356 let w8 = weights::get_weights(8);
357 assert_eq!(w8.len(), 8);
358 assert!(w8[0] > w8[7]);
359
360 let w16 = weights::get_weights(16);
361 assert_eq!(w16.len(), 16);
362 assert_eq!(w16[0], 255);
363 }
364
365 #[test]
366 fn test_smooth_prediction() {
367 let ctx = create_test_context();
368 let predictor = SmoothPredictor::new();
369 let dims = BlockDimensions::new(4, 4);
370 let mut output = vec![0u16; 16];
371
372 predictor.predict(&ctx, &mut output, 4, dims);
373
374 for &val in &output {
376 assert!(val >= 100 && val <= 200, "Value {} out of range", val);
377 }
378
379 assert!(output[0] >= output[15] - 50);
382 }
383
384 #[test]
385 fn test_smooth_v_prediction() {
386 let ctx = create_test_context();
387 let predictor = SmoothVPredictor::new();
388 let dims = BlockDimensions::new(4, 4);
389 let mut output = vec![0u16; 16];
390
391 predictor.predict(&ctx, &mut output, 4, dims);
392
393 for y in 0..4 {
395 let row_start = y * 4;
396 let first = output[row_start];
397 for x in 1..4 {
398 assert_eq!(output[row_start + x], first, "Row {} not uniform", y);
399 }
400 }
401
402 assert!(output[0] > output[12]);
404 }
405
406 #[test]
407 fn test_smooth_h_prediction() {
408 let ctx = create_test_context();
409 let predictor = SmoothHPredictor::new();
410 let dims = BlockDimensions::new(4, 4);
411 let mut output = vec![0u16; 16];
412
413 predictor.predict(&ctx, &mut output, 4, dims);
414
415 for x in 0..4 {
417 let first = output[x];
418 for y in 1..4 {
419 assert_eq!(output[y * 4 + x], first, "Column {} not uniform", x);
420 }
421 }
422
423 assert!(output[0] < output[3]);
425 }
426
427 #[test]
428 fn test_bilinear_interpolate() {
429 let result = bilinear_interpolate(100, 100, 100, 100, 128, 128);
431 assert_eq!(result, 100);
432
433 let result = bilinear_interpolate(200, 100, 100, 200, 128, 128);
435 assert!(result >= 140 && result <= 160);
436 }
437
438 #[test]
439 fn test_weight_interpolation() {
440 let w = weights::interpolate_weight(6, 0);
442 assert!(w > 200); let w = weights::interpolate_weight(6, 5);
445 assert!(w < 100); }
447
448 #[test]
449 fn test_smooth_rectangular_block() {
450 let ctx = create_test_context();
451 let predictor = SmoothPredictor::new();
452 let dims = BlockDimensions::new(8, 4);
453 let mut output = vec![0u16; 32];
454
455 predictor.predict(&ctx, &mut output, 8, dims);
456
457 for &val in &output {
459 assert!(val >= 100 && val <= 200);
460 }
461 }
462}