Skip to main content

zune_jpeg/
components.rs

1/*
2 * Copyright (c) 2023.
3 *
4 * This software is free software;
5 *
6 * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license
7 */
8
9//! This module exports a single struct to store information about
10//! JPEG image components
11//!
12//! The data is extracted from a SOF header.
13
14use alloc::vec::Vec;
15use alloc::{format, vec};
16
17use zune_core::log::trace;
18
19use crate::alloc::string::ToString;
20use crate::decoder::MAX_COMPONENTS;
21use crate::errors::DecodeErrors;
22use crate::upsampler::upsample_no_op;
23const MAX_SAMP_FACTOR: usize = 4;
24
25/// Represents an up-sampler function, this function will be called to upsample
26/// a down-sampled image
27
28pub type UpSampler = fn(
29    input: &[i16],
30    in_near: &[i16],
31    in_far: &[i16],
32    scratch_space: &mut [i16],
33    output: &mut [i16]
34);
35
36/// Component Data from start of frame
37#[derive(Clone)]
38pub(crate) struct Components {
39    /// The type of component that has the metadata below, can be Y,Cb or Cr
40    pub component_id: ComponentID,
41    /// Sub-sampling ratio of this component in the x-plane
42    pub vertical_sample: usize,
43    /// Sub-sampling ratio of this component in the y-plane
44    pub horizontal_sample: usize,
45    /// DC huffman table position
46    pub dc_huff_table: usize,
47    /// AC huffman table position for this element.
48    pub ac_huff_table: usize,
49    /// Quantization table number
50    pub quantization_table_number: u8,
51    /// Specifies quantization table to use with this component
52    pub quantization_table: [i32; 64],
53    /// dc prediction for the component
54    pub dc_pred: i32,
55    /// An up-sampling function, can be basic or SSE, depending
56    /// on the platform
57    pub up_sampler: UpSampler,
58    /// How pixels do we need to go to get to the next line?
59    pub width_stride: usize,
60    /// Component ID for progressive
61    pub id: u8,
62    /// Whether we need to decode this image component.
63    pub needed: bool,
64    /// Upsample scanline
65    pub raw_coeff: Vec<i16>,
66    /// Upsample destination, stores a scanline worth of sub sampled data
67    pub upsample_dest: Vec<i16>,
68    /// previous row, used to handle MCU boundaries
69    pub row_up: Vec<i16>,
70    /// current row, used to handle MCU boundaries again
71    pub row: Vec<i16>,
72    pub first_row_upsample_dest: Vec<i16>,
73    pub idct_pos: usize,
74    pub x: usize,
75    pub w2: usize,
76    pub y: usize,
77    pub sample_ratio: SampleRatios,
78    // a very annoying bug
79    pub fix_an_annoying_bug: usize
80}
81
82impl Components {
83    /// Create a new instance from three bytes from the start of frame
84    #[inline]
85    pub fn from(a: [u8; 3], pos: u8) -> Result<Components, DecodeErrors> {
86        // it's a unique identifier.
87        // doesn't have to be ascending
88        // see tests/inputs/huge_sof_number
89        //
90        // For such cases, use the position of the component
91        // to determine width
92
93        let id = match pos {
94            0 => ComponentID::Y,
95            1 => ComponentID::Cb,
96            2 => ComponentID::Cr,
97            3 => ComponentID::Q,
98            _ => {
99                return Err(DecodeErrors::Format(format!(
100                    "Unknown component id found,{pos}, expected value between 1 and 4"
101                )))
102            }
103        };
104
105        let horizontal_sample = (a[1] >> 4) as usize;
106        let vertical_sample = (a[1] & 0x0f) as usize;
107        // Match libjpeg turbo on checking for sampling factors
108        // Reject anything above 4
109        if horizontal_sample > MAX_SAMP_FACTOR {
110            return Err(DecodeErrors::Format(format!(
111                "Bogus Horizontal Sampling Factor {horizontal_sample}"
112            )));
113        }
114        if vertical_sample > MAX_SAMP_FACTOR {
115            return Err(DecodeErrors::Format(format!(
116                "Bogus Vertical Sampling Factor {vertical_sample}"
117            )));
118        }
119
120        let quantization_table_number = a[2];
121        // confirm quantization number is between 0 and MAX_COMPONENTS
122        if usize::from(quantization_table_number) >= MAX_COMPONENTS {
123            return Err(DecodeErrors::Format(format!(
124                "Too large quantization number :{quantization_table_number}, expected value between 0 and {MAX_COMPONENTS}"
125            )));
126        }
127        // check that upsampling ratios are powers of two
128        // if these fail, it's probably a corrupt image.
129        if !horizontal_sample.is_power_of_two() {
130            return Err(DecodeErrors::Format(format!(
131                "Horizontal sample is not a power of two({horizontal_sample}) cannot decode"
132            )));
133        }
134
135        // if !vertical_sample.is_power_of_two() {
136        //     return Err(DecodeErrors::Format(format!(
137        //         "Vertical sub-sample is not power of two({vertical_sample}) cannot decode"
138        //     )));
139        // }
140        if vertical_sample == 0 {
141            // Check for invalid vertical sample
142            return Err(DecodeErrors::Format("Vertical sample is zero".to_string()));
143        }
144        trace!(
145            "Component ID:{:?} \tHS:{} VS:{} QT:{}",
146            id,
147            horizontal_sample,
148            vertical_sample,
149            quantization_table_number
150        );
151
152        Ok(Components {
153            component_id: id,
154            vertical_sample,
155            horizontal_sample,
156            quantization_table_number,
157            first_row_upsample_dest: vec![],
158            // These two will be set with sof marker
159            dc_huff_table: 0,
160            ac_huff_table: 0,
161            quantization_table: [0; 64],
162            dc_pred: 0,
163            up_sampler: upsample_no_op,
164            // set later
165            width_stride: horizontal_sample,
166            id: a[0],
167            needed: true,
168            raw_coeff: vec![],
169            upsample_dest: vec![],
170            row_up: vec![],
171            row: vec![],
172            idct_pos: 0,
173            x: 0,
174            y: 0,
175            w2: 0,
176            sample_ratio: SampleRatios::None,
177            fix_an_annoying_bug: 1
178        })
179    }
180    /// Setup space for upsampling
181    ///
182    /// During upsample, we need a reference of the last row so that upsampling can
183    /// proceed correctly,
184    /// so we store the last line of every scanline and use it for the next upsampling procedure
185    /// to store this, but since we don't need it for 1v1 upsampling,
186    /// we only call this for routines that need upsampling
187    ///
188    /// # Requirements
189    ///  - width stride of this element is set for the component.
190    pub fn setup_upsample_scanline(&mut self) {
191        self.row = vec![0; self.width_stride * self.vertical_sample];
192        self.row_up = vec![0; self.width_stride * self.vertical_sample];
193        self.first_row_upsample_dest =
194            vec![128; self.vertical_sample * self.width_stride * self.sample_ratio.sample()];
195        self.upsample_dest =
196            vec![0; self.width_stride * self.sample_ratio.sample() * self.fix_an_annoying_bug * 8];
197    }
198}
199
200/// Component ID's
201#[derive(Copy, Debug, Clone, PartialEq, Eq)]
202pub enum ComponentID {
203    /// Luminance channel
204    Y,
205    /// Blue chrominance
206    Cb,
207    /// Red chrominance
208    Cr,
209    /// Q or fourth component
210    Q
211}
212
213#[derive(Copy, Debug, Clone, PartialEq, Eq, Default)]
214pub enum SampleRatios {
215    HV,
216    V,
217    H,
218    Generic(usize, usize),
219    #[default]
220    None
221}
222
223impl SampleRatios {
224    pub fn sample(self) -> usize {
225        match self {
226            SampleRatios::HV => 4,
227            SampleRatios::V | SampleRatios::H => 2,
228            SampleRatios::Generic(a, b) => a * b,
229            SampleRatios::None => 1
230        }
231    }
232}