Coverage Report

Created: 2026-09-01 07:45

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