Coverage Report

Created: 2026-01-19 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rav1e-0.8.1/src/partition.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_camel_case_types)]
11
#![allow(dead_code)]
12
13
use self::BlockSize::*;
14
use self::TxSize::*;
15
use crate::context::*;
16
use crate::frame::*;
17
use crate::predict::*;
18
use crate::recon_intra::*;
19
use crate::serialize::{Deserialize, Serialize};
20
use crate::tiling::*;
21
use crate::transform::TxSize;
22
use crate::util::*;
23
use thiserror::Error;
24
25
use std::mem::transmute;
26
use std::mem::MaybeUninit;
27
28
// LAST_FRAME through ALTREF_FRAME correspond to slots 0-6.
29
#[derive(PartialEq, Eq, PartialOrd, Copy, Clone, Debug)]
30
pub enum RefType {
31
  INTRA_FRAME = 0,
32
  LAST_FRAME = 1,
33
  LAST2_FRAME = 2,
34
  LAST3_FRAME = 3,
35
  GOLDEN_FRAME = 4,
36
  BWDREF_FRAME = 5,
37
  ALTREF2_FRAME = 6,
38
  ALTREF_FRAME = 7,
39
  NONE_FRAME = 8,
40
}
41
42
impl RefType {
43
  /// convert to a ref list index, 0-6 (`INTER_REFS_PER_FRAME`)
44
  ///
45
  /// # Panics
46
  ///
47
  /// - If the ref type is a None or Intra frame
48
  #[inline]
49
0
  pub fn to_index(self) -> usize {
50
0
    match self {
51
      NONE_FRAME => {
52
0
        panic!("Tried to get slot of NONE_FRAME");
53
      }
54
      INTRA_FRAME => {
55
0
        panic!("Tried to get slot of INTRA_FRAME");
56
      }
57
0
      _ => (self as usize) - 1,
58
    }
59
0
  }
Unexecuted instantiation: <rav1e::partition::RefType>::to_index
Unexecuted instantiation: <rav1e::partition::RefType>::to_index
60
  #[inline]
61
0
  pub const fn is_fwd_ref(self) -> bool {
62
0
    (self as usize) < 5
63
0
  }
Unexecuted instantiation: <rav1e::partition::RefType>::is_fwd_ref
Unexecuted instantiation: <rav1e::partition::RefType>::is_fwd_ref
64
  #[inline]
65
0
  pub const fn is_bwd_ref(self) -> bool {
66
0
    (self as usize) >= 5
67
0
  }
Unexecuted instantiation: <rav1e::partition::RefType>::is_bwd_ref
Unexecuted instantiation: <rav1e::partition::RefType>::is_bwd_ref
68
}
69
70
use self::RefType::*;
71
use std::fmt;
72
use std::fmt::Display;
73
74
pub const ALL_INTER_REFS: [RefType; 7] = [
75
  LAST_FRAME,
76
  LAST2_FRAME,
77
  LAST3_FRAME,
78
  GOLDEN_FRAME,
79
  BWDREF_FRAME,
80
  ALTREF2_FRAME,
81
  ALTREF_FRAME,
82
];
83
84
pub const LAST_LAST2_FRAMES: usize = 0; // { LAST_FRAME, LAST2_FRAME }
85
pub const LAST_LAST3_FRAMES: usize = 1; // { LAST_FRAME, LAST3_FRAME }
86
pub const LAST_GOLDEN_FRAMES: usize = 2; // { LAST_FRAME, GOLDEN_FRAME }
87
pub const BWDREF_ALTREF_FRAMES: usize = 3; // { BWDREF_FRAME, ALTREF_FRAME }
88
pub const LAST2_LAST3_FRAMES: usize = 4; // { LAST2_FRAME, LAST3_FRAME }
89
pub const LAST2_GOLDEN_FRAMES: usize = 5; // { LAST2_FRAME, GOLDEN_FRAME }
90
pub const LAST3_GOLDEN_FRAMES: usize = 6; // { LAST3_FRAME, GOLDEN_FRAME }
91
pub const BWDREF_ALTREF2_FRAMES: usize = 7; // { BWDREF_FRAME, ALTREF2_FRAME }
92
pub const ALTREF2_ALTREF_FRAMES: usize = 8; // { ALTREF2_FRAME, ALTREF_FRAME }
93
pub const TOTAL_UNIDIR_COMP_REFS: usize = 9;
94
95
// NOTE: UNIDIR_COMP_REFS is the number of uni-directional reference pairs
96
//       that are explicitly signaled.
97
pub const UNIDIR_COMP_REFS: usize = BWDREF_ALTREF_FRAMES + 1;
98
99
pub const FWD_REFS: usize = 4;
100
pub const BWD_REFS: usize = 3;
101
pub const SINGLE_REFS: usize = 7;
102
pub const TOTAL_REFS_PER_FRAME: usize = 8;
103
pub const INTER_REFS_PER_FRAME: usize = 7;
104
pub const TOTAL_COMP_REFS: usize =
105
  FWD_REFS * BWD_REFS + TOTAL_UNIDIR_COMP_REFS;
106
107
pub const REF_FRAMES_LOG2: usize = 3;
108
pub const REF_FRAMES: usize = 1 << REF_FRAMES_LOG2;
109
110
pub const REF_CONTEXTS: usize = 3;
111
pub const MVREF_ROW_COLS: usize = 3;
112
113
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
114
pub enum PartitionType {
115
  PARTITION_NONE,
116
  PARTITION_HORZ,
117
  PARTITION_VERT,
118
  PARTITION_SPLIT,
119
  PARTITION_HORZ_A, // HORZ split and the top partition is split again
120
  PARTITION_HORZ_B, // HORZ split and the bottom partition is split again
121
  PARTITION_VERT_A, // VERT split and the left partition is split again
122
  PARTITION_VERT_B, // VERT split and the right partition is split again
123
  PARTITION_HORZ_4, // 4:1 horizontal partition
124
  PARTITION_VERT_4, // 4:1 vertical partition
125
  PARTITION_INVALID,
126
}
127
128
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
129
#[cfg_attr(test, derive(Default))]
130
pub enum BlockSize {
131
  BLOCK_4X4,
132
  BLOCK_4X8,
133
  BLOCK_8X4,
134
  BLOCK_8X8,
135
  BLOCK_8X16,
136
  BLOCK_16X8,
137
  BLOCK_16X16,
138
  BLOCK_16X32,
139
  BLOCK_32X16,
140
  BLOCK_32X32,
141
  BLOCK_32X64,
142
  BLOCK_64X32,
143
  #[cfg_attr(test, default)]
144
  BLOCK_64X64,
145
  BLOCK_64X128,
146
  BLOCK_128X64,
147
  BLOCK_128X128,
148
  BLOCK_4X16,
149
  BLOCK_16X4,
150
  BLOCK_8X32,
151
  BLOCK_32X8,
152
  BLOCK_16X64,
153
  BLOCK_64X16,
154
}
155
156
#[derive(Debug, Error, Copy, Clone, Eq, PartialEq)]
157
pub struct InvalidBlockSize;
158
159
impl Display for InvalidBlockSize {
160
0
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161
0
    f.write_str("invalid block size")
162
0
  }
163
}
164
165
impl PartialOrd for BlockSize {
166
  #[inline(always)]
167
0
  fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
168
    use std::cmp::Ordering::{Equal, Greater, Less};
169
    match (
170
0
      self.width().cmp(&other.width()),
171
0
      self.height().cmp(&other.height()),
172
    ) {
173
0
      (Greater, Less) | (Less, Greater) => None,
174
0
      (Equal, Equal) => Some(Equal),
175
0
      (Greater, _) | (_, Greater) => Some(Greater),
176
0
      (Less, _) | (_, Less) => Some(Less),
177
    }
178
0
  }
179
}
180
181
impl BlockSize {
182
  pub const BLOCK_SIZES_ALL: usize = 22;
183
  pub const BLOCK_SIZES: usize = BlockSize::BLOCK_SIZES_ALL - 6; // BLOCK_SIZES_ALL minus 4:1 non-squares, six of them
184
185
  #[inline]
186
  /// # Errors
187
  ///
188
  /// - Returns `InvalidBlockSize` if the given `w` and `h` do not produce
189
  ///   a valid block size.
190
0
  pub fn from_width_and_height_opt(
191
0
    w: usize, h: usize,
192
0
  ) -> Result<BlockSize, InvalidBlockSize> {
193
0
    match (w, h) {
194
0
      (4, 4) => Ok(BLOCK_4X4),
195
0
      (4, 8) => Ok(BLOCK_4X8),
196
0
      (4, 16) => Ok(BLOCK_4X16),
197
0
      (8, 4) => Ok(BLOCK_8X4),
198
0
      (8, 8) => Ok(BLOCK_8X8),
199
0
      (8, 16) => Ok(BLOCK_8X16),
200
0
      (8, 32) => Ok(BLOCK_8X32),
201
0
      (16, 4) => Ok(BLOCK_16X4),
202
0
      (16, 8) => Ok(BLOCK_16X8),
203
0
      (16, 16) => Ok(BLOCK_16X16),
204
0
      (16, 32) => Ok(BLOCK_16X32),
205
0
      (16, 64) => Ok(BLOCK_16X64),
206
0
      (32, 8) => Ok(BLOCK_32X8),
207
0
      (32, 16) => Ok(BLOCK_32X16),
208
0
      (32, 32) => Ok(BLOCK_32X32),
209
0
      (32, 64) => Ok(BLOCK_32X64),
210
0
      (64, 16) => Ok(BLOCK_64X16),
211
0
      (64, 32) => Ok(BLOCK_64X32),
212
0
      (64, 64) => Ok(BLOCK_64X64),
213
0
      (64, 128) => Ok(BLOCK_64X128),
214
0
      (128, 64) => Ok(BLOCK_128X64),
215
0
      (128, 128) => Ok(BLOCK_128X128),
216
0
      _ => Err(InvalidBlockSize),
217
    }
218
0
  }
219
220
  /// # Panics
221
  ///
222
  /// - If the given `w` and `h` do not produce a valid block size.
223
0
  pub fn from_width_and_height(w: usize, h: usize) -> BlockSize {
224
0
    Self::from_width_and_height_opt(w, h).unwrap()
225
0
  }
226
227
  #[inline]
228
0
  pub fn cfl_allowed(self) -> bool {
229
    // TODO: fix me when enabling EXT_PARTITION_TYPES
230
0
    self <= BlockSize::BLOCK_32X32
231
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::cfl_allowed
Unexecuted instantiation: <rav1e::partition::BlockSize>::cfl_allowed
232
233
  #[inline]
234
0
  pub const fn width(self) -> usize {
235
0
    1 << self.width_log2()
236
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::width
Unexecuted instantiation: <rav1e::partition::BlockSize>::width
237
238
  /// width * height
239
  #[inline]
240
0
  pub const fn area(self) -> usize {
241
0
    self.width() * self.height()
242
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::area
Unexecuted instantiation: <rav1e::partition::BlockSize>::area
243
244
  #[inline]
245
0
  pub const fn width_log2(self) -> usize {
246
0
    match self {
247
0
      BLOCK_4X4 | BLOCK_4X8 | BLOCK_4X16 => 2,
248
0
      BLOCK_8X4 | BLOCK_8X8 | BLOCK_8X16 | BLOCK_8X32 => 3,
249
0
      BLOCK_16X4 | BLOCK_16X8 | BLOCK_16X16 | BLOCK_16X32 | BLOCK_16X64 => 4,
250
0
      BLOCK_32X8 | BLOCK_32X16 | BLOCK_32X32 | BLOCK_32X64 => 5,
251
0
      BLOCK_64X16 | BLOCK_64X32 | BLOCK_64X64 | BLOCK_64X128 => 6,
252
0
      BLOCK_128X64 | BLOCK_128X128 => 7,
253
    }
254
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::width_log2
Unexecuted instantiation: <rav1e::partition::BlockSize>::width_log2
255
256
  #[inline]
257
0
  pub const fn width_mi_log2(self) -> usize {
258
0
    self.width_log2() - 2
259
0
  }
260
261
  #[inline]
262
0
  pub const fn width_mi(self) -> usize {
263
0
    self.width() >> MI_SIZE_LOG2
264
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::width_mi
Unexecuted instantiation: <rav1e::partition::BlockSize>::width_mi
265
266
  #[inline]
267
0
  pub fn width_imp_b(self) -> usize {
268
0
    (self.width() >> (IMPORTANCE_BLOCK_TO_BLOCK_SHIFT + BLOCK_TO_PLANE_SHIFT))
269
0
      .max(1)
270
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::width_imp_b
Unexecuted instantiation: <rav1e::partition::BlockSize>::width_imp_b
271
272
  #[inline]
273
0
  pub const fn height(self) -> usize {
274
0
    1 << self.height_log2()
275
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::height
Unexecuted instantiation: <rav1e::partition::BlockSize>::height
276
277
  #[inline]
278
0
  pub const fn height_log2(self) -> usize {
279
0
    match self {
280
0
      BLOCK_4X4 | BLOCK_8X4 | BLOCK_16X4 => 2,
281
0
      BLOCK_4X8 | BLOCK_8X8 | BLOCK_16X8 | BLOCK_32X8 => 3,
282
0
      BLOCK_4X16 | BLOCK_8X16 | BLOCK_16X16 | BLOCK_32X16 | BLOCK_64X16 => 4,
283
0
      BLOCK_8X32 | BLOCK_16X32 | BLOCK_32X32 | BLOCK_64X32 => 5,
284
0
      BLOCK_16X64 | BLOCK_32X64 | BLOCK_64X64 | BLOCK_128X64 => 6,
285
0
      BLOCK_64X128 | BLOCK_128X128 => 7,
286
    }
287
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::height_log2
Unexecuted instantiation: <rav1e::partition::BlockSize>::height_log2
288
289
  #[inline]
290
0
  pub const fn height_mi_log2(self) -> usize {
291
0
    self.height_log2() - 2
292
0
  }
293
294
  #[inline]
295
0
  pub const fn height_mi(self) -> usize {
296
0
    self.height() >> MI_SIZE_LOG2
297
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::height_mi
Unexecuted instantiation: <rav1e::partition::BlockSize>::height_mi
298
299
  #[inline]
300
0
  pub fn height_imp_b(self) -> usize {
301
0
    (self.height() >> (IMPORTANCE_BLOCK_TO_BLOCK_SHIFT + BLOCK_TO_PLANE_SHIFT))
302
0
      .max(1)
303
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::height_imp_b
Unexecuted instantiation: <rav1e::partition::BlockSize>::height_imp_b
304
305
  #[inline]
306
0
  pub const fn tx_size(self) -> TxSize {
307
0
    match self {
308
0
      BLOCK_4X4 => TX_4X4,
309
0
      BLOCK_4X8 => TX_4X8,
310
0
      BLOCK_8X4 => TX_8X4,
311
0
      BLOCK_8X8 => TX_8X8,
312
0
      BLOCK_8X16 => TX_8X16,
313
0
      BLOCK_16X8 => TX_16X8,
314
0
      BLOCK_16X16 => TX_16X16,
315
0
      BLOCK_16X32 => TX_16X32,
316
0
      BLOCK_32X16 => TX_32X16,
317
0
      BLOCK_32X32 => TX_32X32,
318
0
      BLOCK_32X64 => TX_32X64,
319
0
      BLOCK_64X32 => TX_64X32,
320
0
      BLOCK_4X16 => TX_4X16,
321
0
      BLOCK_16X4 => TX_16X4,
322
0
      BLOCK_8X32 => TX_8X32,
323
0
      BLOCK_32X8 => TX_32X8,
324
0
      BLOCK_16X64 => TX_16X64,
325
0
      BLOCK_64X16 => TX_64X16,
326
0
      _ => TX_64X64,
327
    }
328
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::tx_size
Unexecuted instantiation: <rav1e::partition::BlockSize>::tx_size
329
330
  /// Source: `Subsampled_Size` (AV1 specification section 5.11.38)
331
  ///
332
  /// # Errors
333
  ///
334
  /// - Returns `InvalidBlockSize` if the given block size cannot
335
  ///   be subsampled in the requested way.
336
  #[inline]
337
0
  pub const fn subsampled_size(
338
0
    self, xdec: usize, ydec: usize,
339
0
  ) -> Result<BlockSize, InvalidBlockSize> {
340
0
    Ok(match (xdec, ydec) {
341
0
      (0, 0) /* 4:4:4 */ => self,
342
0
      (1, 0) /* 4:2:2 */ => match self {
343
0
        BLOCK_4X4 | BLOCK_8X4 => BLOCK_4X4,
344
0
        BLOCK_8X8 => BLOCK_4X8,
345
0
        BLOCK_16X4 => BLOCK_8X4,
346
0
        BLOCK_16X8 => BLOCK_8X8,
347
0
        BLOCK_16X16 => BLOCK_8X16,
348
0
        BLOCK_32X8 => BLOCK_16X8,
349
0
        BLOCK_32X16 => BLOCK_16X16,
350
0
        BLOCK_32X32 => BLOCK_16X32,
351
0
        BLOCK_64X16 => BLOCK_32X16,
352
0
        BLOCK_64X32 => BLOCK_32X32,
353
0
        BLOCK_64X64 => BLOCK_32X64,
354
0
        BLOCK_128X64 => BLOCK_64X64,
355
0
        BLOCK_128X128 => BLOCK_64X128,
356
0
        _ => return Err(InvalidBlockSize),
357
      },
358
0
      (1, 1) /* 4:2:0 */ => match self {
359
0
        BLOCK_4X4 | BLOCK_4X8 | BLOCK_8X4 | BLOCK_8X8 => BLOCK_4X4,
360
0
        BLOCK_4X16 | BLOCK_8X16 => BLOCK_4X8,
361
0
        BLOCK_8X32 => BLOCK_4X16,
362
0
        BLOCK_16X4 | BLOCK_16X8 => BLOCK_8X4,
363
0
        BLOCK_16X16 => BLOCK_8X8,
364
0
        BLOCK_16X32 => BLOCK_8X16,
365
0
        BLOCK_16X64 => BLOCK_8X32,
366
0
        BLOCK_32X8 => BLOCK_16X4,
367
0
        BLOCK_32X16 => BLOCK_16X8,
368
0
        BLOCK_32X32 => BLOCK_16X16,
369
0
        BLOCK_32X64 => BLOCK_16X32,
370
0
        BLOCK_64X16 => BLOCK_32X8,
371
0
        BLOCK_64X32 => BLOCK_32X16,
372
0
        BLOCK_64X64 => BLOCK_32X32,
373
0
        BLOCK_64X128 => BLOCK_32X64,
374
0
        BLOCK_128X64 => BLOCK_64X32,
375
0
        BLOCK_128X128 => BLOCK_64X64,
376
      },
377
0
      _ => return Err(InvalidBlockSize),
378
    })
379
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::subsampled_size
Unexecuted instantiation: <rav1e::partition::BlockSize>::subsampled_size
380
381
  /// # Panics
382
  ///
383
  /// Will panic if the subsampling is not possible
384
  #[inline]
385
0
  pub fn largest_chroma_tx_size(self, xdec: usize, ydec: usize) -> TxSize {
386
0
    let plane_bsize = self
387
0
      .subsampled_size(xdec, ydec)
388
0
      .expect("invalid block size for this subsampling mode");
389
390
0
    let chroma_tx_size = max_txsize_rect_lookup[plane_bsize as usize];
391
392
0
    av1_get_coded_tx_size(chroma_tx_size)
393
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::largest_chroma_tx_size
Unexecuted instantiation: <rav1e::partition::BlockSize>::largest_chroma_tx_size
394
395
  #[inline]
396
0
  pub const fn is_sqr(self) -> bool {
397
0
    self.width_log2() == self.height_log2()
398
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::is_sqr
Unexecuted instantiation: <rav1e::partition::BlockSize>::is_sqr
399
400
  #[inline]
401
0
  pub const fn is_sub8x8(self, xdec: usize, ydec: usize) -> bool {
402
0
    xdec != 0 && self.width_log2() == 2 || ydec != 0 && self.height_log2() == 2
403
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::is_sub8x8
Unexecuted instantiation: <rav1e::partition::BlockSize>::is_sub8x8
404
405
  #[inline]
406
0
  pub const fn sub8x8_offset(
407
0
    self, xdec: usize, ydec: usize,
408
0
  ) -> (isize, isize) {
409
0
    let offset_x = if xdec != 0 && self.width_log2() == 2 { -1 } else { 0 };
410
0
    let offset_y = if ydec != 0 && self.height_log2() == 2 { -1 } else { 0 };
411
412
0
    (offset_x, offset_y)
413
0
  }
Unexecuted instantiation: <rav1e::partition::BlockSize>::sub8x8_offset
Unexecuted instantiation: <rav1e::partition::BlockSize>::sub8x8_offset
414
415
  /// # Errors
416
  ///
417
  /// - Returns `InvalidBlockSize` if the block size cannot be split
418
  ///   in the requested way.
419
0
  pub const fn subsize(
420
0
    self, partition: PartitionType,
421
0
  ) -> Result<BlockSize, InvalidBlockSize> {
422
    use PartitionType::*;
423
424
0
    Ok(match partition {
425
0
      PARTITION_NONE => self,
426
0
      PARTITION_SPLIT => match self {
427
0
        BLOCK_8X8 => BLOCK_4X4,
428
0
        BLOCK_16X16 => BLOCK_8X8,
429
0
        BLOCK_32X32 => BLOCK_16X16,
430
0
        BLOCK_64X64 => BLOCK_32X32,
431
0
        BLOCK_128X128 => BLOCK_64X64,
432
0
        _ => return Err(InvalidBlockSize),
433
      },
434
0
      PARTITION_HORZ | PARTITION_HORZ_A | PARTITION_HORZ_B => match self {
435
0
        BLOCK_8X8 => BLOCK_8X4,
436
0
        BLOCK_16X16 => BLOCK_16X8,
437
0
        BLOCK_32X32 => BLOCK_32X16,
438
0
        BLOCK_64X64 => BLOCK_64X32,
439
0
        BLOCK_128X128 => BLOCK_128X64,
440
0
        _ => return Err(InvalidBlockSize),
441
      },
442
0
      PARTITION_VERT | PARTITION_VERT_A | PARTITION_VERT_B => match self {
443
0
        BLOCK_8X8 => BLOCK_4X8,
444
0
        BLOCK_16X16 => BLOCK_8X16,
445
0
        BLOCK_32X32 => BLOCK_16X32,
446
0
        BLOCK_64X64 => BLOCK_32X64,
447
0
        BLOCK_128X128 => BLOCK_64X128,
448
0
        _ => return Err(InvalidBlockSize),
449
      },
450
0
      PARTITION_HORZ_4 => match self {
451
0
        BLOCK_16X16 => BLOCK_16X4,
452
0
        BLOCK_32X32 => BLOCK_32X8,
453
0
        BLOCK_64X64 => BLOCK_64X16,
454
0
        _ => return Err(InvalidBlockSize),
455
      },
456
0
      PARTITION_VERT_4 => match self {
457
0
        BLOCK_16X16 => BLOCK_4X16,
458
0
        BLOCK_32X32 => BLOCK_8X32,
459
0
        BLOCK_64X64 => BLOCK_16X64,
460
0
        _ => return Err(InvalidBlockSize),
461
      },
462
0
      _ => return Err(InvalidBlockSize),
463
    })
464
0
  }
465
466
0
  pub const fn is_rect_tx_allowed(self) -> bool {
467
0
    !matches!(
468
0
      self,
469
      BLOCK_4X4
470
        | BLOCK_8X8
471
        | BLOCK_16X16
472
        | BLOCK_32X32
473
        | BLOCK_64X64
474
        | BLOCK_64X128
475
        | BLOCK_128X64
476
        | BLOCK_128X128
477
    )
478
0
  }
479
}
480
481
impl fmt::Display for BlockSize {
482
0
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
483
0
    write!(
484
0
      f,
485
0
      "{}",
486
0
      match self {
487
0
        BlockSize::BLOCK_4X4 => "4x4",
488
0
        BlockSize::BLOCK_4X8 => "4x8",
489
0
        BlockSize::BLOCK_8X4 => "8x4",
490
0
        BlockSize::BLOCK_8X8 => "8x8",
491
0
        BlockSize::BLOCK_8X16 => "8x16",
492
0
        BlockSize::BLOCK_16X8 => "16x8",
493
0
        BlockSize::BLOCK_16X16 => "16x16",
494
0
        BlockSize::BLOCK_16X32 => "16x32",
495
0
        BlockSize::BLOCK_32X16 => "32x16",
496
0
        BlockSize::BLOCK_32X32 => "32x32",
497
0
        BlockSize::BLOCK_32X64 => "32x64",
498
0
        BlockSize::BLOCK_64X32 => "64x32",
499
0
        BlockSize::BLOCK_64X64 => "64x64",
500
0
        BlockSize::BLOCK_64X128 => "64x128",
501
0
        BlockSize::BLOCK_128X64 => "128x64",
502
0
        BlockSize::BLOCK_128X128 => "128x128",
503
0
        BlockSize::BLOCK_4X16 => "4x16",
504
0
        BlockSize::BLOCK_16X4 => "16x4",
505
0
        BlockSize::BLOCK_8X32 => "8x32",
506
0
        BlockSize::BLOCK_32X8 => "32x8",
507
0
        BlockSize::BLOCK_16X64 => "16x64",
508
0
        BlockSize::BLOCK_64X16 => "64x16",
509
      }
510
    )
511
0
  }
512
}
513
514
pub const NEWMV_MODE_CONTEXTS: usize = 7;
515
pub const GLOBALMV_MODE_CONTEXTS: usize = 2;
516
pub const REFMV_MODE_CONTEXTS: usize = 6;
517
pub const INTER_COMPOUND_MODES: usize = 8;
518
519
pub const REFMV_OFFSET: usize = 4;
520
pub const GLOBALMV_OFFSET: usize = 3;
521
pub const NEWMV_CTX_MASK: usize = (1 << GLOBALMV_OFFSET) - 1;
522
pub const GLOBALMV_CTX_MASK: usize =
523
  (1 << (REFMV_OFFSET - GLOBALMV_OFFSET)) - 1;
524
pub const REFMV_CTX_MASK: usize = (1 << (8 - REFMV_OFFSET)) - 1;
525
526
pub static RAV1E_PARTITION_TYPES: &[PartitionType] = &[
527
  PartitionType::PARTITION_NONE,
528
  PartitionType::PARTITION_HORZ,
529
  PartitionType::PARTITION_VERT,
530
  PartitionType::PARTITION_SPLIT,
531
];
532
533
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
534
pub enum GlobalMVMode {
535
  IDENTITY = 0,    // identity transformation, 0-parameter
536
  TRANSLATION = 1, // translational motion 2-parameter
537
  ROTZOOM = 2,     // simplified affine with rotation + zoom only, 4-parameter
538
  AFFINE = 3,      // affine, 6-parameter
539
}
540
541
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
542
pub enum MvSubpelPrecision {
543
  MV_SUBPEL_NONE = -1,
544
  MV_SUBPEL_LOW_PRECISION = 0,
545
  MV_SUBPEL_HIGH_PRECISION,
546
}
547
548
/* Symbols for coding which components are zero jointly */
549
pub const MV_JOINTS: usize = 4;
550
551
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
552
pub enum MvJointType {
553
  MV_JOINT_ZERO = 0,   /* Zero vector */
554
  MV_JOINT_HNZVZ = 1,  /* Vert zero, hor nonzero */
555
  MV_JOINT_HZVNZ = 2,  /* Hor zero, vert nonzero */
556
  MV_JOINT_HNZVNZ = 3, /* Both components nonzero */
557
}
558
559
0
fn supersample_chroma_bsize(
560
0
  bsize: BlockSize, ss_x: usize, ss_y: usize,
561
0
) -> BlockSize {
562
0
  debug_assert!(ss_x < 2);
563
0
  debug_assert!(ss_y < 2);
564
565
0
  match bsize {
566
0
    BLOCK_4X4 => match (ss_x, ss_y) {
567
0
      (1, 1) => BLOCK_8X8,
568
0
      (1, 0) => BLOCK_8X4,
569
0
      (0, 1) => BLOCK_4X8,
570
0
      _ => bsize,
571
    },
572
0
    BLOCK_4X8 => match (ss_x, ss_y) {
573
0
      (1, 1) => BLOCK_8X8,
574
0
      (1, 0) => BLOCK_8X8,
575
0
      (0, 1) => BLOCK_4X8,
576
0
      _ => bsize,
577
    },
578
0
    BLOCK_8X4 => match (ss_x, ss_y) {
579
0
      (1, 1) => BLOCK_8X8,
580
0
      (1, 0) => BLOCK_8X4,
581
0
      (0, 1) => BLOCK_8X8,
582
0
      _ => bsize,
583
    },
584
0
    BLOCK_4X16 => match (ss_x, ss_y) {
585
0
      (1, 1) => BLOCK_8X16,
586
0
      (1, 0) => BLOCK_8X16,
587
0
      (0, 1) => BLOCK_4X16,
588
0
      _ => bsize,
589
    },
590
0
    BLOCK_16X4 => match (ss_x, ss_y) {
591
0
      (1, 1) => BLOCK_16X8,
592
0
      (1, 0) => BLOCK_16X4,
593
0
      (0, 1) => BLOCK_16X8,
594
0
      _ => bsize,
595
    },
596
0
    _ => bsize,
597
  }
598
0
}
599
600
type IntraEdgeBuffer<T> = Aligned<[MaybeUninit<T>; 4 * MAX_TX_SIZE + 1]>;
601
602
#[cfg(any(test, feature = "bench"))]
603
type IntraEdgeMock<T> = Aligned<[T; 4 * MAX_TX_SIZE + 1]>;
604
605
pub struct IntraEdge<'a, T: Pixel>(&'a [T], &'a [T], &'a [T]);
606
607
impl<'a, T: Pixel> IntraEdge<'a, T> {
608
0
  fn new(
609
0
    edge_buf: &'a mut IntraEdgeBuffer<T>, init_left: usize, init_above: usize,
610
0
  ) -> Self {
611
    // SAFETY: Initialized in `get_intra_edges`.
612
0
    let left = unsafe {
613
0
      let begin_left = 2 * MAX_TX_SIZE - init_left;
614
0
      let end_above = 2 * MAX_TX_SIZE + 1 + init_above;
615
0
      slice_assume_init_mut(&mut edge_buf.data[begin_left..end_above])
616
    };
617
0
    let (left, top_left) = left.split_at(init_left);
618
0
    let (top_left, above) = top_left.split_at(1);
619
0
    Self(left, top_left, above)
620
0
  }
Unexecuted instantiation: <rav1e::partition::IntraEdge<u16>>::new
Unexecuted instantiation: <rav1e::partition::IntraEdge<u8>>::new
621
622
0
  pub const fn as_slices(&self) -> (&'a [T], &'a [T], &'a [T]) {
623
0
    (self.0, self.1, self.2)
624
0
  }
Unexecuted instantiation: <rav1e::partition::IntraEdge<u16>>::as_slices
Unexecuted instantiation: <rav1e::partition::IntraEdge<u8>>::as_slices
625
626
0
  pub const fn top_left_ptr(&self) -> *const T {
627
0
    self.1.as_ptr()
628
0
  }
629
630
  #[cfg(any(test, feature = "bench"))]
631
  pub fn mock(edge_buf: &'a IntraEdgeMock<T>) -> Self {
632
    let left = &edge_buf.data[..];
633
    let (left, top_left) = left.split_at(2 * MAX_TX_SIZE);
634
    let (top_left, above) = top_left.split_at(1);
635
    Self(left, top_left, above)
636
  }
637
}
638
639
0
pub fn get_intra_edges<'a, T: Pixel>(
640
0
  edge_buf: &'a mut IntraEdgeBuffer<T>,
641
0
  dst: &PlaneRegion<'_, T>,
642
0
  partition_bo: TileBlockOffset, // partition bo, BlockOffset
643
0
  bx: usize,
644
0
  by: usize,
645
0
  partition_size: BlockSize, // partition size, BlockSize
646
0
  po: PlaneOffset,
647
0
  tx_size: TxSize,
648
0
  bit_depth: usize,
649
0
  opt_mode: Option<PredictionMode>,
650
0
  enable_intra_edge_filter: bool,
651
0
  intra_param: IntraParam,
652
0
) -> IntraEdge<'a, T> {
653
0
  let mut init_left: usize = 0;
654
0
  let mut init_above: usize = 0;
655
656
0
  let plane_cfg = &dst.plane_cfg;
657
658
0
  let base = 128u16 << (bit_depth - 8);
659
660
  {
661
    // left pixels are ordered from bottom to top and right-aligned
662
0
    let (left, not_left) = edge_buf.data.split_at_mut(2 * MAX_TX_SIZE);
663
0
    let (top_left, above) = not_left.split_at_mut(1);
664
665
0
    let x = po.x as usize;
666
0
    let y = po.y as usize;
667
668
0
    let mut needs_left = true;
669
0
    let mut needs_topleft = true;
670
0
    let mut needs_top = true;
671
0
    let mut needs_topright = true;
672
0
    let mut needs_bottomleft = true;
673
0
    let mut needs_topleft_filter = false;
674
675
0
    if let Some(mut mode) = opt_mode {
676
0
      mode = match mode {
677
0
        PredictionMode::PAETH_PRED => match (x, y) {
678
0
          (0, 0) => PredictionMode::DC_PRED,
679
0
          (0, _) => PredictionMode::V_PRED,
680
0
          (_, 0) => PredictionMode::H_PRED,
681
0
          _ => PredictionMode::PAETH_PRED,
682
        },
683
0
        _ => mode,
684
      };
685
686
0
      let p_angle = intra_mode_to_angle(mode)
687
0
        + match intra_param {
688
0
          IntraParam::AngleDelta(val) => (val * ANGLE_STEP) as isize,
689
0
          _ => 0,
690
        };
691
692
0
      let dc_or_cfl =
693
0
        mode == PredictionMode::DC_PRED || mode == PredictionMode::UV_CFL_PRED;
694
695
0
      needs_left = (!dc_or_cfl || x != 0) || (p_angle > 90 && p_angle != 180);
696
0
      needs_topleft = mode == PredictionMode::PAETH_PRED
697
0
        || (mode.is_directional() && p_angle != 90 && p_angle != 180);
698
0
      needs_top = (!dc_or_cfl || y != 0) || (p_angle != 90 && p_angle < 180);
699
0
      needs_topright = mode.is_directional() && p_angle < 90;
700
0
      needs_bottomleft = mode.is_directional() && p_angle > 180;
701
      needs_topleft_filter =
702
0
        enable_intra_edge_filter && p_angle > 90 && p_angle < 180;
703
0
    }
704
705
0
    let rect_w =
706
0
      dst.rect().width.min(dst.plane_cfg.width - dst.rect().x as usize);
707
0
    let rect_h =
708
0
      dst.rect().height.min(dst.plane_cfg.height - dst.rect().y as usize);
709
710
    // Needs left
711
0
    if needs_left {
712
0
      let txh = if y + tx_size.height() > rect_h {
713
0
        rect_h - y
714
      } else {
715
0
        tx_size.height()
716
      };
717
0
      if x != 0 {
718
0
        for i in 0..txh {
719
0
          debug_assert!(y + i < rect_h);
720
0
          left[2 * MAX_TX_SIZE - 1 - i].write(dst[y + i][x - 1]);
721
        }
722
0
        if txh < tx_size.height() {
723
0
          let val = dst[y + txh - 1][x - 1];
724
0
          for i in txh..tx_size.height() {
725
0
            left[2 * MAX_TX_SIZE - 1 - i].write(val);
726
0
          }
727
0
        }
728
      } else {
729
0
        let val = if y != 0 { dst[y - 1][0] } else { T::cast_from(base + 1) };
730
0
        for v in left[2 * MAX_TX_SIZE - tx_size.height()..].iter_mut() {
731
0
          v.write(val);
732
0
        }
733
      }
734
0
      init_left += tx_size.height();
735
0
    }
736
737
    // Needs top
738
0
    if needs_top {
739
0
      let txw = if x + tx_size.width() > rect_w {
740
0
        rect_w - x
741
      } else {
742
0
        tx_size.width()
743
      };
744
0
      if y != 0 {
745
0
        above[..txw].copy_from_slice(
746
          // SAFETY: &[T] and &[MaybeUninit<T>] have the same layout
747
          unsafe {
748
0
            transmute::<&[T], &[MaybeUninit<T>]>(&dst[y - 1][x..x + txw])
749
          },
750
        );
751
0
        if txw < tx_size.width() {
752
0
          let val = dst[y - 1][x + txw - 1];
753
0
          for i in txw..tx_size.width() {
754
0
            above[i].write(val);
755
0
          }
756
0
        }
757
      } else {
758
0
        let val = if x != 0 { dst[0][x - 1] } else { T::cast_from(base - 1) };
759
0
        for v in above[..tx_size.width()].iter_mut() {
760
0
          v.write(val);
761
0
        }
762
      }
763
0
      init_above += tx_size.width();
764
0
    }
765
766
0
    let bx4 = bx * (tx_size.width() >> MI_SIZE_LOG2); // bx,by are in tx block indices
767
0
    let by4 = by * (tx_size.height() >> MI_SIZE_LOG2);
768
769
0
    let have_top = by4 != 0
770
0
      || if plane_cfg.ydec != 0 {
771
0
        partition_bo.0.y > 1
772
      } else {
773
0
        partition_bo.0.y > 0
774
      };
775
0
    let have_left = bx4 != 0
776
0
      || if plane_cfg.xdec != 0 {
777
0
        partition_bo.0.x > 1
778
      } else {
779
0
        partition_bo.0.x > 0
780
      };
781
782
0
    let right_available = x + tx_size.width() < rect_w;
783
0
    let bottom_available = y + tx_size.height() < rect_h;
784
785
0
    let scaled_partition_size =
786
0
      supersample_chroma_bsize(partition_size, plane_cfg.xdec, plane_cfg.ydec);
787
788
    // Needs top right
789
0
    if needs_topright {
790
0
      debug_assert!(plane_cfg.xdec <= 1 && plane_cfg.ydec <= 1);
791
792
0
      let num_avail = if y != 0
793
0
        && has_top_right(
794
0
          scaled_partition_size,
795
0
          partition_bo,
796
0
          have_top,
797
0
          right_available,
798
0
          tx_size,
799
0
          by4,
800
0
          bx4,
801
0
          plane_cfg.xdec,
802
0
          plane_cfg.ydec,
803
        ) {
804
0
        tx_size.width().min(rect_w - x - tx_size.width())
805
      } else {
806
0
        0
807
      };
808
0
      if num_avail > 0 {
809
0
        above[tx_size.width()..][..num_avail].copy_from_slice(
810
0
          // SAFETY: &[T] and &[MaybeUninit<T>] have the same layout
811
0
          unsafe {
812
0
            transmute::<&[T], &[MaybeUninit<T>]>(
813
0
              &dst[y - 1][x + tx_size.width()..][..num_avail],
814
0
            )
815
0
          },
816
0
        );
817
0
      }
818
0
      if num_avail < tx_size.height() {
819
0
        let val = above[tx_size.width() + num_avail - 1];
820
0
        for v in above
821
0
          [tx_size.width() + num_avail..tx_size.width() + tx_size.height()]
822
0
          .iter_mut()
823
0
        {
824
0
          *v = val;
825
0
        }
826
0
      }
827
0
      init_above += tx_size.height();
828
0
    }
829
830
    // SAFETY: The blocks above have initialized the first `init_above` items.
831
0
    let above = unsafe { slice_assume_init_mut(&mut above[..init_above]) };
832
833
    // Needs bottom left
834
0
    if needs_bottomleft {
835
0
      debug_assert!(plane_cfg.xdec <= 1 && plane_cfg.ydec <= 1);
836
837
0
      let num_avail = if x != 0
838
0
        && has_bottom_left(
839
0
          scaled_partition_size,
840
0
          partition_bo,
841
0
          bottom_available,
842
0
          have_left,
843
0
          tx_size,
844
0
          by4,
845
0
          bx4,
846
0
          plane_cfg.xdec,
847
0
          plane_cfg.ydec,
848
        ) {
849
0
        tx_size.height().min(rect_h - y - tx_size.height())
850
      } else {
851
0
        0
852
      };
853
0
      if num_avail > 0 {
854
0
        for i in 0..num_avail {
855
0
          left[2 * MAX_TX_SIZE - tx_size.height() - 1 - i]
856
0
            .write(dst[y + tx_size.height() + i][x - 1]);
857
0
        }
858
0
      }
859
0
      if num_avail < tx_size.width() {
860
0
        let val = left[2 * MAX_TX_SIZE - tx_size.height() - num_avail];
861
0
        for v in left[(2 * MAX_TX_SIZE - tx_size.height() - tx_size.width())
862
0
          ..(2 * MAX_TX_SIZE - tx_size.height() - num_avail)]
863
0
          .iter_mut()
864
0
        {
865
0
          *v = val;
866
0
        }
867
0
      }
868
0
      init_left += tx_size.width();
869
0
    }
870
871
    // SAFETY: The blocks above have initialized last `init_left` items.
872
0
    let left = unsafe {
873
0
      slice_assume_init_mut(&mut left[2 * MAX_TX_SIZE - init_left..])
874
    };
875
876
    // Needs top-left
877
0
    if needs_topleft {
878
0
      let top_left = top_left[0].write(match (x, y) {
879
0
        (0, 0) => T::cast_from(base),
880
0
        (_, 0) => dst[0][x - 1],
881
0
        (0, _) => dst[y - 1][0],
882
0
        _ => dst[y - 1][x - 1],
883
      });
884
885
0
      let (w, h) = (tx_size.width(), tx_size.height());
886
0
      if needs_topleft_filter && w + h >= 24 {
887
0
        let (l, a, tl): (u32, u32, u32) =
888
0
          (left[left.len() - 1].into(), above[0].into(), (*top_left).into());
889
0
        let s = l * 5 + tl * 6 + a * 5;
890
0
891
0
        *top_left = T::cast_from((s + (1 << 3)) >> 4);
892
0
      }
893
0
    } else {
894
0
      top_left[0].write(T::cast_from(base));
895
0
    }
896
  }
897
0
  IntraEdge::new(edge_buf, init_left, init_above)
898
0
}
Unexecuted instantiation: rav1e::partition::get_intra_edges::<u16>
Unexecuted instantiation: rav1e::partition::get_intra_edges::<u8>
899
900
0
pub fn has_tr(bo: TileBlockOffset, bsize: BlockSize) -> bool {
901
0
  let sb_mi_size = BLOCK_64X64.width_mi(); /* Assume 64x64 for now */
902
0
  let mask_row = bo.0.y & LOCAL_BLOCK_MASK;
903
0
  let mask_col = bo.0.x & LOCAL_BLOCK_MASK;
904
0
  let target_n4_w = bsize.width_mi();
905
0
  let target_n4_h = bsize.height_mi();
906
907
0
  let mut bs = target_n4_w.max(target_n4_h);
908
909
0
  if bs > BLOCK_64X64.width_mi() {
910
0
    return false;
911
0
  }
912
913
0
  let mut has_tr = !((mask_row & bs) != 0 && (mask_col & bs) != 0);
914
915
  /* TODO: assert its a power of two */
916
917
0
  while bs < sb_mi_size {
918
0
    if (mask_col & bs) != 0 {
919
0
      if (mask_col & (2 * bs) != 0) && (mask_row & (2 * bs) != 0) {
920
0
        has_tr = false;
921
0
        break;
922
0
      }
923
    } else {
924
0
      break;
925
    }
926
0
    bs <<= 1;
927
  }
928
929
  /* The left hand of two vertical rectangles always has a top right (as the
930
   * block above will have been decoded) */
931
0
  if (target_n4_w < target_n4_h) && (bo.0.x & target_n4_w) == 0 {
932
0
    has_tr = true;
933
0
  }
934
935
  /* The bottom of two horizontal rectangles never has a top right (as the block
936
   * to the right won't have been decoded) */
937
0
  if (target_n4_w > target_n4_h) && (bo.0.y & target_n4_h) != 0 {
938
0
    has_tr = false;
939
0
  }
940
941
  /* The bottom left square of a Vertical A (in the old format) does
942
   * not have a top right as it is decoded before the right hand
943
   * rectangle of the partition */
944
  /*
945
    if blk.partition == PartitionType::PARTITION_VERT_A {
946
      if blk.n4_w == blk.n4_h {
947
        if (mask_row & bs) != 0 {
948
          has_tr = false;
949
        }
950
      }
951
    }
952
  */
953
954
0
  has_tr
955
0
}
956
957
0
pub fn has_bl(bo: TileBlockOffset, bsize: BlockSize) -> bool {
958
0
  let sb_mi_size = BLOCK_64X64.width_mi(); /* Assume 64x64 for now */
959
0
  let mask_row = bo.0.y & LOCAL_BLOCK_MASK;
960
0
  let mask_col = bo.0.x & LOCAL_BLOCK_MASK;
961
0
  let target_n4_w = bsize.width_mi();
962
0
  let target_n4_h = bsize.height_mi();
963
964
0
  let mut bs = target_n4_w.max(target_n4_h);
965
966
0
  if bs > BLOCK_64X64.width_mi() {
967
0
    return false;
968
0
  }
969
970
0
  let mut has_bl =
971
0
    (mask_row & bs) == 0 && (mask_col & bs) == 0 && bs < sb_mi_size;
972
973
  /* TODO: assert its a power of two */
974
975
0
  while 2 * bs < sb_mi_size {
976
0
    if (mask_col & bs) == 0 {
977
0
      if (mask_col & (2 * bs) == 0) && (mask_row & (2 * bs) == 0) {
978
0
        has_bl = true;
979
0
        break;
980
0
      }
981
    } else {
982
0
      break;
983
    }
984
0
    bs <<= 1;
985
  }
986
987
  /* The right hand of two vertical rectangles never has a bottom left (as the
988
   * block below won't have been decoded) */
989
0
  if (target_n4_w < target_n4_h) && (bo.0.x & target_n4_w) != 0 {
990
0
    has_bl = false;
991
0
  }
992
993
  /* The top of two horizontal rectangles always has a bottom left (as the block
994
   * to the left will have been decoded) */
995
0
  if (target_n4_w > target_n4_h) && (bo.0.y & target_n4_h) == 0 {
996
0
    has_bl = true;
997
0
  }
998
999
  /* The bottom left square of a Vertical A (in the old format) does
1000
   * not have a top right as it is decoded before the right hand
1001
   * rectangle of the partition */
1002
  /*
1003
    if blk.partition == PartitionType::PARTITION_VERT_A {
1004
      if blk.n4_w == blk.n4_h {
1005
        if (mask_row & bs) != 0 {
1006
          has_tr = false;
1007
        }
1008
      }
1009
    }
1010
  */
1011
1012
0
  has_bl
1013
0
}
1014
1015
#[cfg(test)]
1016
mod tests {
1017
  use crate::partition::BlockSize::*;
1018
  use crate::partition::{BlockSize, InvalidBlockSize};
1019
1020
  #[test]
1021
  fn from_wh_matches_naive() {
1022
    fn from_wh_opt_naive(
1023
      w: usize, h: usize,
1024
    ) -> Result<BlockSize, InvalidBlockSize> {
1025
      match (w, h) {
1026
        (4, 4) => Ok(BLOCK_4X4),
1027
        (4, 8) => Ok(BLOCK_4X8),
1028
        (8, 4) => Ok(BLOCK_8X4),
1029
        (8, 8) => Ok(BLOCK_8X8),
1030
        (8, 16) => Ok(BLOCK_8X16),
1031
        (16, 8) => Ok(BLOCK_16X8),
1032
        (16, 16) => Ok(BLOCK_16X16),
1033
        (16, 32) => Ok(BLOCK_16X32),
1034
        (32, 16) => Ok(BLOCK_32X16),
1035
        (32, 32) => Ok(BLOCK_32X32),
1036
        (32, 64) => Ok(BLOCK_32X64),
1037
        (64, 32) => Ok(BLOCK_64X32),
1038
        (64, 64) => Ok(BLOCK_64X64),
1039
        (64, 128) => Ok(BLOCK_64X128),
1040
        (128, 64) => Ok(BLOCK_128X64),
1041
        (128, 128) => Ok(BLOCK_128X128),
1042
        (4, 16) => Ok(BLOCK_4X16),
1043
        (16, 4) => Ok(BLOCK_16X4),
1044
        (8, 32) => Ok(BLOCK_8X32),
1045
        (32, 8) => Ok(BLOCK_32X8),
1046
        (16, 64) => Ok(BLOCK_16X64),
1047
        (64, 16) => Ok(BLOCK_64X16),
1048
        _ => Err(InvalidBlockSize),
1049
      }
1050
    }
1051
1052
    for w in 0..256 {
1053
      for h in 0..256 {
1054
        let a = BlockSize::from_width_and_height_opt(w, h);
1055
        let b = from_wh_opt_naive(w, h);
1056
1057
        assert_eq!(a, b);
1058
      }
1059
    }
1060
  }
1061
}