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
// Copyright 2022-2023 Andrew D. Straw.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
// or http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Data representations for YCbCr image data

use super::*;

/// An image in YCbCr format
///
/// This references data stored elsewhere and provides only minimal metadata to
/// describe the actual image data.
///
/// The luma stride must be evenly divisible by 16 and the luma data size must
/// have an integer multiple of 16 rows. For chroma, this number is 8.
pub struct YCbCrImage<'a> {
    /// The data planes for the image
    pub planes: Planes<'a>,
    /// The width of the image, in pixels
    pub width: u32,
    /// The height of the image, in pixels
    pub height: u32,
}

impl<'a> YCbCrImage<'a> {
    pub(crate) fn luma_bit_depth(&self) -> BitDepth {
        match &self.planes {
            Planes::Mono(y) => y.bit_depth,
            Planes::YCbCr((y, _, _)) => y.bit_depth,
        }
    }
}

/// The data plane(s) within an [YCbCrImage].
pub enum Planes<'a> {
    //// Luminance only (monochrome) data.
    Mono(DataPlane<'a>),
    //// Luminance and chrominance data.
    YCbCr((DataPlane<'a>, DataPlane<'a>, DataPlane<'a>)),
}

/// Data for a single plane (luminance or chrominance) of an image.
///
/// The actual data are stored elsewhere and this provides metadata.
pub struct DataPlane<'a> {
    /// The image data
    pub data: &'a [u8],
    /// The row stride of the image data
    pub stride: usize,
    /// The bit depth of the image data
    pub bit_depth: BitDepth,
}

impl<'a> YCbCrImage<'a> {
    pub(crate) fn check_sizes(&self) -> Result<()> {
        match &self.planes {
            Planes::Mono(y_plane) | Planes::YCbCr((y_plane, _, _)) => {
                y_plane.check_sizes(self.width, self.height, 16)?;
            }
        }

        match &self.planes {
            Planes::Mono(_) => {}
            Planes::YCbCr((_, cb_plane, cr_plane)) => {
                for chroma_plane in [cb_plane, cr_plane] {
                    chroma_plane.check_sizes(self.width / 2, self.height / 2, 8)?;
                }
            }
        }
        Ok(())
    }
}

impl<'a> DataPlane<'a> {
    pub(crate) fn check_sizes(&self, width: u32, height: u32, mb_sz: u32) -> Result<()> {
        let (width_factor_num, width_factor_denom) = match self.bit_depth {
            BitDepth::Depth8 => (1, 1),
            BitDepth::Depth12 => (3, 2),
        };
        // Check width
        if self.stride
            < next_multiple(width, mb_sz) as usize * width_factor_num / width_factor_denom
        {
            return Err(Error::DataShapeProblem {
                msg: "stride too small",
                #[cfg(feature = "backtrace")]
                backtrace: Backtrace::capture(),
            });
        }
        // check height
        let num_rows = div_ceil(
            self.data.len().try_into().unwrap(),
            self.stride.try_into().unwrap(),
        );
        if num_rows < next_multiple(height, mb_sz) {
            return Err(Error::DataShapeProblem {
                msg: "number of rows too small",
                #[cfg(feature = "backtrace")]
                backtrace: Backtrace::capture(),
            });
        }

        Ok(())
    }
}