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