Coverage Report

Created: 2026-08-13 08:17

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