/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 | 71.5k | 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 | 71.5k | let id = match pos { |
94 | 39.8k | 0 => ComponentID::Y, |
95 | 14.5k | 1 => ComponentID::Cb, |
96 | 14.3k | 2 => ComponentID::Cr, |
97 | 2.87k | 3 => ComponentID::Q, |
98 | | _ => { |
99 | 1 | return Err(DecodeErrors::Format(format!( |
100 | 1 | "Unknown component id found,{pos}, expected value between 1 and 4" |
101 | 1 | ))) |
102 | | } |
103 | | }; |
104 | | |
105 | 71.5k | let horizontal_sample = (a[1] >> 4) as usize; |
106 | 71.5k | let vertical_sample = (a[1] & 0x0f) as usize; |
107 | | // Match libjpeg turbo on checking for sampling factors |
108 | | // Reject anything above 4 |
109 | 71.5k | if horizontal_sample > MAX_SAMP_FACTOR { |
110 | 6 | return Err(DecodeErrors::Format(format!( |
111 | 6 | "Bogus Horizontal Sampling Factor {horizontal_sample}" |
112 | 6 | ))); |
113 | 71.5k | } |
114 | 71.5k | if vertical_sample > MAX_SAMP_FACTOR { |
115 | 5 | return Err(DecodeErrors::Format(format!( |
116 | 5 | "Bogus Vertical Sampling Factor {vertical_sample}" |
117 | 5 | ))); |
118 | 71.5k | } |
119 | | |
120 | 71.5k | let quantization_table_number = a[2]; |
121 | | // confirm quantization number is between 0 and MAX_COMPONENTS |
122 | 71.5k | if usize::from(quantization_table_number) >= MAX_COMPONENTS { |
123 | 4 | return Err(DecodeErrors::Format(format!( |
124 | 4 | "Too large quantization number :{quantization_table_number}, expected value between 0 and {MAX_COMPONENTS}" |
125 | 4 | ))); |
126 | 71.5k | } |
127 | | // check that upsampling ratios are powers of two |
128 | | // if these fail, it's probably a corrupt image. |
129 | 71.5k | if !horizontal_sample.is_power_of_two() { |
130 | 7 | return Err(DecodeErrors::Format(format!( |
131 | 7 | "Horizontal sample is not a power of two({horizontal_sample}) cannot decode" |
132 | 7 | ))); |
133 | 71.5k | } |
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 | 71.5k | if vertical_sample == 0 { |
141 | | // Check for invalid vertical sample |
142 | 1 | return Err(DecodeErrors::Format("Vertical sample is zero".to_string())); |
143 | 71.5k | } |
144 | | trace!( |
145 | | "Component ID:{:?} \tHS:{} VS:{} QT:{}", |
146 | | id, |
147 | | horizontal_sample, |
148 | | vertical_sample, |
149 | | quantization_table_number |
150 | | ); |
151 | | |
152 | 71.5k | Ok(Components { |
153 | 71.5k | component_id: id, |
154 | 71.5k | vertical_sample, |
155 | 71.5k | horizontal_sample, |
156 | 71.5k | quantization_table_number, |
157 | 71.5k | first_row_upsample_dest: vec![], |
158 | 71.5k | // These two will be set with sof marker |
159 | 71.5k | dc_huff_table: 0, |
160 | 71.5k | ac_huff_table: 0, |
161 | 71.5k | quantization_table: [0; 64], |
162 | 71.5k | dc_pred: 0, |
163 | 71.5k | up_sampler: upsample_no_op, |
164 | 71.5k | // set later |
165 | 71.5k | width_stride: horizontal_sample, |
166 | 71.5k | id: a[0], |
167 | 71.5k | needed: true, |
168 | 71.5k | raw_coeff: vec![], |
169 | 71.5k | upsample_dest: vec![], |
170 | 71.5k | row_up: vec![], |
171 | 71.5k | row: vec![], |
172 | 71.5k | idct_pos: 0, |
173 | 71.5k | x: 0, |
174 | 71.5k | y: 0, |
175 | 71.5k | w2: 0, |
176 | 71.5k | sample_ratio: SampleRatios::None, |
177 | 71.5k | fix_an_annoying_bug: 1 |
178 | 71.5k | }) |
179 | 71.5k | } <zune_jpeg::components::Components>::from Line | Count | Source | 85 | 71.5k | 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 | 71.5k | let id = match pos { | 94 | 39.8k | 0 => ComponentID::Y, | 95 | 14.5k | 1 => ComponentID::Cb, | 96 | 14.3k | 2 => ComponentID::Cr, | 97 | 2.87k | 3 => ComponentID::Q, | 98 | | _ => { | 99 | 1 | return Err(DecodeErrors::Format(format!( | 100 | 1 | "Unknown component id found,{pos}, expected value between 1 and 4" | 101 | 1 | ))) | 102 | | } | 103 | | }; | 104 | | | 105 | 71.5k | let horizontal_sample = (a[1] >> 4) as usize; | 106 | 71.5k | let vertical_sample = (a[1] & 0x0f) as usize; | 107 | | // Match libjpeg turbo on checking for sampling factors | 108 | | // Reject anything above 4 | 109 | 71.5k | if horizontal_sample > MAX_SAMP_FACTOR { | 110 | 6 | return Err(DecodeErrors::Format(format!( | 111 | 6 | "Bogus Horizontal Sampling Factor {horizontal_sample}" | 112 | 6 | ))); | 113 | 71.5k | } | 114 | 71.5k | if vertical_sample > MAX_SAMP_FACTOR { | 115 | 5 | return Err(DecodeErrors::Format(format!( | 116 | 5 | "Bogus Vertical Sampling Factor {vertical_sample}" | 117 | 5 | ))); | 118 | 71.5k | } | 119 | | | 120 | 71.5k | let quantization_table_number = a[2]; | 121 | | // confirm quantization number is between 0 and MAX_COMPONENTS | 122 | 71.5k | if usize::from(quantization_table_number) >= MAX_COMPONENTS { | 123 | 4 | return Err(DecodeErrors::Format(format!( | 124 | 4 | "Too large quantization number :{quantization_table_number}, expected value between 0 and {MAX_COMPONENTS}" | 125 | 4 | ))); | 126 | 71.5k | } | 127 | | // check that upsampling ratios are powers of two | 128 | | // if these fail, it's probably a corrupt image. | 129 | 71.5k | if !horizontal_sample.is_power_of_two() { | 130 | 7 | return Err(DecodeErrors::Format(format!( | 131 | 7 | "Horizontal sample is not a power of two({horizontal_sample}) cannot decode" | 132 | 7 | ))); | 133 | 71.5k | } | 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 | 71.5k | if vertical_sample == 0 { | 141 | | // Check for invalid vertical sample | 142 | 1 | return Err(DecodeErrors::Format("Vertical sample is zero".to_string())); | 143 | 71.5k | } | 144 | | trace!( | 145 | | "Component ID:{:?} \tHS:{} VS:{} QT:{}", | 146 | | id, | 147 | | horizontal_sample, | 148 | | vertical_sample, | 149 | | quantization_table_number | 150 | | ); | 151 | | | 152 | 71.5k | Ok(Components { | 153 | 71.5k | component_id: id, | 154 | 71.5k | vertical_sample, | 155 | 71.5k | horizontal_sample, | 156 | 71.5k | quantization_table_number, | 157 | 71.5k | first_row_upsample_dest: vec![], | 158 | 71.5k | // These two will be set with sof marker | 159 | 71.5k | dc_huff_table: 0, | 160 | 71.5k | ac_huff_table: 0, | 161 | 71.5k | quantization_table: [0; 64], | 162 | 71.5k | dc_pred: 0, | 163 | 71.5k | up_sampler: upsample_no_op, | 164 | 71.5k | // set later | 165 | 71.5k | width_stride: horizontal_sample, | 166 | 71.5k | id: a[0], | 167 | 71.5k | needed: true, | 168 | 71.5k | raw_coeff: vec![], | 169 | 71.5k | upsample_dest: vec![], | 170 | 71.5k | row_up: vec![], | 171 | 71.5k | row: vec![], | 172 | 71.5k | idct_pos: 0, | 173 | 71.5k | x: 0, | 174 | 71.5k | y: 0, | 175 | 71.5k | w2: 0, | 176 | 71.5k | sample_ratio: SampleRatios::None, | 177 | 71.5k | fix_an_annoying_bug: 1 | 178 | 71.5k | }) | 179 | 71.5k | } |
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 | 10.3k | pub fn setup_upsample_scanline(&mut self) { |
191 | 10.3k | self.row = vec![0; self.width_stride * self.vertical_sample]; |
192 | 10.3k | self.row_up = vec![0; self.width_stride * self.vertical_sample]; |
193 | 10.3k | self.first_row_upsample_dest = |
194 | 10.3k | vec![128; self.vertical_sample * self.width_stride * self.sample_ratio.sample()]; |
195 | 10.3k | self.upsample_dest = |
196 | 10.3k | vec![0; self.width_stride * self.sample_ratio.sample() * self.fix_an_annoying_bug * 8]; |
197 | 10.3k | } |
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 | 1.68M | pub fn sample(self) -> usize { |
225 | 1.68M | match self { |
226 | 134k | SampleRatios::HV => 4, |
227 | 1.23M | SampleRatios::V | SampleRatios::H => 2, |
228 | 5.64k | SampleRatios::Generic(a, b) => a * b, |
229 | 301k | SampleRatios::None => 1 |
230 | | } |
231 | 1.68M | } |
232 | | } |