videoframe 0.2.0

A common vocabulary of pixel-format and color-metadata types for video processing pipelines.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
use super::{
  util::{be_encoded_u16_buf, le_encoded_u16_buf},
  *,
};
use std::{vec, vec::Vec};

// ---- Yuv420pFrame16 / Yuv420p10Frame ----------------------------------
//
// Storage is `&[u16]` with sample-indexed strides. Validation mirrors
// the 8-bit [`Yuv420pFrame`] with the addition of the `BITS` guard.

fn p10_planes() -> (Vec<u16>, Vec<u16>, Vec<u16>) {
  // 16×8 frame, chroma 8×4. Y plane solid black (Y=0); UV planes
  // neutral (UV=512 = 10‑bit chroma center). Exact sample values
  // don't matter for the constructor tests that use this helper —
  // they only look at shape, geometry errors, and the reported
  // bits.
  (vec![0u16; 16 * 8], vec![512u16; 8 * 4], vec![512u16; 8 * 4])
}

#[test]
fn yuv420p10_try_new_accepts_valid_tight() {
  let (y, u, v) = p10_planes();
  let f = Yuv420p10Frame::try_new(&y, &u, &v, 16, 8, 16, 8, 8).expect("valid");
  assert_eq!(f.width(), 16);
  assert_eq!(f.height(), 8);
  assert_eq!(f.bits(), 10);
}

#[test]
fn yuv420p10_try_new_accepts_odd_height() {
  // 16x9 → chroma_height = 5. Y plane 16*9 = 144 samples, U/V 8*5 = 40.
  let y = vec![0u16; 16 * 9];
  let u = vec![512u16; 8 * 5];
  let v = vec![512u16; 8 * 5];
  let f = Yuv420p10Frame::try_new(&y, &u, &v, 16, 9, 16, 8, 8).expect("odd height valid");
  assert_eq!(f.height(), 9);
}

#[test]
fn yuv420p10_try_new_rejects_odd_width() {
  let (y, u, v) = p10_planes();
  let e = Yuv420p10Frame::try_new(&y, &u, &v, 15, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(
    e,
    Yuv420pFrame16Error::WidthAlignment(WidthAlignment {
      required: WidthAlignmentRequirement::Even,
      ..
    })
  ));
}

#[test]
fn yuv420p10_try_new_rejects_zero_dim() {
  let (y, u, v) = p10_planes();
  let e = Yuv420p10Frame::try_new(&y, &u, &v, 0, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(e, Yuv420pFrame16Error::ZeroDimension(_)));
}

#[test]
fn yuv420p10_try_new_rejects_short_y_plane() {
  let y = vec![0u16; 10];
  let u = vec![512u16; 8 * 4];
  let v = vec![512u16; 8 * 4];
  let e = Yuv420p10Frame::try_new(&y, &u, &v, 16, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(e, Yuv420pFrame16Error::InsufficientYPlane(_)));
}

#[test]
fn yuv420p10_try_new_rejects_short_u_plane() {
  let y = vec![0u16; 16 * 8];
  let u = vec![512u16; 4];
  let v = vec![512u16; 8 * 4];
  let e = Yuv420p10Frame::try_new(&y, &u, &v, 16, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(e, Yuv420pFrame16Error::InsufficientUPlane(_)));
}

#[test]
fn yuv420p_frame16_try_new_rejects_unsupported_bits() {
  // BITS must be in {9, 10, 12, 14, 16}. 11, 15, etc. are rejected
  // before any plane math runs.
  let y = vec![0u16; 16 * 8];
  let u = vec![128u16; 8 * 4];
  let v = vec![128u16; 8 * 4];
  let e = Yuv420pFrame16::<11>::try_new(&y, &u, &v, 16, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(e, Yuv420pFrame16Error::UnsupportedBits(_)));
  let e15 = Yuv420pFrame16::<15>::try_new(&y, &u, &v, 16, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(e15, Yuv420pFrame16Error::UnsupportedBits(_)));
}

#[test]
fn yuv420p16_try_new_accepts_12_14_and_16() {
  let y = vec![0u16; 16 * 8];
  let u = vec![2048u16; 8 * 4];
  let v = vec![2048u16; 8 * 4];
  let f12 = Yuv420pFrame16::<12>::try_new(&y, &u, &v, 16, 8, 16, 8, 8).expect("12-bit valid");
  assert_eq!(f12.bits(), 12);
  let f14 = Yuv420pFrame16::<14>::try_new(&y, &u, &v, 16, 8, 16, 8, 8).expect("14-bit valid");
  assert_eq!(f14.bits(), 14);
  let f16 = Yuv420p16Frame::try_new(&y, &u, &v, 16, 8, 16, 8, 8).expect("16-bit valid");
  assert_eq!(f16.bits(), 16);
}

#[test]
fn yuv420p16_try_new_checked_accepts_full_u16_range() {
  // At 16 bits the full u16 range is valid — max sample = 65535.
  let y = vec![65535u16; 16 * 8];
  let u = vec![32768u16; 8 * 4];
  let v = vec![32768u16; 8 * 4];
  Yuv420p16Frame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8)
    .expect("every u16 value is in range at 16 bits");
}

#[test]
fn p016_try_new_accepts_16bit() {
  let y = vec![0xFFFFu16; 16 * 8];
  let uv = vec![0x8000u16; 16 * 4];
  let f = P016Frame::try_new(&y, &uv, 16, 8, 16, 16).expect("P016 valid");
  assert_eq!(f.bits(), 16);
}

#[test]
fn p016_try_new_checked_is_a_noop() {
  // At BITS == 16 there are zero "low" bits to check — every u16
  // value is a valid P016 sample because `16 - BITS == 0`. The
  // checked constructor therefore accepts everything. This pins
  // that behavior in a test: at 16 bits the semantic distinction
  // between P016 and yuv420p16le **cannot be detected** from
  // sample values at all (no bit pattern is packing-specific).
  let y = vec![0x1234u16; 16 * 8];
  let uv = vec![0x5678u16; 16 * 4];
  P016Frame::try_new_checked(&y, &uv, 16, 8, 16, 16)
    .expect("every u16 passes the low-bits check at BITS == 16");
}

#[test]
fn pn_try_new_rejects_bits_other_than_10_12_16() {
  let y = vec![0u16; 16 * 8];
  let uv = vec![0u16; 16 * 4];
  let e14 = PnFrame::<14>::try_new(&y, &uv, 16, 8, 16, 16).unwrap_err();
  assert!(matches!(e14, PnFrameError::UnsupportedBits(_)));
  let e11 = PnFrame::<11>::try_new(&y, &uv, 16, 8, 16, 16).unwrap_err();
  assert!(matches!(e11, PnFrameError::UnsupportedBits(_)));
}

#[test]
#[should_panic(expected = "invalid Yuv420pFrame16")]
fn yuv420p10_new_panics_on_invalid() {
  let y = vec![0u16; 10];
  let u = vec![512u16; 8 * 4];
  let v = vec![512u16; 8 * 4];
  let _ = Yuv420p10Frame::new(&y, &u, &v, 16, 8, 16, 8, 8);
}

#[cfg(target_pointer_width = "32")]
#[test]
fn yuv420p10_try_new_rejects_geometry_overflow() {
  // Sample count overflow on 32-bit. Same rationale as the 8-bit
  // version — strides are in `u16` elements here, so the same
  // `0x1_0000 * 0x1_0000` product overflows `usize`.
  let big: u32 = 0x1_0000;
  let y: [u16; 0] = [];
  let u: [u16; 0] = [];
  let v: [u16; 0] = [];
  let e = Yuv420p10Frame::try_new(&y, &u, &v, big, big, big, big / 2, big / 2).unwrap_err();
  assert!(matches!(e, Yuv420pFrame16Error::GeometryOverflow(_)));
}

#[test]
fn yuv420p10_try_new_checked_accepts_in_range_samples() {
  // Same valid frame as `yuv420p10_try_new_accepts_valid_tight`,
  // but run through the checked constructor. All samples live in
  // the 10‑bit range.
  //
  // Wrap host-native `p10_planes()` via `le_encoded_u16_buf` so the buffer
  // honors the LE-encoded byte contract on every host: `try_new_checked`
  // applies `u16::from_le` to each sample before the range check, and a
  // raw host-native u16 of `512` decodes to `0x0002` on a BE host —
  // making the original literal-fed test vacuously pass for the wrong
  // reason there.
  let (intended_y, intended_u, intended_v) = p10_planes();
  let y = le_encoded_u16_buf(&intended_y);
  let u = le_encoded_u16_buf(&intended_u);
  let v = le_encoded_u16_buf(&intended_v);
  let f = Yuv420p10Frame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8).expect("valid");
  assert_eq!(f.width(), 16);
  assert_eq!(f.bits(), 10);
}

#[test]
fn yuv420p10_try_new_checked_rejects_y_high_bit_set() {
  // A Y sample with bit 15 set — typical of `p010` packing where
  // the 10 active bits sit in the high bits. `try_new` would
  // accept this and let the SIMD kernels produce arch‑dependent
  // garbage; `try_new_checked` catches it up front.
  let mut intended_y = vec![0u16; 16 * 8];
  intended_y[3 * 16 + 5] = 0x8000; // bit 15 set → way above 1023
  let intended_uv = vec![512u16; 8 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let u = le_encoded_u16_buf(&intended_uv);
  let v = le_encoded_u16_buf(&intended_uv);
  let e = Yuv420p10Frame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8).unwrap_err();
  match e {
    Yuv420pFrame16Error::SampleOutOfRange(p) => {
      assert_eq!(p.plane(), Yuv420pFrame16Plane::Y);
      assert_eq!(p.value(), 0x8000);
      assert_eq!(p.max_valid(), 1023);
    }
    other => panic!("expected SampleOutOfRange, got {other:?}"),
  }
}

#[test]
fn yuv420p10_try_new_checked_rejects_u_plane_sample() {
  // Offending sample in the U plane — error must name U, not Y or V.
  let intended_y = vec![0u16; 16 * 8];
  let mut intended_u = vec![512u16; 8 * 4];
  intended_u[2 * 8 + 3] = 1024; // just above max
  let intended_v = vec![512u16; 8 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let u = le_encoded_u16_buf(&intended_u);
  let v = le_encoded_u16_buf(&intended_v);
  let e = Yuv420p10Frame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(e, Yuv420pFrame16Error::SampleOutOfRange(_)));
}

#[test]
fn yuv420p10_try_new_checked_rejects_v_plane_sample() {
  // `try_new_checked` applies `u16::from_le` before the range check, so
  // pass LE-encoded byte storage so the test asserts the same logical
  // values on every host. (Without the wrap, a host-native `0xFFFF`
  // happens to be byte-palindromic and still triggers rejection on BE,
  // but the surrounding `512`s decode to `0x0002` — the test rejects on
  // accident, not on the value it claims.)
  let intended_y = vec![0u16; 16 * 8];
  let intended_u = vec![512u16; 8 * 4];
  let mut intended_v = vec![512u16; 8 * 4];
  intended_v[8 + 7] = 0xFFFF; // all bits set
  let y = le_encoded_u16_buf(&intended_y);
  let u = le_encoded_u16_buf(&intended_u);
  let v = le_encoded_u16_buf(&intended_v);
  let e = Yuv420p10Frame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(e, Yuv420pFrame16Error::SampleOutOfRange(_)));
}

#[test]
fn yuv420p10_try_new_checked_accepts_exact_max_sample() {
  // Boundary: sample value == (1 << BITS) - 1 is valid.
  let mut intended_y = vec![0u16; 16 * 8];
  intended_y[0] = 1023;
  let intended_uv = vec![512u16; 8 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let u = le_encoded_u16_buf(&intended_uv);
  let v = le_encoded_u16_buf(&intended_uv);
  Yuv420p10Frame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8).expect("1023 is in range");
}

#[test]
fn yuv420p10_try_new_checked_reports_geometry_errors_first() {
  // If geometry is invalid, we never get to the sample scan — the
  // same errors as `try_new` surface first. Prevents the checked
  // path from doing unnecessary O(N) work on inputs that would
  // fail for a simpler reason.
  let y = vec![0u16; 10]; // Too small.
  let u = vec![512u16; 8 * 4];
  let v = vec![512u16; 8 * 4];
  let e = Yuv420p10Frame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(e, Yuv420pFrame16Error::InsufficientYPlane(_)));
}

// ---- P010Frame ---------------------------------------------------------
//
// Semi‑planar 10‑bit. Plane shape mirrors Nv12Frame (Y + interleaved
// UV) but sample width is `u16` with the 10 active bits in the
// **high** 10 of each element (`value << 6`). Strides are in
// samples, not bytes.

fn p010_planes() -> (Vec<u16>, Vec<u16>) {
  // 16×8 frame — UV plane carries 16 u16 × 4 chroma rows = 64 u16.
  // P010 white Y = 1023 << 6 = 0xFFC0; neutral UV = 512 << 6 = 0x8000.
  (vec![0xFFC0u16; 16 * 8], vec![0x8000u16; 16 * 4])
}

#[test]
fn p010_try_new_accepts_valid_tight() {
  let (y, uv) = p010_planes();
  let f = P010Frame::try_new(&y, &uv, 16, 8, 16, 16).expect("valid");
  assert_eq!(f.width(), 16);
  assert_eq!(f.height(), 8);
  assert_eq!(f.uv_stride(), 16);
}

#[test]
fn p010_try_new_accepts_odd_height() {
  // 640×481 — same concrete odd‑height case covered by NV12 / NV21.
  let y = vec![0u16; 640 * 481];
  let uv = vec![0x8000u16; 640 * 241];
  let f = P010Frame::try_new(&y, &uv, 640, 481, 640, 640).expect("odd height valid");
  assert_eq!(f.height(), 481);
}

#[test]
fn p010_try_new_rejects_odd_width() {
  let (y, uv) = p010_planes();
  let e = P010Frame::try_new(&y, &uv, 15, 8, 16, 16).unwrap_err();
  assert!(matches!(
    e,
    PnFrameError::WidthAlignment(WidthAlignment {
      required: WidthAlignmentRequirement::Even,
      ..
    })
  ));
}

#[test]
fn p010_try_new_rejects_zero_dim() {
  let (y, uv) = p010_planes();
  let e = P010Frame::try_new(&y, &uv, 0, 8, 16, 16).unwrap_err();
  assert!(matches!(e, PnFrameError::ZeroDimension(_)));
}

#[test]
fn p010_try_new_rejects_y_stride_under_width() {
  let (y, uv) = p010_planes();
  let e = P010Frame::try_new(&y, &uv, 16, 8, 8, 16).unwrap_err();
  assert!(matches!(e, PnFrameError::InsufficientYStride(_)));
}

#[test]
fn p010_try_new_rejects_uv_stride_under_width() {
  let (y, uv) = p010_planes();
  let e = P010Frame::try_new(&y, &uv, 16, 8, 16, 8).unwrap_err();
  assert!(matches!(e, PnFrameError::InsufficientUvStride(_)));
}

#[test]
fn p010_try_new_rejects_odd_uv_stride() {
  // uv_stride = 17 passes the size check (>= width = 16) but is
  // odd, which would mis-align the (U, V) pair on every other row.
  let y = vec![0u16; 16 * 8];
  let uv = vec![0x8000u16; 17 * 4];
  let e = P010Frame::try_new(&y, &uv, 16, 8, 16, 17).unwrap_err();
  assert!(matches!(e, PnFrameError::UvStrideOdd(_)));
}

#[test]
fn p210_try_new_rejects_odd_uv_stride() {
  // PnFrame422 chroma is half-width × full-height with 2 u16 per
  // pair → uv_row_elems = width. Same odd-stride bug as P010.
  let y = vec![0u16; 16 * 8];
  let uv = vec![0x8000u16; 17 * 8];
  let e = P210Frame::try_new(&y, &uv, 16, 8, 16, 17).unwrap_err();
  assert!(matches!(e, PnFrameError::UvStrideOdd(_)));
}

#[test]
fn p410_try_new_rejects_odd_uv_stride() {
  // PnFrame444 chroma is full-width × full-height with 2 u16 per
  // pair → uv_row_elems = 2 * width = 32. uv_stride = 33 passes
  // the size check but is odd.
  let y = vec![0u16; 16 * 8];
  let uv = vec![0x8000u16; 33 * 8];
  let e = P410Frame::try_new(&y, &uv, 16, 8, 16, 33).unwrap_err();
  assert!(matches!(e, PnFrameError::UvStrideOdd(_)));
}

#[test]
fn p010_try_new_rejects_short_y_plane() {
  let y = vec![0u16; 10];
  let uv = vec![0x8000u16; 16 * 4];
  let e = P010Frame::try_new(&y, &uv, 16, 8, 16, 16).unwrap_err();
  assert!(matches!(e, PnFrameError::InsufficientYPlane(_)));
}

#[test]
fn p010_try_new_rejects_short_uv_plane() {
  let y = vec![0u16; 16 * 8];
  let uv = vec![0x8000u16; 8];
  let e = P010Frame::try_new(&y, &uv, 16, 8, 16, 16).unwrap_err();
  assert!(matches!(e, PnFrameError::InsufficientUvPlane(_)));
}

#[test]
#[should_panic(expected = "invalid PnFrame")]
fn p010_new_panics_on_invalid() {
  let y = vec![0u16; 10];
  let uv = vec![0x8000u16; 16 * 4];
  let _ = P010Frame::new(&y, &uv, 16, 8, 16, 16);
}

#[cfg(target_pointer_width = "32")]
#[test]
fn p010_try_new_rejects_geometry_overflow() {
  let big: u32 = 0x1_0000;
  let y: [u16; 0] = [];
  let uv: [u16; 0] = [];
  let e = P010Frame::try_new(&y, &uv, big, big, big, big).unwrap_err();
  assert!(matches!(e, PnFrameError::GeometryOverflow(_)));
}

#[test]
fn p010_try_new_checked_accepts_shifted_samples() {
  // Valid P010 samples: low 6 bits zero.
  let intended_y = vec![0xFFC0u16; 16 * 8];
  let intended_uv = vec![0x8000u16; 16 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let uv = le_encoded_u16_buf(&intended_uv);
  P010Frame::try_new_checked(&y, &uv, 16, 8, 16, 16).expect("shifted samples valid");
}

#[test]
fn p010_try_new_checked_rejects_y_low_bits_set() {
  // A Y sample with low 6 bits set — characteristic of yuv420p10le
  // packing (value in low 10 bits) accidentally handed to the P010
  // constructor. `try_new_checked` catches this; plain `try_new`
  // would let the kernel mask it down and produce wrong colors.
  let mut intended_y = vec![0xFFC0u16; 16 * 8];
  intended_y[3 * 16 + 5] = 0x03FF; // 10-bit value in low bits — wrong packing
  let intended_uv = vec![0x8000u16; 16 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let uv = le_encoded_u16_buf(&intended_uv);
  let e = P010Frame::try_new_checked(&y, &uv, 16, 8, 16, 16).unwrap_err();
  match e {
    PnFrameError::SampleLowBitsSet(p) => {
      assert_eq!(p.plane(), P010FramePlane::Y);
      assert_eq!(p.value(), 0x03FF);
    }
    other => panic!("expected SampleLowBitsSet, got {other:?}"),
  }
}

#[test]
fn p010_try_new_checked_rejects_uv_plane_sample() {
  let intended_y = vec![0xFFC0u16; 16 * 8];
  let mut intended_uv = vec![0x8000u16; 16 * 4];
  intended_uv[2 * 16 + 3] = 0x0001; // low bit set
  let y = le_encoded_u16_buf(&intended_y);
  let uv = le_encoded_u16_buf(&intended_uv);
  let e = P010Frame::try_new_checked(&y, &uv, 16, 8, 16, 16).unwrap_err();
  assert!(matches!(e, PnFrameError::SampleLowBitsSet(_)));
}

#[test]
fn p010_try_new_checked_reports_geometry_errors_first() {
  let y = vec![0u16; 10]; // Too small.
  let uv = vec![0x8000u16; 16 * 4];
  let e = P010Frame::try_new_checked(&y, &uv, 16, 8, 16, 16).unwrap_err();
  assert!(matches!(e, PnFrameError::InsufficientYPlane(_)));
}

/// Regression documenting a **known limitation** of
/// [`P010Frame::try_new_checked`]: the low‑6‑bits‑zero check is a
/// packing sanity check, not a provenance validator. A
/// `yuv420p10le` buffer whose samples all happen to be multiples
/// of 64 — e.g. `Y = 64` (limited‑range black, `0x0040`) and
/// `UV = 512` (neutral chroma, `0x0200`) — passes the check
/// silently, even though the layout is wrong and downstream P010
/// kernels will produce incorrect output.
///
/// The test asserts the check accepts these values so the limit
/// is visible in the test log; any future attempt to tighten the
/// constructor into a real provenance validator will need to
/// update or replace this test.
#[test]
fn p010_try_new_checked_accepts_ambiguous_yuv420p10le_samples() {
  // `yuv420p10le`-style samples, all multiples of 64: low 6 bits
  // are zero, so they pass the P010 sanity check even though this
  // is wrong data for a P010 frame.
  let intended_y = vec![0x0040u16; 16 * 8]; // limited-range black in 10-bit low-packed
  let intended_uv = vec![0x0200u16; 16 * 4]; // neutral chroma in 10-bit low-packed
  let y = le_encoded_u16_buf(&intended_y);
  let uv = le_encoded_u16_buf(&intended_uv);
  let f = P010Frame::try_new_checked(&y, &uv, 16, 8, 16, 16)
    .expect("known limitation: low-6-bits-zero check cannot tell yuv420p10le from P010");
  assert_eq!(f.width(), 16);
  // Downstream decoding of this frame would produce wrong colors
  // (every `>> 6` extracts 1 from Y=0x0040 and 8 from UV=0x0200,
  // which P010 kernels then bias/scale as if those were the 10-bit
  // source values). That's accepted behavior — the type system,
  // not `try_new_checked`, is what keeps yuv420p10le out of P010.
}

#[test]
fn p012_try_new_checked_accepts_shifted_samples() {
  // Valid P012 samples: low 4 bits zero (12-bit value << 4).
  let y = vec![(2048u16) << 4; 16 * 8]; // 12-bit mid-gray shifted up
  let uv = vec![(2048u16) << 4; 16 * 4];
  P012Frame::try_new_checked(&y, &uv, 16, 8, 16, 16).expect("shifted samples valid");
}

#[test]
fn p012_try_new_checked_rejects_low_bits_set() {
  // A Y sample with any of the low 4 bits set — e.g. yuv420p12le
  // value 0x0ABC landing where P012 expects `value << 4`. The check
  // catches samples like this that are obviously mispacked.
  let mut intended_y = vec![(2048u16) << 4; 16 * 8];
  intended_y[3 * 16 + 5] = 0x0ABC; // low 4 bits = 0xC ≠ 0
  let intended_uv = vec![(2048u16) << 4; 16 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let uv = le_encoded_u16_buf(&intended_uv);
  let e = P012Frame::try_new_checked(&y, &uv, 16, 8, 16, 16).unwrap_err();
  match e {
    PnFrameError::SampleLowBitsSet(p) => {
      assert_eq!(p.plane(), PnFramePlane::Y);
      assert_eq!(p.value(), 0x0ABC);
      assert_eq!(p.low_bits(), 4);
    }
    other => panic!("expected SampleLowBitsSet, got {other:?}"),
  }
}

/// Regression documenting a **worse known limitation** of
/// [`P012Frame::try_new_checked`] compared to P010: because the
/// low‑bits check only has 4 bits to work with at `BITS == 12`,
/// every multiple‑of‑16 `yuv420p12le` value passes silently. The
/// practical impact is that common limited‑range flat‑region
/// content in real decoder output — `Y = 256` (limited‑range
/// black), `UV = 2048` (neutral chroma), `Y = 1024` (full black)
/// — is entirely invisible to this check.
///
/// This test pins the limitation with a reproducible input so
/// that:
/// 1. Users reading the test suite can see the exact failure
///    mode for `try_new_checked` on 12‑bit data.
/// 2. Any future attempt to strengthen `try_new_checked` (e.g.,
///    into a statistical provenance heuristic) has a concrete
///    input to validate against.
/// 3. The `PnFrame` docs' warning about this limitation has a
///    named test to point to.
///
/// For P012, the type system (choosing [`P012Frame`] vs
/// [`Yuv420p12Frame`] at construction based on decoder metadata)
/// is the only reliable provenance guarantee.
#[test]
fn p012_try_new_checked_accepts_low_packed_flat_content_by_design() {
  // All values are multiples of 16 — exactly the set that slips
  // through a 4-low-bits-zero check. `yuv420p12le` limited-range
  // black and neutral chroma both satisfy this.
  let intended_y = vec![0x0100u16; 16 * 8]; // Y = 256 (limited-range black), multiple of 16
  let intended_uv = vec![0x0800u16; 16 * 4]; // UV = 2048 (neutral chroma), multiple of 16
  let y = le_encoded_u16_buf(&intended_y);
  let uv = le_encoded_u16_buf(&intended_uv);
  let f = P012Frame::try_new_checked(&y, &uv, 16, 8, 16, 16)
    .expect("known limitation: 4-low-bits-zero check cannot tell yuv420p12le from P012");
  assert_eq!(f.width(), 16);
  // Downstream P012 kernels would extract `>> 4` — giving Y=16 and
  // UV=128 instead of the intended Y=256 and UV=2048. Silent color
  // corruption. The type system, not `try_new_checked`, must
  // guarantee provenance for 12-bit.
}

// ---- Host-independent BE-host regressions (codex round-2) -----------
//
// These tests build the planes explicitly from LE-encoded bytes via
// `to_le_bytes` and read back as `&[u16]` via `from_ne_bytes`. On an
// LE host the resulting `u16` values are identical to the intended
// literals; on a BE host every `u16` is byte-swapped relative to the
// intent, exercising the `u16::from_le` normalization inside the
// validators. Without that normalization the validators would falsely
// reject every valid LE-encoded plane on a BE host.
//
// Each family covers (1) a positive case — a logical LE buffer of
// valid samples that must be accepted on both LE and BE hosts — and
// (2) a negative case where a sample is invalid even after `from_le`
// normalization, ensuring the validator still surfaces real errors.

#[test]
fn yuv420p10_try_new_checked_accepts_le_encoded_buffer_on_any_host() {
  // 10-bit-low-packed white = 1023 (LE bytes [0xFF, 0x03]).
  let intended_y = vec![1023u16; 16 * 8];
  let intended_uv = vec![512u16; 8 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let u = le_encoded_u16_buf(&intended_uv);
  let v = le_encoded_u16_buf(&intended_uv);
  Yuv420p10Frame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8)
    .expect("LE-encoded valid yuv420p10le must be accepted on both LE and BE hosts");
}

#[test]
fn yuv420p10_try_new_checked_rejects_le_encoded_out_of_range_on_any_host() {
  // After `u16::from_le` normalization the offending sample is 1024
  // (just above the 10-bit max of 1023). On both LE and BE hosts the
  // validator must catch this — the LE-encoded byte buffer carries the
  // logical value 1024 in `u[2 * 8 + 3]`.
  let intended_y = vec![0u16; 16 * 8];
  let mut intended_u = vec![512u16; 8 * 4];
  intended_u[2 * 8 + 3] = 1024;
  let intended_v = vec![512u16; 8 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let u = le_encoded_u16_buf(&intended_u);
  let v = le_encoded_u16_buf(&intended_v);
  let e = Yuv420p10Frame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(e, Yuv420pFrame16Error::SampleOutOfRange(_)));
}

#[test]
fn p010_try_new_checked_accepts_le_encoded_buffer_on_any_host() {
  // P010 white = 1023 << 6 = 0xFFC0; LE bytes [0xC0, 0xFF]. On a BE
  // host these bytes read back as host-native 0xC0FF (low 6 bits =
  // 0x3F) — the validator's `from_le` normalization must recover the
  // intended 0xFFC0 before the low-bits check.
  let intended_y = vec![0xFFC0u16; 16 * 8];
  let intended_uv = vec![0x8000u16; 16 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let uv = le_encoded_u16_buf(&intended_uv);
  P010Frame::try_new_checked(&y, &uv, 16, 8, 16, 16)
    .expect("LE-encoded valid P010 must be accepted on both LE and BE hosts");
}

#[test]
fn p010_try_new_checked_rejects_le_encoded_low_bits_on_any_host() {
  // After `u16::from_le` normalization, a logical 0x03FF has all six
  // low bits set — characteristic of `yuv420p10le` data accidentally
  // handed to the P010 constructor. The validator must reject this on
  // both LE and BE hosts.
  let mut intended_y = vec![0xFFC0u16; 16 * 8];
  intended_y[3 * 16 + 5] = 0x03FF;
  let intended_uv = vec![0x8000u16; 16 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let uv = le_encoded_u16_buf(&intended_uv);
  let e = P010Frame::try_new_checked(&y, &uv, 16, 8, 16, 16).unwrap_err();
  assert!(matches!(e, PnFrameError::SampleLowBitsSet(_)));
}

#[test]
fn p012_try_new_checked_accepts_le_encoded_buffer_on_any_host() {
  // P012 mid-gray = 2048 << 4 = 0x8000; LE bytes [0x00, 0x80]. On a BE
  // host these read back as host-native 0x0080 — the validator must
  // `from_le` to recover 0x8000 before the low-4-bits check.
  let intended_y = vec![(2048u16) << 4; 16 * 8];
  let intended_uv = vec![(2048u16) << 4; 16 * 4];
  let y = le_encoded_u16_buf(&intended_y);
  let uv = le_encoded_u16_buf(&intended_uv);
  P012Frame::try_new_checked(&y, &uv, 16, 8, 16, 16)
    .expect("LE-encoded valid P012 must be accepted on both LE and BE hosts");
}

// ---- BE checked-constructor regressions -------------------------------
//
// `try_new_checked` on `*BeFrame` (i.e. `<const BE = true>`) MUST
// normalize via `u16::from_be` before the bit / range check. Without
// the BE flag wired into the validator, valid BE samples (e.g. P010
// white = 0xFFC0 BE-encoded as bytes [0xFF, 0xC0], read host-native
// as 0xFFC0 on BE host or 0xC0FF on LE host) would falsely fail.
//
// These tests build BE-encoded byte buffers so on every host the
// validator sees the post-`from_be` logical sample.

#[test]
fn p010_be_try_new_checked_accepts_be_encoded_buffer_on_any_host() {
  // Valid P010 white sample = 0xFFC0 (10 active bits in high 10).
  // Encoded BE on the wire as bytes [0xFF, 0xC0]; on a LE host these
  // read back as host-native 0xC0FF (low 6 bits = 0x3F). Without the
  // BE-aware normalization, the validator would reject every sample.
  let intended_y = vec![0xFFC0u16; 16 * 8];
  let intended_uv = vec![0x8000u16; 16 * 4];
  let y = be_encoded_u16_buf(&intended_y);
  let uv = be_encoded_u16_buf(&intended_uv);
  P010BeFrame::try_new_checked(&y, &uv, 16, 8, 16, 16)
    .expect("BE-encoded valid P010 must be accepted on both LE and BE hosts");
}

#[test]
fn p010_be_try_new_checked_rejects_be_encoded_low_bits_set() {
  // Logical 0xFFCF after `from_be` normalization has low 4 bits set
  // (low 6-bit mask = 0x000F & 0x3F = 0x0F). The validator must
  // surface this as `SampleLowBitsSet` even on a LE host where the
  // raw u16 reads as 0xCFFF before normalization.
  let mut intended_y = vec![0xFFC0u16; 16 * 8];
  intended_y[3 * 16 + 5] = 0xFFCF;
  let intended_uv = vec![0x8000u16; 16 * 4];
  let y = be_encoded_u16_buf(&intended_y);
  let uv = be_encoded_u16_buf(&intended_uv);
  let e = P010BeFrame::try_new_checked(&y, &uv, 16, 8, 16, 16).unwrap_err();
  assert!(matches!(e, PnFrameError::SampleLowBitsSet(_)));
}

#[test]
fn yuv420p10_be_try_new_checked_accepts_be_encoded_buffer_on_any_host() {
  // 10-bit-low-packed white = 1023; BE-encoded on the wire so on a LE
  // host the raw u16 reads as 0xFF03. The validator must `from_be`
  // back to 1023 before the range check.
  let intended_y = vec![1023u16; 16 * 8];
  let intended_uv = vec![512u16; 8 * 4];
  let y = be_encoded_u16_buf(&intended_y);
  let u = be_encoded_u16_buf(&intended_uv);
  let v = be_encoded_u16_buf(&intended_uv);
  Yuv420p10BeFrame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8)
    .expect("BE-encoded valid yuv420p10be must be accepted on both LE and BE hosts");
}

#[test]
fn yuv420p10_be_try_new_checked_rejects_be_encoded_out_of_range() {
  // Logical 1024 (just above 10-bit max) BE-encoded — must be rejected
  // on every host.
  let intended_y = vec![0u16; 16 * 8];
  let mut intended_u = vec![512u16; 8 * 4];
  intended_u[2 * 8 + 3] = 1024;
  let intended_v = vec![512u16; 8 * 4];
  let y = be_encoded_u16_buf(&intended_y);
  let u = be_encoded_u16_buf(&intended_u);
  let v = be_encoded_u16_buf(&intended_v);
  let e = Yuv420p10BeFrame::try_new_checked(&y, &u, &v, 16, 8, 16, 8, 8).unwrap_err();
  assert!(matches!(e, Yuv420pFrame16Error::SampleOutOfRange(_)));
}