jpeg_encoder/
image_buffer.rs

1#![allow(clippy::identity_op)]
2
3use alloc::vec::Vec;
4
5use crate::encoder::JpegColorType;
6
7/// Conversion from RGB to YCbCr
8#[inline]
9pub 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    let r = r as i32;
19    let g = g as i32;
20    let b = b as i32;
21
22    let y = 19595 * r + 38470 * g + 7471 * b;
23    let cb = -11059 * r - 21709 * g + 32768 * b + (128 << 16);
24    let cr = 32768 * r - 27439 * g - 5329 * b + (128 << 16);
25
26    let y = (y + 0x7FFF) >> 16;
27    let cb = (cb + 0x7FFF) >> 16;
28    let cr = (cr + 0x7FFF) >> 16;
29
30    (y as u8, cb as u8, cr as u8)
31}
32
33/// Conversion from CMYK to YCCK (YCbCrK)
34#[inline]
35pub fn cmyk_to_ycck(c: u8, m: u8, y: u8, k: u8) -> (u8, u8, u8, u8) {
36    let (y, cb, cr) = rgb_to_ycbcr(c, m, y);
37    (y, cb, cr, 255 - k)
38}
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/// ```
86pub 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
100pub(crate) struct GrayImage<'a>(pub &'a [u8], pub u16, pub u16);
101
102impl<'a> ImageBuffer for GrayImage<'a> {
103    fn get_jpeg_color_type(&self) -> JpegColorType {
104        JpegColorType::Luma
105    }
106
107    fn width(&self) -> u16 {
108        self.1
109    }
110
111    fn height(&self) -> u16 {
112        self.2
113    }
114
115    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
116        for x in 0..self.width() {
117            let offset = usize::from(y) * usize::from(self.1) + usize::from(x);
118            buffers[0].push(self.0[offset + 0]);
119        }
120    }
121}
122
123macro_rules! ycbcr_image {
124    ($name:ident, $num_colors:expr, $o1:expr, $o2:expr, $o3:expr) => {
125        pub(crate) struct $name<'a>(pub &'a [u8], pub u16, pub u16);
126
127        impl<'a> ImageBuffer for $name<'a> {
128            fn get_jpeg_color_type(&self) -> JpegColorType {
129                JpegColorType::Ycbcr
130            }
131
132            fn width(&self) -> u16 {
133                self.1
134            }
135
136            fn height(&self) -> u16 {
137                self.2
138            }
139
140            #[inline(always)]
141            fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
142                for x in 0..self.width() {
143                    let offset =
144                        (usize::from(y) * usize::from(self.1) + usize::from(x)) * $num_colors;
145                    let (y, cb, cr) = rgb_to_ycbcr(
146                        self.0[offset + $o1],
147                        self.0[offset + $o2],
148                        self.0[offset + $o3],
149                    );
150
151                    buffers[0].push(y);
152                    buffers[1].push(cb);
153                    buffers[2].push(cr);
154                }
155            }
156        }
157    };
158}
159
160ycbcr_image!(RgbImage, 3, 0, 1, 2);
161ycbcr_image!(RgbaImage, 4, 0, 1, 2);
162ycbcr_image!(BgrImage, 3, 2, 1, 0);
163ycbcr_image!(BgraImage, 4, 2, 1, 0);
164
165pub(crate) struct YCbCrImage<'a>(pub &'a [u8], pub u16, pub u16);
166
167impl<'a> ImageBuffer for YCbCrImage<'a> {
168    fn get_jpeg_color_type(&self) -> JpegColorType {
169        JpegColorType::Ycbcr
170    }
171
172    fn width(&self) -> u16 {
173        self.1
174    }
175
176    fn height(&self) -> u16 {
177        self.2
178    }
179
180    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
181        for x in 0..self.width() {
182            let offset = (usize::from(y) * usize::from(self.1) + usize::from(x)) * 3;
183
184            buffers[0].push(self.0[offset + 0]);
185            buffers[1].push(self.0[offset + 1]);
186            buffers[2].push(self.0[offset + 2]);
187        }
188    }
189}
190
191pub(crate) struct CmykImage<'a>(pub &'a [u8], pub u16, pub u16);
192
193impl<'a> ImageBuffer for CmykImage<'a> {
194    fn get_jpeg_color_type(&self) -> JpegColorType {
195        JpegColorType::Cmyk
196    }
197
198    fn width(&self) -> u16 {
199        self.1
200    }
201
202    fn height(&self) -> u16 {
203        self.2
204    }
205
206    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
207        for x in 0..self.width() {
208            let offset = (usize::from(y) * usize::from(self.1) + usize::from(x)) * 4;
209
210            buffers[0].push(255 - self.0[offset + 0]);
211            buffers[1].push(255 - self.0[offset + 1]);
212            buffers[2].push(255 - self.0[offset + 2]);
213            buffers[3].push(255 - self.0[offset + 3]);
214        }
215    }
216}
217
218pub(crate) struct CmykAsYcckImage<'a>(pub &'a [u8], pub u16, pub u16);
219
220impl<'a> ImageBuffer for CmykAsYcckImage<'a> {
221    fn get_jpeg_color_type(&self) -> JpegColorType {
222        JpegColorType::Ycck
223    }
224
225    fn width(&self) -> u16 {
226        self.1
227    }
228
229    fn height(&self) -> u16 {
230        self.2
231    }
232
233    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
234        for x in 0..self.width() {
235            let offset = (usize::from(y) * usize::from(self.1) + usize::from(x)) * 4;
236
237            let (y, cb, cr, k) = cmyk_to_ycck(
238                self.0[offset + 0],
239                self.0[offset + 1],
240                self.0[offset + 2],
241                self.0[offset + 3],
242            );
243
244            buffers[0].push(y);
245            buffers[1].push(cb);
246            buffers[2].push(cr);
247            buffers[3].push(k);
248        }
249    }
250}
251
252pub(crate) struct YcckImage<'a>(pub &'a [u8], pub u16, pub u16);
253
254impl<'a> ImageBuffer for YcckImage<'a> {
255    fn get_jpeg_color_type(&self) -> JpegColorType {
256        JpegColorType::Ycck
257    }
258
259    fn width(&self) -> u16 {
260        self.1
261    }
262
263    fn height(&self) -> u16 {
264        self.2
265    }
266
267    fn fill_buffers(&self, y: u16, buffers: &mut [Vec<u8>; 4]) {
268        for x in 0..self.width() {
269            let offset = (usize::from(y) * usize::from(self.1) + usize::from(x)) * 4;
270
271            buffers[0].push(self.0[offset + 0]);
272            buffers[1].push(self.0[offset + 1]);
273            buffers[2].push(self.0[offset + 2]);
274            buffers[3].push(self.0[offset + 3]);
275        }
276    }
277}
278
279#[cfg(test)]
280mod tests {
281    use crate::rgb_to_ycbcr;
282
283    fn assert_rgb_to_ycbcr(rgb: [u8; 3], ycbcr: [u8; 3]) {
284        let (y, cb, cr) = rgb_to_ycbcr(rgb[0], rgb[1], rgb[2]);
285        assert_eq!([y, cb, cr], ycbcr);
286    }
287
288    #[test]
289    fn test_rgb_to_ycbcr() {
290
291        assert_rgb_to_ycbcr([0, 0, 0], [0, 128, 128]);
292        assert_rgb_to_ycbcr([255, 255, 255], [255, 128, 128]);
293        assert_rgb_to_ycbcr([255, 0, 0], [76, 85, 255]);
294        assert_rgb_to_ycbcr([0, 255, 0], [150, 44, 21]);
295        assert_rgb_to_ycbcr([0, 0, 255], [29, 255, 107]);
296
297        // Values taken from libjpeg for a common image
298
299        assert_rgb_to_ycbcr([59, 109, 6], [82, 85, 111]);
300        assert_rgb_to_ycbcr([29, 60, 11], [45, 109, 116]);
301        assert_rgb_to_ycbcr([57, 114, 26], [87, 94, 107]);
302        assert_rgb_to_ycbcr([30, 60, 6], [45, 106, 117]);
303        assert_rgb_to_ycbcr([41, 75, 11], [58, 102, 116]);
304        assert_rgb_to_ycbcr([145, 184, 108], [164, 97, 115]);
305        assert_rgb_to_ycbcr([33, 85, 7], [61, 98, 108]);
306        assert_rgb_to_ycbcr([61, 90, 40], [76, 108, 118]);
307        assert_rgb_to_ycbcr([75, 127, 45], [102, 96, 109]);
308        assert_rgb_to_ycbcr([30, 56, 14], [43, 111, 118]);
309        assert_rgb_to_ycbcr([106, 142, 81], [124, 104, 115]);
310        assert_rgb_to_ycbcr([35, 59, 11], [46, 108, 120]);
311        assert_rgb_to_ycbcr([170, 203, 123], [184, 94, 118]);
312        assert_rgb_to_ycbcr([45, 87, 16], [66, 100, 113]);
313        assert_rgb_to_ycbcr([59, 109, 21], [84, 92, 110]);
314        assert_rgb_to_ycbcr([100, 167, 36], [132, 74, 105]);
315        assert_rgb_to_ycbcr([17, 53, 5], [37, 110, 114]);
316        assert_rgb_to_ycbcr([226, 244, 220], [236, 119, 121]);
317        assert_rgb_to_ycbcr([192, 214, 120], [197, 85, 125]);
318        assert_rgb_to_ycbcr([63, 107, 22], [84, 93, 113]);
319        assert_rgb_to_ycbcr([44, 78, 19], [61, 104, 116]);
320        assert_rgb_to_ycbcr([72, 106, 54], [90, 108, 115]);
321        assert_rgb_to_ycbcr([99, 123, 73], [110, 107, 120]);
322        assert_rgb_to_ycbcr([188, 216, 148], [200, 99, 120]);
323        assert_rgb_to_ycbcr([19, 46, 7], [33, 113, 118]);
324        assert_rgb_to_ycbcr([56, 95, 40], [77, 107, 113]);
325        assert_rgb_to_ycbcr([81, 120, 56], [101, 103, 114]);
326        assert_rgb_to_ycbcr([9, 30, 0], [20, 117, 120]);
327        assert_rgb_to_ycbcr([90, 118, 46], [101, 97, 120]);
328        assert_rgb_to_ycbcr([24, 52, 0], [38, 107, 118]);
329        assert_rgb_to_ycbcr([32, 69, 9], [51, 104, 114]);
330        assert_rgb_to_ycbcr([74, 134, 33], [105, 88, 106]);
331        assert_rgb_to_ycbcr([37, 74, 7], [55, 101, 115]);
332        assert_rgb_to_ycbcr([69, 119, 31], [94, 92, 110]);
333        assert_rgb_to_ycbcr([63, 112, 21], [87, 91, 111]);
334        assert_rgb_to_ycbcr([90, 148, 17], [116, 72, 110]);
335        assert_rgb_to_ycbcr([50, 97, 30], [75, 102, 110]);
336        assert_rgb_to_ycbcr([99, 129, 72], [114, 105, 118]);
337        assert_rgb_to_ycbcr([161, 196, 57], [170, 64, 122]);
338        assert_rgb_to_ycbcr([10, 26, 1], [18, 118, 122]);
339        assert_rgb_to_ycbcr([87, 128, 68], [109, 105, 112]);
340        assert_rgb_to_ycbcr([111, 155, 73], [132, 94, 113]);
341        assert_rgb_to_ycbcr([33, 75, 11], [55, 103, 112]);
342        assert_rgb_to_ycbcr([70, 122, 51], [98, 101, 108]);
343        assert_rgb_to_ycbcr([22, 74, 3], [50, 101, 108]);
344        assert_rgb_to_ycbcr([88, 142, 45], [115, 89, 109]);
345        assert_rgb_to_ycbcr([66, 107, 40], [87, 101, 113]);
346        assert_rgb_to_ycbcr([18, 45, 0], [32, 110, 118]);
347        assert_rgb_to_ycbcr([163, 186, 88], [168, 83, 124]);
348        assert_rgb_to_ycbcr([47, 104, 4], [76, 88, 108]);
349        assert_rgb_to_ycbcr([147, 211, 114], [181, 90, 104]);
350        assert_rgb_to_ycbcr([42, 77, 18], [60, 104, 115]);
351        assert_rgb_to_ycbcr([37, 72, 6], [54, 101, 116]);
352        assert_rgb_to_ycbcr([84, 140, 55], [114, 95, 107]);
353        assert_rgb_to_ycbcr([46, 98, 25], [74, 100, 108]);
354        assert_rgb_to_ycbcr([48, 97, 20], [74, 98, 110]);
355        assert_rgb_to_ycbcr([189, 224, 156], [206, 100, 116]);
356        assert_rgb_to_ycbcr([36, 83, 0], [59, 94, 111]);
357        assert_rgb_to_ycbcr([159, 186, 114], [170, 97, 120]);
358        assert_rgb_to_ycbcr([75, 118, 46], [97, 99, 112]);
359        assert_rgb_to_ycbcr([193, 233, 158], [212, 97, 114]);
360        assert_rgb_to_ycbcr([76, 116, 48], [96, 101, 114]);
361        assert_rgb_to_ycbcr([108, 157, 79], [133, 97, 110]);
362        assert_rgb_to_ycbcr([180, 208, 155], [194, 106, 118]);
363        assert_rgb_to_ycbcr([74, 126, 53], [102, 100, 108]);
364        assert_rgb_to_ycbcr([72, 123, 46], [99, 98, 109]);
365        assert_rgb_to_ycbcr([71, 123, 34], [97, 92, 109]);
366        assert_rgb_to_ycbcr([130, 184, 72], [155, 81, 110]);
367        assert_rgb_to_ycbcr([30, 61, 17], [47, 111, 116]);
368        assert_rgb_to_ycbcr([27, 71, 0], [50, 100, 112]);
369        assert_rgb_to_ycbcr([45, 73, 24], [59, 108, 118]);
370        assert_rgb_to_ycbcr([139, 175, 93], [155, 93, 117]);
371        assert_rgb_to_ycbcr([11, 38, 0], [26, 114, 118]);
372        assert_rgb_to_ycbcr([34, 87, 15], [63, 101, 107]);
373        assert_rgb_to_ycbcr([43, 76, 35], [61, 113, 115]);
374        assert_rgb_to_ycbcr([18, 35, 7], [27, 117, 122]);
375        assert_rgb_to_ycbcr([69, 97, 48], [83, 108, 118]);
376        assert_rgb_to_ycbcr([139, 176, 50], [151, 71, 120]);
377        assert_rgb_to_ycbcr([21, 51, 7], [37, 111, 117]);
378        assert_rgb_to_ycbcr([209, 249, 189], [230, 105, 113]);
379        assert_rgb_to_ycbcr([32, 66, 14], [50, 108, 115]);
380        assert_rgb_to_ycbcr([100, 143, 67], [121, 97, 113]);
381        assert_rgb_to_ycbcr([40, 96, 14], [70, 96, 107]);
382        assert_rgb_to_ycbcr([88, 130, 64], [110, 102, 112]);
383        assert_rgb_to_ycbcr([52, 112, 14], [83, 89, 106]);
384        assert_rgb_to_ycbcr([49, 72, 25], [60, 108, 120]);
385        assert_rgb_to_ycbcr([144, 193, 75], [165, 77, 113]);
386        assert_rgb_to_ycbcr([49, 94, 1], [70, 89, 113]);
387    }
388}