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}
212