Coverage Report

Created: 2025-12-05 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/jpeg-encoder-0.6.1/src/writer.rs
Line
Count
Source
1
use crate::encoder::Component;
2
use crate::huffman::{CodingClass, HuffmanTable};
3
use crate::marker::{Marker, SOFType};
4
use crate::quantization::QuantizationTable;
5
use crate::EncodingError;
6
7
/// Density settings
8
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
9
pub enum Density {
10
    /// No pixel density is set, which means "1 pixel per pixel"
11
    None,
12
13
    /// Horizontal and vertical dots per inch (dpi)
14
    Inch { x: u16, y: u16 },
15
16
    /// Horizontal and vertical dots per centimeters
17
    Centimeter { x: u16, y: u16 },
18
}
19
20
/// Zig-zag sequence of quantized DCT coefficients
21
///
22
/// Figure A.6
23
pub static ZIGZAG: [u8; 64] = [
24
    0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20,
25
    13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
26
    52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
27
];
28
29
const BUFFER_SIZE: usize = core::mem::size_of::<usize>() * 8;
30
31
/// A no_std alternative for `std::io::Write`
32
///
33
/// An implementation of a subset of `std::io::Write` necessary to use the encoder without `std`.
34
/// This trait is implemented for `std::io::Write` if the `std` feature is enabled.
35
pub trait JfifWrite {
36
    /// Writes the whole buffer. The behavior must be identical to std::io::Write::write_all
37
    /// # Errors
38
    ///
39
    /// Return an error if the data can't be written
40
    fn write_all(&mut self, buf: &[u8]) -> Result<(), EncodingError>;
41
}
42
43
#[cfg(not(feature = "std"))]
44
impl<W: JfifWrite + ?Sized> JfifWrite for &mut W {
45
    fn write_all(&mut self, buf: &[u8]) -> Result<(), EncodingError> {
46
        (**self).write_all(buf)
47
    }
48
}
49
50
#[cfg(not(feature = "std"))]
51
impl JfifWrite for alloc::vec::Vec<u8> {
52
    fn write_all(&mut self, buf: &[u8]) -> Result<(), EncodingError> {
53
        self.extend_from_slice(buf);
54
        Ok(())
55
    }
56
}
57
58
#[cfg(feature = "std")]
59
impl<W: std::io::Write + ?Sized> JfifWrite for W {
60
    #[inline(always)]
61
0
    fn write_all(&mut self, buf: &[u8]) -> Result<(), EncodingError> {
62
0
        self.write_all(buf)?;
63
0
        Ok(())
64
0
    }
Unexecuted instantiation: <_ as jpeg_encoder::writer::JfifWrite>::write_all
Unexecuted instantiation: <&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>> as jpeg_encoder::writer::JfifWrite>::write_all
65
}
66
67
pub(crate) struct JfifWriter<W: JfifWrite> {
68
    w: W,
69
    bit_buffer: usize,
70
    free_bits: i8,
71
}
72
73
impl<W: JfifWrite> JfifWriter<W> {
74
0
    pub fn new(w: W) -> Self {
75
0
        JfifWriter {
76
0
            w,
77
0
            bit_buffer: 0,
78
0
            free_bits: BUFFER_SIZE as i8,
79
0
        }
80
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::new
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new
81
82
    #[inline(always)]
83
0
    pub fn write(&mut self, buf: &[u8]) -> Result<(), EncodingError> {
84
0
        self.w.write_all(buf)
85
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write
86
87
    #[inline(always)]
88
0
    pub fn write_u8(&mut self, value: u8) -> Result<(), EncodingError> {
89
0
        self.w.write_all(&[value])
90
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_u8
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_u8
91
92
    #[inline(always)]
93
0
    pub fn write_u16(&mut self, value: u16) -> Result<(), EncodingError> {
94
0
        self.w.write_all(&value.to_be_bytes())
95
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_u16
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_u16
96
97
0
    pub fn finalize_bit_buffer(&mut self) -> Result<(), EncodingError> {
98
0
        self.write_bits(0x7F, 7)?;
99
0
        self.flush_bit_buffer()?;
100
0
        self.bit_buffer = 0;
101
0
        self.free_bits = BUFFER_SIZE as i8;
102
103
0
        Ok(())
104
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::finalize_bit_buffer
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::finalize_bit_buffer
105
106
0
    pub fn flush_bit_buffer(&mut self) -> Result<(), EncodingError> {
107
0
        while self.free_bits <= (BUFFER_SIZE as i8 - 8) {
108
0
            self.flush_byte_from_bit_buffer(self.free_bits)?;
109
0
            self.free_bits += 8;
110
        }
111
112
0
        Ok(())
113
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::flush_bit_buffer
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::flush_bit_buffer
114
115
    #[inline(always)]
116
0
    fn flush_byte_from_bit_buffer(&mut self, free_bits: i8) -> Result<(), EncodingError> {
117
0
        let value = (self.bit_buffer >> (BUFFER_SIZE as i8 - 8 - free_bits)) & 0xFF;
118
119
0
        self.write_u8(value as u8)?;
120
121
0
        if value == 0xFF {
122
0
            self.write_u8(0x00)?;
123
0
        }
124
125
0
        Ok(())
126
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::flush_byte_from_bit_buffer
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::flush_byte_from_bit_buffer
127
128
    #[inline(always)]
129
    #[allow(overflowing_literals)]
130
0
    fn write_bit_buffer(&mut self) -> Result<(), EncodingError> {
131
0
        if (self.bit_buffer
132
0
            & 0x8080808080808080
133
0
            & !(self.bit_buffer.wrapping_add(0x0101010101010101)))
134
0
            != 0
135
        {
136
0
            for i in 0..(BUFFER_SIZE / 8) {
137
0
                self.flush_byte_from_bit_buffer((i * 8) as i8)?;
138
            }
139
0
            Ok(())
140
        } else {
141
0
            self.w.write_all(&self.bit_buffer.to_be_bytes())
142
        }
143
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_bit_buffer
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_bit_buffer
144
145
0
    pub fn write_bits(&mut self, value: u32, size: u8) -> Result<(), EncodingError> {
146
0
        let size = size as i8;
147
0
        let value = value as usize;
148
149
0
        let free_bits = self.free_bits - size;
150
151
0
        if free_bits < 0 {
152
0
            self.bit_buffer = (self.bit_buffer << (size + free_bits)) | (value >> -free_bits);
153
0
            self.write_bit_buffer()?;
154
0
            self.bit_buffer = value;
155
0
            self.free_bits = free_bits + BUFFER_SIZE as i8;
156
0
        } else {
157
0
            self.free_bits = free_bits;
158
0
            self.bit_buffer = (self.bit_buffer << size) | value;
159
0
        }
160
0
        Ok(())
161
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_bits
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_bits
162
163
0
    pub fn write_marker(&mut self, marker: Marker) -> Result<(), EncodingError> {
164
0
        self.write(&[0xFF, marker.into()])
165
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_marker
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_marker
166
167
0
    pub fn write_segment(&mut self, marker: Marker, data: &[u8]) -> Result<(), EncodingError> {
168
0
        self.write_marker(marker)?;
169
0
        self.write_u16(data.len() as u16 + 2)?;
170
0
        self.write(data)?;
171
172
0
        Ok(())
173
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_segment
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_segment
174
175
0
    pub fn write_header(&mut self, density: &Density) -> Result<(), EncodingError> {
176
0
        self.write_marker(Marker::APP(0))?;
177
0
        self.write_u16(16)?;
178
179
0
        self.write(b"JFIF\0")?;
180
0
        self.write(&[0x01, 0x02])?;
181
182
0
        match *density {
183
            Density::None => {
184
0
                self.write_u8(0x00)?;
185
0
                self.write_u16(1)?;
186
0
                self.write_u16(1)?;
187
            }
188
0
            Density::Inch { x, y } => {
189
0
                self.write_u8(0x01)?;
190
0
                self.write_u16(x)?;
191
0
                self.write_u16(y)?;
192
            }
193
0
            Density::Centimeter { x, y } => {
194
0
                self.write_u8(0x02)?;
195
0
                self.write_u16(x)?;
196
0
                self.write_u16(y)?;
197
            }
198
        }
199
200
0
        self.write(&[0x00, 0x00])
201
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_header
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_header
202
203
    /// Append huffman table segment
204
    ///
205
    /// - `class`: 0 for DC or 1 for AC
206
    /// - `dest`: 0 for luma or 1 for chroma tables
207
    ///
208
    /// Layout:
209
    /// ```txt
210
    /// |--------|---------------|--------------------------|--------------------|--------|
211
    /// | 0xFFC4 | 16 bit length | 4 bit class / 4 bit dest |  16 byte num codes | values |
212
    /// |--------|---------------|--------------------------|--------------------|--------|
213
    /// ```
214
    ///
215
0
    pub fn write_huffman_segment(
216
0
        &mut self,
217
0
        class: CodingClass,
218
0
        destination: u8,
219
0
        table: &HuffmanTable,
220
0
    ) -> Result<(), EncodingError> {
221
0
        assert!(destination < 4, "Bad destination: {}", destination);
222
223
0
        self.write_marker(Marker::DHT)?;
224
0
        self.write_u16(2 + 1 + 16 + table.values().len() as u16)?;
225
226
0
        self.write_u8(((class as u8) << 4) | destination)?;
227
0
        self.write(table.length())?;
228
0
        self.write(table.values())?;
229
230
0
        Ok(())
231
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_huffman_segment
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_huffman_segment
232
233
    /// Append a quantization table
234
    ///
235
    /// - `precision`: 0 which means 1 byte per value.
236
    /// - `dest`: 0 for luma or 1 for chroma tables
237
    ///
238
    /// Layout:
239
    /// ```txt
240
    /// |--------|---------------|------------------------------|--------|--------|-----|--------|
241
    /// | 0xFFDB | 16 bit length | 4 bit precision / 4 bit dest | V(0,0) | V(0,1) | ... | V(7,7) |
242
    /// |--------|---------------|------------------------------|--------|--------|-----|--------|
243
    /// ```
244
    ///
245
0
    pub fn write_quantization_segment(
246
0
        &mut self,
247
0
        destination: u8,
248
0
        table: &QuantizationTable,
249
0
    ) -> Result<(), EncodingError> {
250
0
        assert!(destination < 4, "Bad destination: {}", destination);
251
252
0
        self.write_marker(Marker::DQT)?;
253
0
        self.write_u16(2 + 1 + 64)?;
254
255
0
        self.write_u8(destination)?;
256
257
0
        for &v in ZIGZAG.iter() {
258
0
            self.write_u8(table.get(v as usize))?;
259
        }
260
261
0
        Ok(())
262
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_quantization_segment
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_quantization_segment
263
264
0
    pub fn write_dri(&mut self, restart_interval: u16) -> Result<(), EncodingError> {
265
0
        self.write_marker(Marker::DRI)?;
266
0
        self.write_u16(4)?;
267
0
        self.write_u16(restart_interval)
268
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_dri
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_dri
269
270
    #[inline]
271
0
    pub fn huffman_encode(&mut self, val: u8, table: &HuffmanTable) -> Result<(), EncodingError> {
272
0
        let &(size, code) = table.get_for_value(val);
273
0
        self.write_bits(code as u32, size)
274
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::huffman_encode
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::huffman_encode
275
276
    #[inline]
277
0
    pub fn huffman_encode_value(
278
0
        &mut self,
279
0
        size: u8,
280
0
        symbol: u8,
281
0
        value: u16,
282
0
        table: &HuffmanTable,
283
0
    ) -> Result<(), EncodingError> {
284
0
        let &(num_bits, code) = table.get_for_value(symbol);
285
286
0
        let mut temp = value as u32;
287
0
        temp |= (code as u32) << size;
288
0
        let size = size + num_bits;
289
290
0
        self.write_bits(temp, size)
291
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::huffman_encode_value
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::huffman_encode_value
292
293
0
    pub fn write_block(
294
0
        &mut self,
295
0
        block: &[i16; 64],
296
0
        prev_dc: i16,
297
0
        dc_table: &HuffmanTable,
298
0
        ac_table: &HuffmanTable,
299
0
    ) -> Result<(), EncodingError> {
300
0
        self.write_dc(block[0], prev_dc, dc_table)?;
301
0
        self.write_ac_block(block, 1, 64, ac_table)
302
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_block
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_block
303
304
0
    pub fn write_dc(
305
0
        &mut self,
306
0
        value: i16,
307
0
        prev_dc: i16,
308
0
        dc_table: &HuffmanTable,
309
0
    ) -> Result<(), EncodingError> {
310
0
        let diff = value - prev_dc;
311
0
        let (size, value) = get_code(diff);
312
313
0
        self.huffman_encode_value(size, size, value, dc_table)?;
314
315
0
        Ok(())
316
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_dc
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_dc
317
318
0
    pub fn write_ac_block(
319
0
        &mut self,
320
0
        block: &[i16; 64],
321
0
        start: usize,
322
0
        end: usize,
323
0
        ac_table: &HuffmanTable,
324
0
    ) -> Result<(), EncodingError> {
325
0
        let mut zero_run = 0;
326
327
0
        for &value in &block[start..end] {
328
0
            if value == 0 {
329
0
                zero_run += 1;
330
0
            } else {
331
0
                while zero_run > 15 {
332
0
                    self.huffman_encode(0xF0, ac_table)?;
333
0
                    zero_run -= 16;
334
                }
335
336
0
                let (size, value) = get_code(value);
337
0
                let symbol = (zero_run << 4) | size;
338
339
0
                self.huffman_encode_value(size, symbol, value, ac_table)?;
340
341
0
                zero_run = 0;
342
            }
343
        }
344
345
0
        if zero_run > 0 {
346
0
            self.huffman_encode(0x00, ac_table)?;
347
0
        }
348
349
0
        Ok(())
350
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_ac_block
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_ac_block
351
352
0
    pub fn write_frame_header(
353
0
        &mut self,
354
0
        width: u16,
355
0
        height: u16,
356
0
        components: &[Component],
357
0
        progressive: bool,
358
0
    ) -> Result<(), EncodingError> {
359
0
        if progressive {
360
0
            self.write_marker(Marker::SOF(SOFType::ProgressiveDCT))?;
361
        } else {
362
0
            self.write_marker(Marker::SOF(SOFType::BaselineDCT))?;
363
        }
364
365
0
        self.write_u16(2 + 1 + 2 + 2 + 1 + (components.len() as u16) * 3)?;
366
367
        // Precision
368
0
        self.write_u8(8)?;
369
370
0
        self.write_u16(height)?;
371
0
        self.write_u16(width)?;
372
373
0
        self.write_u8(components.len() as u8)?;
374
375
0
        for component in components.iter() {
376
0
            self.write_u8(component.id)?;
377
0
            self.write_u8(
378
0
                (component.horizontal_sampling_factor << 4) | component.vertical_sampling_factor,
379
0
            )?;
380
0
            self.write_u8(component.quantization_table)?;
381
        }
382
383
0
        Ok(())
384
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_frame_header
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header
385
386
0
    pub fn write_scan_header(
387
0
        &mut self,
388
0
        components: &[&Component],
389
0
        spectral: Option<(u8, u8)>,
390
0
    ) -> Result<(), EncodingError> {
391
0
        self.write_marker(Marker::SOS)?;
392
393
0
        self.write_u16(2 + 1 + (components.len() as u16) * 2 + 3)?;
394
395
0
        self.write_u8(components.len() as u8)?;
396
397
0
        for component in components.iter() {
398
0
            self.write_u8(component.id)?;
399
0
            self.write_u8((component.dc_huffman_table << 4) | component.ac_huffman_table)?;
400
        }
401
402
0
        let (spectral_start, spectral_end) = spectral.unwrap_or((0, 63));
403
404
        // Start of spectral or predictor selection
405
0
        self.write_u8(spectral_start)?;
406
407
        // End of spectral selection
408
0
        self.write_u8(spectral_end)?;
409
410
        // Successive approximation bit position high and low
411
0
        self.write_u8(0)?;
412
413
0
        Ok(())
414
0
    }
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<_>>::write_scan_header
Unexecuted instantiation: <jpeg_encoder::writer::JfifWriter<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_scan_header
415
}
416
417
#[inline]
418
0
pub(crate) fn get_code(value: i16) -> (u8, u16) {
419
0
    let temp = value - (value.is_negative() as i16);
420
0
    let temp2 = value.abs();
421
422
    /*
423
     * Doing this instead of 16 - temp2.leading_zeros()
424
     * Gives the compiler the information that leadings_zeros
425
     * is always called on a non zero value, which removes a branch on x86
426
     */
427
0
    let num_bits = 15 - (temp2 << 1 | 1).leading_zeros() as u16;
428
429
0
    let coefficient = temp & ((1 << num_bits as usize) - 1);
430
431
0
    (num_bits as u8, coefficient as u16)
432
0
}
Unexecuted instantiation: jpeg_encoder::writer::get_code
Unexecuted instantiation: jpeg_encoder::writer::get_code