/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zune-jpeg-0.5.15/src/components.rs
Line | Count | Source |
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::alloc::string::ToString; |
20 | | use crate::decoder::MAX_COMPONENTS; |
21 | | use crate::errors::DecodeErrors; |
22 | | use crate::upsampler::upsample_no_op; |
23 | | const 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 | | |
28 | | pub 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)] |
38 | | pub(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 | | |
82 | | impl Components { |
83 | | /// Create a new instance from three bytes from the start of frame |
84 | | #[inline] |
85 | 0 | 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 | 0 | let id = match pos { |
94 | 0 | 0 => ComponentID::Y, |
95 | 0 | 1 => ComponentID::Cb, |
96 | 0 | 2 => ComponentID::Cr, |
97 | 0 | 3 => ComponentID::Q, |
98 | | _ => { |
99 | 0 | return Err(DecodeErrors::Format(format!( |
100 | 0 | "Unknown component id found,{pos}, expected value between 1 and 4" |
101 | 0 | ))) |
102 | | } |
103 | | }; |
104 | | |
105 | 0 | let horizontal_sample = (a[1] >> 4) as usize; |
106 | 0 | let vertical_sample = (a[1] & 0x0f) as usize; |
107 | | // Match libjpeg turbo on checking for sampling factors |
108 | | // Reject anything above 4 |
109 | 0 | if horizontal_sample > MAX_SAMP_FACTOR { |
110 | 0 | return Err(DecodeErrors::Format(format!( |
111 | 0 | "Bogus Horizontal Sampling Factor {horizontal_sample}" |
112 | 0 | ))); |
113 | 0 | } |
114 | 0 | if vertical_sample > MAX_SAMP_FACTOR { |
115 | 0 | return Err(DecodeErrors::Format(format!( |
116 | 0 | "Bogus Vertical Sampling Factor {vertical_sample}" |
117 | 0 | ))); |
118 | 0 | } |
119 | | |
120 | 0 | let quantization_table_number = a[2]; |
121 | | // confirm quantization number is between 0 and MAX_COMPONENTS |
122 | 0 | if usize::from(quantization_table_number) >= MAX_COMPONENTS { |
123 | 0 | return Err(DecodeErrors::Format(format!( |
124 | 0 | "Too large quantization number :{quantization_table_number}, expected value between 0 and {MAX_COMPONENTS}" |
125 | 0 | ))); |
126 | 0 | } |
127 | | // check that upsampling ratios are powers of two |
128 | | // if these fail, it's probably a corrupt image. |
129 | 0 | if !horizontal_sample.is_power_of_two() { |
130 | 0 | return Err(DecodeErrors::Format(format!( |
131 | 0 | "Horizontal sample is not a power of two({horizontal_sample}) cannot decode" |
132 | 0 | ))); |
133 | 0 | } |
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 | 0 | if vertical_sample == 0 { |
141 | | // Check for invalid vertical sample |
142 | 0 | return Err(DecodeErrors::Format("Vertical sample is zero".to_string())); |
143 | 0 | } |
144 | 0 | trace!( |
145 | 0 | "Component ID:{:?} \tHS:{} VS:{} QT:{}", |
146 | | id, |
147 | | horizontal_sample, |
148 | | vertical_sample, |
149 | | quantization_table_number |
150 | | ); |
151 | | |
152 | 0 | Ok(Components { |
153 | 0 | component_id: id, |
154 | 0 | vertical_sample, |
155 | 0 | horizontal_sample, |
156 | 0 | quantization_table_number, |
157 | 0 | first_row_upsample_dest: vec![], |
158 | 0 | // These two will be set with sof marker |
159 | 0 | dc_huff_table: 0, |
160 | 0 | ac_huff_table: 0, |
161 | 0 | quantization_table: [0; 64], |
162 | 0 | dc_pred: 0, |
163 | 0 | up_sampler: upsample_no_op, |
164 | 0 | // set later |
165 | 0 | width_stride: horizontal_sample, |
166 | 0 | id: a[0], |
167 | 0 | needed: true, |
168 | 0 | raw_coeff: vec![], |
169 | 0 | upsample_dest: vec![], |
170 | 0 | row_up: vec![], |
171 | 0 | row: vec![], |
172 | 0 | idct_pos: 0, |
173 | 0 | x: 0, |
174 | 0 | y: 0, |
175 | 0 | w2: 0, |
176 | 0 | sample_ratio: SampleRatios::None, |
177 | 0 | fix_an_annoying_bug: 1 |
178 | 0 | }) |
179 | 0 | } Unexecuted instantiation: <zune_jpeg::components::Components>::from Unexecuted instantiation: <zune_jpeg::components::Components>::from |
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 | 0 | pub fn setup_upsample_scanline(&mut self) { |
191 | 0 | self.row = vec![0; self.width_stride * self.vertical_sample]; |
192 | 0 | self.row_up = vec![0; self.width_stride * self.vertical_sample]; |
193 | 0 | self.first_row_upsample_dest = |
194 | 0 | vec![128; self.vertical_sample * self.width_stride * self.sample_ratio.sample()]; |
195 | 0 | self.upsample_dest = |
196 | 0 | vec![0; self.width_stride * self.sample_ratio.sample() * self.fix_an_annoying_bug * 8]; |
197 | 0 | } |
198 | | } |
199 | | |
200 | | /// Component ID's |
201 | | #[derive(Copy, Debug, Clone, PartialEq, Eq)] |
202 | | pub 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)] |
214 | | pub enum SampleRatios { |
215 | | HV, |
216 | | V, |
217 | | H, |
218 | | Generic(usize, usize), |
219 | | #[default] |
220 | | None |
221 | | } |
222 | | |
223 | | impl SampleRatios { |
224 | 0 | pub fn sample(self) -> usize { |
225 | 0 | match self { |
226 | 0 | SampleRatios::HV => 4, |
227 | 0 | SampleRatios::V | SampleRatios::H => 2, |
228 | 0 | SampleRatios::Generic(a, b) => a * b, |
229 | 0 | SampleRatios::None => 1 |
230 | | } |
231 | 0 | } |
232 | | } |