1use crate::common::error::{JpegError, Result};
11use crate::common::icc;
12use crate::common::quant_table::QuantTable;
13use crate::common::types::*;
14use crate::decode::bitstream::BitReader;
15use crate::decode::marker::{JpegMetadata, MarkerReader, ScanInfo};
16use crate::decode::pipeline::{upsample_generic_nearest, Image};
17use crate::decode::progressive;
18use crate::simd::{self, SimdRoutines};
19#[allow(unused_imports)]
20use alloc::vec::Vec;
21#[allow(unused_imports)]
22use alloc::{format, vec};
23
24struct CompInfo {
26 blocks_x: usize,
27 blocks_y: usize,
28 h_samp: usize,
29 v_samp: usize,
30 comp_w: usize,
31}
32
33pub struct ProgressiveDecoder {
39 raw_data: Vec<u8>,
41 metadata: JpegMetadata,
43 routines: SimdRoutines,
45 coeff_bufs: Vec<Vec<[i16; 64]>>,
47 ac_max_k_bufs: Vec<Vec<u8>>,
50 comp_infos: Vec<CompInfo>,
52 mcus_x: usize,
54 mcus_y: usize,
56 max_h: usize,
58 max_v: usize,
60 scans_consumed: usize,
62}
63
64impl ProgressiveDecoder {
65 pub fn new(data: &[u8]) -> Result<Self> {
70 Self::with_limits(data, crate::common::types::DecodeLimits::default())
71 }
72
73 pub fn with_limits(data: &[u8], limits: crate::common::types::DecodeLimits) -> Result<Self> {
76 let mut reader: MarkerReader<'_> = MarkerReader::new(data);
77 reader.set_scan_cap(limits.max_scans);
78 let metadata: JpegMetadata = reader.read_markers()?;
79
80 if !metadata.frame.is_progressive {
81 return Err(JpegError::Unsupported(
82 "ProgressiveDecoder requires a progressive JPEG (SOF2)".into(),
83 ));
84 }
85
86 let frame = &metadata.frame;
87 let max_h: usize = frame
88 .components
89 .iter()
90 .map(|c| c.horizontal_sampling as usize)
91 .max()
92 .unwrap_or(1);
93 let max_v: usize = frame
94 .components
95 .iter()
96 .map(|c| c.vertical_sampling as usize)
97 .max()
98 .unwrap_or(1);
99
100 limits.check_frame(frame.width as usize, frame.height as usize)?;
104 if metadata.scans.len() > limits.max_scans {
108 return Err(JpegError::LimitExceeded {
109 what: "progressive scan count",
110 actual: metadata.scans.len() as u64,
111 limit: limits.max_scans as u64,
112 });
113 }
114 if let Some(max_mem) = limits.max_memory {
117 let total_pixels: u64 = (frame.width as u64) * (frame.height as u64);
118 let nc: u64 = frame.components.len() as u64;
119 let estimated: u64 =
120 total_pixels * (2 * nc) + total_pixels * nc / 64 + data.len() as u64;
121 if estimated > max_mem {
122 return Err(JpegError::LimitExceeded {
123 what: "estimated decode memory",
124 actual: estimated,
125 limit: max_mem,
126 });
127 }
128 }
129
130 let mcu_w: usize = max_h * 8;
131 let mcu_h: usize = max_v * 8;
132 let mcus_x: usize = (frame.width as usize).div_ceil(mcu_w);
133 let mcus_y: usize = (frame.height as usize).div_ceil(mcu_h);
134
135 let comp_infos: Vec<CompInfo> = frame
136 .components
137 .iter()
138 .map(|comp| {
139 let h_samp: usize = comp.horizontal_sampling as usize;
140 let v_samp: usize = comp.vertical_sampling as usize;
141 CompInfo {
142 blocks_x: mcus_x * h_samp,
143 blocks_y: mcus_y * v_samp,
144 h_samp,
145 v_samp,
146 comp_w: mcus_x * h_samp * 8,
148 }
149 })
150 .collect();
151
152 let coeff_bufs: Vec<Vec<[i16; 64]>> = comp_infos
154 .iter()
155 .map(|ci| vec![[0i16; 64]; ci.blocks_x * ci.blocks_y])
156 .collect();
157 let ac_max_k_bufs: Vec<Vec<u8>> = comp_infos
158 .iter()
159 .map(|ci| vec![0u8; ci.blocks_x * ci.blocks_y])
160 .collect();
161
162 let routines: SimdRoutines = simd::detect();
163
164 Ok(Self {
165 raw_data: data.to_vec(),
166 metadata,
167 routines,
168 coeff_bufs,
169 ac_max_k_bufs,
170 comp_infos,
171 mcus_x,
172 mcus_y,
173 max_h,
174 max_v,
175 scans_consumed: 0,
176 })
177 }
178
179 pub fn has_multiple_scans(&self) -> bool {
181 self.metadata.scans.len() > 1
182 }
183
184 pub fn num_scans(&self) -> usize {
186 self.metadata.scans.len()
187 }
188
189 pub fn width(&self) -> usize {
191 self.metadata.frame.width as usize
192 }
193
194 pub fn height(&self) -> usize {
196 self.metadata.frame.height as usize
197 }
198
199 pub fn consume_input(&mut self) -> Result<bool> {
202 let scan_idx: usize = self.scans_consumed;
203 if scan_idx >= self.metadata.scans.len() {
204 return Ok(false);
205 }
206
207 self.decode_one_scan(scan_idx)?;
208 self.scans_consumed += 1;
209 Ok(true)
210 }
211
212 pub fn input_complete(&self) -> bool {
214 self.scans_consumed >= self.metadata.scans.len()
215 }
216
217 pub fn scans_consumed(&self) -> usize {
219 self.scans_consumed
220 }
221
222 pub fn output(&self) -> Result<Image> {
227 let frame = &self.metadata.frame;
228 let block_size: usize = 8;
229 let num_components: usize = frame.components.len();
230 let out_width: usize = frame.width as usize;
231 let out_height: usize = frame.height as usize;
232 let full_width: usize = self.mcus_x * self.max_h * block_size;
233 let full_height: usize = self.mcus_y * self.max_v * block_size;
234
235 let quant_tables: Vec<&QuantTable> = frame
237 .components
238 .iter()
239 .map(|comp| {
240 self.metadata.quant_tables[comp.quant_table_index as usize]
241 .as_ref()
242 .ok_or_else(|| {
243 JpegError::CorruptData(format!(
244 "missing quant table {}",
245 comp.quant_table_index
246 ))
247 })
248 })
249 .collect::<Result<Vec<_>>>()?;
250
251 let mut component_planes: Vec<Vec<u8>> = self
253 .comp_infos
254 .iter()
255 .map(|ci| {
256 let size: usize = ci.comp_w * ci.blocks_y * block_size;
257 let mut v: Vec<u8> = Vec::with_capacity(size);
258 #[allow(clippy::uninit_vec)]
259 unsafe {
260 v.set_len(size)
261 };
262 v
263 })
264 .collect();
265
266 for (comp_idx, ci) in self.comp_infos.iter().enumerate() {
267 let qt_values: &[u16; 64] = &quant_tables[comp_idx].values;
268 for by in 0..ci.blocks_y {
269 for bx in 0..ci.blocks_x {
270 let block_idx: usize = by * ci.blocks_x + bx;
271 let coeffs: &[i16; 64] = &self.coeff_bufs[comp_idx][block_idx];
272
273 let px_x: usize = bx * block_size;
274 let px_y: usize = by * block_size;
275 let dst_offset: usize = px_y * ci.comp_w + px_x;
276
277 unsafe {
278 let dst: *mut u8 = component_planes[comp_idx].as_mut_ptr().add(dst_offset);
279 self.idct_islow_strided(coeffs, qt_values, dst, ci.comp_w);
280 }
281 }
282 }
283 }
284
285 let icc_profile: Option<Vec<u8>> = icc::reassemble_icc_profile(&self.metadata.icc_chunks);
287 let exif_data: Option<Vec<u8>> = self.metadata.exif_data.clone();
288
289 if num_components == 1 {
290 self.assemble_grayscale(
291 &component_planes,
292 out_width,
293 out_height,
294 icc_profile,
295 exif_data,
296 )
297 } else if num_components == 3 {
298 self.assemble_ycbcr(
299 &component_planes,
300 frame,
301 out_width,
302 out_height,
303 full_width,
304 full_height,
305 icc_profile,
306 exif_data,
307 )
308 } else if num_components == 4 {
309 self.assemble_4_component(
310 &component_planes,
311 frame,
312 out_width,
313 out_height,
314 full_width,
315 full_height,
316 icc_profile,
317 exif_data,
318 )
319 } else {
320 Err(JpegError::Unsupported(format!(
321 "{} components not supported in progressive output",
322 num_components
323 )))
324 }
325 }
326
327 pub fn finish(mut self) -> Result<Image> {
330 while self.consume_input()? {}
331 self.output()
332 }
333
334 #[inline(always)]
341 unsafe fn idct_islow_strided(
342 &self,
343 coeffs: &[i16; 64],
344 quant: &[u16; 64],
345 output: *mut u8,
346 stride: usize,
347 ) {
348 unsafe {
349 #[cfg(all(target_arch = "aarch64", feature = "simd"))]
350 {
351 return crate::simd::aarch64::idct::neon_idct_islow_strided(
352 coeffs, quant, output, stride,
353 );
354 }
355
356 #[cfg(all(target_arch = "x86_64", feature = "simd"))]
357 {
358 if crate::cpu_has!("avx2") {
359 return crate::simd::x86_64::avx2_idct::avx2_idct_islow_strided(
360 coeffs, quant, output, stride,
361 );
362 }
363 if crate::cpu_has!("sse2") {
364 return crate::simd::x86_64::idct::sse2_idct_islow_strided(
365 coeffs, quant, output, stride,
366 );
367 }
368 }
369
370 #[allow(unreachable_code)]
371 {
372 let mut tmp = [0u8; 64];
373 (self.routines.idct_islow)(coeffs, quant, &mut tmp);
374 for row in 0..8 {
375 core::ptr::copy_nonoverlapping(
376 tmp.as_ptr().add(row * 8),
377 output.add(row * stride),
378 8,
379 );
380 }
381 }
382 }
383 }
384
385 fn decode_one_scan(&mut self, scan_idx: usize) -> Result<()> {
387 let scan_info: &ScanInfo = &self.metadata.scans[scan_idx];
389 let ss: u8 = scan_info.header.spec_start;
390 let se: u8 = scan_info.header.spec_end;
391 let ah: u8 = scan_info.header.succ_high;
392 let al: u8 = scan_info.header.succ_low;
393 let is_dc: bool = ss == 0 && se == 0;
394 let data_offset: usize = scan_info.data_offset;
395 let restart_interval: u16 = scan_info.restart_interval;
396 let num_scan_components: usize = scan_info.header.components.len();
397
398 let scan_components: Vec<ScanComponentSelector> = scan_info.header.components.clone();
400
401 let dc_tables: [Option<alloc::sync::Arc<crate::common::huffman_table::HuffmanTable>>; 4] =
404 scan_info.dc_huffman_tables.clone();
405 let ac_tables: [Option<alloc::sync::Arc<crate::common::huffman_table::HuffmanTable>>; 4] =
406 scan_info.ac_huffman_tables.clone();
407
408 let entropy_data: &[u8] = &self.raw_data[data_offset..];
409 let mut bit_reader: BitReader = BitReader::new(entropy_data);
410
411 let scan_comp_indices: Vec<usize> = scan_components
413 .iter()
414 .map(|sc| {
415 self.metadata
416 .frame
417 .components
418 .iter()
419 .position(|fc| fc.id == sc.component_id)
420 .ok_or_else(|| {
421 JpegError::CorruptData(format!(
422 "scan references unknown component {}",
423 sc.component_id
424 ))
425 })
426 })
427 .collect::<Result<Vec<_>>>()?;
428
429 if num_scan_components > 1 {
430 let mut dc_preds = [0i16; 4];
432 let mut mcu_count: u32 = 0;
433
434 for mcu_y in 0..self.mcus_y {
435 for mcu_x in 0..self.mcus_x {
436 if restart_interval > 0
437 && mcu_count > 0
438 && mcu_count.is_multiple_of(restart_interval as u32)
439 {
440 bit_reader.reset();
441 dc_preds = [0i16; 4];
442 }
443
444 for (si, &comp_idx) in scan_comp_indices.iter().enumerate() {
445 let blocks_x: usize = self.comp_infos[comp_idx].blocks_x;
446 let h_samp: usize = self.comp_infos[comp_idx].h_samp;
447 let v_samp: usize = self.comp_infos[comp_idx].v_samp;
448 let sc = &scan_components[si];
449
450 let dc_table =
451 dc_tables[sc.dc_table_index as usize]
452 .as_ref()
453 .ok_or_else(|| {
454 JpegError::CorruptData(format!(
455 "missing DC table {}",
456 sc.dc_table_index
457 ))
458 })?;
459
460 for v in 0..v_samp {
461 for h in 0..h_samp {
462 let bx: usize = mcu_x * h_samp + h;
463 let by: usize = mcu_y * v_samp + v;
464 let block_idx: usize = by * blocks_x + bx;
465 let coeffs: &mut [i16; 64] =
466 &mut self.coeff_bufs[comp_idx][block_idx];
467
468 if is_dc {
469 if ah == 0 {
470 progressive::decode_dc_first(
471 &mut bit_reader,
472 dc_table,
473 &mut dc_preds[comp_idx],
474 coeffs,
475 al,
476 )?;
477 } else {
478 progressive::decode_dc_refine(&mut bit_reader, coeffs, al)?;
479 }
480 }
481 }
482 }
483 }
484
485 mcu_count += 1;
486 }
487 }
488 Ok(())
489 } else {
490 let comp_idx: usize = scan_comp_indices[0];
492 let sc = &scan_components[0];
493 let blocks_x: usize = self.comp_infos[comp_idx].blocks_x;
494 let blocks_y: usize = self.comp_infos[comp_idx].blocks_y;
495 let mut dc_pred: i16 = 0;
496 let mut eob_run: u16 = 0;
497 let mut mcu_count: u32 = 0;
498
499 let dc_table_ref = if is_dc {
500 Some(
501 dc_tables[sc.dc_table_index as usize]
502 .as_ref()
503 .ok_or_else(|| {
504 JpegError::CorruptData(format!(
505 "missing DC table {}",
506 sc.dc_table_index
507 ))
508 })?,
509 )
510 } else {
511 None
512 };
513
514 let ac_table_ref = if !is_dc || se > 0 {
515 Some(
516 ac_tables[sc.ac_table_index as usize]
517 .as_ref()
518 .ok_or_else(|| {
519 JpegError::CorruptData(format!(
520 "missing AC table {}",
521 sc.ac_table_index
522 ))
523 })?,
524 )
525 } else {
526 None
527 };
528
529 for by in 0..blocks_y {
530 for bx in 0..blocks_x {
531 if restart_interval > 0
532 && mcu_count > 0
533 && mcu_count.is_multiple_of(restart_interval as u32)
534 {
535 bit_reader.reset();
536 dc_pred = 0;
537 eob_run = 0;
538 }
539
540 let block_idx: usize = by * blocks_x + bx;
541 let coeffs: &mut [i16; 64] = &mut self.coeff_bufs[comp_idx][block_idx];
542
543 if is_dc {
544 if ah == 0 {
545 progressive::decode_dc_first(
546 &mut bit_reader,
547 dc_table_ref.unwrap(),
548 &mut dc_pred,
549 coeffs,
550 al,
551 )?;
552 } else {
553 progressive::decode_dc_refine(&mut bit_reader, coeffs, al)?;
554 }
555 } else if ah == 0 {
556 progressive::decode_ac_first_tracked(
557 &mut bit_reader,
558 ac_table_ref.unwrap(),
559 coeffs,
560 ss,
561 se,
562 al,
563 &mut eob_run,
564 &mut self.ac_max_k_bufs[comp_idx][block_idx],
565 )?;
566 } else {
567 progressive::decode_ac_refine_tracked(
568 &mut bit_reader,
569 ac_table_ref.unwrap(),
570 coeffs,
571 ss,
572 se,
573 al,
574 &mut eob_run,
575 &mut self.ac_max_k_bufs[comp_idx][block_idx],
576 )?;
577 }
578
579 mcu_count += 1;
580 }
581 }
582 Ok(())
583 }
584 }
585
586 fn assemble_grayscale(
588 &self,
589 component_planes: &[Vec<u8>],
590 out_width: usize,
591 out_height: usize,
592 icc_profile: Option<Vec<u8>>,
593 exif_data: Option<Vec<u8>>,
594 ) -> Result<Image> {
595 let comp_w: usize = self.comp_infos[0].comp_w;
596 let mut data: Vec<u8> = Vec::with_capacity(out_width * out_height);
597 for y in 0..out_height {
598 data.extend_from_slice(&component_planes[0][y * comp_w..y * comp_w + out_width]);
599 }
600 Ok(Image {
601 xmp_data: self.metadata.xmp_data.clone(),
602 iptc_data: self.metadata.iptc_data.clone(),
603 width: out_width,
604 height: out_height,
605 pixel_format: PixelFormat::Grayscale,
606 precision: 8,
607 data,
608 icc_profile,
609 exif_data,
610 comment: self.metadata.comment.clone(),
611 density: self.metadata.density,
612 saved_markers: self.metadata.saved_markers.clone(),
613 warnings: Vec::new(),
614 })
615 }
616
617 #[allow(clippy::too_many_arguments)]
619 fn assemble_ycbcr(
620 &self,
621 component_planes: &[Vec<u8>],
622 frame: &FrameHeader,
623 out_width: usize,
624 out_height: usize,
625 full_width: usize,
626 full_height: usize,
627 icc_profile: Option<Vec<u8>>,
628 exif_data: Option<Vec<u8>>,
629 ) -> Result<Image> {
630 let out_format: PixelFormat = PixelFormat::Rgb;
631 let bpp: usize = out_format.bytes_per_pixel();
632
633 let y_comp = &frame.components[0];
645 let cb_comp = &frame.components[1];
646 let cr_comp = &frame.components[2];
647 if y_comp.horizontal_sampling as usize != self.max_h
648 || y_comp.vertical_sampling as usize != self.max_v
649 || cb_comp.horizontal_sampling != cr_comp.horizontal_sampling
650 || cb_comp.vertical_sampling != cr_comp.vertical_sampling
651 {
652 return Err(JpegError::Unsupported(format!(
653 "non-standard YCbCr sampling Y={}x{}, Cb={}x{}, Cr={}x{} \
654 (max={}x{}); progressive assembly requires Y at full \
655 sampling and Cb/Cr matching",
656 y_comp.horizontal_sampling,
657 y_comp.vertical_sampling,
658 cb_comp.horizontal_sampling,
659 cb_comp.vertical_sampling,
660 cr_comp.horizontal_sampling,
661 cr_comp.vertical_sampling,
662 self.max_h,
663 self.max_v
664 )));
665 }
666
667 let y_plane: &[u8] = &component_planes[0];
668 let y_width: usize = self.comp_infos[0].comp_w;
669
670 let cb_w: usize = self.comp_infos[1].comp_w;
671 let cb_h: usize = self.comp_infos[1].blocks_y * 8;
672
673 let h_factor: usize = self.max_h / cb_comp.horizontal_sampling as usize;
674 let v_factor: usize = self.max_v / cb_comp.vertical_sampling as usize;
675
676 let y_comp = &frame.components[0];
689 let cr_comp = &frame.components[2];
690 let all_full_sampling = y_comp.horizontal_sampling as usize == self.max_h
691 && y_comp.vertical_sampling as usize == self.max_v
692 && cb_comp.horizontal_sampling as usize == self.max_h
693 && cb_comp.vertical_sampling as usize == self.max_v
694 && cr_comp.horizontal_sampling as usize == self.max_h
695 && cr_comp.vertical_sampling as usize == self.max_v;
696
697 if h_factor == 1 && v_factor == 1 && all_full_sampling {
698 let data_size: usize = out_width * out_height * bpp;
700 let mut data: Vec<u8> = Vec::with_capacity(data_size);
701 #[allow(clippy::uninit_vec)]
702 unsafe {
703 data.set_len(data_size)
704 };
705 for y in 0..out_height {
706 self.ycbcr_to_rgb_row(
707 &y_plane[y * y_width..],
708 &component_planes[1][y * cb_w..],
709 &component_planes[2][y * cb_w..],
710 &mut data[y * out_width * bpp..],
711 out_width,
712 );
713 }
714 Ok(Image {
715 xmp_data: self.metadata.xmp_data.clone(),
716 iptc_data: self.metadata.iptc_data.clone(),
717 width: out_width,
718 height: out_height,
719 pixel_format: out_format,
720 precision: 8,
721 data,
722 icc_profile,
723 exif_data,
724 comment: self.metadata.comment.clone(),
725 density: self.metadata.density,
726 saved_markers: self.metadata.saved_markers.clone(),
727 warnings: Vec::new(),
728 })
729 } else {
730 let alloc_size: usize = full_width * full_height;
732 let mut cb_full: Vec<u8> = Vec::with_capacity(alloc_size);
733 let mut cr_full: Vec<u8> = Vec::with_capacity(alloc_size);
734 unsafe {
735 cb_full.set_len(alloc_size);
736 cr_full.set_len(alloc_size);
737 }
738
739 if h_factor == 2 && v_factor == 1 {
740 for row in 0..cb_h {
741 self.fancy_upsample_h2v1(
742 &component_planes[1][row * cb_w..],
743 cb_w,
744 &mut cb_full[row * full_width..],
745 );
746 self.fancy_upsample_h2v1(
747 &component_planes[2][row * cb_w..],
748 cb_w,
749 &mut cr_full[row * full_width..],
750 );
751 }
752 } else if h_factor == 2 && v_factor == 2 {
753 self.fancy_h2v2(&component_planes[1], cb_w, cb_h, &mut cb_full, full_width);
754 self.fancy_h2v2(&component_planes[2], cb_w, cb_h, &mut cr_full, full_width);
755 } else if h_factor == 1 && v_factor == 2 {
756 self.fancy_h1v2(&component_planes[1], cb_w, cb_h, &mut cb_full, full_width);
757 self.fancy_h1v2(&component_planes[2], cb_w, cb_h, &mut cr_full, full_width);
758 } else if h_factor == 4 && v_factor == 1 {
759 upsample_generic_nearest(
761 &component_planes[1],
762 cb_w,
763 cb_h,
764 &mut cb_full,
765 full_width,
766 h_factor,
767 1,
768 );
769 upsample_generic_nearest(
770 &component_planes[2],
771 cb_w,
772 cb_h,
773 &mut cr_full,
774 full_width,
775 h_factor,
776 1,
777 );
778 } else if h_factor == 1 && v_factor == 4 {
779 upsample_generic_nearest(
781 &component_planes[1],
782 cb_w,
783 cb_h,
784 &mut cb_full,
785 full_width,
786 1,
787 v_factor,
788 );
789 upsample_generic_nearest(
790 &component_planes[2],
791 cb_w,
792 cb_h,
793 &mut cr_full,
794 full_width,
795 1,
796 v_factor,
797 );
798 } else {
799 return Err(JpegError::Unsupported(format!(
800 "subsampling {}x{} not supported in progressive output",
801 h_factor, v_factor
802 )));
803 }
804
805 let data_size: usize = out_width * out_height * bpp;
806 let mut data: Vec<u8> = Vec::with_capacity(data_size);
807 #[allow(clippy::uninit_vec)]
808 unsafe {
809 data.set_len(data_size)
810 };
811 for y in 0..out_height {
812 self.ycbcr_to_rgb_row(
813 &y_plane[y * y_width..],
814 &cb_full[y * full_width..],
815 &cr_full[y * full_width..],
816 &mut data[y * out_width * bpp..],
817 out_width,
818 );
819 }
820
821 Ok(Image {
822 xmp_data: self.metadata.xmp_data.clone(),
823 iptc_data: self.metadata.iptc_data.clone(),
824 width: out_width,
825 height: out_height,
826 pixel_format: out_format,
827 precision: 8,
828 data,
829 icc_profile,
830 exif_data,
831 comment: self.metadata.comment.clone(),
832 density: self.metadata.density,
833 saved_markers: self.metadata.saved_markers.clone(),
834 warnings: Vec::new(),
835 })
836 }
837 }
838
839 #[allow(clippy::too_many_arguments)]
841 fn assemble_4_component(
842 &self,
843 component_planes: &[Vec<u8>],
844 _frame: &FrameHeader,
845 out_width: usize,
846 out_height: usize,
847 _full_width: usize,
848 _full_height: usize,
849 icc_profile: Option<Vec<u8>>,
850 exif_data: Option<Vec<u8>>,
851 ) -> Result<Image> {
852 let bpp: usize = 4;
854 let data_size: usize = out_width * out_height * bpp;
855 let mut data: Vec<u8> = Vec::with_capacity(data_size);
856 #[allow(clippy::uninit_vec)]
857 unsafe {
858 data.set_len(data_size)
859 };
860
861 for y in 0..out_height {
862 for x in 0..out_width {
863 for c in 0..4 {
864 let comp_w: usize = self.comp_infos[c].comp_w;
865 data[y * out_width * bpp + x * bpp + c] = component_planes[c][y * comp_w + x];
866 }
867 }
868 }
869
870 Ok(Image {
871 xmp_data: self.metadata.xmp_data.clone(),
872 iptc_data: self.metadata.iptc_data.clone(),
873 width: out_width,
874 height: out_height,
875 pixel_format: PixelFormat::Cmyk,
876 precision: 8,
877 data,
878 icc_profile,
879 exif_data,
880 comment: self.metadata.comment.clone(),
881 density: self.metadata.density,
882 saved_markers: self.metadata.saved_markers.clone(),
883 warnings: Vec::new(),
884 })
885 }
886
887 #[inline(always)]
891 fn ycbcr_to_rgb_row(&self, y: &[u8], cb: &[u8], cr: &[u8], out: &mut [u8], width: usize) {
892 #[cfg(all(target_arch = "aarch64", feature = "simd"))]
893 {
894 return crate::simd::aarch64::color::neon_ycbcr_to_rgb_row(y, cb, cr, out, width);
895 }
896
897 #[allow(unreachable_code)]
898 (self.routines.ycbcr_to_rgb_row)(y, cb, cr, out, width)
899 }
900
901 #[inline(always)]
902 fn fancy_upsample_h2v1(&self, input: &[u8], in_width: usize, output: &mut [u8]) {
903 #[cfg(all(target_arch = "aarch64", feature = "simd"))]
904 {
905 return crate::simd::aarch64::upsample::neon_fancy_upsample_h2v1(
906 input, in_width, output,
907 );
908 }
909
910 #[allow(unreachable_code)]
911 (self.routines.fancy_upsample_h2v1)(input, in_width, output)
912 }
913
914 fn fancy_h2v2(
915 &self,
916 input: &[u8],
917 in_width: usize,
918 in_height: usize,
919 output: &mut [u8],
920 out_width: usize,
921 ) {
922 #[cfg(all(target_arch = "aarch64", feature = "simd"))]
923 {
924 crate::simd::aarch64::upsample::neon_fancy_upsample_h2v2(
925 input, in_width, in_height, output, out_width,
926 )
927 }
928
929 #[cfg(all(target_arch = "wasm32", feature = "simd"))]
930 {
931 return crate::simd::wasm32::upsample::wasm_fancy_upsample_h2v2(
932 input, in_width, in_height, output, out_width,
933 );
934 }
935
936 #[allow(unreachable_code)]
938 {
939 crate::decode::upsample::fancy_h2v2(
940 input,
941 in_width,
942 in_height,
943 output,
944 out_width,
945 in_height * 2,
946 );
947 }
948 }
949
950 fn fancy_h1v2(
951 &self,
952 input: &[u8],
953 in_width: usize,
954 in_height: usize,
955 output: &mut [u8],
956 out_width: usize,
957 ) {
958 let actual_rows: usize = input.len().checked_div(in_width).unwrap_or(0);
966 let safe_height: usize = in_height.min(actual_rows);
967 for y in 0..safe_height {
968 let cur_row = &input[y * in_width..(y + 1) * in_width];
969 let above = if y > 0 {
970 &input[(y - 1) * in_width..y * in_width]
971 } else {
972 cur_row
973 };
974 let below = if y + 1 < safe_height {
975 &input[(y + 1) * in_width..(y + 2) * in_width]
976 } else {
977 cur_row
978 };
979
980 let out_y_top: usize = y * 2;
981 let out_y_bot: usize = y * 2 + 1;
982
983 for i in 0..in_width {
985 output[out_y_top * out_width + i] =
986 ((3 * cur_row[i] as u16 + above[i] as u16 + 1) >> 2) as u8;
987 output[out_y_bot * out_width + i] =
988 ((3 * cur_row[i] as u16 + below[i] as u16 + 2) >> 2) as u8;
989 }
990 }
991 let written: usize = safe_height * 2 * out_width;
995 let cap: usize = in_height * 2 * out_width;
996 if written < cap && cap <= output.len() {
997 output[written..cap].fill(0);
998 }
999 }
1000}