na_mpeg2_decoder/
convert.rs

1use crate::video::{Frame, PixelFormat};
2
3/// Convert a decoded YUV frame to RGBA (BT.601 limited range).
4///
5/// `out_rgba` must have length `frame.width * frame.height * 4`.
6/// The function does not allocate.
7pub fn frame_to_rgba_bt601_limited(frame: &Frame, out_rgba: &mut [u8]) {
8    debug_assert_eq!(out_rgba.len(), frame.width * frame.height * 4);
9
10    match frame.format {
11        PixelFormat::Yuv420p | PixelFormat::Yuv422p | PixelFormat::Yuv444p => {}
12    }
13
14    let (cx, cy) = frame.chroma_shifts();
15    let w_uv = frame.width >> cx;
16
17    for y in 0..frame.height {
18        let y_row = &frame.data_y[y * frame.linesize_y..][..frame.width];
19        let uv_y = y >> cy;
20        let u_row = &frame.data_u[uv_y * frame.linesize_u..][..w_uv];
21        let v_row = &frame.data_v[uv_y * frame.linesize_v..][..w_uv];
22
23        for x in 0..frame.width {
24            let yv = y_row[x] as i32;
25            let uv_x = x >> cx;
26            let u = u_row[uv_x] as i32;
27            let v = v_row[uv_x] as i32;
28            let (r, g, b) = yuv_to_rgb_bt601_limited(yv, u, v);
29            let o = (y * frame.width + x) * 4;
30            out_rgba[o + 0] = r;
31            out_rgba[o + 1] = g;
32            out_rgba[o + 2] = b;
33            out_rgba[o + 3] = 255;
34        }
35    }
36}
37
38/// Convert a decoded frame to grayscale RGBA by duplicating the luma plane.
39pub fn frame_to_gray_rgba(frame: &Frame, out_rgba: &mut [u8]) {
40    debug_assert_eq!(out_rgba.len(), frame.width * frame.height * 4);
41    for y in 0..frame.height {
42        let src = &frame.data_y[y * frame.linesize_y..][..frame.width];
43        for x in 0..frame.width {
44            let v = src[x];
45            let o = (y * frame.width + x) * 4;
46            out_rgba[o + 0] = v;
47            out_rgba[o + 1] = v;
48            out_rgba[o + 2] = v;
49            out_rgba[o + 3] = 255;
50        }
51    }
52}
53
54#[inline]
55fn yuv_to_rgb_bt601_limited(y: i32, u: i32, v: i32) -> (u8, u8, u8) {
56    // BT.601 limited range (MPEG). Clamp for safety.
57    let c = (y - 16).max(0);
58    let d = u - 128;
59    let e = v - 128;
60
61    let r = (298 * c + 409 * e + 128) >> 8;
62    let g = (298 * c - 100 * d - 208 * e + 128) >> 8;
63    let b = (298 * c + 516 * d + 128) >> 8;
64
65    (clamp_u8(r), clamp_u8(g), clamp_u8(b))
66}
67
68#[inline]
69fn clamp_u8(v: i32) -> u8 {
70    if v < 0 {
71        0
72    } else if v > 255 {
73        255
74    } else {
75        v as u8
76    }
77}