Coverage Report

Created: 2026-03-20 07:09

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/image_buffer.rs
Line
Count
Source
1
#![allow(clippy::identity_op)]
2
3
use alloc::vec::Vec;
4
5
use crate::encoder::JpegColorType;
6
7
/// Conversion from RGB to YCbCr
8
#[inline]
9
0
pub fn rgb_to_ycbcr(r: u8, g: u8, b: u8) -> (u8, u8, u8) {
10
    // To avoid floating point math this scales everything by 2^16 which gives
11
    // a precision of approx 4 digits.
12
    //
13
    // Non scaled conversion:
14
    // Y  =  0.29900 * R + 0.58700 * G + 0.11400 * B
15
    // Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B  + 128
16
    // Cr =  0.50000 * R - 0.41869 * G - 0.08131 * B  + 128
17
18
0
    let r = r as i32;
19
0
    let g = g as i32;
20
0
    let b = b as i32;
21
22
0
    let y = 19595 * r + 38470 * g + 7471 * b;
23
0
    let cb = -11059 * r - 21709 * g + 32768 * b + (128 << 16);
24
0
    let cr = 32768 * r - 27439 * g - 5329 * b + (128 << 16);
25
26
0
    let y = (y + 0x7FFF) >> 16;
27
0
    let cb = (cb + 0x7FFF) >> 16;
28
0
    let cr = (cr + 0x7FFF) >> 16;
29
30
0
    (y as u8, cb as u8, cr as u8)
31
0
}
Unexecuted instantiation: jpeg_encoder::image_buffer::rgb_to_ycbcr
Unexecuted instantiation: jpeg_encoder::image_buffer::rgb_to_ycbcr
32
33
/// Conversion from CMYK to YCCK (YCbCrK)
34
#[inline]
35
0
pub fn cmyk_to_ycck(c: u8, m: u8, y: u8, k: u8) -> (u8, u8, u8, u8) {
36
0
    let (y, cb, cr) = rgb_to_ycbcr(c, m, y);
37
0
    (y, cb, cr, 255 - k)
38
0
}
39
40
/// # Buffer used as input value for image encoding
41
///
42
/// Image encoding with [Encoder::encode_image](crate::Encoder::encode_image) needs an ImageBuffer
43
/// as input for the image data. For convenience the [Encoder::encode](crate::Encoder::encode)
44
/// function contains implementations for common byte based pixel formats.
45
/// Users that needs other pixel formats or don't have the data available as byte slices
46
/// can create their own buffer implementations.
47
///
48
/// ## Example: ImageBuffer implementation for RgbImage from the `image` crate
49
/// ```no_compile
50
/// use image::RgbImage;
51
/// use jpeg_encoder::{ImageBuffer, JpegColorType, rgb_to_ycbcr};
52
///
53
/// pub struct RgbImageBuffer {
54
///     image: RgbImage,
55
/// }
56
///
57
/// impl ImageBuffer for RgbImageBuffer {
58
///     fn get_jpeg_color_type(&self) -> JpegColorType {
59
///         // Rgb images are encoded as YCbCr in JFIF files
60
///         JpegColorType::Ycbcr
61
///     }
62
///
63
///     fn width(&self) -> u16 {
64
///         self.image.width() as u16
65
///     }
66
///
67
///     fn height(&self) -> u16 {
68
///         self.image.height() as u16
69
///     }
70
///
71
///     fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]){
72
///         for x in 0..self.width() {
73
///             let pixel = self.image.get_pixel(x as u32 ,y as u32);
74
///
75
///             let (y,cb,cr) = rgb_to_ycbcr(pixel[0], pixel[1], pixel[2]);
76
///
77
///             // For YCbCr the 4th buffer is not used
78
///             buffers[0].push(y);
79
///             buffers[1].push(cb);
80
///             buffers[2].push(cr);
81
///         }
82
///     }
83
/// }
84
///
85
/// ```
86
pub trait ImageBuffer {
87
    /// The color type used in the image encoding
88
    fn get_jpeg_color_type(&self) -> JpegColorType;
89
90
    /// Width of the image
91
    fn width(&self) -> u16;
92
93
    /// Height of the image
94
    fn height(&self) -> u16;
95
96
    /// Add color values for the row to color component buffers
97
    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]);
98
}
99
100
pub(crate) struct GrayImage<'a>(pub &'a [u8], pub u16, pub u16);
101
102
impl<'a> ImageBuffer for GrayImage<'a> {
103
0
    fn get_jpeg_color_type(&self) -> JpegColorType {
104
0
        JpegColorType::Luma
105
0
    }
106
107
0
    fn width(&self) -> u16 {
108
0
        self.1
109
0
    }
110
111
0
    fn height(&self) -> u16 {
112
0
        self.2
113
0
    }
114
115
0
    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
116
0
        let line = get_line(self.0, y, self.width(), 1);
117
118
0
        for &pixel in line {
119
0
            buffers[0].push(pixel);
120
0
        }
121
0
    }
122
}
123
124
#[inline(always)]
125
0
fn get_line(data: &[u8], y: u16, width: u16, num_colors: usize) -> &[u8] {
126
0
    let width = usize::from(width);
127
0
    let y = usize::from(y);
128
129
0
    let start = y * width * num_colors;
130
0
    let end = start + width * num_colors;
131
132
0
    &data[start..end]
133
0
}
134
135
macro_rules! ycbcr_image {
136
    ($name:ident, $num_colors:expr, $o1:expr, $o2:expr, $o3:expr) => {
137
        pub struct $name<'a>(pub &'a [u8], pub u16, pub u16);
138
139
        impl<'a> ImageBuffer for $name<'a> {
140
0
            fn get_jpeg_color_type(&self) -> JpegColorType {
141
0
                JpegColorType::Ycbcr
142
0
            }
Unexecuted instantiation: <jpeg_encoder::image_buffer::RgbImage as jpeg_encoder::image_buffer::ImageBuffer>::get_jpeg_color_type
Unexecuted instantiation: <jpeg_encoder::image_buffer::RgbaImage as jpeg_encoder::image_buffer::ImageBuffer>::get_jpeg_color_type
Unexecuted instantiation: <jpeg_encoder::image_buffer::BgrImage as jpeg_encoder::image_buffer::ImageBuffer>::get_jpeg_color_type
Unexecuted instantiation: <jpeg_encoder::image_buffer::BgraImage as jpeg_encoder::image_buffer::ImageBuffer>::get_jpeg_color_type
143
144
0
            fn width(&self) -> u16 {
145
0
                self.1
146
0
            }
Unexecuted instantiation: <jpeg_encoder::image_buffer::RgbImage as jpeg_encoder::image_buffer::ImageBuffer>::width
Unexecuted instantiation: <jpeg_encoder::image_buffer::RgbaImage as jpeg_encoder::image_buffer::ImageBuffer>::width
Unexecuted instantiation: <jpeg_encoder::image_buffer::BgrImage as jpeg_encoder::image_buffer::ImageBuffer>::width
Unexecuted instantiation: <jpeg_encoder::image_buffer::BgraImage as jpeg_encoder::image_buffer::ImageBuffer>::width
147
148
0
            fn height(&self) -> u16 {
149
0
                self.2
150
0
            }
Unexecuted instantiation: <jpeg_encoder::image_buffer::RgbImage as jpeg_encoder::image_buffer::ImageBuffer>::height
Unexecuted instantiation: <jpeg_encoder::image_buffer::RgbaImage as jpeg_encoder::image_buffer::ImageBuffer>::height
Unexecuted instantiation: <jpeg_encoder::image_buffer::BgrImage as jpeg_encoder::image_buffer::ImageBuffer>::height
Unexecuted instantiation: <jpeg_encoder::image_buffer::BgraImage as jpeg_encoder::image_buffer::ImageBuffer>::height
151
152
            #[inline(always)]
153
0
            fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
154
0
                let line = get_line(self.0, y, self.width(), $num_colors);
155
156
                // Doing the convertion in chunks allows the compiler to vectorize the code
157
                // A size of 16 seems optimal for SSE and AVX capable hardware
158
                const CHUNK_SIZE: usize = 16;
159
160
0
                let mut y_buffer = [0; CHUNK_SIZE];
161
0
                let mut cb_buffer = [0; CHUNK_SIZE];
162
0
                let mut cr_buffer = [0; CHUNK_SIZE];
163
164
0
                for chuck in line.chunks_exact($num_colors * CHUNK_SIZE) {
165
0
                    for i in (0..CHUNK_SIZE) {
166
0
                        let (y, cb, cr) = rgb_to_ycbcr(
167
0
                            chuck[i * $num_colors + $o1],
168
0
                            chuck[i * $num_colors + $o2],
169
0
                            chuck[i * $num_colors + $o3],
170
                        );
171
172
0
                        y_buffer[i] = y;
173
0
                        cb_buffer[i] = cb;
174
0
                        cr_buffer[i] = cr;
175
                    }
176
177
0
                    buffers[0].extend_from_slice(&y_buffer);
178
0
                    buffers[1].extend_from_slice(&cb_buffer);
179
0
                    buffers[2].extend_from_slice(&cr_buffer);
180
                }
181
182
                // Add the remaining pixels in case the number of
183
                // pixels is not a multiple of CHUNK_SIZE
184
0
                let pixel = line.len() / $num_colors;
185
0
                for i in pixel / CHUNK_SIZE * CHUNK_SIZE..pixel {
186
0
                    let (y, cb, cr) = rgb_to_ycbcr(
187
0
                        line[i * $num_colors + $o1],
188
0
                        line[i * $num_colors + $o2],
189
0
                        line[i * $num_colors + $o3],
190
0
                    );
191
0
192
0
                    buffers[0].push(y);
193
0
                    buffers[1].push(cb);
194
0
                    buffers[2].push(cr);
195
0
                }
196
0
            }
Unexecuted instantiation: <jpeg_encoder::image_buffer::RgbImage as jpeg_encoder::image_buffer::ImageBuffer>::fill_buffers
Unexecuted instantiation: <jpeg_encoder::image_buffer::RgbaImage as jpeg_encoder::image_buffer::ImageBuffer>::fill_buffers
Unexecuted instantiation: <jpeg_encoder::image_buffer::BgrImage as jpeg_encoder::image_buffer::ImageBuffer>::fill_buffers
Unexecuted instantiation: <jpeg_encoder::image_buffer::BgraImage as jpeg_encoder::image_buffer::ImageBuffer>::fill_buffers
197
        }
198
    };
199
}
200
201
ycbcr_image!(RgbImage, 3, 0, 1, 2);
202
ycbcr_image!(RgbaImage, 4, 0, 1, 2);
203
ycbcr_image!(BgrImage, 3, 2, 1, 0);
204
ycbcr_image!(BgraImage, 4, 2, 1, 0);
205
206
pub(crate) struct YCbCrImage<'a>(pub &'a [u8], pub u16, pub u16);
207
208
impl<'a> ImageBuffer for YCbCrImage<'a> {
209
0
    fn get_jpeg_color_type(&self) -> JpegColorType {
210
0
        JpegColorType::Ycbcr
211
0
    }
212
213
0
    fn width(&self) -> u16 {
214
0
        self.1
215
0
    }
216
217
0
    fn height(&self) -> u16 {
218
0
        self.2
219
0
    }
220
221
0
    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
222
0
        let line = get_line(self.0, y, self.width(), 3);
223
224
0
        for pixel in line.chunks_exact(3) {
225
0
            buffers[0].push(pixel[0]);
226
0
            buffers[1].push(pixel[1]);
227
0
            buffers[2].push(pixel[2]);
228
0
        }
229
0
    }
230
}
231
232
pub(crate) struct CmykImage<'a>(pub &'a [u8], pub u16, pub u16);
233
234
impl<'a> ImageBuffer for CmykImage<'a> {
235
0
    fn get_jpeg_color_type(&self) -> JpegColorType {
236
0
        JpegColorType::Cmyk
237
0
    }
238
239
0
    fn width(&self) -> u16 {
240
0
        self.1
241
0
    }
242
243
0
    fn height(&self) -> u16 {
244
0
        self.2
245
0
    }
246
247
0
    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
248
0
        let line = get_line(self.0, y, self.width(), 4);
249
250
0
        for pixel in line.chunks_exact(4) {
251
0
            buffers[0].push(255 - pixel[0]);
252
0
            buffers[1].push(255 - pixel[1]);
253
0
            buffers[2].push(255 - pixel[2]);
254
0
            buffers[3].push(255 - pixel[3]);
255
0
        }
256
0
    }
257
}
258
259
pub(crate) struct CmykAsYcckImage<'a>(pub &'a [u8], pub u16, pub u16);
260
261
impl<'a> ImageBuffer for CmykAsYcckImage<'a> {
262
0
    fn get_jpeg_color_type(&self) -> JpegColorType {
263
0
        JpegColorType::Ycck
264
0
    }
265
266
0
    fn width(&self) -> u16 {
267
0
        self.1
268
0
    }
269
270
0
    fn height(&self) -> u16 {
271
0
        self.2
272
0
    }
273
274
0
    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
275
0
        let line = get_line(self.0, y, self.width(), 4);
276
277
0
        for pixel in line.chunks_exact(4) {
278
0
            let (y, cb, cr, k) = cmyk_to_ycck(pixel[0], pixel[1], pixel[2], pixel[3]);
279
0
280
0
            buffers[0].push(y);
281
0
            buffers[1].push(cb);
282
0
            buffers[2].push(cr);
283
0
            buffers[3].push(k);
284
0
        }
285
0
    }
286
}
287
288
pub(crate) struct YcckImage<'a>(pub &'a [u8], pub u16, pub u16);
289
290
impl<'a> ImageBuffer for YcckImage<'a> {
291
0
    fn get_jpeg_color_type(&self) -> JpegColorType {
292
0
        JpegColorType::Ycck
293
0
    }
294
295
0
    fn width(&self) -> u16 {
296
0
        self.1
297
0
    }
298
299
0
    fn height(&self) -> u16 {
300
0
        self.2
301
0
    }
302
303
0
    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
304
0
        let line = get_line(self.0, y, self.width(), 4);
305
306
0
        for pixel in line.chunks_exact(4) {
307
0
            buffers[0].push(pixel[0]);
308
0
            buffers[1].push(pixel[1]);
309
0
            buffers[2].push(pixel[2]);
310
0
            buffers[3].push(pixel[3]);
311
0
        }
312
0
    }
313
}
314
315
#[cfg(test)]
316
mod tests {
317
    use crate::rgb_to_ycbcr;
318
319
    fn assert_rgb_to_ycbcr(rgb: [u8; 3], ycbcr: [u8; 3]) {
320
        let (y, cb, cr) = rgb_to_ycbcr(rgb[0], rgb[1], rgb[2]);
321
        assert_eq!([y, cb, cr], ycbcr);
322
    }
323
324
    #[test]
325
    fn test_rgb_to_ycbcr() {
326
        assert_rgb_to_ycbcr([0, 0, 0], [0, 128, 128]);
327
        assert_rgb_to_ycbcr([255, 255, 255], [255, 128, 128]);
328
        assert_rgb_to_ycbcr([255, 0, 0], [76, 85, 255]);
329
        assert_rgb_to_ycbcr([0, 255, 0], [150, 44, 21]);
330
        assert_rgb_to_ycbcr([0, 0, 255], [29, 255, 107]);
331
332
        // Values taken from libjpeg for a common image
333
334
        assert_rgb_to_ycbcr([59, 109, 6], [82, 85, 111]);
335
        assert_rgb_to_ycbcr([29, 60, 11], [45, 109, 116]);
336
        assert_rgb_to_ycbcr([57, 114, 26], [87, 94, 107]);
337
        assert_rgb_to_ycbcr([30, 60, 6], [45, 106, 117]);
338
        assert_rgb_to_ycbcr([41, 75, 11], [58, 102, 116]);
339
        assert_rgb_to_ycbcr([145, 184, 108], [164, 97, 115]);
340
        assert_rgb_to_ycbcr([33, 85, 7], [61, 98, 108]);
341
        assert_rgb_to_ycbcr([61, 90, 40], [76, 108, 118]);
342
        assert_rgb_to_ycbcr([75, 127, 45], [102, 96, 109]);
343
        assert_rgb_to_ycbcr([30, 56, 14], [43, 111, 118]);
344
        assert_rgb_to_ycbcr([106, 142, 81], [124, 104, 115]);
345
        assert_rgb_to_ycbcr([35, 59, 11], [46, 108, 120]);
346
        assert_rgb_to_ycbcr([170, 203, 123], [184, 94, 118]);
347
        assert_rgb_to_ycbcr([45, 87, 16], [66, 100, 113]);
348
        assert_rgb_to_ycbcr([59, 109, 21], [84, 92, 110]);
349
        assert_rgb_to_ycbcr([100, 167, 36], [132, 74, 105]);
350
        assert_rgb_to_ycbcr([17, 53, 5], [37, 110, 114]);
351
        assert_rgb_to_ycbcr([226, 244, 220], [236, 119, 121]);
352
        assert_rgb_to_ycbcr([192, 214, 120], [197, 85, 125]);
353
        assert_rgb_to_ycbcr([63, 107, 22], [84, 93, 113]);
354
        assert_rgb_to_ycbcr([44, 78, 19], [61, 104, 116]);
355
        assert_rgb_to_ycbcr([72, 106, 54], [90, 108, 115]);
356
        assert_rgb_to_ycbcr([99, 123, 73], [110, 107, 120]);
357
        assert_rgb_to_ycbcr([188, 216, 148], [200, 99, 120]);
358
        assert_rgb_to_ycbcr([19, 46, 7], [33, 113, 118]);
359
        assert_rgb_to_ycbcr([56, 95, 40], [77, 107, 113]);
360
        assert_rgb_to_ycbcr([81, 120, 56], [101, 103, 114]);
361
        assert_rgb_to_ycbcr([9, 30, 0], [20, 117, 120]);
362
        assert_rgb_to_ycbcr([90, 118, 46], [101, 97, 120]);
363
        assert_rgb_to_ycbcr([24, 52, 0], [38, 107, 118]);
364
        assert_rgb_to_ycbcr([32, 69, 9], [51, 104, 114]);
365
        assert_rgb_to_ycbcr([74, 134, 33], [105, 88, 106]);
366
        assert_rgb_to_ycbcr([37, 74, 7], [55, 101, 115]);
367
        assert_rgb_to_ycbcr([69, 119, 31], [94, 92, 110]);
368
        assert_rgb_to_ycbcr([63, 112, 21], [87, 91, 111]);
369
        assert_rgb_to_ycbcr([90, 148, 17], [116, 72, 110]);
370
        assert_rgb_to_ycbcr([50, 97, 30], [75, 102, 110]);
371
        assert_rgb_to_ycbcr([99, 129, 72], [114, 105, 118]);
372
        assert_rgb_to_ycbcr([161, 196, 57], [170, 64, 122]);
373
        assert_rgb_to_ycbcr([10, 26, 1], [18, 118, 122]);
374
        assert_rgb_to_ycbcr([87, 128, 68], [109, 105, 112]);
375
        assert_rgb_to_ycbcr([111, 155, 73], [132, 94, 113]);
376
        assert_rgb_to_ycbcr([33, 75, 11], [55, 103, 112]);
377
        assert_rgb_to_ycbcr([70, 122, 51], [98, 101, 108]);
378
        assert_rgb_to_ycbcr([22, 74, 3], [50, 101, 108]);
379
        assert_rgb_to_ycbcr([88, 142, 45], [115, 89, 109]);
380
        assert_rgb_to_ycbcr([66, 107, 40], [87, 101, 113]);
381
        assert_rgb_to_ycbcr([18, 45, 0], [32, 110, 118]);
382
        assert_rgb_to_ycbcr([163, 186, 88], [168, 83, 124]);
383
        assert_rgb_to_ycbcr([47, 104, 4], [76, 88, 108]);
384
        assert_rgb_to_ycbcr([147, 211, 114], [181, 90, 104]);
385
        assert_rgb_to_ycbcr([42, 77, 18], [60, 104, 115]);
386
        assert_rgb_to_ycbcr([37, 72, 6], [54, 101, 116]);
387
        assert_rgb_to_ycbcr([84, 140, 55], [114, 95, 107]);
388
        assert_rgb_to_ycbcr([46, 98, 25], [74, 100, 108]);
389
        assert_rgb_to_ycbcr([48, 97, 20], [74, 98, 110]);
390
        assert_rgb_to_ycbcr([189, 224, 156], [206, 100, 116]);
391
        assert_rgb_to_ycbcr([36, 83, 0], [59, 94, 111]);
392
        assert_rgb_to_ycbcr([159, 186, 114], [170, 97, 120]);
393
        assert_rgb_to_ycbcr([75, 118, 46], [97, 99, 112]);
394
        assert_rgb_to_ycbcr([193, 233, 158], [212, 97, 114]);
395
        assert_rgb_to_ycbcr([76, 116, 48], [96, 101, 114]);
396
        assert_rgb_to_ycbcr([108, 157, 79], [133, 97, 110]);
397
        assert_rgb_to_ycbcr([180, 208, 155], [194, 106, 118]);
398
        assert_rgb_to_ycbcr([74, 126, 53], [102, 100, 108]);
399
        assert_rgb_to_ycbcr([72, 123, 46], [99, 98, 109]);
400
        assert_rgb_to_ycbcr([71, 123, 34], [97, 92, 109]);
401
        assert_rgb_to_ycbcr([130, 184, 72], [155, 81, 110]);
402
        assert_rgb_to_ycbcr([30, 61, 17], [47, 111, 116]);
403
        assert_rgb_to_ycbcr([27, 71, 0], [50, 100, 112]);
404
        assert_rgb_to_ycbcr([45, 73, 24], [59, 108, 118]);
405
        assert_rgb_to_ycbcr([139, 175, 93], [155, 93, 117]);
406
        assert_rgb_to_ycbcr([11, 38, 0], [26, 114, 118]);
407
        assert_rgb_to_ycbcr([34, 87, 15], [63, 101, 107]);
408
        assert_rgb_to_ycbcr([43, 76, 35], [61, 113, 115]);
409
        assert_rgb_to_ycbcr([18, 35, 7], [27, 117, 122]);
410
        assert_rgb_to_ycbcr([69, 97, 48], [83, 108, 118]);
411
        assert_rgb_to_ycbcr([139, 176, 50], [151, 71, 120]);
412
        assert_rgb_to_ycbcr([21, 51, 7], [37, 111, 117]);
413
        assert_rgb_to_ycbcr([209, 249, 189], [230, 105, 113]);
414
        assert_rgb_to_ycbcr([32, 66, 14], [50, 108, 115]);
415
        assert_rgb_to_ycbcr([100, 143, 67], [121, 97, 113]);
416
        assert_rgb_to_ycbcr([40, 96, 14], [70, 96, 107]);
417
        assert_rgb_to_ycbcr([88, 130, 64], [110, 102, 112]);
418
        assert_rgb_to_ycbcr([52, 112, 14], [83, 89, 106]);
419
        assert_rgb_to_ycbcr([49, 72, 25], [60, 108, 120]);
420
        assert_rgb_to_ycbcr([144, 193, 75], [165, 77, 113]);
421
        assert_rgb_to_ycbcr([49, 94, 1], [70, 89, 113]);
422
    }
423
}