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