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