Coverage Report

Created: 2026-02-26 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rav1e-0.8.1/src/predict.rs
Line
Count
Source
1
// Copyright (c) 2017-2022, The rav1e contributors. All rights reserved
2
//
3
// This source code is subject to the terms of the BSD 2 Clause License and
4
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
5
// was not distributed with this source code in the LICENSE file, you can
6
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
7
// Media Patent License 1.0 was not distributed with this source code in the
8
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
9
10
#![allow(non_upper_case_globals)]
11
#![allow(non_camel_case_types)]
12
#![allow(dead_code)]
13
14
use std::mem::MaybeUninit;
15
16
cfg_if::cfg_if! {
17
  if #[cfg(nasm_x86_64)] {
18
    pub use crate::asm::x86::predict::*;
19
  } else if #[cfg(asm_neon)] {
20
    pub use crate::asm::aarch64::predict::*;
21
  } else {
22
    pub use self::rust::*;
23
  }
24
}
25
26
use aligned_vec::{avec, ABox};
27
28
use crate::context::{TileBlockOffset, MAX_SB_SIZE_LOG2, MAX_TX_SIZE};
29
use crate::cpu_features::CpuFeatureLevel;
30
use crate::encoder::FrameInvariants;
31
use crate::frame::*;
32
use crate::mc::*;
33
use crate::partition::*;
34
use crate::tiling::*;
35
use crate::transform::*;
36
use crate::util::*;
37
38
pub const ANGLE_STEP: i8 = 3;
39
40
// TODO: Review the order of this list.
41
// The order impacts compression efficiency.
42
pub static RAV1E_INTRA_MODES: &[PredictionMode] = &[
43
  PredictionMode::DC_PRED,
44
  PredictionMode::H_PRED,
45
  PredictionMode::V_PRED,
46
  PredictionMode::SMOOTH_PRED,
47
  PredictionMode::SMOOTH_H_PRED,
48
  PredictionMode::SMOOTH_V_PRED,
49
  PredictionMode::PAETH_PRED,
50
  PredictionMode::D45_PRED,
51
  PredictionMode::D135_PRED,
52
  PredictionMode::D113_PRED,
53
  PredictionMode::D157_PRED,
54
  PredictionMode::D203_PRED,
55
  PredictionMode::D67_PRED,
56
];
57
58
pub static RAV1E_INTER_MODES_MINIMAL: &[PredictionMode] =
59
  &[PredictionMode::NEARESTMV];
60
61
pub static RAV1E_INTER_COMPOUND_MODES: &[PredictionMode] = &[
62
  PredictionMode::GLOBAL_GLOBALMV,
63
  PredictionMode::NEAREST_NEARESTMV,
64
  PredictionMode::NEW_NEWMV,
65
  PredictionMode::NEAREST_NEWMV,
66
  PredictionMode::NEW_NEARESTMV,
67
  PredictionMode::NEAR_NEAR0MV,
68
  PredictionMode::NEAR_NEAR1MV,
69
  PredictionMode::NEAR_NEAR2MV,
70
];
71
72
// There are more modes than in the spec because every allowed
73
// drl index for NEAR modes is considered its own mode.
74
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Default)]
75
pub enum PredictionMode {
76
  #[default]
77
  DC_PRED, // Average of above and left pixels
78
  V_PRED,      // Vertical
79
  H_PRED,      // Horizontal
80
  D45_PRED,    // Directional 45  degree
81
  D135_PRED,   // Directional 135 degree
82
  D113_PRED,   // Directional 113 degree
83
  D157_PRED,   // Directional 157 degree
84
  D203_PRED,   // Directional 203 degree
85
  D67_PRED,    // Directional 67  degree
86
  SMOOTH_PRED, // Combination of horizontal and vertical interpolation
87
  SMOOTH_V_PRED,
88
  SMOOTH_H_PRED,
89
  PAETH_PRED,
90
  UV_CFL_PRED,
91
  NEARESTMV,
92
  NEAR0MV,
93
  NEAR1MV,
94
  NEAR2MV,
95
  GLOBALMV,
96
  NEWMV,
97
  // Compound ref compound modes
98
  NEAREST_NEARESTMV,
99
  NEAR_NEAR0MV,
100
  NEAR_NEAR1MV,
101
  NEAR_NEAR2MV,
102
  NEAREST_NEWMV,
103
  NEW_NEARESTMV,
104
  NEAR_NEW0MV,
105
  NEAR_NEW1MV,
106
  NEAR_NEW2MV,
107
  NEW_NEAR0MV,
108
  NEW_NEAR1MV,
109
  NEW_NEAR2MV,
110
  GLOBAL_GLOBALMV,
111
  NEW_NEWMV,
112
}
113
114
// This is a higher number than in the spec and cannot be used
115
// for bitstream writing purposes.
116
pub const PREDICTION_MODES: usize = 34;
117
118
#[derive(Copy, Clone, Debug)]
119
pub enum PredictionVariant {
120
  NONE,
121
  LEFT,
122
  TOP,
123
  BOTH,
124
}
125
126
impl PredictionVariant {
127
  #[inline]
128
0
  const fn new(x: usize, y: usize) -> Self {
129
0
    match (x, y) {
130
0
      (0, 0) => PredictionVariant::NONE,
131
0
      (_, 0) => PredictionVariant::LEFT,
132
0
      (0, _) => PredictionVariant::TOP,
133
0
      _ => PredictionVariant::BOTH,
134
    }
135
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionVariant>::new
Unexecuted instantiation: <rav1e::predict::PredictionVariant>::new
136
}
137
138
0
pub const fn intra_mode_to_angle(mode: PredictionMode) -> isize {
139
0
  match mode {
140
0
    PredictionMode::V_PRED => 90,
141
0
    PredictionMode::H_PRED => 180,
142
0
    PredictionMode::D45_PRED => 45,
143
0
    PredictionMode::D135_PRED => 135,
144
0
    PredictionMode::D113_PRED => 113,
145
0
    PredictionMode::D157_PRED => 157,
146
0
    PredictionMode::D203_PRED => 203,
147
0
    PredictionMode::D67_PRED => 67,
148
0
    _ => 0,
149
  }
150
0
}
151
152
impl PredictionMode {
153
  #[inline]
154
0
  pub fn is_compound(self) -> bool {
155
0
    self >= PredictionMode::NEAREST_NEARESTMV
156
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_compound
Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_compound
157
  #[inline]
158
0
  pub fn has_nearmv(self) -> bool {
159
0
    self == PredictionMode::NEAR0MV
160
0
      || self == PredictionMode::NEAR1MV
161
0
      || self == PredictionMode::NEAR2MV
162
0
      || self == PredictionMode::NEAR_NEAR0MV
163
0
      || self == PredictionMode::NEAR_NEAR1MV
164
0
      || self == PredictionMode::NEAR_NEAR2MV
165
0
      || self == PredictionMode::NEAR_NEW0MV
166
0
      || self == PredictionMode::NEAR_NEW1MV
167
0
      || self == PredictionMode::NEAR_NEW2MV
168
0
      || self == PredictionMode::NEW_NEAR0MV
169
0
      || self == PredictionMode::NEW_NEAR1MV
170
0
      || self == PredictionMode::NEW_NEAR2MV
171
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::has_nearmv
Unexecuted instantiation: <rav1e::predict::PredictionMode>::has_nearmv
172
  #[inline]
173
0
  pub fn has_newmv(self) -> bool {
174
0
    self == PredictionMode::NEWMV
175
0
      || self == PredictionMode::NEW_NEWMV
176
0
      || self == PredictionMode::NEAREST_NEWMV
177
0
      || self == PredictionMode::NEW_NEARESTMV
178
0
      || self == PredictionMode::NEAR_NEW0MV
179
0
      || self == PredictionMode::NEAR_NEW1MV
180
0
      || self == PredictionMode::NEAR_NEW2MV
181
0
      || self == PredictionMode::NEW_NEAR0MV
182
0
      || self == PredictionMode::NEW_NEAR1MV
183
0
      || self == PredictionMode::NEW_NEAR2MV
184
0
  }
185
  #[inline]
186
0
  pub fn ref_mv_idx(self) -> usize {
187
0
    if self == PredictionMode::NEAR0MV
188
0
      || self == PredictionMode::NEAR1MV
189
0
      || self == PredictionMode::NEAR2MV
190
    {
191
0
      self as usize - PredictionMode::NEAR0MV as usize + 1
192
0
    } else if self == PredictionMode::NEAR_NEAR0MV
193
0
      || self == PredictionMode::NEAR_NEAR1MV
194
0
      || self == PredictionMode::NEAR_NEAR2MV
195
    {
196
0
      self as usize - PredictionMode::NEAR_NEAR0MV as usize + 1
197
    } else {
198
0
      1
199
    }
200
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::ref_mv_idx
Unexecuted instantiation: <rav1e::predict::PredictionMode>::ref_mv_idx
201
202
  /// # Panics
203
  ///
204
  /// - If called on an inter `PredictionMode`
205
0
  pub fn predict_intra<T: Pixel>(
206
0
    self, tile_rect: TileRect, dst: &mut PlaneRegionMut<'_, T>,
207
0
    tx_size: TxSize, bit_depth: usize, ac: &[i16], intra_param: IntraParam,
208
0
    ief_params: Option<IntraEdgeFilterParameters>, edge_buf: &IntraEdge<T>,
209
0
    cpu: CpuFeatureLevel,
210
0
  ) {
211
0
    assert!(self.is_intra());
212
0
    let &Rect { x: frame_x, y: frame_y, .. } = dst.rect();
213
0
    debug_assert!(frame_x >= 0 && frame_y >= 0);
214
    // x and y are expressed relative to the tile
215
0
    let x = frame_x as usize - tile_rect.x;
216
0
    let y = frame_y as usize - tile_rect.y;
217
218
0
    let variant = PredictionVariant::new(x, y);
219
220
0
    let alpha = match intra_param {
221
0
      IntraParam::Alpha(val) => val,
222
0
      _ => 0,
223
    };
224
0
    let angle_delta = match intra_param {
225
0
      IntraParam::AngleDelta(val) => val,
226
0
      _ => 0,
227
    };
228
229
0
    let mode = match self {
230
0
      PredictionMode::PAETH_PRED => match variant {
231
0
        PredictionVariant::NONE => PredictionMode::DC_PRED,
232
0
        PredictionVariant::TOP => PredictionMode::V_PRED,
233
0
        PredictionVariant::LEFT => PredictionMode::H_PRED,
234
0
        PredictionVariant::BOTH => PredictionMode::PAETH_PRED,
235
      },
236
0
      PredictionMode::UV_CFL_PRED if alpha == 0 => PredictionMode::DC_PRED,
237
0
      _ => self,
238
    };
239
240
0
    let angle = match mode {
241
0
      PredictionMode::UV_CFL_PRED => alpha as isize,
242
0
      _ => intra_mode_to_angle(mode) + (angle_delta * ANGLE_STEP) as isize,
243
    };
244
245
0
    dispatch_predict_intra::<T>(
246
0
      mode, variant, dst, tx_size, bit_depth, ac, angle, ief_params, edge_buf,
247
0
      cpu,
248
    );
249
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_intra::<u16>
Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_intra::<u8>
250
251
  #[inline]
252
0
  pub fn is_intra(self) -> bool {
253
0
    self < PredictionMode::NEARESTMV
254
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_intra
Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_intra
255
256
  #[inline]
257
0
  pub fn is_cfl(self) -> bool {
258
0
    self == PredictionMode::UV_CFL_PRED
259
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_cfl
Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_cfl
260
261
  #[inline]
262
0
  pub fn is_directional(self) -> bool {
263
0
    self >= PredictionMode::V_PRED && self <= PredictionMode::D67_PRED
264
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_directional
Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_directional
265
266
  #[inline(always)]
267
0
  pub const fn angle_delta_count(self) -> i8 {
268
0
    match self {
269
      PredictionMode::V_PRED
270
      | PredictionMode::H_PRED
271
      | PredictionMode::D45_PRED
272
      | PredictionMode::D135_PRED
273
      | PredictionMode::D113_PRED
274
      | PredictionMode::D157_PRED
275
      | PredictionMode::D203_PRED
276
0
      | PredictionMode::D67_PRED => 7,
277
0
      _ => 1,
278
    }
279
0
  }
280
281
  // Used by inter prediction to extract the fractional component of a mv and
282
  // obtain the correct PlaneSlice to operate on.
283
  #[inline]
284
0
  fn get_mv_params<T: Pixel>(
285
0
    rec_plane: &Plane<T>, po: PlaneOffset, mv: MotionVector,
286
0
  ) -> (i32, i32, PlaneSlice<T>) {
287
0
    let &PlaneConfig { xdec, ydec, .. } = &rec_plane.cfg;
288
0
    let row_offset = mv.row as i32 >> (3 + ydec);
289
0
    let col_offset = mv.col as i32 >> (3 + xdec);
290
0
    let row_frac = ((mv.row as i32) << (1 - ydec)) & 0xf;
291
0
    let col_frac = ((mv.col as i32) << (1 - xdec)) & 0xf;
292
0
    let qo = PlaneOffset {
293
0
      x: po.x + col_offset as isize - 3,
294
0
      y: po.y + row_offset as isize - 3,
295
0
    };
296
0
    (row_frac, col_frac, rec_plane.slice(qo).clamp().subslice(3, 3))
297
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::get_mv_params::<u16>
Unexecuted instantiation: <rav1e::predict::PredictionMode>::get_mv_params::<u8>
298
299
  /// Inter prediction with a single reference (i.e. not compound mode)
300
  ///
301
  /// # Panics
302
  ///
303
  /// - If called on an intra `PredictionMode`
304
0
  pub fn predict_inter_single<T: Pixel>(
305
0
    self, fi: &FrameInvariants<T>, tile_rect: TileRect, p: usize,
306
0
    po: PlaneOffset, dst: &mut PlaneRegionMut<'_, T>, width: usize,
307
0
    height: usize, ref_frame: RefType, mv: MotionVector,
308
0
  ) {
309
0
    assert!(!self.is_intra());
310
0
    let frame_po = tile_rect.to_frame_plane_offset(po);
311
312
0
    let mode = fi.default_filter;
313
314
0
    if let Some(ref rec) =
315
0
      fi.rec_buffer.frames[fi.ref_frames[ref_frame.to_index()] as usize]
316
0
    {
317
0
      let (row_frac, col_frac, src) =
318
0
        PredictionMode::get_mv_params(&rec.frame.planes[p], frame_po, mv);
319
0
      put_8tap(
320
0
        dst,
321
0
        src,
322
0
        width,
323
0
        height,
324
0
        col_frac,
325
0
        row_frac,
326
0
        mode,
327
0
        mode,
328
0
        fi.sequence.bit_depth,
329
0
        fi.cpu_feature_level,
330
0
      );
331
0
    }
332
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter_single::<u16>
Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter_single::<u8>
333
334
  /// Inter prediction with two references.
335
  ///
336
  /// # Panics
337
  ///
338
  /// - If called on an intra `PredictionMode`
339
0
  pub fn predict_inter_compound<T: Pixel>(
340
0
    self, fi: &FrameInvariants<T>, tile_rect: TileRect, p: usize,
341
0
    po: PlaneOffset, dst: &mut PlaneRegionMut<'_, T>, width: usize,
342
0
    height: usize, ref_frames: [RefType; 2], mvs: [MotionVector; 2],
343
0
    buffer: &mut InterCompoundBuffers,
344
0
  ) {
345
0
    assert!(!self.is_intra());
346
0
    let frame_po = tile_rect.to_frame_plane_offset(po);
347
348
0
    let mode = fi.default_filter;
349
350
0
    for i in 0..2 {
351
0
      if let Some(ref rec) =
352
0
        fi.rec_buffer.frames[fi.ref_frames[ref_frames[i].to_index()] as usize]
353
0
      {
354
0
        let (row_frac, col_frac, src) = PredictionMode::get_mv_params(
355
0
          &rec.frame.planes[p],
356
0
          frame_po,
357
0
          mvs[i],
358
0
        );
359
0
        prep_8tap(
360
0
          buffer.get_buffer_mut(i),
361
0
          src,
362
0
          width,
363
0
          height,
364
0
          col_frac,
365
0
          row_frac,
366
0
          mode,
367
0
          mode,
368
0
          fi.sequence.bit_depth,
369
0
          fi.cpu_feature_level,
370
0
        );
371
0
      }
372
    }
373
0
    mc_avg(
374
0
      dst,
375
0
      buffer.get_buffer(0),
376
0
      buffer.get_buffer(1),
377
0
      width,
378
0
      height,
379
0
      fi.sequence.bit_depth,
380
0
      fi.cpu_feature_level,
381
    );
382
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter_compound::<u16>
Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter_compound::<u8>
383
384
  /// Inter prediction that determines whether compound mode is being used based
385
  /// on the second [`RefType`] in [`ref_frames`].
386
0
  pub fn predict_inter<T: Pixel>(
387
0
    self, fi: &FrameInvariants<T>, tile_rect: TileRect, p: usize,
388
0
    po: PlaneOffset, dst: &mut PlaneRegionMut<'_, T>, width: usize,
389
0
    height: usize, ref_frames: [RefType; 2], mvs: [MotionVector; 2],
390
0
    compound_buffer: &mut InterCompoundBuffers,
391
0
  ) {
392
0
    let is_compound = ref_frames[1] != RefType::INTRA_FRAME
393
0
      && ref_frames[1] != RefType::NONE_FRAME;
394
395
0
    if !is_compound {
396
0
      self.predict_inter_single(
397
0
        fi,
398
0
        tile_rect,
399
0
        p,
400
0
        po,
401
0
        dst,
402
0
        width,
403
0
        height,
404
0
        ref_frames[0],
405
0
        mvs[0],
406
      )
407
0
    } else {
408
0
      self.predict_inter_compound(
409
0
        fi,
410
0
        tile_rect,
411
0
        p,
412
0
        po,
413
0
        dst,
414
0
        width,
415
0
        height,
416
0
        ref_frames,
417
0
        mvs,
418
0
        compound_buffer,
419
0
      );
420
0
    }
421
0
  }
Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter::<u16>
Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter::<u8>
422
}
423
424
/// A pair of buffers holding the interpolation of two references. Use for
425
/// compound inter prediction.
426
#[derive(Debug)]
427
pub struct InterCompoundBuffers {
428
  data: ABox<[i16]>,
429
}
430
431
impl InterCompoundBuffers {
432
  // Size of one of the two buffers used.
433
  const BUFFER_SIZE: usize = 1 << (2 * MAX_SB_SIZE_LOG2);
434
435
  /// Get the buffer for eith
436
  #[inline]
437
0
  fn get_buffer_mut(&mut self, i: usize) -> &mut [i16] {
438
0
    match i {
439
0
      0 => &mut self.data[0..Self::BUFFER_SIZE],
440
0
      1 => &mut self.data[Self::BUFFER_SIZE..2 * Self::BUFFER_SIZE],
441
0
      _ => panic!(),
442
    }
443
0
  }
Unexecuted instantiation: <rav1e::predict::InterCompoundBuffers>::get_buffer_mut
Unexecuted instantiation: <rav1e::predict::InterCompoundBuffers>::get_buffer_mut
444
445
  #[inline]
446
0
  fn get_buffer(&self, i: usize) -> &[i16] {
447
0
    match i {
448
0
      0 => &self.data[0..Self::BUFFER_SIZE],
449
0
      1 => &self.data[Self::BUFFER_SIZE..2 * Self::BUFFER_SIZE],
450
0
      _ => panic!(),
451
    }
452
0
  }
Unexecuted instantiation: <rav1e::predict::InterCompoundBuffers>::get_buffer
Unexecuted instantiation: <rav1e::predict::InterCompoundBuffers>::get_buffer
453
}
454
455
impl Default for InterCompoundBuffers {
456
0
  fn default() -> Self {
457
0
    Self { data: avec![0; 2 * Self::BUFFER_SIZE].into_boxed_slice() }
458
0
  }
459
}
460
461
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
462
pub enum InterIntraMode {
463
  II_DC_PRED,
464
  II_V_PRED,
465
  II_H_PRED,
466
  II_SMOOTH_PRED,
467
  INTERINTRA_MODES,
468
}
469
470
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
471
pub enum CompoundType {
472
  COMPOUND_AVERAGE,
473
  COMPOUND_WEDGE,
474
  COMPOUND_DIFFWTD,
475
  COMPOUND_TYPES,
476
}
477
478
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
479
pub enum MotionMode {
480
  SIMPLE_TRANSLATION,
481
  OBMC_CAUSAL,   // 2-sided OBMC
482
  WARPED_CAUSAL, // 2-sided WARPED
483
  MOTION_MODES,
484
}
485
486
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
487
pub enum PaletteSize {
488
  TWO_COLORS,
489
  THREE_COLORS,
490
  FOUR_COLORS,
491
  FIVE_COLORS,
492
  SIX_COLORS,
493
  SEVEN_COLORS,
494
  EIGHT_COLORS,
495
  PALETTE_SIZES,
496
}
497
498
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
499
pub enum PaletteColor {
500
  PALETTE_COLOR_ONE,
501
  PALETTE_COLOR_TWO,
502
  PALETTE_COLOR_THREE,
503
  PALETTE_COLOR_FOUR,
504
  PALETTE_COLOR_FIVE,
505
  PALETTE_COLOR_SIX,
506
  PALETTE_COLOR_SEVEN,
507
  PALETTE_COLOR_EIGHT,
508
  PALETTE_COLORS,
509
}
510
511
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
512
pub enum FilterIntraMode {
513
  FILTER_DC_PRED,
514
  FILTER_V_PRED,
515
  FILTER_H_PRED,
516
  FILTER_D157_PRED,
517
  FILTER_PAETH_PRED,
518
  FILTER_INTRA_MODES,
519
}
520
521
#[derive(Copy, Clone, Debug)]
522
pub enum IntraParam {
523
  AngleDelta(i8),
524
  Alpha(i16),
525
  None,
526
}
527
528
#[derive(Debug, Clone, Copy, Default)]
529
pub struct AngleDelta {
530
  pub y: i8,
531
  pub uv: i8,
532
}
533
534
#[derive(Copy, Clone, Default)]
535
pub struct IntraEdgeFilterParameters {
536
  pub plane: usize,
537
  pub above_ref_frame_types: Option<[RefType; 2]>,
538
  pub left_ref_frame_types: Option<[RefType; 2]>,
539
  pub above_mode: Option<PredictionMode>,
540
  pub left_mode: Option<PredictionMode>,
541
}
542
543
impl IntraEdgeFilterParameters {
544
0
  pub fn new(
545
0
    plane: usize, above_ctx: Option<CodedBlockInfo>,
546
0
    left_ctx: Option<CodedBlockInfo>,
547
0
  ) -> Self {
548
    IntraEdgeFilterParameters {
549
0
      plane,
550
0
      above_mode: match above_ctx {
551
0
        Some(bi) => match plane {
552
0
          0 => bi.luma_mode,
553
0
          _ => bi.chroma_mode,
554
        }
555
0
        .into(),
556
0
        None => None,
557
      },
558
0
      left_mode: match left_ctx {
559
0
        Some(bi) => match plane {
560
0
          0 => bi.luma_mode,
561
0
          _ => bi.chroma_mode,
562
        }
563
0
        .into(),
564
0
        None => None,
565
      },
566
0
      above_ref_frame_types: above_ctx.map(|bi| bi.reference_types),
567
0
      left_ref_frame_types: left_ctx.map(|bi| bi.reference_types),
568
    }
569
0
  }
570
571
  /// # Panics
572
  ///
573
  /// - If the appropriate ref frame types are not set on `self`
574
0
  pub fn use_smooth_filter(self) -> bool {
575
0
    let above_smooth = match self.above_mode {
576
      Some(PredictionMode::SMOOTH_PRED)
577
      | Some(PredictionMode::SMOOTH_V_PRED)
578
      | Some(PredictionMode::SMOOTH_H_PRED) => {
579
0
        self.plane == 0
580
0
          || self.above_ref_frame_types.unwrap()[0] == RefType::INTRA_FRAME
581
      }
582
0
      _ => false,
583
    };
584
585
0
    let left_smooth = match self.left_mode {
586
      Some(PredictionMode::SMOOTH_PRED)
587
      | Some(PredictionMode::SMOOTH_V_PRED)
588
      | Some(PredictionMode::SMOOTH_H_PRED) => {
589
0
        self.plane == 0
590
0
          || self.left_ref_frame_types.unwrap()[0] == RefType::INTRA_FRAME
591
      }
592
0
      _ => false,
593
    };
594
595
0
    above_smooth || left_smooth
596
0
  }
597
}
598
599
// Weights are quadratic from '1' to '1 / block_size', scaled by 2^sm_weight_log2_scale.
600
const sm_weight_log2_scale: u8 = 8;
601
602
// Smooth predictor weights
603
#[rustfmt::skip]
604
static sm_weight_arrays: [u8; 2 * MAX_TX_SIZE] = [
605
    // Unused, because we always offset by bs, which is at least 2.
606
    0, 0,
607
    // bs = 2
608
    255, 128,
609
    // bs = 4
610
    255, 149, 85, 64,
611
    // bs = 8
612
    255, 197, 146, 105, 73, 50, 37, 32,
613
    // bs = 16
614
    255, 225, 196, 170, 145, 123, 102, 84, 68, 54, 43, 33, 26, 20, 17, 16,
615
    // bs = 32
616
    255, 240, 225, 210, 196, 182, 169, 157, 145, 133, 122, 111, 101, 92, 83, 74,
617
    66, 59, 52, 45, 39, 34, 29, 25, 21, 17, 14, 12, 10, 9, 8, 8,
618
    // bs = 64
619
    255, 248, 240, 233, 225, 218, 210, 203, 196, 189, 182, 176, 169, 163, 156,
620
    150, 144, 138, 133, 127, 121, 116, 111, 106, 101, 96, 91, 86, 82, 77, 73, 69,
621
    65, 61, 57, 54, 50, 47, 44, 41, 38, 35, 32, 29, 27, 25, 22, 20, 18, 16, 15,
622
    13, 12, 10, 9, 8, 7, 6, 6, 5, 5, 4, 4, 4,
623
];
624
625
#[inline(always)]
626
0
const fn get_scaled_luma_q0(alpha_q3: i16, ac_pred_q3: i16) -> i32 {
627
0
  let scaled_luma_q6 = (alpha_q3 as i32) * (ac_pred_q3 as i32);
628
0
  let abs_scaled_luma_q0 = (scaled_luma_q6.abs() + 32) >> 6;
629
0
  if scaled_luma_q6 < 0 {
630
0
    -abs_scaled_luma_q0
631
  } else {
632
0
    abs_scaled_luma_q0
633
  }
634
0
}
635
636
/// # Returns
637
///
638
/// Initialized luma AC coefficients
639
///
640
/// # Panics
641
///
642
/// - If the block size is invalid for subsampling
643
///
644
0
pub fn luma_ac<'ac, T: Pixel>(
645
0
  ac: &'ac mut [MaybeUninit<i16>], ts: &mut TileStateMut<'_, T>,
646
0
  tile_bo: TileBlockOffset, bsize: BlockSize, tx_size: TxSize,
647
0
  fi: &FrameInvariants<T>,
648
0
) -> &'ac mut [i16] {
649
  use crate::context::MI_SIZE_LOG2;
650
651
0
  let PlaneConfig { xdec, ydec, .. } = ts.input.planes[1].cfg;
652
0
  let plane_bsize = bsize.subsampled_size(xdec, ydec).unwrap();
653
654
  // ensure ac has the right length, so there aren't any uninitialized elements at the end
655
0
  let ac = &mut ac[..plane_bsize.area()];
656
657
0
  let bo = if bsize.is_sub8x8(xdec, ydec) {
658
0
    let offset = bsize.sub8x8_offset(xdec, ydec);
659
0
    tile_bo.with_offset(offset.0, offset.1)
660
  } else {
661
0
    tile_bo
662
  };
663
0
  let rec = &ts.rec.planes[0];
664
0
  let luma = &rec.subregion(Area::BlockStartingAt { bo: bo.0 });
665
0
  let frame_bo = ts.to_frame_block_offset(bo);
666
667
0
  let frame_clipped_bw: usize =
668
0
    ((fi.w_in_b - frame_bo.0.x) << MI_SIZE_LOG2).min(bsize.width());
669
0
  let frame_clipped_bh: usize =
670
0
    ((fi.h_in_b - frame_bo.0.y) << MI_SIZE_LOG2).min(bsize.height());
671
672
  // Similar to 'MaxLumaW' and 'MaxLumaH' stated in https://aomediacodec.github.io/av1-spec/#transform-block-semantics
673
0
  let max_luma_w = if bsize.width() > BlockSize::BLOCK_8X8.width() {
674
0
    let txw_log2 = tx_size.width_log2();
675
0
    ((frame_clipped_bw + (1 << txw_log2) - 1) >> txw_log2) << txw_log2
676
  } else {
677
0
    bsize.width()
678
  };
679
0
  let max_luma_h = if bsize.height() > BlockSize::BLOCK_8X8.height() {
680
0
    let txh_log2 = tx_size.height_log2();
681
0
    ((frame_clipped_bh + (1 << txh_log2) - 1) >> txh_log2) << txh_log2
682
  } else {
683
0
    bsize.height()
684
  };
685
686
0
  let w_pad = (bsize.width() - max_luma_w) >> (2 + xdec);
687
0
  let h_pad = (bsize.height() - max_luma_h) >> (2 + ydec);
688
0
  let cpu = fi.cpu_feature_level;
689
690
0
  (match (xdec, ydec) {
691
0
    (0, 0) => pred_cfl_ac::<T, 0, 0>,
692
0
    (1, 0) => pred_cfl_ac::<T, 1, 0>,
693
0
    (_, _) => pred_cfl_ac::<T, 1, 1>,
694
0
  })(ac, luma, plane_bsize, w_pad, h_pad, cpu);
695
696
  // SAFETY: it relies on individual pred_cfl_ac implementations to initialize the ac
697
0
  unsafe { slice_assume_init_mut(ac) }
698
0
}
Unexecuted instantiation: rav1e::predict::luma_ac::<u16>
Unexecuted instantiation: rav1e::predict::luma_ac::<u8>
699
700
pub(crate) mod rust {
701
  use super::*;
702
  use std::mem::size_of;
703
704
  #[inline(always)]
705
0
  pub fn dispatch_predict_intra<T: Pixel>(
706
0
    mode: PredictionMode, variant: PredictionVariant,
707
0
    dst: &mut PlaneRegionMut<'_, T>, tx_size: TxSize, bit_depth: usize,
708
0
    ac: &[i16], angle: isize, ief_params: Option<IntraEdgeFilterParameters>,
709
0
    edge_buf: &IntraEdge<T>, _cpu: CpuFeatureLevel,
710
0
  ) {
711
0
    let width = tx_size.width();
712
0
    let height = tx_size.height();
713
714
    // left pixels are ordered from bottom to top and right-aligned
715
0
    let (left, top_left, above) = edge_buf.as_slices();
716
717
0
    let above_slice = above;
718
0
    let left_slice = &left[left.len().saturating_sub(height)..];
719
0
    let left_and_left_below_slice =
720
0
      &left[left.len().saturating_sub(width + height)..];
721
722
0
    match mode {
723
      PredictionMode::DC_PRED => {
724
0
        (match variant {
725
0
          PredictionVariant::NONE => pred_dc_128,
726
0
          PredictionVariant::LEFT => pred_dc_left,
727
0
          PredictionVariant::TOP => pred_dc_top,
728
0
          PredictionVariant::BOTH => pred_dc,
729
0
        })(dst, above_slice, left_slice, width, height, bit_depth)
730
      }
731
0
      PredictionMode::V_PRED if angle == 90 => {
732
0
        pred_v(dst, above_slice, width, height)
733
      }
734
0
      PredictionMode::H_PRED if angle == 180 => {
735
0
        pred_h(dst, left_slice, width, height)
736
      }
737
      PredictionMode::H_PRED
738
      | PredictionMode::V_PRED
739
      | PredictionMode::D45_PRED
740
      | PredictionMode::D135_PRED
741
      | PredictionMode::D113_PRED
742
      | PredictionMode::D157_PRED
743
      | PredictionMode::D203_PRED
744
0
      | PredictionMode::D67_PRED => pred_directional(
745
0
        dst,
746
0
        above_slice,
747
0
        left_and_left_below_slice,
748
0
        top_left,
749
0
        angle as usize,
750
0
        width,
751
0
        height,
752
0
        bit_depth,
753
0
        ief_params,
754
      ),
755
      PredictionMode::SMOOTH_PRED => {
756
0
        pred_smooth(dst, above_slice, left_slice, width, height)
757
      }
758
      PredictionMode::SMOOTH_V_PRED => {
759
0
        pred_smooth_v(dst, above_slice, left_slice, width, height)
760
      }
761
      PredictionMode::SMOOTH_H_PRED => {
762
0
        pred_smooth_h(dst, above_slice, left_slice, width, height)
763
      }
764
      PredictionMode::PAETH_PRED => {
765
0
        pred_paeth(dst, above_slice, left_slice, top_left[0], width, height)
766
      }
767
0
      PredictionMode::UV_CFL_PRED => (match variant {
768
0
        PredictionVariant::NONE => pred_cfl_128,
769
0
        PredictionVariant::LEFT => pred_cfl_left,
770
0
        PredictionVariant::TOP => pred_cfl_top,
771
0
        PredictionVariant::BOTH => pred_cfl,
772
      })(
773
0
        dst,
774
0
        ac,
775
0
        angle as i16,
776
0
        above_slice,
777
0
        left_slice,
778
0
        width,
779
0
        height,
780
0
        bit_depth,
781
      ),
782
0
      _ => unimplemented!(),
783
    }
784
0
  }
Unexecuted instantiation: rav1e::predict::rust::dispatch_predict_intra::<u16>
Unexecuted instantiation: rav1e::predict::rust::dispatch_predict_intra::<u8>
785
786
0
  pub(crate) fn pred_dc<T: Pixel>(
787
0
    output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T], width: usize,
788
0
    height: usize, _bit_depth: usize,
789
0
  ) {
790
0
    let edges = left[..height].iter().chain(above[..width].iter());
791
0
    let len = (width + height) as u32;
792
0
    let avg = (edges.fold(0u32, |acc, &v| {
793
0
      let v: u32 = v.into();
794
0
      v + acc
795
0
    }) + (len >> 1))
Unexecuted instantiation: rav1e::predict::rust::pred_dc::<u16>::{closure#0}
Unexecuted instantiation: rav1e::predict::rust::pred_dc::<u8>::{closure#0}
796
0
      / len;
797
0
    let avg = T::cast_from(avg);
798
799
0
    for line in output.rows_iter_mut().take(height) {
800
0
      line[..width].fill(avg);
801
0
    }
802
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_dc::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_dc::<u8>
803
804
0
  pub(crate) fn pred_dc_128<T: Pixel>(
805
0
    output: &mut PlaneRegionMut<'_, T>, _above: &[T], _left: &[T],
806
0
    width: usize, height: usize, bit_depth: usize,
807
0
  ) {
808
0
    let v = T::cast_from(128u32 << (bit_depth - 8));
809
0
    for line in output.rows_iter_mut().take(height) {
810
0
      line[..width].fill(v);
811
0
    }
812
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_dc_128::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_dc_128::<u8>
813
814
0
  pub(crate) fn pred_dc_left<T: Pixel>(
815
0
    output: &mut PlaneRegionMut<'_, T>, _above: &[T], left: &[T],
816
0
    width: usize, height: usize, _bit_depth: usize,
817
0
  ) {
818
0
    let sum = left[..].iter().fold(0u32, |acc, &v| {
819
0
      let v: u32 = v.into();
820
0
      v + acc
821
0
    });
Unexecuted instantiation: rav1e::predict::rust::pred_dc_left::<u16>::{closure#0}
Unexecuted instantiation: rav1e::predict::rust::pred_dc_left::<u8>::{closure#0}
822
0
    let avg = T::cast_from((sum + (height >> 1) as u32) / height as u32);
823
0
    for line in output.rows_iter_mut().take(height) {
824
0
      line[..width].fill(avg);
825
0
    }
826
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_dc_left::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_dc_left::<u8>
827
828
0
  pub(crate) fn pred_dc_top<T: Pixel>(
829
0
    output: &mut PlaneRegionMut<'_, T>, above: &[T], _left: &[T],
830
0
    width: usize, height: usize, _bit_depth: usize,
831
0
  ) {
832
0
    let sum = above[..width].iter().fold(0u32, |acc, &v| {
833
0
      let v: u32 = v.into();
834
0
      v + acc
835
0
    });
Unexecuted instantiation: rav1e::predict::rust::pred_dc_top::<u16>::{closure#0}
Unexecuted instantiation: rav1e::predict::rust::pred_dc_top::<u8>::{closure#0}
836
0
    let avg = T::cast_from((sum + (width >> 1) as u32) / width as u32);
837
0
    for line in output.rows_iter_mut().take(height) {
838
0
      line[..width].fill(avg);
839
0
    }
840
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_dc_top::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_dc_top::<u8>
841
842
0
  pub(crate) fn pred_h<T: Pixel>(
843
0
    output: &mut PlaneRegionMut<'_, T>, left: &[T], width: usize,
844
0
    height: usize,
845
0
  ) {
846
0
    for (line, l) in output.rows_iter_mut().zip(left[..height].iter().rev()) {
847
0
      line[..width].fill(*l);
848
0
    }
849
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_h::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_h::<u8>
850
851
0
  pub(crate) fn pred_v<T: Pixel>(
852
0
    output: &mut PlaneRegionMut<'_, T>, above: &[T], width: usize,
853
0
    height: usize,
854
0
  ) {
855
0
    for line in output.rows_iter_mut().take(height) {
856
0
      line[..width].copy_from_slice(&above[..width])
857
    }
858
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_v::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_v::<u8>
859
860
0
  pub(crate) fn pred_paeth<T: Pixel>(
861
0
    output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T],
862
0
    above_left: T, width: usize, height: usize,
863
0
  ) {
864
0
    for r in 0..height {
865
0
      let row = &mut output[r];
866
0
      for c in 0..width {
867
        // Top-left pixel is fixed in libaom
868
0
        let raw_top_left: i32 = above_left.into();
869
0
        let raw_left: i32 = left[height - 1 - r].into();
870
0
        let raw_top: i32 = above[c].into();
871
872
0
        let p_base = raw_top + raw_left - raw_top_left;
873
0
        let p_left = (p_base - raw_left).abs();
874
0
        let p_top = (p_base - raw_top).abs();
875
0
        let p_top_left = (p_base - raw_top_left).abs();
876
877
        // Return nearest to base of left, top and top_left
878
0
        if p_left <= p_top && p_left <= p_top_left {
879
0
          row[c] = T::cast_from(raw_left);
880
0
        } else if p_top <= p_top_left {
881
0
          row[c] = T::cast_from(raw_top);
882
0
        } else {
883
0
          row[c] = T::cast_from(raw_top_left);
884
0
        }
885
      }
886
    }
887
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_paeth::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_paeth::<u8>
888
889
0
  pub(crate) fn pred_smooth<T: Pixel>(
890
0
    output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T], width: usize,
891
0
    height: usize,
892
0
  ) {
893
0
    let below_pred = left[0]; // estimated by bottom-left pixel
894
0
    let right_pred = above[width - 1]; // estimated by top-right pixel
895
0
    let sm_weights_w = &sm_weight_arrays[width..];
896
0
    let sm_weights_h = &sm_weight_arrays[height..];
897
898
0
    let log2_scale = 1 + sm_weight_log2_scale;
899
0
    let scale = 1_u16 << sm_weight_log2_scale;
900
901
    // Weights sanity checks
902
0
    assert!((sm_weights_w[0] as u16) < scale);
903
0
    assert!((sm_weights_h[0] as u16) < scale);
904
0
    assert!((scale - sm_weights_w[width - 1] as u16) < scale);
905
0
    assert!((scale - sm_weights_h[height - 1] as u16) < scale);
906
    // ensures no overflow when calculating predictor
907
0
    assert!(log2_scale as usize + size_of::<T>() < 31);
908
909
0
    for r in 0..height {
910
0
      let row = &mut output[r];
911
0
      for c in 0..width {
912
0
        let pixels = [above[c], below_pred, left[height - 1 - r], right_pred];
913
914
0
        let weights = [
915
0
          sm_weights_h[r] as u16,
916
0
          scale - sm_weights_h[r] as u16,
917
0
          sm_weights_w[c] as u16,
918
0
          scale - sm_weights_w[c] as u16,
919
0
        ];
920
921
0
        assert!(
922
0
          scale >= (sm_weights_h[r] as u16)
923
0
            && scale >= (sm_weights_w[c] as u16)
924
        );
925
926
        // Sum up weighted pixels
927
0
        let mut this_pred: u32 = weights
928
0
          .iter()
929
0
          .zip(pixels.iter())
930
0
          .map(|(w, p)| {
931
0
            let p: u32 = (*p).into();
932
0
            (*w as u32) * p
933
0
          })
Unexecuted instantiation: rav1e::predict::rust::pred_smooth::<u16>::{closure#0}
Unexecuted instantiation: rav1e::predict::rust::pred_smooth::<u8>::{closure#0}
934
0
          .sum();
935
0
        this_pred = (this_pred + (1 << (log2_scale - 1))) >> log2_scale;
936
937
0
        row[c] = T::cast_from(this_pred);
938
      }
939
    }
940
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_smooth::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_smooth::<u8>
941
942
0
  pub(crate) fn pred_smooth_h<T: Pixel>(
943
0
    output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T], width: usize,
944
0
    height: usize,
945
0
  ) {
946
0
    let right_pred = above[width - 1]; // estimated by top-right pixel
947
0
    let sm_weights = &sm_weight_arrays[width..];
948
949
0
    let log2_scale = sm_weight_log2_scale;
950
0
    let scale = 1_u16 << sm_weight_log2_scale;
951
952
    // Weights sanity checks
953
0
    assert!((sm_weights[0] as u16) < scale);
954
0
    assert!((scale - sm_weights[width - 1] as u16) < scale);
955
    // ensures no overflow when calculating predictor
956
0
    assert!(log2_scale as usize + size_of::<T>() < 31);
957
958
0
    for r in 0..height {
959
0
      let row = &mut output[r];
960
0
      for c in 0..width {
961
0
        let pixels = [left[height - 1 - r], right_pred];
962
0
        let weights = [sm_weights[c] as u16, scale - sm_weights[c] as u16];
963
964
0
        assert!(scale >= sm_weights[c] as u16);
965
966
0
        let mut this_pred: u32 = weights
967
0
          .iter()
968
0
          .zip(pixels.iter())
969
0
          .map(|(w, p)| {
970
0
            let p: u32 = (*p).into();
971
0
            (*w as u32) * p
972
0
          })
Unexecuted instantiation: rav1e::predict::rust::pred_smooth_h::<u16>::{closure#0}
Unexecuted instantiation: rav1e::predict::rust::pred_smooth_h::<u8>::{closure#0}
973
0
          .sum();
974
0
        this_pred = (this_pred + (1 << (log2_scale - 1))) >> log2_scale;
975
976
0
        row[c] = T::cast_from(this_pred);
977
      }
978
    }
979
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_smooth_h::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_smooth_h::<u8>
980
981
0
  pub(crate) fn pred_smooth_v<T: Pixel>(
982
0
    output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T], width: usize,
983
0
    height: usize,
984
0
  ) {
985
0
    let below_pred = left[0]; // estimated by bottom-left pixel
986
0
    let sm_weights = &sm_weight_arrays[height..];
987
988
0
    let log2_scale = sm_weight_log2_scale;
989
0
    let scale = 1_u16 << sm_weight_log2_scale;
990
991
    // Weights sanity checks
992
0
    assert!((sm_weights[0] as u16) < scale);
993
0
    assert!((scale - sm_weights[height - 1] as u16) < scale);
994
    // ensures no overflow when calculating predictor
995
0
    assert!(log2_scale as usize + size_of::<T>() < 31);
996
997
0
    for r in 0..height {
998
0
      let row = &mut output[r];
999
0
      for c in 0..width {
1000
0
        let pixels = [above[c], below_pred];
1001
0
        let weights = [sm_weights[r] as u16, scale - sm_weights[r] as u16];
1002
1003
0
        assert!(scale >= sm_weights[r] as u16);
1004
1005
0
        let mut this_pred: u32 = weights
1006
0
          .iter()
1007
0
          .zip(pixels.iter())
1008
0
          .map(|(w, p)| {
1009
0
            let p: u32 = (*p).into();
1010
0
            (*w as u32) * p
1011
0
          })
Unexecuted instantiation: rav1e::predict::rust::pred_smooth_v::<u16>::{closure#0}
Unexecuted instantiation: rav1e::predict::rust::pred_smooth_v::<u8>::{closure#0}
1012
0
          .sum();
1013
0
        this_pred = (this_pred + (1 << (log2_scale - 1))) >> log2_scale;
1014
1015
0
        row[c] = T::cast_from(this_pred);
1016
      }
1017
    }
1018
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_smooth_v::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_smooth_v::<u8>
1019
1020
0
  pub(crate) fn pred_cfl_ac<T: Pixel, const XDEC: usize, const YDEC: usize>(
1021
0
    ac: &mut [MaybeUninit<i16>], luma: &PlaneRegion<'_, T>,
1022
0
    plane_bsize: BlockSize, w_pad: usize, h_pad: usize, _cpu: CpuFeatureLevel,
1023
0
  ) {
1024
0
    let max_luma_w = (plane_bsize.width() - w_pad * 4) << XDEC;
1025
0
    let max_luma_h = (plane_bsize.height() - h_pad * 4) << YDEC;
1026
0
    let max_luma_x: usize = max_luma_w.max(8) - (1 << XDEC);
1027
0
    let max_luma_y: usize = max_luma_h.max(8) - (1 << YDEC);
1028
0
    let mut sum: i32 = 0;
1029
1030
0
    let ac = &mut ac[..plane_bsize.area()];
1031
1032
0
    for (sub_y, ac_rows) in
1033
0
      ac.chunks_exact_mut(plane_bsize.width()).enumerate()
1034
    {
1035
0
      for (sub_x, ac_item) in ac_rows.iter_mut().enumerate() {
1036
        // Refer to https://aomediacodec.github.io/av1-spec/#predict-chroma-from-luma-process
1037
0
        let luma_y = sub_y << YDEC;
1038
0
        let luma_x = sub_x << XDEC;
1039
0
        let y = luma_y.min(max_luma_y);
1040
0
        let x = luma_x.min(max_luma_x);
1041
0
        let mut sample: i16 = i16::cast_from(luma[y][x]);
1042
0
        if XDEC != 0 {
1043
0
          sample += i16::cast_from(luma[y][x + 1]);
1044
0
        }
1045
0
        if YDEC != 0 {
1046
0
          debug_assert!(XDEC != 0);
1047
0
          sample += i16::cast_from(luma[y + 1][x])
1048
0
            + i16::cast_from(luma[y + 1][x + 1]);
1049
0
        }
1050
0
        sample <<= 3 - XDEC - YDEC;
1051
0
        ac_item.write(sample);
1052
0
        sum += sample as i32;
1053
      }
1054
    }
1055
    // SAFETY: the loop above has initialized all items
1056
0
    let ac = unsafe { slice_assume_init_mut(ac) };
1057
0
    let shift = plane_bsize.width_log2() + plane_bsize.height_log2();
1058
0
    let average = ((sum + (1 << (shift - 1))) >> shift) as i16;
1059
1060
0
    for val in ac {
1061
0
      *val -= average;
1062
0
    }
1063
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u16, 0, 0>
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u16, 1, 1>
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u16, 1, 0>
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u8, 0, 0>
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u8, 1, 1>
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u8, 1, 0>
1064
1065
0
  pub(crate) fn pred_cfl_inner<T: Pixel>(
1066
0
    output: &mut PlaneRegionMut<'_, T>, ac: &[i16], alpha: i16, width: usize,
1067
0
    height: usize, bit_depth: usize,
1068
0
  ) {
1069
0
    if alpha == 0 {
1070
0
      return;
1071
0
    }
1072
0
    debug_assert!(ac.len() >= width * height);
1073
0
    assert!(output.plane_cfg.stride >= width);
1074
0
    assert!(output.rows_iter().len() >= height);
1075
1076
0
    let sample_max = (1 << bit_depth) - 1;
1077
0
    let avg: i32 = output[0][0].into();
1078
1079
0
    for (line, luma) in
1080
0
      output.rows_iter_mut().zip(ac.chunks_exact(width)).take(height)
1081
    {
1082
0
      for (v, &l) in line[..width].iter_mut().zip(luma[..width].iter()) {
1083
0
        *v = T::cast_from(
1084
0
          (avg + get_scaled_luma_q0(alpha, l)).clamp(0, sample_max),
1085
0
        );
1086
0
      }
1087
    }
1088
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_inner::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_inner::<u8>
1089
1090
0
  pub(crate) fn pred_cfl<T: Pixel>(
1091
0
    output: &mut PlaneRegionMut<'_, T>, ac: &[i16], alpha: i16, above: &[T],
1092
0
    left: &[T], width: usize, height: usize, bit_depth: usize,
1093
0
  ) {
1094
0
    pred_dc(output, above, left, width, height, bit_depth);
1095
0
    pred_cfl_inner(output, ac, alpha, width, height, bit_depth);
1096
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_cfl::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_cfl::<u8>
1097
1098
0
  pub(crate) fn pred_cfl_128<T: Pixel>(
1099
0
    output: &mut PlaneRegionMut<'_, T>, ac: &[i16], alpha: i16, above: &[T],
1100
0
    left: &[T], width: usize, height: usize, bit_depth: usize,
1101
0
  ) {
1102
0
    pred_dc_128(output, above, left, width, height, bit_depth);
1103
0
    pred_cfl_inner(output, ac, alpha, width, height, bit_depth);
1104
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_128::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_128::<u8>
1105
1106
0
  pub(crate) fn pred_cfl_left<T: Pixel>(
1107
0
    output: &mut PlaneRegionMut<'_, T>, ac: &[i16], alpha: i16, above: &[T],
1108
0
    left: &[T], width: usize, height: usize, bit_depth: usize,
1109
0
  ) {
1110
0
    pred_dc_left(output, above, left, width, height, bit_depth);
1111
0
    pred_cfl_inner(output, ac, alpha, width, height, bit_depth);
1112
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_left::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_left::<u8>
1113
1114
0
  pub(crate) fn pred_cfl_top<T: Pixel>(
1115
0
    output: &mut PlaneRegionMut<'_, T>, ac: &[i16], alpha: i16, above: &[T],
1116
0
    left: &[T], width: usize, height: usize, bit_depth: usize,
1117
0
  ) {
1118
0
    pred_dc_top(output, above, left, width, height, bit_depth);
1119
0
    pred_cfl_inner(output, ac, alpha, width, height, bit_depth);
1120
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_top::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_cfl_top::<u8>
1121
1122
  #[allow(clippy::collapsible_if)]
1123
  #[allow(clippy::collapsible_else_if)]
1124
  #[allow(clippy::needless_return)]
1125
0
  pub(crate) const fn select_ief_strength(
1126
0
    width: usize, height: usize, smooth_filter: bool, angle_delta: isize,
1127
0
  ) -> u8 {
1128
0
    let block_wh = width + height;
1129
0
    let abs_delta = angle_delta.unsigned_abs();
1130
1131
0
    if smooth_filter {
1132
0
      if block_wh <= 8 {
1133
0
        if abs_delta >= 64 {
1134
0
          return 2;
1135
0
        }
1136
0
        if abs_delta >= 40 {
1137
0
          return 1;
1138
0
        }
1139
0
      } else if block_wh <= 16 {
1140
0
        if abs_delta >= 48 {
1141
0
          return 2;
1142
0
        }
1143
0
        if abs_delta >= 20 {
1144
0
          return 1;
1145
0
        }
1146
0
      } else if block_wh <= 24 {
1147
0
        if abs_delta >= 4 {
1148
0
          return 3;
1149
0
        }
1150
      } else {
1151
0
        return 3;
1152
      }
1153
    } else {
1154
0
      if block_wh <= 8 {
1155
0
        if abs_delta >= 56 {
1156
0
          return 1;
1157
0
        }
1158
0
      } else if block_wh <= 16 {
1159
0
        if abs_delta >= 40 {
1160
0
          return 1;
1161
0
        }
1162
0
      } else if block_wh <= 24 {
1163
0
        if abs_delta >= 32 {
1164
0
          return 3;
1165
0
        }
1166
0
        if abs_delta >= 16 {
1167
0
          return 2;
1168
0
        }
1169
0
        if abs_delta >= 8 {
1170
0
          return 1;
1171
0
        }
1172
0
      } else if block_wh <= 32 {
1173
0
        if abs_delta >= 32 {
1174
0
          return 3;
1175
0
        }
1176
0
        if abs_delta >= 4 {
1177
0
          return 2;
1178
0
        }
1179
0
        return 1;
1180
      } else {
1181
0
        return 3;
1182
      }
1183
    }
1184
1185
0
    return 0;
1186
0
  }
1187
1188
0
  pub(crate) const fn select_ief_upsample(
1189
0
    width: usize, height: usize, smooth_filter: bool, angle_delta: isize,
1190
0
  ) -> bool {
1191
0
    let block_wh = width + height;
1192
0
    let abs_delta = angle_delta.unsigned_abs();
1193
1194
0
    if abs_delta == 0 || abs_delta >= 40 {
1195
0
      false
1196
0
    } else if smooth_filter {
1197
0
      block_wh <= 8
1198
    } else {
1199
0
      block_wh <= 16
1200
    }
1201
0
  }
1202
1203
0
  pub(crate) fn filter_edge<T: Pixel>(
1204
0
    size: usize, strength: u8, edge: &mut [T],
1205
0
  ) {
1206
    const INTRA_EDGE_KERNEL: [[u32; 5]; 3] =
1207
      [[0, 4, 8, 4, 0], [0, 5, 6, 5, 0], [2, 4, 4, 4, 2]];
1208
1209
0
    if strength == 0 {
1210
0
      return;
1211
0
    }
1212
1213
    // Copy the edge buffer to avoid predicting from
1214
    // just-filtered samples.
1215
0
    let mut edge_filtered = [MaybeUninit::<T>::uninit(); MAX_TX_SIZE * 4 + 1];
1216
0
    let edge_filtered =
1217
0
      init_slice_repeat_mut(&mut edge_filtered[..edge.len()], T::zero());
1218
0
    edge_filtered.copy_from_slice(&edge[..edge.len()]);
1219
1220
0
    for i in 1..size {
1221
0
      let mut s = 0;
1222
1223
0
      for j in 0..INTRA_EDGE_KERNEL[0].len() {
1224
0
        let k = (i + j).saturating_sub(2).min(size - 1);
1225
0
        s += INTRA_EDGE_KERNEL[(strength - 1) as usize][j]
1226
0
          * edge[k].to_u32().unwrap();
1227
0
      }
1228
1229
0
      edge_filtered[i] = T::cast_from((s + 8) >> 4);
1230
    }
1231
0
    edge.copy_from_slice(edge_filtered);
1232
0
  }
Unexecuted instantiation: rav1e::predict::rust::filter_edge::<u16>
Unexecuted instantiation: rav1e::predict::rust::filter_edge::<u8>
1233
1234
0
  pub(crate) fn upsample_edge<T: Pixel>(
1235
0
    size: usize, edge: &mut [T], bit_depth: usize,
1236
0
  ) {
1237
    // The input edge should be valid in the -1..size range,
1238
    // where the -1 index is the top-left edge pixel. Since
1239
    // negative indices are unsafe in Rust, the caller is
1240
    // expected to globally offset it by 1, which makes the
1241
    // input range 0..=size.
1242
0
    let mut dup = [MaybeUninit::<T>::uninit(); MAX_TX_SIZE];
1243
0
    let dup = init_slice_repeat_mut(&mut dup[..size + 3], T::zero());
1244
0
    dup[0] = edge[0];
1245
0
    dup[1..=size + 1].copy_from_slice(&edge[0..=size]);
1246
0
    dup[size + 2] = edge[size];
1247
1248
    // Past here the edge is being filtered, and its
1249
    // effective range is shifted from -1..size to
1250
    // -2..2*size-1. Again, because this is safe Rust,
1251
    // we cannot use negative indices, and the actual range
1252
    // will be 0..=2*size. The caller is expected to adjust
1253
    // its indices on receipt of the filtered edge.
1254
0
    edge[0] = dup[0];
1255
1256
0
    for i in 0..size {
1257
0
      let mut s = -dup[i].to_i32().unwrap()
1258
0
        + (9 * dup[i + 1].to_i32().unwrap())
1259
0
        + (9 * dup[i + 2].to_i32().unwrap())
1260
0
        - dup[i + 3].to_i32().unwrap();
1261
0
      s = ((s + 8) / 16).clamp(0, (1 << bit_depth) - 1);
1262
0
1263
0
      edge[2 * i + 1] = T::cast_from(s);
1264
0
      edge[2 * i + 2] = dup[i + 2];
1265
0
    }
1266
0
  }
Unexecuted instantiation: rav1e::predict::rust::upsample_edge::<u16>
Unexecuted instantiation: rav1e::predict::rust::upsample_edge::<u8>
1267
1268
0
  pub(crate) const fn dr_intra_derivative(p_angle: usize) -> usize {
1269
0
    match p_angle {
1270
0
      3 => 1023,
1271
0
      6 => 547,
1272
0
      9 => 372,
1273
0
      14 => 273,
1274
0
      17 => 215,
1275
0
      20 => 178,
1276
0
      23 => 151,
1277
0
      26 => 132,
1278
0
      29 => 116,
1279
0
      32 => 102,
1280
0
      36 => 90,
1281
0
      39 => 80,
1282
0
      42 => 71,
1283
0
      45 => 64,
1284
0
      48 => 57,
1285
0
      51 => 51,
1286
0
      54 => 45,
1287
0
      58 => 40,
1288
0
      61 => 35,
1289
0
      64 => 31,
1290
0
      67 => 27,
1291
0
      70 => 23,
1292
0
      73 => 19,
1293
0
      76 => 15,
1294
0
      81 => 11,
1295
0
      84 => 7,
1296
0
      87 => 3,
1297
0
      _ => 0,
1298
    }
1299
0
  }
1300
1301
0
  pub(crate) fn pred_directional<T: Pixel>(
1302
0
    output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T],
1303
0
    top_left: &[T], p_angle: usize, width: usize, height: usize,
1304
0
    bit_depth: usize, ief_params: Option<IntraEdgeFilterParameters>,
1305
0
  ) {
1306
0
    let sample_max = (1 << bit_depth) - 1;
1307
1308
0
    let max_x = output.plane_cfg.width as isize - 1;
1309
0
    let max_y = output.plane_cfg.height as isize - 1;
1310
1311
0
    let mut upsample_above = false;
1312
0
    let mut upsample_left = false;
1313
1314
0
    let mut above_edge: &[T] = above;
1315
0
    let mut left_edge: &[T] = left;
1316
0
    let top_left_edge: T = top_left[0];
1317
1318
0
    let enable_edge_filter = ief_params.is_some();
1319
1320
    // Initialize above and left edge buffers of the largest possible needed size if upsampled
1321
    // The first value is the top left pixel, also mutable and indexed at -1 in the spec
1322
0
    let mut above_filtered = [MaybeUninit::<T>::uninit(); MAX_TX_SIZE * 4 + 1];
1323
0
    let above_filtered = init_slice_repeat_mut(
1324
0
      &mut above_filtered[..=(width + height) * 2],
1325
0
      T::zero(),
1326
    );
1327
0
    let mut left_filtered = [MaybeUninit::<T>::uninit(); MAX_TX_SIZE * 4 + 1];
1328
0
    let left_filtered = init_slice_repeat_mut(
1329
0
      &mut left_filtered[..=(width + height) * 2],
1330
0
      T::zero(),
1331
    );
1332
1333
0
    if enable_edge_filter {
1334
0
      let above_len = above.len().min(above_filtered.len() - 1);
1335
0
      let left_len = left.len().min(left_filtered.len() - 1);
1336
0
      above_filtered[1..=above_len].clone_from_slice(&above[..above_len]);
1337
0
      for i in 1..=left_len {
1338
0
        left_filtered[i] = left[left.len() - i];
1339
0
      }
1340
1341
0
      let smooth_filter = ief_params.unwrap().use_smooth_filter();
1342
1343
0
      if p_angle != 90 && p_angle != 180 {
1344
0
        above_filtered[0] = top_left_edge;
1345
0
        left_filtered[0] = top_left_edge;
1346
1347
0
        let num_px = (
1348
0
          width.min((max_x - output.rect().x + 1).try_into().unwrap())
1349
0
            + if p_angle < 90 { height } else { 0 }
1350
            + 1, // above
1351
0
          height.min((max_y - output.rect().y + 1).try_into().unwrap())
1352
0
            + if p_angle > 180 { width } else { 0 }
1353
            + 1, // left
1354
        );
1355
1356
0
        let filter_strength = select_ief_strength(
1357
0
          width,
1358
0
          height,
1359
0
          smooth_filter,
1360
0
          p_angle as isize - 90,
1361
        );
1362
0
        filter_edge(num_px.0, filter_strength, above_filtered);
1363
0
        let filter_strength = select_ief_strength(
1364
0
          width,
1365
0
          height,
1366
0
          smooth_filter,
1367
0
          p_angle as isize - 180,
1368
        );
1369
0
        filter_edge(num_px.1, filter_strength, left_filtered);
1370
0
      }
1371
1372
0
      let num_px = (
1373
0
        width + if p_angle < 90 { height } else { 0 }, // above
1374
0
        height + if p_angle > 180 { width } else { 0 }, // left
1375
      );
1376
1377
0
      upsample_above = select_ief_upsample(
1378
0
        width,
1379
0
        height,
1380
0
        smooth_filter,
1381
0
        p_angle as isize - 90,
1382
0
      );
1383
0
      if upsample_above {
1384
0
        upsample_edge(num_px.0, above_filtered, bit_depth);
1385
0
      }
1386
0
      upsample_left = select_ief_upsample(
1387
0
        width,
1388
0
        height,
1389
0
        smooth_filter,
1390
0
        p_angle as isize - 180,
1391
0
      );
1392
0
      if upsample_left {
1393
0
        upsample_edge(num_px.1, left_filtered, bit_depth);
1394
0
      }
1395
1396
0
      left_filtered.reverse();
1397
0
      above_edge = above_filtered;
1398
0
      left_edge = left_filtered;
1399
0
    }
1400
1401
0
    let dx = if p_angle < 90 {
1402
0
      dr_intra_derivative(p_angle)
1403
0
    } else if p_angle > 90 && p_angle < 180 {
1404
0
      dr_intra_derivative(180 - p_angle)
1405
    } else {
1406
0
      0 // undefined
1407
    };
1408
1409
0
    let dy = if p_angle > 90 && p_angle < 180 {
1410
0
      dr_intra_derivative(p_angle - 90)
1411
0
    } else if p_angle > 180 {
1412
0
      dr_intra_derivative(270 - p_angle)
1413
    } else {
1414
0
      0 // undefined
1415
    };
1416
1417
    // edge buffer index offsets applied due to the fact
1418
    // that we cannot safely use negative indices in Rust
1419
0
    let upsample_above = upsample_above as usize;
1420
0
    let upsample_left = upsample_left as usize;
1421
0
    let offset_above = (enable_edge_filter as usize) << upsample_above;
1422
0
    let offset_left = (enable_edge_filter as usize) << upsample_left;
1423
1424
0
    if p_angle < 90 {
1425
0
      for i in 0..height {
1426
0
        let row = &mut output[i];
1427
0
        for j in 0..width {
1428
0
          let idx = (i + 1) * dx;
1429
0
          let base = (idx >> (6 - upsample_above)) + (j << upsample_above);
1430
0
          let shift = (((idx << upsample_above) >> 1) & 31) as i32;
1431
0
          let max_base_x = (height + width - 1) << upsample_above;
1432
0
          let v = (if base < max_base_x {
1433
0
            let a: i32 = above_edge[base + offset_above].into();
1434
0
            let b: i32 = above_edge[base + 1 + offset_above].into();
1435
0
            round_shift(a * (32 - shift) + b * shift, 5)
1436
          } else {
1437
0
            let c: i32 = above_edge[max_base_x + offset_above].into();
1438
0
            c
1439
          })
1440
0
          .clamp(0, sample_max);
1441
0
          row[j] = T::cast_from(v);
1442
        }
1443
      }
1444
0
    } else if p_angle > 90 && p_angle < 180 {
1445
0
      for i in 0..height {
1446
0
        let row = &mut output[i];
1447
0
        for j in 0..width {
1448
0
          let idx = (j << 6) as isize - ((i + 1) * dx) as isize;
1449
0
          let base = idx >> (6 - upsample_above);
1450
0
          if base >= -(1 << upsample_above) {
1451
0
            let shift = (((idx << upsample_above) >> 1) & 31) as i32;
1452
0
            let a: i32 = if !enable_edge_filter && base < 0 {
1453
0
              top_left_edge
1454
            } else {
1455
0
              above_edge[(base + offset_above as isize) as usize]
1456
            }
1457
0
            .into();
1458
0
            let b: i32 =
1459
0
              above_edge[(base + 1 + offset_above as isize) as usize].into();
1460
0
            let v = round_shift(a * (32 - shift) + b * shift, 5)
1461
0
              .clamp(0, sample_max);
1462
0
            row[j] = T::cast_from(v);
1463
          } else {
1464
0
            let idx = (i << 6) as isize - ((j + 1) * dy) as isize;
1465
0
            let base = idx >> (6 - upsample_left);
1466
0
            let shift = (((idx << upsample_left) >> 1) & 31) as i32;
1467
0
            let l = left_edge.len() - 1;
1468
0
            let a: i32 = if !enable_edge_filter && base < 0 {
1469
0
              top_left_edge
1470
0
            } else if (base + offset_left as isize) == -2 {
1471
0
              left_edge[0]
1472
            } else {
1473
0
              left_edge[l - (base + offset_left as isize) as usize]
1474
            }
1475
0
            .into();
1476
0
            let b: i32 = if (base + offset_left as isize) == -2 {
1477
0
              left_edge[1]
1478
            } else {
1479
0
              left_edge[l - (base + offset_left as isize + 1) as usize]
1480
            }
1481
0
            .into();
1482
0
            let v = round_shift(a * (32 - shift) + b * shift, 5)
1483
0
              .clamp(0, sample_max);
1484
0
            row[j] = T::cast_from(v);
1485
          }
1486
        }
1487
      }
1488
0
    } else if p_angle > 180 {
1489
0
      for i in 0..height {
1490
0
        let row = &mut output[i];
1491
0
        for j in 0..width {
1492
0
          let idx = (j + 1) * dy;
1493
0
          let base = (idx >> (6 - upsample_left)) + (i << upsample_left);
1494
0
          let shift = (((idx << upsample_left) >> 1) & 31) as i32;
1495
0
          let l = left_edge.len() - 1;
1496
0
          let a: i32 = left_edge[l.saturating_sub(base + offset_left)].into();
1497
0
          let b: i32 =
1498
0
            left_edge[l.saturating_sub(base + offset_left + 1)].into();
1499
0
          let v =
1500
0
            round_shift(a * (32 - shift) + b * shift, 5).clamp(0, sample_max);
1501
0
          row[j] = T::cast_from(v);
1502
0
        }
1503
      }
1504
0
    }
1505
0
  }
Unexecuted instantiation: rav1e::predict::rust::pred_directional::<u16>
Unexecuted instantiation: rav1e::predict::rust::pred_directional::<u8>
1506
}
1507
1508
#[cfg(test)]
1509
mod test {
1510
  use super::*;
1511
  use crate::predict::rust::*;
1512
  use num_traits::*;
1513
1514
  #[test]
1515
  fn pred_matches_u8() {
1516
    let edge_buf =
1517
      Aligned::from_fn(|i| (i + 32).saturating_sub(MAX_TX_SIZE * 2).as_());
1518
    let (all_left, top_left, above) = IntraEdge::mock(&edge_buf).as_slices();
1519
    let left = &all_left[all_left.len() - 4..];
1520
1521
    let mut output = Plane::from_slice(&[0u8; 4 * 4], 4);
1522
1523
    pred_dc(&mut output.as_region_mut(), above, left, 4, 4, 8);
1524
    assert_eq!(&output.data[..], [32u8; 16]);
1525
1526
    pred_dc_top(&mut output.as_region_mut(), above, left, 4, 4, 8);
1527
    assert_eq!(&output.data[..], [35u8; 16]);
1528
1529
    pred_dc_left(&mut output.as_region_mut(), above, left, 4, 4, 8);
1530
    assert_eq!(&output.data[..], [30u8; 16]);
1531
1532
    pred_dc_128(&mut output.as_region_mut(), above, left, 4, 4, 8);
1533
    assert_eq!(&output.data[..], [128u8; 16]);
1534
1535
    pred_v(&mut output.as_region_mut(), above, 4, 4);
1536
    assert_eq!(
1537
      &output.data[..],
1538
      [33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36]
1539
    );
1540
1541
    pred_h(&mut output.as_region_mut(), left, 4, 4);
1542
    assert_eq!(
1543
      &output.data[..],
1544
      [31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 28, 28, 28, 28]
1545
    );
1546
1547
    pred_paeth(&mut output.as_region_mut(), above, left, top_left[0], 4, 4);
1548
    assert_eq!(
1549
      &output.data[..],
1550
      [32, 34, 35, 36, 30, 32, 32, 36, 29, 32, 32, 32, 28, 28, 32, 32]
1551
    );
1552
1553
    pred_smooth(&mut output.as_region_mut(), above, left, 4, 4);
1554
    assert_eq!(
1555
      &output.data[..],
1556
      [32, 34, 35, 35, 30, 32, 33, 34, 29, 31, 32, 32, 29, 30, 32, 32]
1557
    );
1558
1559
    pred_smooth_h(&mut output.as_region_mut(), above, left, 4, 4);
1560
    assert_eq!(
1561
      &output.data[..],
1562
      [31, 33, 34, 35, 30, 33, 34, 35, 29, 32, 34, 34, 28, 31, 33, 34]
1563
    );
1564
1565
    pred_smooth_v(&mut output.as_region_mut(), above, left, 4, 4);
1566
    assert_eq!(
1567
      &output.data[..],
1568
      [33, 34, 35, 36, 31, 31, 32, 33, 30, 30, 30, 31, 29, 30, 30, 30]
1569
    );
1570
1571
    let left = &all_left[all_left.len() - 8..];
1572
    let angles = [
1573
      3, 6, 9, 14, 17, 20, 23, 26, 29, 32, 36, 39, 42, 45, 48, 51, 54, 58, 61,
1574
      64, 67, 70, 73, 76, 81, 84, 87,
1575
    ];
1576
    let expected = [
1577
      [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40],
1578
      [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40],
1579
      [39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40],
1580
      [37, 38, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40],
1581
      [36, 37, 38, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40],
1582
      [36, 37, 38, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40],
1583
      [35, 36, 37, 38, 38, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40],
1584
      [35, 36, 37, 38, 37, 38, 39, 40, 39, 40, 40, 40, 40, 40, 40, 40],
1585
      [35, 36, 37, 38, 37, 38, 39, 40, 38, 39, 40, 40, 40, 40, 40, 40],
1586
      [35, 36, 37, 38, 36, 37, 38, 39, 38, 39, 40, 40, 39, 40, 40, 40],
1587
      [34, 35, 36, 37, 36, 37, 38, 39, 37, 38, 39, 40, 39, 40, 40, 40],
1588
      [34, 35, 36, 37, 36, 37, 38, 39, 37, 38, 39, 40, 38, 39, 40, 40],
1589
      [34, 35, 36, 37, 35, 36, 37, 38, 36, 37, 38, 39, 37, 38, 39, 40],
1590
      [34, 35, 36, 37, 35, 36, 37, 38, 36, 37, 38, 39, 37, 38, 39, 40],
1591
      [34, 35, 36, 37, 35, 36, 37, 38, 36, 37, 38, 39, 37, 38, 39, 40],
1592
      [34, 35, 36, 37, 35, 36, 37, 38, 35, 36, 37, 38, 36, 37, 38, 39],
1593
      [34, 35, 36, 37, 34, 35, 36, 37, 35, 36, 37, 38, 36, 37, 38, 39],
1594
      [34, 35, 36, 37, 34, 35, 36, 37, 35, 36, 37, 38, 36, 37, 38, 39],
1595
      [34, 35, 36, 37, 34, 35, 36, 37, 35, 36, 37, 38, 35, 36, 37, 38],
1596
      [33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37, 35, 36, 37, 38],
1597
      [33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37, 35, 36, 37, 38],
1598
      [33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37, 34, 35, 36, 37],
1599
      [33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37, 34, 35, 36, 37],
1600
      [33, 34, 35, 36, 33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37],
1601
      [33, 34, 35, 36, 33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37],
1602
      [33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36],
1603
      [33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36],
1604
    ];
1605
    for (&angle, expected) in angles.iter().zip(expected.iter()) {
1606
      pred_directional(
1607
        &mut output.as_region_mut(),
1608
        above,
1609
        left,
1610
        top_left,
1611
        angle,
1612
        4,
1613
        4,
1614
        8,
1615
        None,
1616
      );
1617
      assert_eq!(&output.data[..], expected);
1618
    }
1619
  }
1620
1621
  #[test]
1622
  fn pred_max() {
1623
    let max12bit = 4096 - 1;
1624
    let above = [max12bit; 32];
1625
    let left = [max12bit; 32];
1626
1627
    let mut o = Plane::from_slice(&vec![0u16; 32 * 32], 32);
1628
1629
    pred_dc(&mut o.as_region_mut(), &above[..4], &left[..4], 4, 4, 16);
1630
1631
    for l in o.data.chunks(32).take(4) {
1632
      for v in l[..4].iter() {
1633
        assert_eq!(*v, max12bit);
1634
      }
1635
    }
1636
1637
    pred_h(&mut o.as_region_mut(), &left[..4], 4, 4);
1638
1639
    for l in o.data.chunks(32).take(4) {
1640
      for v in l[..4].iter() {
1641
        assert_eq!(*v, max12bit);
1642
      }
1643
    }
1644
1645
    pred_v(&mut o.as_region_mut(), &above[..4], 4, 4);
1646
1647
    for l in o.data.chunks(32).take(4) {
1648
      for v in l[..4].iter() {
1649
        assert_eq!(*v, max12bit);
1650
      }
1651
    }
1652
1653
    let above_left = max12bit;
1654
1655
    pred_paeth(
1656
      &mut o.as_region_mut(),
1657
      &above[..4],
1658
      &left[..4],
1659
      above_left,
1660
      4,
1661
      4,
1662
    );
1663
1664
    for l in o.data.chunks(32).take(4) {
1665
      for v in l[..4].iter() {
1666
        assert_eq!(*v, max12bit);
1667
      }
1668
    }
1669
1670
    pred_smooth(&mut o.as_region_mut(), &above[..4], &left[..4], 4, 4);
1671
1672
    for l in o.data.chunks(32).take(4) {
1673
      for v in l[..4].iter() {
1674
        assert_eq!(*v, max12bit);
1675
      }
1676
    }
1677
1678
    pred_smooth_h(&mut o.as_region_mut(), &above[..4], &left[..4], 4, 4);
1679
1680
    for l in o.data.chunks(32).take(4) {
1681
      for v in l[..4].iter() {
1682
        assert_eq!(*v, max12bit);
1683
      }
1684
    }
1685
1686
    pred_smooth_v(&mut o.as_region_mut(), &above[..4], &left[..4], 4, 4);
1687
1688
    for l in o.data.chunks(32).take(4) {
1689
      for v in l[..4].iter() {
1690
        assert_eq!(*v, max12bit);
1691
      }
1692
    }
1693
  }
1694
}