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 | |
14 | use alloc::vec::Vec; |
15 | use alloc::{format, vec}; |
16 | |
17 | use zune_core::log::trace; |
18 | |
19 | use crate::decoder::MAX_COMPONENTS; |
20 | use crate::errors::DecodeErrors; |
21 | use 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 | |
26 | pub 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)] |
36 | pub(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 | |
80 | impl 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)] |
184 | pub 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)] |
196 | pub enum SampleRatios { |
197 | HV, |
198 | V, |
199 | H, |
200 | None |
201 | } |
202 | |
203 | impl 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 | } |
212 | |