Coverage Report

Created: 2026-04-12 07:31

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