Coverage Report

Created: 2026-05-16 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}