/rust/registry/src/index.crates.io-6f17d22bba15001f/zune-jpeg-0.4.19/src/mcu.rs
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2023. |
3 | | * |
4 | | * This software is free software; |
5 | | * |
6 | | * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license |
7 | | */ |
8 | | |
9 | | use alloc::{format, vec}; |
10 | | use core::cmp::min; |
11 | | use zune_core::bytestream::ZReaderTrait; |
12 | | use zune_core::colorspace::ColorSpace; |
13 | | use zune_core::colorspace::ColorSpace::Luma; |
14 | | use zune_core::log::{error, trace, warn}; |
15 | | |
16 | | use crate::bitstream::BitStream; |
17 | | use crate::components::SampleRatios; |
18 | | use crate::decoder::MAX_COMPONENTS; |
19 | | use crate::errors::DecodeErrors; |
20 | | use crate::marker::Marker; |
21 | | use crate::mcu_prog::get_marker; |
22 | | use crate::misc::{calculate_padded_width, setup_component_params}; |
23 | | use crate::worker::{color_convert, upsample}; |
24 | | use crate::JpegDecoder; |
25 | | |
26 | | /// The size of a DC block for a MCU. |
27 | | |
28 | | pub const DCT_BLOCK: usize = 64; |
29 | | |
30 | | impl<T: ZReaderTrait> JpegDecoder<T> { |
31 | | /// Check for existence of DC and AC Huffman Tables |
32 | 1.89k | pub(crate) fn check_tables(&self) -> Result<(), DecodeErrors> { |
33 | | // check that dc and AC tables exist outside the hot path |
34 | 5.58k | for component in &self.components { |
35 | 3.71k | let _ = &self |
36 | 3.71k | .dc_huffman_tables |
37 | 3.71k | .get(component.dc_huff_table) |
38 | 3.71k | .as_ref() |
39 | 3.71k | .ok_or_else(|| { |
40 | 2 | DecodeErrors::HuffmanDecode(format!( |
41 | 2 | "No Huffman DC table for component {:?} ", |
42 | 2 | component.component_id |
43 | 2 | )) |
44 | 3.71k | })? Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::check_tables::{closure#0} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Line | Count | Source | 39 | 1 | .ok_or_else(|| { | 40 | 1 | DecodeErrors::HuffmanDecode(format!( | 41 | 1 | "No Huffman DC table for component {:?} ", | 42 | 1 | component.component_id | 43 | 1 | )) | 44 | 1 | })? |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#0} Line | Count | Source | 39 | 1 | .ok_or_else(|| { | 40 | 1 | DecodeErrors::HuffmanDecode(format!( | 41 | 1 | "No Huffman DC table for component {:?} ", | 42 | 1 | component.component_id | 43 | 1 | )) | 44 | 1 | })? |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#0} |
45 | 3.70k | .as_ref() |
46 | 3.70k | .ok_or_else(|| { |
47 | 5 | DecodeErrors::HuffmanDecode(format!( |
48 | 5 | "No DC table for component {:?}", |
49 | 5 | component.component_id |
50 | 5 | )) |
51 | 3.70k | })?; Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::check_tables::{closure#1} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Line | Count | Source | 46 | 3 | .ok_or_else(|| { | 47 | 3 | DecodeErrors::HuffmanDecode(format!( | 48 | 3 | "No DC table for component {:?}", | 49 | 3 | component.component_id | 50 | 3 | )) | 51 | 3 | })?; |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#1} Line | Count | Source | 46 | 1 | .ok_or_else(|| { | 47 | 1 | DecodeErrors::HuffmanDecode(format!( | 48 | 1 | "No DC table for component {:?}", | 49 | 1 | component.component_id | 50 | 1 | )) | 51 | 1 | })?; |
<zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#1} Line | Count | Source | 46 | 1 | .ok_or_else(|| { | 47 | 1 | DecodeErrors::HuffmanDecode(format!( | 48 | 1 | "No DC table for component {:?}", | 49 | 1 | component.component_id | 50 | 1 | )) | 51 | 1 | })?; |
|
52 | | |
53 | 3.70k | let _ = &self |
54 | 3.70k | .ac_huffman_tables |
55 | 3.70k | .get(component.ac_huff_table) |
56 | 3.70k | .as_ref() |
57 | 3.70k | .ok_or_else(|| { |
58 | 4 | DecodeErrors::HuffmanDecode(format!( |
59 | 4 | "No Huffman AC table for component {:?} ", |
60 | 4 | component.component_id |
61 | 4 | )) |
62 | 3.70k | })? Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::check_tables::{closure#2} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} Line | Count | Source | 57 | 2 | .ok_or_else(|| { | 58 | 2 | DecodeErrors::HuffmanDecode(format!( | 59 | 2 | "No Huffman AC table for component {:?} ", | 60 | 2 | component.component_id | 61 | 2 | )) | 62 | 2 | })? |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#2} <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#2} Line | Count | Source | 57 | 2 | .ok_or_else(|| { | 58 | 2 | DecodeErrors::HuffmanDecode(format!( | 59 | 2 | "No Huffman AC table for component {:?} ", | 60 | 2 | component.component_id | 61 | 2 | )) | 62 | 2 | })? |
|
63 | 3.70k | .as_ref() |
64 | 3.70k | .ok_or_else(|| { |
65 | 3 | DecodeErrors::HuffmanDecode(format!( |
66 | 3 | "No AC table for component {:?}", |
67 | 3 | component.component_id |
68 | 3 | )) |
69 | 3.70k | })?; Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::check_tables::{closure#3} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} Line | Count | Source | 64 | 2 | .ok_or_else(|| { | 65 | 2 | DecodeErrors::HuffmanDecode(format!( | 66 | 2 | "No AC table for component {:?}", | 67 | 2 | component.component_id | 68 | 2 | )) | 69 | 2 | })?; |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} Line | Count | Source | 64 | 1 | .ok_or_else(|| { | 65 | 1 | DecodeErrors::HuffmanDecode(format!( | 66 | 1 | "No AC table for component {:?}", | 67 | 1 | component.component_id | 68 | 1 | )) | 69 | 1 | })?; |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables::{closure#3} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables::{closure#3} |
70 | | } |
71 | 1.87k | Ok(()) |
72 | 1.89k | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::check_tables <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables Line | Count | Source | 32 | 775 | pub(crate) fn check_tables(&self) -> Result<(), DecodeErrors> { | 33 | | // check that dc and AC tables exist outside the hot path | 34 | 1.56k | for component in &self.components { | 35 | 793 | let _ = &self | 36 | 793 | .dc_huffman_tables | 37 | 793 | .get(component.dc_huff_table) | 38 | 793 | .as_ref() | 39 | 793 | .ok_or_else(|| { | 40 | | DecodeErrors::HuffmanDecode(format!( | 41 | | "No Huffman DC table for component {:?} ", | 42 | | component.component_id | 43 | | )) | 44 | 793 | })? | 45 | 792 | .as_ref() | 46 | 792 | .ok_or_else(|| { | 47 | | DecodeErrors::HuffmanDecode(format!( | 48 | | "No DC table for component {:?}", | 49 | | component.component_id | 50 | | )) | 51 | 792 | })?; | 52 | | | 53 | 789 | let _ = &self | 54 | 789 | .ac_huffman_tables | 55 | 789 | .get(component.ac_huff_table) | 56 | 789 | .as_ref() | 57 | 789 | .ok_or_else(|| { | 58 | | DecodeErrors::HuffmanDecode(format!( | 59 | | "No Huffman AC table for component {:?} ", | 60 | | component.component_id | 61 | | )) | 62 | 789 | })? | 63 | 787 | .as_ref() | 64 | 787 | .ok_or_else(|| { | 65 | | DecodeErrors::HuffmanDecode(format!( | 66 | | "No AC table for component {:?}", | 67 | | component.component_id | 68 | | )) | 69 | 787 | })?; | 70 | | } | 71 | 767 | Ok(()) | 72 | 775 | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables Line | Count | Source | 32 | 371 | pub(crate) fn check_tables(&self) -> Result<(), DecodeErrors> { | 33 | | // check that dc and AC tables exist outside the hot path | 34 | 1.16k | for component in &self.components { | 35 | 792 | let _ = &self | 36 | 792 | .dc_huffman_tables | 37 | 792 | .get(component.dc_huff_table) | 38 | 792 | .as_ref() | 39 | 792 | .ok_or_else(|| { | 40 | | DecodeErrors::HuffmanDecode(format!( | 41 | | "No Huffman DC table for component {:?} ", | 42 | | component.component_id | 43 | | )) | 44 | 792 | })? | 45 | 792 | .as_ref() | 46 | 792 | .ok_or_else(|| { | 47 | | DecodeErrors::HuffmanDecode(format!( | 48 | | "No DC table for component {:?}", | 49 | | component.component_id | 50 | | )) | 51 | 792 | })?; | 52 | | | 53 | 792 | let _ = &self | 54 | 792 | .ac_huffman_tables | 55 | 792 | .get(component.ac_huff_table) | 56 | 792 | .as_ref() | 57 | 792 | .ok_or_else(|| { | 58 | | DecodeErrors::HuffmanDecode(format!( | 59 | | "No Huffman AC table for component {:?} ", | 60 | | component.component_id | 61 | | )) | 62 | 792 | })? | 63 | 792 | .as_ref() | 64 | 792 | .ok_or_else(|| { | 65 | | DecodeErrors::HuffmanDecode(format!( | 66 | | "No AC table for component {:?}", | 67 | | component.component_id | 68 | | )) | 69 | 792 | })?; | 70 | | } | 71 | 370 | Ok(()) | 72 | 371 | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::check_tables Line | Count | Source | 32 | 68 | pub(crate) fn check_tables(&self) -> Result<(), DecodeErrors> { | 33 | | // check that dc and AC tables exist outside the hot path | 34 | 134 | for component in &self.components { | 35 | 68 | let _ = &self | 36 | 68 | .dc_huffman_tables | 37 | 68 | .get(component.dc_huff_table) | 38 | 68 | .as_ref() | 39 | 68 | .ok_or_else(|| { | 40 | | DecodeErrors::HuffmanDecode(format!( | 41 | | "No Huffman DC table for component {:?} ", | 42 | | component.component_id | 43 | | )) | 44 | 68 | })? | 45 | 67 | .as_ref() | 46 | 67 | .ok_or_else(|| { | 47 | | DecodeErrors::HuffmanDecode(format!( | 48 | | "No DC table for component {:?}", | 49 | | component.component_id | 50 | | )) | 51 | 67 | })?; | 52 | | | 53 | 66 | let _ = &self | 54 | 66 | .ac_huffman_tables | 55 | 66 | .get(component.ac_huff_table) | 56 | 66 | .as_ref() | 57 | 66 | .ok_or_else(|| { | 58 | | DecodeErrors::HuffmanDecode(format!( | 59 | | "No Huffman AC table for component {:?} ", | 60 | | component.component_id | 61 | | )) | 62 | 66 | })? | 63 | 66 | .as_ref() | 64 | 66 | .ok_or_else(|| { | 65 | | DecodeErrors::HuffmanDecode(format!( | 66 | | "No AC table for component {:?}", | 67 | | component.component_id | 68 | | )) | 69 | 66 | })?; | 70 | | } | 71 | 66 | Ok(()) | 72 | 68 | } |
<zune_jpeg::decoder::JpegDecoder<&[u8]>>::check_tables Line | Count | Source | 32 | 677 | pub(crate) fn check_tables(&self) -> Result<(), DecodeErrors> { | 33 | | // check that dc and AC tables exist outside the hot path | 34 | 2.73k | for component in &self.components { | 35 | 2.05k | let _ = &self | 36 | 2.05k | .dc_huffman_tables | 37 | 2.05k | .get(component.dc_huff_table) | 38 | 2.05k | .as_ref() | 39 | 2.05k | .ok_or_else(|| { | 40 | | DecodeErrors::HuffmanDecode(format!( | 41 | | "No Huffman DC table for component {:?} ", | 42 | | component.component_id | 43 | | )) | 44 | 2.05k | })? | 45 | 2.05k | .as_ref() | 46 | 2.05k | .ok_or_else(|| { | 47 | | DecodeErrors::HuffmanDecode(format!( | 48 | | "No DC table for component {:?}", | 49 | | component.component_id | 50 | | )) | 51 | 2.05k | })?; | 52 | | | 53 | 2.05k | let _ = &self | 54 | 2.05k | .ac_huffman_tables | 55 | 2.05k | .get(component.ac_huff_table) | 56 | 2.05k | .as_ref() | 57 | 2.05k | .ok_or_else(|| { | 58 | | DecodeErrors::HuffmanDecode(format!( | 59 | | "No Huffman AC table for component {:?} ", | 60 | | component.component_id | 61 | | )) | 62 | 2.05k | })? | 63 | 2.05k | .as_ref() | 64 | 2.05k | .ok_or_else(|| { | 65 | | DecodeErrors::HuffmanDecode(format!( | 66 | | "No AC table for component {:?}", | 67 | | component.component_id | 68 | | )) | 69 | 2.05k | })?; | 70 | | } | 71 | 674 | Ok(()) | 72 | 677 | } |
|
73 | | |
74 | | /// Decode MCUs and carry out post processing. |
75 | | /// |
76 | | /// This is the main decoder loop for the library, the hot path. |
77 | | /// |
78 | | /// Because of this, we pull in some very crazy optimization tricks hence readability is a pinch |
79 | | /// here. |
80 | | #[allow( |
81 | | clippy::similar_names, |
82 | | clippy::too_many_lines, |
83 | | clippy::cast_possible_truncation |
84 | | )] |
85 | | #[inline(never)] |
86 | 1.91k | pub(crate) fn decode_mcu_ycbcr_baseline( |
87 | 1.91k | &mut self, pixels: &mut [u8] |
88 | 1.91k | ) -> Result<(), DecodeErrors> { |
89 | 1.91k | setup_component_params(self)?; |
90 | | |
91 | | // check dc and AC tables |
92 | 1.89k | self.check_tables()?; |
93 | | |
94 | | let (mut mcu_width, mut mcu_height); |
95 | | |
96 | 1.87k | if self.is_interleaved { |
97 | | // set upsampling functions |
98 | 681 | self.set_upsampling()?; |
99 | | |
100 | 681 | mcu_width = self.mcu_x; |
101 | 681 | mcu_height = self.mcu_y; |
102 | 1.19k | } else { |
103 | 1.19k | // For non-interleaved images( (1*1) subsampling) |
104 | 1.19k | // number of MCU's are the widths (+7 to account for paddings) divided bu 8. |
105 | 1.19k | mcu_width = ((self.info.width + 7) / 8) as usize; |
106 | 1.19k | mcu_height = ((self.info.height + 7) / 8) as usize; |
107 | 1.19k | } |
108 | 1.87k | if self.is_interleaved |
109 | 681 | && self.input_colorspace.num_components() > 1 |
110 | 507 | && self.options.jpeg_get_out_colorspace().num_components() == 1 |
111 | 0 | && (self.sub_sample_ratio == SampleRatios::V |
112 | 0 | || self.sub_sample_ratio == SampleRatios::HV) |
113 | 0 | { |
114 | 0 | // For a specific set of images, e.g interleaved, |
115 | 0 | // when converting from YcbCr to grayscale, we need to |
116 | 0 | // take into account mcu height since the MCU decoding needs to take |
117 | 0 | // it into account for padding purposes and the post processor |
118 | 0 | // parses two rows per mcu width. |
119 | 0 | // |
120 | 0 | // set coeff to be 2 to ensure that we increment two rows |
121 | 0 | // for every mcu processed also |
122 | 0 | mcu_height *= self.v_max; |
123 | 0 | mcu_height /= self.h_max; |
124 | 0 | self.coeff = 2; |
125 | 1.87k | } |
126 | | |
127 | 1.87k | if self.input_colorspace == ColorSpace::Luma && self.is_interleaved { |
128 | 174 | warn!("Grayscale image with down-sampled component, resetting component details"); |
129 | 174 | |
130 | 174 | self.reset_params(); |
131 | 174 | |
132 | 174 | mcu_width = ((self.info.width + 7) / 8) as usize; |
133 | 174 | mcu_height = ((self.info.height + 7) / 8) as usize; |
134 | 1.70k | } |
135 | 1.87k | let width = usize::from(self.info.width); |
136 | 1.87k | |
137 | 1.87k | let padded_width = calculate_padded_width(width, self.sub_sample_ratio); |
138 | 1.87k | |
139 | 1.87k | let mut stream = BitStream::new(); |
140 | 1.87k | let mut tmp = [0_i32; DCT_BLOCK]; |
141 | 1.87k | |
142 | 1.87k | let comp_len = self.components.len(); |
143 | | |
144 | 3.69k | for (pos, comp) in self.components.iter_mut().enumerate() { |
145 | | // Allocate only needed components. |
146 | | // |
147 | | // For special colorspaces i.e YCCK and CMYK, just allocate all of the needed |
148 | | // components. |
149 | 3.69k | if min( |
150 | 3.69k | self.options.jpeg_get_out_colorspace().num_components() - 1, |
151 | 3.69k | pos |
152 | 3.69k | ) == pos |
153 | 287 | || comp_len == 4 |
154 | | // Special colorspace |
155 | 3.69k | { |
156 | 3.69k | // allocate enough space to hold a whole MCU width |
157 | 3.69k | // this means we should take into account sampling ratios |
158 | 3.69k | // `*8` is because each MCU spans 8 widths. |
159 | 3.69k | let len = comp.width_stride * comp.vertical_sample * 8; |
160 | 3.69k | |
161 | 3.69k | comp.needed = true; |
162 | 3.69k | comp.raw_coeff = vec![0; len]; |
163 | 3.69k | } else { |
164 | 0 | comp.needed = false; |
165 | 0 | } |
166 | | } |
167 | | |
168 | 1.87k | let mut pixels_written = 0; |
169 | 1.87k | |
170 | 1.87k | let is_hv = usize::from(self.is_interleaved); |
171 | 1.87k | let upsampler_scratch_size = is_hv * self.components[0].width_stride; |
172 | 1.87k | let mut upsampler_scratch_space = vec![0; upsampler_scratch_size]; |
173 | | |
174 | 191k | for i in 0..mcu_height { |
175 | | // Report if we have no more bytes |
176 | | // This may generate false negatives since we over-read bytes |
177 | | // hence that why 37 is chosen(we assume if we over-read more than 37 bytes, we have a problem) |
178 | 191k | if stream.overread_by > 37 |
179 | | // favourite number :) |
180 | | { |
181 | 0 | if self.options.get_strict_mode() { |
182 | 0 | return Err(DecodeErrors::FormatStatic("Premature end of buffer")); |
183 | 0 | }; |
184 | 0 |
|
185 | 0 | error!("Premature end of buffer"); |
186 | 0 | break; |
187 | 191k | } |
188 | | |
189 | | // decode a whole MCU width, |
190 | | // this takes into account interleaved components. |
191 | 191k | let terminate = self.decode_mcu_width(mcu_width, &mut tmp, &mut stream)?; |
192 | | // if i >=7{ |
193 | | // panic!() |
194 | | // } |
195 | | // process that width up until it's impossible |
196 | | |
197 | 190k | self.post_process( |
198 | 190k | pixels, |
199 | 190k | i, |
200 | 190k | mcu_height, |
201 | 190k | width, |
202 | 190k | padded_width, |
203 | 190k | &mut pixels_written, |
204 | 190k | &mut upsampler_scratch_space |
205 | 190k | )?; |
206 | 190k | if terminate { |
207 | 25 | warn!("Got terminate signal, will not process further"); |
208 | 25 | return Ok(()); |
209 | 190k | } |
210 | | } |
211 | | // it may happen that some images don't have the whole buffer |
212 | | // so we can't panic in case of that |
213 | | // assert_eq!(pixels_written, pixels.len()); |
214 | | |
215 | | // For UHD usecases that tie two images separating them with EOI and |
216 | | // SOI markers, it may happen that we do not reach this image end of image |
217 | | // So this ensures we reach it |
218 | | // Ensure we read EOI |
219 | 914 | if !stream.seen_eoi { |
220 | 730 | let marker = get_marker(&mut self.stream, &mut stream); |
221 | 730 | match marker { |
222 | 77 | Ok(_m) => { |
223 | 77 | trace!("Found marker {:?}", _m); |
224 | 77 | } |
225 | 653 | Err(_) => { |
226 | 653 | // ignore error |
227 | 653 | } |
228 | | } |
229 | 184 | } |
230 | | |
231 | | trace!("Finished decoding image"); |
232 | | |
233 | 914 | Ok(()) |
234 | 1.91k | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode_mcu_ycbcr_baseline <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline Line | Count | Source | 86 | 776 | pub(crate) fn decode_mcu_ycbcr_baseline( | 87 | 776 | &mut self, pixels: &mut [u8] | 88 | 776 | ) -> Result<(), DecodeErrors> { | 89 | 776 | setup_component_params(self)?; | 90 | | | 91 | | // check dc and AC tables | 92 | 775 | self.check_tables()?; | 93 | | | 94 | | let (mut mcu_width, mut mcu_height); | 95 | | | 96 | 767 | if self.is_interleaved { | 97 | | // set upsampling functions | 98 | 16 | self.set_upsampling()?; | 99 | | | 100 | 16 | mcu_width = self.mcu_x; | 101 | 16 | mcu_height = self.mcu_y; | 102 | 751 | } else { | 103 | 751 | // For non-interleaved images( (1*1) subsampling) | 104 | 751 | // number of MCU's are the widths (+7 to account for paddings) divided bu 8. | 105 | 751 | mcu_width = ((self.info.width + 7) / 8) as usize; | 106 | 751 | mcu_height = ((self.info.height + 7) / 8) as usize; | 107 | 751 | } | 108 | 767 | if self.is_interleaved | 109 | 16 | && self.input_colorspace.num_components() > 1 | 110 | 3 | && self.options.jpeg_get_out_colorspace().num_components() == 1 | 111 | 0 | && (self.sub_sample_ratio == SampleRatios::V | 112 | 0 | || self.sub_sample_ratio == SampleRatios::HV) | 113 | 0 | { | 114 | 0 | // For a specific set of images, e.g interleaved, | 115 | 0 | // when converting from YcbCr to grayscale, we need to | 116 | 0 | // take into account mcu height since the MCU decoding needs to take | 117 | 0 | // it into account for padding purposes and the post processor | 118 | 0 | // parses two rows per mcu width. | 119 | 0 | // | 120 | 0 | // set coeff to be 2 to ensure that we increment two rows | 121 | 0 | // for every mcu processed also | 122 | 0 | mcu_height *= self.v_max; | 123 | 0 | mcu_height /= self.h_max; | 124 | 0 | self.coeff = 2; | 125 | 767 | } | 126 | | | 127 | 767 | if self.input_colorspace == ColorSpace::Luma && self.is_interleaved { | 128 | 13 | warn!("Grayscale image with down-sampled component, resetting component details"); | 129 | 13 | | 130 | 13 | self.reset_params(); | 131 | 13 | | 132 | 13 | mcu_width = ((self.info.width + 7) / 8) as usize; | 133 | 13 | mcu_height = ((self.info.height + 7) / 8) as usize; | 134 | 754 | } | 135 | 767 | let width = usize::from(self.info.width); | 136 | 767 | | 137 | 767 | let padded_width = calculate_padded_width(width, self.sub_sample_ratio); | 138 | 767 | | 139 | 767 | let mut stream = BitStream::new(); | 140 | 767 | let mut tmp = [0_i32; DCT_BLOCK]; | 141 | 767 | | 142 | 767 | let comp_len = self.components.len(); | 143 | | | 144 | 785 | for (pos, comp) in self.components.iter_mut().enumerate() { | 145 | | // Allocate only needed components. | 146 | | // | 147 | | // For special colorspaces i.e YCCK and CMYK, just allocate all of the needed | 148 | | // components. | 149 | 785 | if min( | 150 | 785 | self.options.jpeg_get_out_colorspace().num_components() - 1, | 151 | 785 | pos | 152 | 785 | ) == pos | 153 | 6 | || comp_len == 4 | 154 | | // Special colorspace | 155 | 785 | { | 156 | 785 | // allocate enough space to hold a whole MCU width | 157 | 785 | // this means we should take into account sampling ratios | 158 | 785 | // `*8` is because each MCU spans 8 widths. | 159 | 785 | let len = comp.width_stride * comp.vertical_sample * 8; | 160 | 785 | | 161 | 785 | comp.needed = true; | 162 | 785 | comp.raw_coeff = vec![0; len]; | 163 | 785 | } else { | 164 | 0 | comp.needed = false; | 165 | 0 | } | 166 | | } | 167 | | | 168 | 767 | let mut pixels_written = 0; | 169 | 767 | | 170 | 767 | let is_hv = usize::from(self.is_interleaved); | 171 | 767 | let upsampler_scratch_size = is_hv * self.components[0].width_stride; | 172 | 767 | let mut upsampler_scratch_space = vec![0; upsampler_scratch_size]; | 173 | | | 174 | 95.0k | for i in 0..mcu_height { | 175 | | // Report if we have no more bytes | 176 | | // This may generate false negatives since we over-read bytes | 177 | | // hence that why 37 is chosen(we assume if we over-read more than 37 bytes, we have a problem) | 178 | 95.0k | if stream.overread_by > 37 | 179 | | // favourite number :) | 180 | | { | 181 | 0 | if self.options.get_strict_mode() { | 182 | 0 | return Err(DecodeErrors::FormatStatic("Premature end of buffer")); | 183 | 0 | }; | 184 | 0 |
| 185 | 0 | error!("Premature end of buffer"); | 186 | 0 | break; | 187 | 95.0k | } | 188 | | | 189 | | // decode a whole MCU width, | 190 | | // this takes into account interleaved components. | 191 | 95.0k | let terminate = self.decode_mcu_width(mcu_width, &mut tmp, &mut stream)?; | 192 | | // if i >=7{ | 193 | | // panic!() | 194 | | // } | 195 | | // process that width up until it's impossible | 196 | | | 197 | 94.6k | self.post_process( | 198 | 94.6k | pixels, | 199 | 94.6k | i, | 200 | 94.6k | mcu_height, | 201 | 94.6k | width, | 202 | 94.6k | padded_width, | 203 | 94.6k | &mut pixels_written, | 204 | 94.6k | &mut upsampler_scratch_space | 205 | 94.6k | )?; | 206 | 94.6k | if terminate { | 207 | 0 | warn!("Got terminate signal, will not process further"); | 208 | 0 | return Ok(()); | 209 | 94.6k | } | 210 | | } | 211 | | // it may happen that some images don't have the whole buffer | 212 | | // so we can't panic in case of that | 213 | | // assert_eq!(pixels_written, pixels.len()); | 214 | | | 215 | | // For UHD usecases that tie two images separating them with EOI and | 216 | | // SOI markers, it may happen that we do not reach this image end of image | 217 | | // So this ensures we reach it | 218 | | // Ensure we read EOI | 219 | 375 | if !stream.seen_eoi { | 220 | 272 | let marker = get_marker(&mut self.stream, &mut stream); | 221 | 272 | match marker { | 222 | 42 | Ok(_m) => { | 223 | 42 | trace!("Found marker {:?}", _m); | 224 | 42 | } | 225 | 230 | Err(_) => { | 226 | 230 | // ignore error | 227 | 230 | } | 228 | | } | 229 | 103 | } | 230 | | | 231 | | trace!("Finished decoding image"); | 232 | | | 233 | 375 | Ok(()) | 234 | 776 | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline Line | Count | Source | 86 | 388 | pub(crate) fn decode_mcu_ycbcr_baseline( | 87 | 388 | &mut self, pixels: &mut [u8] | 88 | 388 | ) -> Result<(), DecodeErrors> { | 89 | 388 | setup_component_params(self)?; | 90 | | | 91 | | // check dc and AC tables | 92 | 371 | self.check_tables()?; | 93 | | | 94 | | let (mut mcu_width, mut mcu_height); | 95 | | | 96 | 370 | if self.is_interleaved { | 97 | | // set upsampling functions | 98 | 355 | self.set_upsampling()?; | 99 | | | 100 | 355 | mcu_width = self.mcu_x; | 101 | 355 | mcu_height = self.mcu_y; | 102 | 15 | } else { | 103 | 15 | // For non-interleaved images( (1*1) subsampling) | 104 | 15 | // number of MCU's are the widths (+7 to account for paddings) divided bu 8. | 105 | 15 | mcu_width = ((self.info.width + 7) / 8) as usize; | 106 | 15 | mcu_height = ((self.info.height + 7) / 8) as usize; | 107 | 15 | } | 108 | 370 | if self.is_interleaved | 109 | 355 | && self.input_colorspace.num_components() > 1 | 110 | 194 | && self.options.jpeg_get_out_colorspace().num_components() == 1 | 111 | 0 | && (self.sub_sample_ratio == SampleRatios::V | 112 | 0 | || self.sub_sample_ratio == SampleRatios::HV) | 113 | 0 | { | 114 | 0 | // For a specific set of images, e.g interleaved, | 115 | 0 | // when converting from YcbCr to grayscale, we need to | 116 | 0 | // take into account mcu height since the MCU decoding needs to take | 117 | 0 | // it into account for padding purposes and the post processor | 118 | 0 | // parses two rows per mcu width. | 119 | 0 | // | 120 | 0 | // set coeff to be 2 to ensure that we increment two rows | 121 | 0 | // for every mcu processed also | 122 | 0 | mcu_height *= self.v_max; | 123 | 0 | mcu_height /= self.h_max; | 124 | 0 | self.coeff = 2; | 125 | 370 | } | 126 | | | 127 | 370 | if self.input_colorspace == ColorSpace::Luma && self.is_interleaved { | 128 | 161 | warn!("Grayscale image with down-sampled component, resetting component details"); | 129 | 161 | | 130 | 161 | self.reset_params(); | 131 | 161 | | 132 | 161 | mcu_width = ((self.info.width + 7) / 8) as usize; | 133 | 161 | mcu_height = ((self.info.height + 7) / 8) as usize; | 134 | 209 | } | 135 | 370 | let width = usize::from(self.info.width); | 136 | 370 | | 137 | 370 | let padded_width = calculate_padded_width(width, self.sub_sample_ratio); | 138 | 370 | | 139 | 370 | let mut stream = BitStream::new(); | 140 | 370 | let mut tmp = [0_i32; DCT_BLOCK]; | 141 | 370 | | 142 | 370 | let comp_len = self.components.len(); | 143 | | | 144 | 791 | for (pos, comp) in self.components.iter_mut().enumerate() { | 145 | | // Allocate only needed components. | 146 | | // | 147 | | // For special colorspaces i.e YCCK and CMYK, just allocate all of the needed | 148 | | // components. | 149 | 791 | if min( | 150 | 791 | self.options.jpeg_get_out_colorspace().num_components() - 1, | 151 | 791 | pos | 152 | 791 | ) == pos | 153 | 3 | || comp_len == 4 | 154 | | // Special colorspace | 155 | 791 | { | 156 | 791 | // allocate enough space to hold a whole MCU width | 157 | 791 | // this means we should take into account sampling ratios | 158 | 791 | // `*8` is because each MCU spans 8 widths. | 159 | 791 | let len = comp.width_stride * comp.vertical_sample * 8; | 160 | 791 | | 161 | 791 | comp.needed = true; | 162 | 791 | comp.raw_coeff = vec![0; len]; | 163 | 791 | } else { | 164 | 0 | comp.needed = false; | 165 | 0 | } | 166 | | } | 167 | | | 168 | 370 | let mut pixels_written = 0; | 169 | 370 | | 170 | 370 | let is_hv = usize::from(self.is_interleaved); | 171 | 370 | let upsampler_scratch_size = is_hv * self.components[0].width_stride; | 172 | 370 | let mut upsampler_scratch_space = vec![0; upsampler_scratch_size]; | 173 | | | 174 | 45.5k | for i in 0..mcu_height { | 175 | | // Report if we have no more bytes | 176 | | // This may generate false negatives since we over-read bytes | 177 | | // hence that why 37 is chosen(we assume if we over-read more than 37 bytes, we have a problem) | 178 | 45.5k | if stream.overread_by > 37 | 179 | | // favourite number :) | 180 | | { | 181 | 0 | if self.options.get_strict_mode() { | 182 | 0 | return Err(DecodeErrors::FormatStatic("Premature end of buffer")); | 183 | 0 | }; | 184 | 0 |
| 185 | 0 | error!("Premature end of buffer"); | 186 | 0 | break; | 187 | 45.5k | } | 188 | | | 189 | | // decode a whole MCU width, | 190 | | // this takes into account interleaved components. | 191 | 45.5k | let terminate = self.decode_mcu_width(mcu_width, &mut tmp, &mut stream)?; | 192 | | // if i >=7{ | 193 | | // panic!() | 194 | | // } | 195 | | // process that width up until it's impossible | 196 | | | 197 | 45.3k | self.post_process( | 198 | 45.3k | pixels, | 199 | 45.3k | i, | 200 | 45.3k | mcu_height, | 201 | 45.3k | width, | 202 | 45.3k | padded_width, | 203 | 45.3k | &mut pixels_written, | 204 | 45.3k | &mut upsampler_scratch_space | 205 | 45.3k | )?; | 206 | 45.3k | if terminate { | 207 | 9 | warn!("Got terminate signal, will not process further"); | 208 | 9 | return Ok(()); | 209 | 45.3k | } | 210 | | } | 211 | | // it may happen that some images don't have the whole buffer | 212 | | // so we can't panic in case of that | 213 | | // assert_eq!(pixels_written, pixels.len()); | 214 | | | 215 | | // For UHD usecases that tie two images separating them with EOI and | 216 | | // SOI markers, it may happen that we do not reach this image end of image | 217 | | // So this ensures we reach it | 218 | | // Ensure we read EOI | 219 | 128 | if !stream.seen_eoi { | 220 | 100 | let marker = get_marker(&mut self.stream, &mut stream); | 221 | 100 | match marker { | 222 | 1 | Ok(_m) => { | 223 | 1 | trace!("Found marker {:?}", _m); | 224 | 1 | } | 225 | 99 | Err(_) => { | 226 | 99 | // ignore error | 227 | 99 | } | 228 | | } | 229 | 28 | } | 230 | | | 231 | | trace!("Finished decoding image"); | 232 | | | 233 | 128 | Ok(()) | 234 | 388 | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_ycbcr_baseline Line | Count | Source | 86 | 68 | pub(crate) fn decode_mcu_ycbcr_baseline( | 87 | 68 | &mut self, pixels: &mut [u8] | 88 | 68 | ) -> Result<(), DecodeErrors> { | 89 | 68 | setup_component_params(self)?; | 90 | | | 91 | | // check dc and AC tables | 92 | 68 | self.check_tables()?; | 93 | | | 94 | | let (mut mcu_width, mut mcu_height); | 95 | | | 96 | 66 | if self.is_interleaved { | 97 | | // set upsampling functions | 98 | 0 | self.set_upsampling()?; | 99 | | | 100 | 0 | mcu_width = self.mcu_x; | 101 | 0 | mcu_height = self.mcu_y; | 102 | 66 | } else { | 103 | 66 | // For non-interleaved images( (1*1) subsampling) | 104 | 66 | // number of MCU's are the widths (+7 to account for paddings) divided bu 8. | 105 | 66 | mcu_width = ((self.info.width + 7) / 8) as usize; | 106 | 66 | mcu_height = ((self.info.height + 7) / 8) as usize; | 107 | 66 | } | 108 | 66 | if self.is_interleaved | 109 | 0 | && self.input_colorspace.num_components() > 1 | 110 | 0 | && self.options.jpeg_get_out_colorspace().num_components() == 1 | 111 | 0 | && (self.sub_sample_ratio == SampleRatios::V | 112 | 0 | || self.sub_sample_ratio == SampleRatios::HV) | 113 | 0 | { | 114 | 0 | // For a specific set of images, e.g interleaved, | 115 | 0 | // when converting from YcbCr to grayscale, we need to | 116 | 0 | // take into account mcu height since the MCU decoding needs to take | 117 | 0 | // it into account for padding purposes and the post processor | 118 | 0 | // parses two rows per mcu width. | 119 | 0 | // | 120 | 0 | // set coeff to be 2 to ensure that we increment two rows | 121 | 0 | // for every mcu processed also | 122 | 0 | mcu_height *= self.v_max; | 123 | 0 | mcu_height /= self.h_max; | 124 | 0 | self.coeff = 2; | 125 | 66 | } | 126 | | | 127 | 66 | if self.input_colorspace == ColorSpace::Luma && self.is_interleaved { | 128 | 0 | warn!("Grayscale image with down-sampled component, resetting component details"); | 129 | 0 |
| 130 | 0 | self.reset_params(); | 131 | 0 |
| 132 | 0 | mcu_width = ((self.info.width + 7) / 8) as usize; | 133 | 0 | mcu_height = ((self.info.height + 7) / 8) as usize; | 134 | 66 | } | 135 | 66 | let width = usize::from(self.info.width); | 136 | 66 | | 137 | 66 | let padded_width = calculate_padded_width(width, self.sub_sample_ratio); | 138 | 66 | | 139 | 66 | let mut stream = BitStream::new(); | 140 | 66 | let mut tmp = [0_i32; DCT_BLOCK]; | 141 | 66 | | 142 | 66 | let comp_len = self.components.len(); | 143 | | | 144 | 66 | for (pos, comp) in self.components.iter_mut().enumerate() { | 145 | | // Allocate only needed components. | 146 | | // | 147 | | // For special colorspaces i.e YCCK and CMYK, just allocate all of the needed | 148 | | // components. | 149 | 66 | if min( | 150 | 66 | self.options.jpeg_get_out_colorspace().num_components() - 1, | 151 | 66 | pos | 152 | 66 | ) == pos | 153 | 0 | || comp_len == 4 | 154 | | // Special colorspace | 155 | 66 | { | 156 | 66 | // allocate enough space to hold a whole MCU width | 157 | 66 | // this means we should take into account sampling ratios | 158 | 66 | // `*8` is because each MCU spans 8 widths. | 159 | 66 | let len = comp.width_stride * comp.vertical_sample * 8; | 160 | 66 | | 161 | 66 | comp.needed = true; | 162 | 66 | comp.raw_coeff = vec![0; len]; | 163 | 66 | } else { | 164 | 0 | comp.needed = false; | 165 | 0 | } | 166 | | } | 167 | | | 168 | 66 | let mut pixels_written = 0; | 169 | 66 | | 170 | 66 | let is_hv = usize::from(self.is_interleaved); | 171 | 66 | let upsampler_scratch_size = is_hv * self.components[0].width_stride; | 172 | 66 | let mut upsampler_scratch_space = vec![0; upsampler_scratch_size]; | 173 | | | 174 | 871 | for i in 0..mcu_height { | 175 | | // Report if we have no more bytes | 176 | | // This may generate false negatives since we over-read bytes | 177 | | // hence that why 37 is chosen(we assume if we over-read more than 37 bytes, we have a problem) | 178 | 871 | if stream.overread_by > 37 | 179 | | // favourite number :) | 180 | | { | 181 | 0 | if self.options.get_strict_mode() { | 182 | 0 | return Err(DecodeErrors::FormatStatic("Premature end of buffer")); | 183 | 0 | }; | 184 | 0 |
| 185 | 0 | error!("Premature end of buffer"); | 186 | 0 | break; | 187 | 871 | } | 188 | | | 189 | | // decode a whole MCU width, | 190 | | // this takes into account interleaved components. | 191 | 871 | let terminate = self.decode_mcu_width(mcu_width, &mut tmp, &mut stream)?; | 192 | | // if i >=7{ | 193 | | // panic!() | 194 | | // } | 195 | | // process that width up until it's impossible | 196 | | | 197 | 851 | self.post_process( | 198 | 851 | pixels, | 199 | 851 | i, | 200 | 851 | mcu_height, | 201 | 851 | width, | 202 | 851 | padded_width, | 203 | 851 | &mut pixels_written, | 204 | 851 | &mut upsampler_scratch_space | 205 | 851 | )?; | 206 | 851 | if terminate { | 207 | 0 | warn!("Got terminate signal, will not process further"); | 208 | 0 | return Ok(()); | 209 | 851 | } | 210 | | } | 211 | | // it may happen that some images don't have the whole buffer | 212 | | // so we can't panic in case of that | 213 | | // assert_eq!(pixels_written, pixels.len()); | 214 | | | 215 | | // For UHD usecases that tie two images separating them with EOI and | 216 | | // SOI markers, it may happen that we do not reach this image end of image | 217 | | // So this ensures we reach it | 218 | | // Ensure we read EOI | 219 | 46 | if !stream.seen_eoi { | 220 | 28 | let marker = get_marker(&mut self.stream, &mut stream); | 221 | 28 | match marker { | 222 | 3 | Ok(_m) => { | 223 | 3 | trace!("Found marker {:?}", _m); | 224 | 3 | } | 225 | 25 | Err(_) => { | 226 | 25 | // ignore error | 227 | 25 | } | 228 | | } | 229 | 18 | } | 230 | | | 231 | | trace!("Finished decoding image"); | 232 | | | 233 | 46 | Ok(()) | 234 | 68 | } |
<zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_ycbcr_baseline Line | Count | Source | 86 | 685 | pub(crate) fn decode_mcu_ycbcr_baseline( | 87 | 685 | &mut self, pixels: &mut [u8] | 88 | 685 | ) -> Result<(), DecodeErrors> { | 89 | 685 | setup_component_params(self)?; | 90 | | | 91 | | // check dc and AC tables | 92 | 677 | self.check_tables()?; | 93 | | | 94 | | let (mut mcu_width, mut mcu_height); | 95 | | | 96 | 674 | if self.is_interleaved { | 97 | | // set upsampling functions | 98 | 310 | self.set_upsampling()?; | 99 | | | 100 | 310 | mcu_width = self.mcu_x; | 101 | 310 | mcu_height = self.mcu_y; | 102 | 364 | } else { | 103 | 364 | // For non-interleaved images( (1*1) subsampling) | 104 | 364 | // number of MCU's are the widths (+7 to account for paddings) divided bu 8. | 105 | 364 | mcu_width = ((self.info.width + 7) / 8) as usize; | 106 | 364 | mcu_height = ((self.info.height + 7) / 8) as usize; | 107 | 364 | } | 108 | 674 | if self.is_interleaved | 109 | 310 | && self.input_colorspace.num_components() > 1 | 110 | 310 | && self.options.jpeg_get_out_colorspace().num_components() == 1 | 111 | 0 | && (self.sub_sample_ratio == SampleRatios::V | 112 | 0 | || self.sub_sample_ratio == SampleRatios::HV) | 113 | 0 | { | 114 | 0 | // For a specific set of images, e.g interleaved, | 115 | 0 | // when converting from YcbCr to grayscale, we need to | 116 | 0 | // take into account mcu height since the MCU decoding needs to take | 117 | 0 | // it into account for padding purposes and the post processor | 118 | 0 | // parses two rows per mcu width. | 119 | 0 | // | 120 | 0 | // set coeff to be 2 to ensure that we increment two rows | 121 | 0 | // for every mcu processed also | 122 | 0 | mcu_height *= self.v_max; | 123 | 0 | mcu_height /= self.h_max; | 124 | 0 | self.coeff = 2; | 125 | 674 | } | 126 | | | 127 | 674 | if self.input_colorspace == ColorSpace::Luma && self.is_interleaved { | 128 | 0 | warn!("Grayscale image with down-sampled component, resetting component details"); | 129 | 0 |
| 130 | 0 | self.reset_params(); | 131 | 0 |
| 132 | 0 | mcu_width = ((self.info.width + 7) / 8) as usize; | 133 | 0 | mcu_height = ((self.info.height + 7) / 8) as usize; | 134 | 674 | } | 135 | 674 | let width = usize::from(self.info.width); | 136 | 674 | | 137 | 674 | let padded_width = calculate_padded_width(width, self.sub_sample_ratio); | 138 | 674 | | 139 | 674 | let mut stream = BitStream::new(); | 140 | 674 | let mut tmp = [0_i32; DCT_BLOCK]; | 141 | 674 | | 142 | 674 | let comp_len = self.components.len(); | 143 | | | 144 | 2.05k | for (pos, comp) in self.components.iter_mut().enumerate() { | 145 | | // Allocate only needed components. | 146 | | // | 147 | | // For special colorspaces i.e YCCK and CMYK, just allocate all of the needed | 148 | | // components. | 149 | 2.05k | if min( | 150 | 2.05k | self.options.jpeg_get_out_colorspace().num_components() - 1, | 151 | 2.05k | pos | 152 | 2.05k | ) == pos | 153 | 278 | || comp_len == 4 | 154 | | // Special colorspace | 155 | 2.05k | { | 156 | 2.05k | // allocate enough space to hold a whole MCU width | 157 | 2.05k | // this means we should take into account sampling ratios | 158 | 2.05k | // `*8` is because each MCU spans 8 widths. | 159 | 2.05k | let len = comp.width_stride * comp.vertical_sample * 8; | 160 | 2.05k | | 161 | 2.05k | comp.needed = true; | 162 | 2.05k | comp.raw_coeff = vec![0; len]; | 163 | 2.05k | } else { | 164 | 0 | comp.needed = false; | 165 | 0 | } | 166 | | } | 167 | | | 168 | 674 | let mut pixels_written = 0; | 169 | 674 | | 170 | 674 | let is_hv = usize::from(self.is_interleaved); | 171 | 674 | let upsampler_scratch_size = is_hv * self.components[0].width_stride; | 172 | 674 | let mut upsampler_scratch_space = vec![0; upsampler_scratch_size]; | 173 | | | 174 | 49.5k | for i in 0..mcu_height { | 175 | | // Report if we have no more bytes | 176 | | // This may generate false negatives since we over-read bytes | 177 | | // hence that why 37 is chosen(we assume if we over-read more than 37 bytes, we have a problem) | 178 | 49.5k | if stream.overread_by > 37 | 179 | | // favourite number :) | 180 | | { | 181 | 0 | if self.options.get_strict_mode() { | 182 | 0 | return Err(DecodeErrors::FormatStatic("Premature end of buffer")); | 183 | 0 | }; | 184 | 0 |
| 185 | 0 | error!("Premature end of buffer"); | 186 | 0 | break; | 187 | 49.5k | } | 188 | | | 189 | | // decode a whole MCU width, | 190 | | // this takes into account interleaved components. | 191 | 49.5k | let terminate = self.decode_mcu_width(mcu_width, &mut tmp, &mut stream)?; | 192 | | // if i >=7{ | 193 | | // panic!() | 194 | | // } | 195 | | // process that width up until it's impossible | 196 | | | 197 | 49.2k | self.post_process( | 198 | 49.2k | pixels, | 199 | 49.2k | i, | 200 | 49.2k | mcu_height, | 201 | 49.2k | width, | 202 | 49.2k | padded_width, | 203 | 49.2k | &mut pixels_written, | 204 | 49.2k | &mut upsampler_scratch_space | 205 | 49.2k | )?; | 206 | 49.2k | if terminate { | 207 | 16 | warn!("Got terminate signal, will not process further"); | 208 | 16 | return Ok(()); | 209 | 49.2k | } | 210 | | } | 211 | | // it may happen that some images don't have the whole buffer | 212 | | // so we can't panic in case of that | 213 | | // assert_eq!(pixels_written, pixels.len()); | 214 | | | 215 | | // For UHD usecases that tie two images separating them with EOI and | 216 | | // SOI markers, it may happen that we do not reach this image end of image | 217 | | // So this ensures we reach it | 218 | | // Ensure we read EOI | 219 | 365 | if !stream.seen_eoi { | 220 | 330 | let marker = get_marker(&mut self.stream, &mut stream); | 221 | 330 | match marker { | 222 | 31 | Ok(_m) => { | 223 | 31 | trace!("Found marker {:?}", _m); | 224 | 31 | } | 225 | 299 | Err(_) => { | 226 | 299 | // ignore error | 227 | 299 | } | 228 | | } | 229 | 35 | } | 230 | | | 231 | | trace!("Finished decoding image"); | 232 | | | 233 | 365 | Ok(()) | 234 | 685 | } |
|
235 | 191k | fn decode_mcu_width( |
236 | 191k | &mut self, mcu_width: usize, tmp: &mut [i32; 64], stream: &mut BitStream |
237 | 191k | ) -> Result<bool, DecodeErrors> { |
238 | 66.6M | for j in 0..mcu_width { |
239 | | // iterate over components |
240 | 147M | for component in &mut self.components { |
241 | 80.9M | let dc_table = self.dc_huffman_tables[component.dc_huff_table % MAX_COMPONENTS] |
242 | 80.9M | .as_ref() |
243 | 80.9M | .unwrap(); |
244 | 80.9M | |
245 | 80.9M | let ac_table = self.ac_huffman_tables[component.ac_huff_table % MAX_COMPONENTS] |
246 | 80.9M | .as_ref() |
247 | 80.9M | .unwrap(); |
248 | 80.9M | |
249 | 80.9M | let qt_table = &component.quantization_table; |
250 | 80.9M | let channel = &mut component.raw_coeff; |
251 | | |
252 | | // If image is interleaved iterate over scan components, |
253 | | // otherwise if it-s non-interleaved, these routines iterate in |
254 | | // trivial scanline order(Y,Cb,Cr) |
255 | 85.9M | for v_samp in 0..component.vertical_sample { |
256 | 92.9M | for h_samp in 0..component.horizontal_sample { |
257 | | // Fill the array with zeroes, decode_mcu_block expects |
258 | | // a zero based array. |
259 | 92.9M | tmp.fill(0); |
260 | 92.9M | |
261 | 92.9M | stream.decode_mcu_block( |
262 | 92.9M | &mut self.stream, |
263 | 92.9M | dc_table, |
264 | 92.9M | ac_table, |
265 | 92.9M | qt_table, |
266 | 92.9M | tmp, |
267 | 92.9M | &mut component.dc_pred |
268 | 92.9M | )?; |
269 | | |
270 | 92.9M | if component.needed { |
271 | 92.9M | let idct_position = { |
272 | 92.9M | // derived from stb and rewritten for my tastes |
273 | 92.9M | let c2 = v_samp * 8; |
274 | 92.9M | let c3 = ((j * component.horizontal_sample) + h_samp) * 8; |
275 | 92.9M | |
276 | 92.9M | component.width_stride * c2 + c3 |
277 | 92.9M | }; |
278 | 92.9M | |
279 | 92.9M | let idct_pos = channel.get_mut(idct_position..).unwrap(); |
280 | 92.9M | // call idct. |
281 | 92.9M | (self.idct_func)(tmp, idct_pos, component.width_stride); |
282 | 92.9M | } |
283 | | } |
284 | | } |
285 | | } |
286 | 66.6M | self.todo = self.todo.wrapping_sub(1); |
287 | 66.6M | |
288 | 66.6M | if self.todo == 0 { |
289 | 86.7k | self.handle_rst_main(stream)?; |
290 | 66.5M | } |
291 | | |
292 | | // After all interleaved components, that's an MCU |
293 | | // handle stream markers |
294 | | // |
295 | | // In some corrupt images, it may occur that header markers occur in the stream. |
296 | | // The spec EXPLICITLY FORBIDS this, specifically, in |
297 | | // routine F.2.2.5 it says |
298 | | // `The only valid marker which may occur within the Huffman coded data is the RSTm marker.` |
299 | | // |
300 | | // But libjpeg-turbo allows it because of some weird reason. so I'll also |
301 | | // allow it because of some weird reason. |
302 | 66.6M | if let Some(m) = stream.marker { |
303 | 7.58M | if m == Marker::EOI { |
304 | 201 | // acknowledge and ignore EOI marker. |
305 | 201 | stream.marker.take(); |
306 | 201 | trace!("Found EOI marker"); |
307 | 201 | // Google Introduced the Ultra-HD image format which is basically |
308 | 201 | // stitching two images into one container. |
309 | 201 | // They basically separate two images via a EOI and SOI marker |
310 | 201 | // so let's just ensure if we ever see EOI, we never read past that |
311 | 201 | // ever. |
312 | 201 | // https://github.com/google/libultrahdr |
313 | 201 | stream.seen_eoi = true; |
314 | 7.58M | } else if let Marker::RST(_) = m { |
315 | | //debug_assert_eq!(self.todo, 0); |
316 | 7.58M | if self.todo == 0 { |
317 | 0 | self.handle_rst(stream)?; |
318 | 7.58M | } |
319 | | } else { |
320 | 825 | if self.options.get_strict_mode() { |
321 | 101 | return Err(DecodeErrors::Format(format!( |
322 | 101 | "Marker {m:?} found where not expected" |
323 | 101 | ))); |
324 | 724 | } |
325 | 724 | error!( |
326 | 724 | "Marker `{:?}` Found within Huffman Stream, possibly corrupt jpeg", |
327 | 724 | m |
328 | 724 | ); |
329 | 724 | self.parse_marker_inner(m)?; |
330 | 516 | if m == Marker::SOS { |
331 | 25 | return Ok(true); |
332 | 491 | } |
333 | | } |
334 | 59.0M | } |
335 | | } |
336 | 190k | Ok(false) |
337 | 191k | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode_mcu_width <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width Line | Count | Source | 235 | 95.0k | fn decode_mcu_width( | 236 | 95.0k | &mut self, mcu_width: usize, tmp: &mut [i32; 64], stream: &mut BitStream | 237 | 95.0k | ) -> Result<bool, DecodeErrors> { | 238 | 3.48M | for j in 0..mcu_width { | 239 | | // iterate over components | 240 | 6.97M | for component in &mut self.components { | 241 | 3.48M | let dc_table = self.dc_huffman_tables[component.dc_huff_table % MAX_COMPONENTS] | 242 | 3.48M | .as_ref() | 243 | 3.48M | .unwrap(); | 244 | 3.48M | | 245 | 3.48M | let ac_table = self.ac_huffman_tables[component.ac_huff_table % MAX_COMPONENTS] | 246 | 3.48M | .as_ref() | 247 | 3.48M | .unwrap(); | 248 | 3.48M | | 249 | 3.48M | let qt_table = &component.quantization_table; | 250 | 3.48M | let channel = &mut component.raw_coeff; | 251 | | | 252 | | // If image is interleaved iterate over scan components, | 253 | | // otherwise if it-s non-interleaved, these routines iterate in | 254 | | // trivial scanline order(Y,Cb,Cr) | 255 | 3.48M | for v_samp in 0..component.vertical_sample { | 256 | 3.48M | for h_samp in 0..component.horizontal_sample { | 257 | | // Fill the array with zeroes, decode_mcu_block expects | 258 | | // a zero based array. | 259 | 3.48M | tmp.fill(0); | 260 | 3.48M | | 261 | 3.48M | stream.decode_mcu_block( | 262 | 3.48M | &mut self.stream, | 263 | 3.48M | dc_table, | 264 | 3.48M | ac_table, | 265 | 3.48M | qt_table, | 266 | 3.48M | tmp, | 267 | 3.48M | &mut component.dc_pred | 268 | 3.48M | )?; | 269 | | | 270 | 3.48M | if component.needed { | 271 | 3.48M | let idct_position = { | 272 | 3.48M | // derived from stb and rewritten for my tastes | 273 | 3.48M | let c2 = v_samp * 8; | 274 | 3.48M | let c3 = ((j * component.horizontal_sample) + h_samp) * 8; | 275 | 3.48M | | 276 | 3.48M | component.width_stride * c2 + c3 | 277 | 3.48M | }; | 278 | 3.48M | | 279 | 3.48M | let idct_pos = channel.get_mut(idct_position..).unwrap(); | 280 | 3.48M | // call idct. | 281 | 3.48M | (self.idct_func)(tmp, idct_pos, component.width_stride); | 282 | 3.48M | } | 283 | | } | 284 | | } | 285 | | } | 286 | 3.48M | self.todo = self.todo.wrapping_sub(1); | 287 | 3.48M | | 288 | 3.48M | if self.todo == 0 { | 289 | 0 | self.handle_rst_main(stream)?; | 290 | 3.48M | } | 291 | | | 292 | | // After all interleaved components, that's an MCU | 293 | | // handle stream markers | 294 | | // | 295 | | // In some corrupt images, it may occur that header markers occur in the stream. | 296 | | // The spec EXPLICITLY FORBIDS this, specifically, in | 297 | | // routine F.2.2.5 it says | 298 | | // `The only valid marker which may occur within the Huffman coded data is the RSTm marker.` | 299 | | // | 300 | | // But libjpeg-turbo allows it because of some weird reason. so I'll also | 301 | | // allow it because of some weird reason. | 302 | 3.48M | if let Some(m) = stream.marker { | 303 | 242k | if m == Marker::EOI { | 304 | 120 | // acknowledge and ignore EOI marker. | 305 | 120 | stream.marker.take(); | 306 | 120 | trace!("Found EOI marker"); | 307 | 120 | // Google Introduced the Ultra-HD image format which is basically | 308 | 120 | // stitching two images into one container. | 309 | 120 | // They basically separate two images via a EOI and SOI marker | 310 | 120 | // so let's just ensure if we ever see EOI, we never read past that | 311 | 120 | // ever. | 312 | 120 | // https://github.com/google/libultrahdr | 313 | 120 | stream.seen_eoi = true; | 314 | 242k | } else if let Marker::RST(_) = m { | 315 | | //debug_assert_eq!(self.todo, 0); | 316 | 242k | if self.todo == 0 { | 317 | 0 | self.handle_rst(stream)?; | 318 | 242k | } | 319 | | } else { | 320 | 89 | if self.options.get_strict_mode() { | 321 | 89 | return Err(DecodeErrors::Format(format!( | 322 | 89 | "Marker {m:?} found where not expected" | 323 | 89 | ))); | 324 | 0 | } | 325 | 0 | error!( | 326 | 0 | "Marker `{:?}` Found within Huffman Stream, possibly corrupt jpeg", | 327 | 0 | m | 328 | 0 | ); | 329 | 0 | self.parse_marker_inner(m)?; | 330 | 0 | if m == Marker::SOS { | 331 | 0 | return Ok(true); | 332 | 0 | } | 333 | | } | 334 | 3.24M | } | 335 | | } | 336 | 94.6k | Ok(false) | 337 | 95.0k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width Line | Count | Source | 235 | 45.5k | fn decode_mcu_width( | 236 | 45.5k | &mut self, mcu_width: usize, tmp: &mut [i32; 64], stream: &mut BitStream | 237 | 45.5k | ) -> Result<bool, DecodeErrors> { | 238 | 58.9M | for j in 0..mcu_width { | 239 | | // iterate over components | 240 | 121M | for component in &mut self.components { | 241 | 63.0M | let dc_table = self.dc_huffman_tables[component.dc_huff_table % MAX_COMPONENTS] | 242 | 63.0M | .as_ref() | 243 | 63.0M | .unwrap(); | 244 | 63.0M | | 245 | 63.0M | let ac_table = self.ac_huffman_tables[component.ac_huff_table % MAX_COMPONENTS] | 246 | 63.0M | .as_ref() | 247 | 63.0M | .unwrap(); | 248 | 63.0M | | 249 | 63.0M | let qt_table = &component.quantization_table; | 250 | 63.0M | let channel = &mut component.raw_coeff; | 251 | | | 252 | | // If image is interleaved iterate over scan components, | 253 | | // otherwise if it-s non-interleaved, these routines iterate in | 254 | | // trivial scanline order(Y,Cb,Cr) | 255 | 66.6M | for v_samp in 0..component.vertical_sample { | 256 | 69.3M | for h_samp in 0..component.horizontal_sample { | 257 | | // Fill the array with zeroes, decode_mcu_block expects | 258 | | // a zero based array. | 259 | 69.3M | tmp.fill(0); | 260 | 69.3M | | 261 | 69.3M | stream.decode_mcu_block( | 262 | 69.3M | &mut self.stream, | 263 | 69.3M | dc_table, | 264 | 69.3M | ac_table, | 265 | 69.3M | qt_table, | 266 | 69.3M | tmp, | 267 | 69.3M | &mut component.dc_pred | 268 | 69.3M | )?; | 269 | | | 270 | 69.3M | if component.needed { | 271 | 69.3M | let idct_position = { | 272 | 69.3M | // derived from stb and rewritten for my tastes | 273 | 69.3M | let c2 = v_samp * 8; | 274 | 69.3M | let c3 = ((j * component.horizontal_sample) + h_samp) * 8; | 275 | 69.3M | | 276 | 69.3M | component.width_stride * c2 + c3 | 277 | 69.3M | }; | 278 | 69.3M | | 279 | 69.3M | let idct_pos = channel.get_mut(idct_position..).unwrap(); | 280 | 69.3M | // call idct. | 281 | 69.3M | (self.idct_func)(tmp, idct_pos, component.width_stride); | 282 | 69.3M | } | 283 | | } | 284 | | } | 285 | | } | 286 | 58.9M | self.todo = self.todo.wrapping_sub(1); | 287 | 58.9M | | 288 | 58.9M | if self.todo == 0 { | 289 | 63.3k | self.handle_rst_main(stream)?; | 290 | 58.8M | } | 291 | | | 292 | | // After all interleaved components, that's an MCU | 293 | | // handle stream markers | 294 | | // | 295 | | // In some corrupt images, it may occur that header markers occur in the stream. | 296 | | // The spec EXPLICITLY FORBIDS this, specifically, in | 297 | | // routine F.2.2.5 it says | 298 | | // `The only valid marker which may occur within the Huffman coded data is the RSTm marker.` | 299 | | // | 300 | | // But libjpeg-turbo allows it because of some weird reason. so I'll also | 301 | | // allow it because of some weird reason. | 302 | 58.9M | if let Some(m) = stream.marker { | 303 | 7.16M | if m == Marker::EOI { | 304 | 28 | // acknowledge and ignore EOI marker. | 305 | 28 | stream.marker.take(); | 306 | 28 | trace!("Found EOI marker"); | 307 | 28 | // Google Introduced the Ultra-HD image format which is basically | 308 | 28 | // stitching two images into one container. | 309 | 28 | // They basically separate two images via a EOI and SOI marker | 310 | 28 | // so let's just ensure if we ever see EOI, we never read past that | 311 | 28 | // ever. | 312 | 28 | // https://github.com/google/libultrahdr | 313 | 28 | stream.seen_eoi = true; | 314 | 7.16M | } else if let Marker::RST(_) = m { | 315 | | //debug_assert_eq!(self.todo, 0); | 316 | 7.16M | if self.todo == 0 { | 317 | 0 | self.handle_rst(stream)?; | 318 | 7.16M | } | 319 | | } else { | 320 | 428 | if self.options.get_strict_mode() { | 321 | 0 | return Err(DecodeErrors::Format(format!( | 322 | 0 | "Marker {m:?} found where not expected" | 323 | 0 | ))); | 324 | 428 | } | 325 | 428 | error!( | 326 | 428 | "Marker `{:?}` Found within Huffman Stream, possibly corrupt jpeg", | 327 | 428 | m | 328 | 428 | ); | 329 | 428 | self.parse_marker_inner(m)?; | 330 | 322 | if m == Marker::SOS { | 331 | 9 | return Ok(true); | 332 | 313 | } | 333 | | } | 334 | 51.7M | } | 335 | | } | 336 | 45.3k | Ok(false) | 337 | 45.5k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_mcu_width Line | Count | Source | 235 | 871 | fn decode_mcu_width( | 236 | 871 | &mut self, mcu_width: usize, tmp: &mut [i32; 64], stream: &mut BitStream | 237 | 871 | ) -> Result<bool, DecodeErrors> { | 238 | 22.0k | for j in 0..mcu_width { | 239 | | // iterate over components | 240 | 44.0k | for component in &mut self.components { | 241 | 22.0k | let dc_table = self.dc_huffman_tables[component.dc_huff_table % MAX_COMPONENTS] | 242 | 22.0k | .as_ref() | 243 | 22.0k | .unwrap(); | 244 | 22.0k | | 245 | 22.0k | let ac_table = self.ac_huffman_tables[component.ac_huff_table % MAX_COMPONENTS] | 246 | 22.0k | .as_ref() | 247 | 22.0k | .unwrap(); | 248 | 22.0k | | 249 | 22.0k | let qt_table = &component.quantization_table; | 250 | 22.0k | let channel = &mut component.raw_coeff; | 251 | | | 252 | | // If image is interleaved iterate over scan components, | 253 | | // otherwise if it-s non-interleaved, these routines iterate in | 254 | | // trivial scanline order(Y,Cb,Cr) | 255 | 22.0k | for v_samp in 0..component.vertical_sample { | 256 | 22.0k | for h_samp in 0..component.horizontal_sample { | 257 | | // Fill the array with zeroes, decode_mcu_block expects | 258 | | // a zero based array. | 259 | 22.0k | tmp.fill(0); | 260 | 22.0k | | 261 | 22.0k | stream.decode_mcu_block( | 262 | 22.0k | &mut self.stream, | 263 | 22.0k | dc_table, | 264 | 22.0k | ac_table, | 265 | 22.0k | qt_table, | 266 | 22.0k | tmp, | 267 | 22.0k | &mut component.dc_pred | 268 | 22.0k | )?; | 269 | | | 270 | 22.0k | if component.needed { | 271 | 22.0k | let idct_position = { | 272 | 22.0k | // derived from stb and rewritten for my tastes | 273 | 22.0k | let c2 = v_samp * 8; | 274 | 22.0k | let c3 = ((j * component.horizontal_sample) + h_samp) * 8; | 275 | 22.0k | | 276 | 22.0k | component.width_stride * c2 + c3 | 277 | 22.0k | }; | 278 | 22.0k | | 279 | 22.0k | let idct_pos = channel.get_mut(idct_position..).unwrap(); | 280 | 22.0k | // call idct. | 281 | 22.0k | (self.idct_func)(tmp, idct_pos, component.width_stride); | 282 | 22.0k | } | 283 | | } | 284 | | } | 285 | | } | 286 | 22.0k | self.todo = self.todo.wrapping_sub(1); | 287 | 22.0k | | 288 | 22.0k | if self.todo == 0 { | 289 | 0 | self.handle_rst_main(stream)?; | 290 | 22.0k | } | 291 | | | 292 | | // After all interleaved components, that's an MCU | 293 | | // handle stream markers | 294 | | // | 295 | | // In some corrupt images, it may occur that header markers occur in the stream. | 296 | | // The spec EXPLICITLY FORBIDS this, specifically, in | 297 | | // routine F.2.2.5 it says | 298 | | // `The only valid marker which may occur within the Huffman coded data is the RSTm marker.` | 299 | | // | 300 | | // But libjpeg-turbo allows it because of some weird reason. so I'll also | 301 | | // allow it because of some weird reason. | 302 | 22.0k | if let Some(m) = stream.marker { | 303 | 30 | if m == Marker::EOI { | 304 | 18 | // acknowledge and ignore EOI marker. | 305 | 18 | stream.marker.take(); | 306 | 18 | trace!("Found EOI marker"); | 307 | 18 | // Google Introduced the Ultra-HD image format which is basically | 308 | 18 | // stitching two images into one container. | 309 | 18 | // They basically separate two images via a EOI and SOI marker | 310 | 18 | // so let's just ensure if we ever see EOI, we never read past that | 311 | 18 | // ever. | 312 | 18 | // https://github.com/google/libultrahdr | 313 | 18 | stream.seen_eoi = true; | 314 | 18 | } else if let Marker::RST(_) = m { | 315 | | //debug_assert_eq!(self.todo, 0); | 316 | 0 | if self.todo == 0 { | 317 | 0 | self.handle_rst(stream)?; | 318 | 0 | } | 319 | | } else { | 320 | 12 | if self.options.get_strict_mode() { | 321 | 12 | return Err(DecodeErrors::Format(format!( | 322 | 12 | "Marker {m:?} found where not expected" | 323 | 12 | ))); | 324 | 0 | } | 325 | 0 | error!( | 326 | 0 | "Marker `{:?}` Found within Huffman Stream, possibly corrupt jpeg", | 327 | 0 | m | 328 | 0 | ); | 329 | 0 | self.parse_marker_inner(m)?; | 330 | 0 | if m == Marker::SOS { | 331 | 0 | return Ok(true); | 332 | 0 | } | 333 | | } | 334 | 22.0k | } | 335 | | } | 336 | 851 | Ok(false) | 337 | 871 | } |
<zune_jpeg::decoder::JpegDecoder<&[u8]>>::decode_mcu_width Line | Count | Source | 235 | 49.5k | fn decode_mcu_width( | 236 | 49.5k | &mut self, mcu_width: usize, tmp: &mut [i32; 64], stream: &mut BitStream | 237 | 49.5k | ) -> Result<bool, DecodeErrors> { | 238 | 4.21M | for j in 0..mcu_width { | 239 | | // iterate over components | 240 | 18.6M | for component in &mut self.components { | 241 | 14.4M | let dc_table = self.dc_huffman_tables[component.dc_huff_table % MAX_COMPONENTS] | 242 | 14.4M | .as_ref() | 243 | 14.4M | .unwrap(); | 244 | 14.4M | | 245 | 14.4M | let ac_table = self.ac_huffman_tables[component.ac_huff_table % MAX_COMPONENTS] | 246 | 14.4M | .as_ref() | 247 | 14.4M | .unwrap(); | 248 | 14.4M | | 249 | 14.4M | let qt_table = &component.quantization_table; | 250 | 14.4M | let channel = &mut component.raw_coeff; | 251 | | | 252 | | // If image is interleaved iterate over scan components, | 253 | | // otherwise if it-s non-interleaved, these routines iterate in | 254 | | // trivial scanline order(Y,Cb,Cr) | 255 | 15.8M | for v_samp in 0..component.vertical_sample { | 256 | 20.0M | for h_samp in 0..component.horizontal_sample { | 257 | | // Fill the array with zeroes, decode_mcu_block expects | 258 | | // a zero based array. | 259 | 20.0M | tmp.fill(0); | 260 | 20.0M | | 261 | 20.0M | stream.decode_mcu_block( | 262 | 20.0M | &mut self.stream, | 263 | 20.0M | dc_table, | 264 | 20.0M | ac_table, | 265 | 20.0M | qt_table, | 266 | 20.0M | tmp, | 267 | 20.0M | &mut component.dc_pred | 268 | 20.0M | )?; | 269 | | | 270 | 20.0M | if component.needed { | 271 | 20.0M | let idct_position = { | 272 | 20.0M | // derived from stb and rewritten for my tastes | 273 | 20.0M | let c2 = v_samp * 8; | 274 | 20.0M | let c3 = ((j * component.horizontal_sample) + h_samp) * 8; | 275 | 20.0M | | 276 | 20.0M | component.width_stride * c2 + c3 | 277 | 20.0M | }; | 278 | 20.0M | | 279 | 20.0M | let idct_pos = channel.get_mut(idct_position..).unwrap(); | 280 | 20.0M | // call idct. | 281 | 20.0M | (self.idct_func)(tmp, idct_pos, component.width_stride); | 282 | 20.0M | } | 283 | | } | 284 | | } | 285 | | } | 286 | 4.21M | self.todo = self.todo.wrapping_sub(1); | 287 | 4.21M | | 288 | 4.21M | if self.todo == 0 { | 289 | 23.3k | self.handle_rst_main(stream)?; | 290 | 4.19M | } | 291 | | | 292 | | // After all interleaved components, that's an MCU | 293 | | // handle stream markers | 294 | | // | 295 | | // In some corrupt images, it may occur that header markers occur in the stream. | 296 | | // The spec EXPLICITLY FORBIDS this, specifically, in | 297 | | // routine F.2.2.5 it says | 298 | | // `The only valid marker which may occur within the Huffman coded data is the RSTm marker.` | 299 | | // | 300 | | // But libjpeg-turbo allows it because of some weird reason. so I'll also | 301 | | // allow it because of some weird reason. | 302 | 4.21M | if let Some(m) = stream.marker { | 303 | 181k | if m == Marker::EOI { | 304 | 35 | // acknowledge and ignore EOI marker. | 305 | 35 | stream.marker.take(); | 306 | 35 | trace!("Found EOI marker"); | 307 | 35 | // Google Introduced the Ultra-HD image format which is basically | 308 | 35 | // stitching two images into one container. | 309 | 35 | // They basically separate two images via a EOI and SOI marker | 310 | 35 | // so let's just ensure if we ever see EOI, we never read past that | 311 | 35 | // ever. | 312 | 35 | // https://github.com/google/libultrahdr | 313 | 35 | stream.seen_eoi = true; | 314 | 181k | } else if let Marker::RST(_) = m { | 315 | | //debug_assert_eq!(self.todo, 0); | 316 | 180k | if self.todo == 0 { | 317 | 0 | self.handle_rst(stream)?; | 318 | 180k | } | 319 | | } else { | 320 | 296 | if self.options.get_strict_mode() { | 321 | 0 | return Err(DecodeErrors::Format(format!( | 322 | 0 | "Marker {m:?} found where not expected" | 323 | 0 | ))); | 324 | 296 | } | 325 | 296 | error!( | 326 | 296 | "Marker `{:?}` Found within Huffman Stream, possibly corrupt jpeg", | 327 | 296 | m | 328 | 296 | ); | 329 | 296 | self.parse_marker_inner(m)?; | 330 | 194 | if m == Marker::SOS { | 331 | 16 | return Ok(true); | 332 | 178 | } | 333 | | } | 334 | 4.03M | } | 335 | | } | 336 | 49.2k | Ok(false) | 337 | 49.5k | } |
|
338 | | // handle RST markers. |
339 | | // No-op if not using restarts |
340 | | // this routine is shared with mcu_prog |
341 | | #[cold] |
342 | 95.7k | pub(crate) fn handle_rst(&mut self, stream: &mut BitStream) -> Result<(), DecodeErrors> { |
343 | 95.7k | self.todo = self.restart_interval; |
344 | | |
345 | 95.7k | if let Some(marker) = stream.marker { |
346 | | // Found a marker |
347 | | // Read stream and see what marker is stored there |
348 | 60.2k | match marker { |
349 | 59.5k | Marker::RST(_) => { |
350 | 59.5k | // reset stream |
351 | 59.5k | stream.reset(); |
352 | 59.5k | // Initialize dc predictions to zero for all components |
353 | 177k | self.components.iter_mut().for_each(|x| x.dc_pred = 0); Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Line | Count | Source | 353 | 119k | self.components.iter_mut().for_each(|x| x.dc_pred = 0); |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst::{closure#0} <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst::{closure#0} Line | Count | Source | 353 | 58.1k | self.components.iter_mut().for_each(|x| x.dc_pred = 0); |
|
354 | 59.5k | // Start iterating again. from position. |
355 | 59.5k | } |
356 | 660 | Marker::EOI => { |
357 | 660 | // silent pass |
358 | 660 | } |
359 | | _ => { |
360 | 23 | return Err(DecodeErrors::MCUError(format!( |
361 | 23 | "Marker {marker:?} found in bitstream, possibly corrupt jpeg" |
362 | 23 | ))); |
363 | | } |
364 | | } |
365 | 35.5k | } |
366 | 95.7k | Ok(()) |
367 | 95.7k | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::handle_rst <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst Line | Count | Source | 342 | 5 | pub(crate) fn handle_rst(&mut self, stream: &mut BitStream) -> Result<(), DecodeErrors> { | 343 | 5 | self.todo = self.restart_interval; | 344 | | | 345 | 5 | if let Some(marker) = stream.marker { | 346 | | // Found a marker | 347 | | // Read stream and see what marker is stored there | 348 | 0 | match marker { | 349 | 0 | Marker::RST(_) => { | 350 | 0 | // reset stream | 351 | 0 | stream.reset(); | 352 | 0 | // Initialize dc predictions to zero for all components | 353 | 0 | self.components.iter_mut().for_each(|x| x.dc_pred = 0); | 354 | 0 | // Start iterating again. from position. | 355 | 0 | } | 356 | 0 | Marker::EOI => { | 357 | 0 | // silent pass | 358 | 0 | } | 359 | | _ => { | 360 | 0 | return Err(DecodeErrors::MCUError(format!( | 361 | 0 | "Marker {marker:?} found in bitstream, possibly corrupt jpeg" | 362 | 0 | ))); | 363 | | } | 364 | | } | 365 | 5 | } | 366 | 5 | Ok(()) | 367 | 5 | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Line | Count | Source | 342 | 65.0k | pub(crate) fn handle_rst(&mut self, stream: &mut BitStream) -> Result<(), DecodeErrors> { | 343 | 65.0k | self.todo = self.restart_interval; | 344 | | | 345 | 65.0k | if let Some(marker) = stream.marker { | 346 | | // Found a marker | 347 | | // Read stream and see what marker is stored there | 348 | 40.6k | match marker { | 349 | 39.9k | Marker::RST(_) => { | 350 | 39.9k | // reset stream | 351 | 39.9k | stream.reset(); | 352 | 39.9k | // Initialize dc predictions to zero for all components | 353 | 39.9k | self.components.iter_mut().for_each(|x| x.dc_pred = 0); | 354 | 39.9k | // Start iterating again. from position. | 355 | 39.9k | } | 356 | 647 | Marker::EOI => { | 357 | 647 | // silent pass | 358 | 647 | } | 359 | | _ => { | 360 | 10 | return Err(DecodeErrors::MCUError(format!( | 361 | 10 | "Marker {marker:?} found in bitstream, possibly corrupt jpeg" | 362 | 10 | ))); | 363 | | } | 364 | | } | 365 | 24.4k | } | 366 | 65.0k | Ok(()) | 367 | 65.0k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::handle_rst <zune_jpeg::decoder::JpegDecoder<&[u8]>>::handle_rst Line | Count | Source | 342 | 30.7k | pub(crate) fn handle_rst(&mut self, stream: &mut BitStream) -> Result<(), DecodeErrors> { | 343 | 30.7k | self.todo = self.restart_interval; | 344 | | | 345 | 30.7k | if let Some(marker) = stream.marker { | 346 | | // Found a marker | 347 | | // Read stream and see what marker is stored there | 348 | 19.5k | match marker { | 349 | 19.5k | Marker::RST(_) => { | 350 | 19.5k | // reset stream | 351 | 19.5k | stream.reset(); | 352 | 19.5k | // Initialize dc predictions to zero for all components | 353 | 19.5k | self.components.iter_mut().for_each(|x| x.dc_pred = 0); | 354 | 19.5k | // Start iterating again. from position. | 355 | 19.5k | } | 356 | 13 | Marker::EOI => { | 357 | 13 | // silent pass | 358 | 13 | } | 359 | | _ => { | 360 | 13 | return Err(DecodeErrors::MCUError(format!( | 361 | 13 | "Marker {marker:?} found in bitstream, possibly corrupt jpeg" | 362 | 13 | ))); | 363 | | } | 364 | | } | 365 | 11.1k | } | 366 | 30.6k | Ok(()) | 367 | 30.7k | } |
|
368 | | #[allow(clippy::too_many_lines, clippy::too_many_arguments)] |
369 | 489k | pub(crate) fn post_process( |
370 | 489k | &mut self, pixels: &mut [u8], i: usize, mcu_height: usize, width: usize, |
371 | 489k | padded_width: usize, pixels_written: &mut usize, upsampler_scratch_space: &mut [i16] |
372 | 489k | ) -> Result<(), DecodeErrors> { |
373 | 489k | let out_colorspace_components = self.options.jpeg_get_out_colorspace().num_components(); |
374 | 489k | |
375 | 489k | let mut px = *pixels_written; |
376 | 489k | // indicates whether image is vertically up-sampled |
377 | 489k | let is_vertically_sampled = self |
378 | 489k | .components |
379 | 489k | .iter() |
380 | 726k | .any(|c| c.sample_ratio == SampleRatios::HV || c.sample_ratio == SampleRatios::V); Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::post_process::{closure#0} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} Line | Count | Source | 380 | 94.6k | .any(|c| c.sample_ratio == SampleRatios::HV || c.sample_ratio == SampleRatios::V); |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} Line | Count | Source | 380 | 180k | .any(|c| c.sample_ratio == SampleRatios::HV || c.sample_ratio == SampleRatios::V); |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#0} Line | Count | Source | 380 | 851 | .any(|c| c.sample_ratio == SampleRatios::HV || c.sample_ratio == SampleRatios::V); |
<zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#0} Line | Count | Source | 380 | 450k | .any(|c| c.sample_ratio == SampleRatios::HV || c.sample_ratio == SampleRatios::V); |
|
381 | 489k | |
382 | 489k | let mut comp_len = self.components.len(); |
383 | 489k | |
384 | 489k | // If we are moving from YCbCr-> Luma, we do not allocate storage for other components, so we |
385 | 489k | // will panic when we are trying to read samples, so for that case, |
386 | 489k | // hardcode it so that we don't panic when doing |
387 | 489k | // *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width] |
388 | 489k | if out_colorspace_components < comp_len && self.options.jpeg_get_out_colorspace() == Luma { |
389 | 0 | comp_len = out_colorspace_components; |
390 | 489k | } |
391 | 489k | let mut color_conv_function = |
392 | 542k | |num_iters: usize, samples: [&[i16]; 4]| -> Result<(), DecodeErrors> { |
393 | 4.36M | for (pos, output) in pixels[px..] |
394 | 542k | .chunks_exact_mut(width * out_colorspace_components) |
395 | 542k | .take(num_iters) |
396 | 542k | .enumerate() |
397 | | { |
398 | 4.36M | let mut raw_samples: [&[i16]; 4] = [&[], &[], &[], &[]]; |
399 | | |
400 | | // iterate over each line, since color-convert needs only |
401 | | // one line |
402 | 7.73M | for (j, samp) in raw_samples.iter_mut().enumerate().take(comp_len) { |
403 | 7.73M | *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width]; |
404 | 7.73M | } |
405 | 4.36M | color_convert( |
406 | 4.36M | &raw_samples, |
407 | 4.36M | self.color_convert_16, |
408 | 4.36M | self.input_colorspace, |
409 | 4.36M | self.options.jpeg_get_out_colorspace(), |
410 | 4.36M | output, |
411 | 4.36M | width, |
412 | 4.36M | padded_width |
413 | 4.36M | )?; |
414 | 4.36M | px += width * out_colorspace_components; |
415 | | } |
416 | 542k | Ok(()) |
417 | 542k | }; Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::post_process::{closure#1} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} Line | Count | Source | 392 | 94.6k | |num_iters: usize, samples: [&[i16]; 4]| -> Result<(), DecodeErrors> { | 393 | 756k | for (pos, output) in pixels[px..] | 394 | 94.6k | .chunks_exact_mut(width * out_colorspace_components) | 395 | 94.6k | .take(num_iters) | 396 | 94.6k | .enumerate() | 397 | | { | 398 | 756k | let mut raw_samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 399 | | | 400 | | // iterate over each line, since color-convert needs only | 401 | | // one line | 402 | 756k | for (j, samp) in raw_samples.iter_mut().enumerate().take(comp_len) { | 403 | 756k | *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width]; | 404 | 756k | } | 405 | 756k | color_convert( | 406 | 756k | &raw_samples, | 407 | 756k | self.color_convert_16, | 408 | 756k | self.input_colorspace, | 409 | 756k | self.options.jpeg_get_out_colorspace(), | 410 | 756k | output, | 411 | 756k | width, | 412 | 756k | padded_width | 413 | 756k | )?; | 414 | 756k | px += width * out_colorspace_components; | 415 | | } | 416 | 94.6k | Ok(()) | 417 | 94.6k | }; |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} Line | Count | Source | 392 | 180k | |num_iters: usize, samples: [&[i16]; 4]| -> Result<(), DecodeErrors> { | 393 | 1.46M | for (pos, output) in pixels[px..] | 394 | 180k | .chunks_exact_mut(width * out_colorspace_components) | 395 | 180k | .take(num_iters) | 396 | 180k | .enumerate() | 397 | | { | 398 | 1.46M | let mut raw_samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 399 | | | 400 | | // iterate over each line, since color-convert needs only | 401 | | // one line | 402 | 2.43M | for (j, samp) in raw_samples.iter_mut().enumerate().take(comp_len) { | 403 | 2.43M | *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width]; | 404 | 2.43M | } | 405 | 1.46M | color_convert( | 406 | 1.46M | &raw_samples, | 407 | 1.46M | self.color_convert_16, | 408 | 1.46M | self.input_colorspace, | 409 | 1.46M | self.options.jpeg_get_out_colorspace(), | 410 | 1.46M | output, | 411 | 1.46M | width, | 412 | 1.46M | padded_width | 413 | 1.46M | )?; | 414 | 1.46M | px += width * out_colorspace_components; | 415 | | } | 416 | 180k | Ok(()) | 417 | 180k | }; |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#1} Line | Count | Source | 392 | 851 | |num_iters: usize, samples: [&[i16]; 4]| -> Result<(), DecodeErrors> { | 393 | 6.70k | for (pos, output) in pixels[px..] | 394 | 851 | .chunks_exact_mut(width * out_colorspace_components) | 395 | 851 | .take(num_iters) | 396 | 851 | .enumerate() | 397 | | { | 398 | 6.70k | let mut raw_samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 399 | | | 400 | | // iterate over each line, since color-convert needs only | 401 | | // one line | 402 | 6.70k | for (j, samp) in raw_samples.iter_mut().enumerate().take(comp_len) { | 403 | 6.70k | *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width]; | 404 | 6.70k | } | 405 | 6.70k | color_convert( | 406 | 6.70k | &raw_samples, | 407 | 6.70k | self.color_convert_16, | 408 | 6.70k | self.input_colorspace, | 409 | 6.70k | self.options.jpeg_get_out_colorspace(), | 410 | 6.70k | output, | 411 | 6.70k | width, | 412 | 6.70k | padded_width | 413 | 6.70k | )?; | 414 | 6.70k | px += width * out_colorspace_components; | 415 | | } | 416 | 851 | Ok(()) | 417 | 851 | }; |
<zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#1} Line | Count | Source | 392 | 266k | |num_iters: usize, samples: [&[i16]; 4]| -> Result<(), DecodeErrors> { | 393 | 2.13M | for (pos, output) in pixels[px..] | 394 | 266k | .chunks_exact_mut(width * out_colorspace_components) | 395 | 266k | .take(num_iters) | 396 | 266k | .enumerate() | 397 | | { | 398 | 2.13M | let mut raw_samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 399 | | | 400 | | // iterate over each line, since color-convert needs only | 401 | | // one line | 402 | 4.53M | for (j, samp) in raw_samples.iter_mut().enumerate().take(comp_len) { | 403 | 4.53M | *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width]; | 404 | 4.53M | } | 405 | 2.13M | color_convert( | 406 | 2.13M | &raw_samples, | 407 | 2.13M | self.color_convert_16, | 408 | 2.13M | self.input_colorspace, | 409 | 2.13M | self.options.jpeg_get_out_colorspace(), | 410 | 2.13M | output, | 411 | 2.13M | width, | 412 | 2.13M | padded_width | 413 | 2.13M | )?; | 414 | 2.13M | px += width * out_colorspace_components; | 415 | | } | 416 | 266k | Ok(()) | 417 | 266k | }; |
|
418 | | |
419 | 489k | let comps = &mut self.components[..]; |
420 | 489k | |
421 | 489k | if self.is_interleaved && self.options.jpeg_get_out_colorspace() != ColorSpace::Luma { |
422 | | { |
423 | | // duplicated so that we can check that samples match |
424 | | // Fixes bug https://github.com/etemesi254/zune-image/issues/151 |
425 | 82.3k | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; |
426 | | |
427 | 247k | for (samp, component) in samples.iter_mut().zip(comps.iter()) { |
428 | 247k | *samp = if component.sample_ratio == SampleRatios::None { |
429 | 81.7k | &component.raw_coeff |
430 | | } else { |
431 | 166k | &component.upsample_dest |
432 | | }; |
433 | | } |
434 | | } |
435 | 247k | for comp in comps.iter_mut() { |
436 | 247k | upsample( |
437 | 247k | comp, |
438 | 247k | mcu_height, |
439 | 247k | i, |
440 | 247k | upsampler_scratch_space, |
441 | 247k | is_vertically_sampled |
442 | 247k | ); |
443 | 247k | } |
444 | | |
445 | 82.3k | if is_vertically_sampled { |
446 | 53.4k | if i > 0 { |
447 | | // write the last line, it wasn't up-sampled as we didn't have row_down |
448 | | // yet |
449 | 52.7k | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; |
450 | | |
451 | 158k | for (samp, component) in samples.iter_mut().zip(comps.iter()) { |
452 | 158k | *samp = &component.first_row_upsample_dest; |
453 | 158k | } |
454 | | |
455 | | // ensure length matches for all samples |
456 | 52.7k | let _first_len = samples[0].len(); |
457 | 52.7k | |
458 | 52.7k | // This was a good check, but can be caused to panic, esp on invalid/corrupt images. |
459 | 52.7k | // See one in issue https://github.com/etemesi254/zune-image/issues/262, so for now |
460 | 52.7k | // we just ignore and generate invalid images at the end. |
461 | 52.7k | |
462 | 52.7k | // |
463 | 52.7k | // |
464 | 52.7k | // for samp in samples.iter().take(comp_len) { |
465 | 52.7k | // assert_eq!(first_len, samp.len()); |
466 | 52.7k | // } |
467 | 52.7k | let num_iters = self.coeff * self.v_max; |
468 | 52.7k | |
469 | 52.7k | color_conv_function(num_iters, samples)?; |
470 | 761 | } |
471 | | |
472 | | // After up-sampling the last row, save any row that can be used for |
473 | | // a later up-sampling, |
474 | | // |
475 | | // E.g the Y sample is not sampled but we haven't finished upsampling the last row of |
476 | | // the previous mcu, since we don't have the down row, so save it |
477 | 160k | for component in comps.iter_mut() { |
478 | 160k | if component.sample_ratio != SampleRatios::H { |
479 | 152k | // We don't care about H sampling factors, since it's copied in the workers function |
480 | 152k | |
481 | 152k | // copy last row to be used for the next color conversion |
482 | 152k | let size = component.vertical_sample |
483 | 152k | * component.width_stride |
484 | 152k | * component.sample_ratio.sample(); |
485 | 152k | |
486 | 152k | let last_bytes = |
487 | 152k | component.raw_coeff.rchunks_exact_mut(size).next().unwrap(); |
488 | 152k | |
489 | 152k | component |
490 | 152k | .first_row_upsample_dest |
491 | 152k | .copy_from_slice(last_bytes); |
492 | 152k | } |
493 | | } |
494 | 28.8k | } |
495 | | |
496 | 82.3k | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; |
497 | | |
498 | 247k | for (samp, component) in samples.iter_mut().zip(comps.iter()) { |
499 | 247k | *samp = if component.sample_ratio == SampleRatios::None { |
500 | 81.7k | &component.raw_coeff |
501 | | } else { |
502 | 166k | &component.upsample_dest |
503 | | }; |
504 | | } |
505 | | |
506 | | // we either do 7 or 8 MCU's depending on the state, this only applies to |
507 | | // vertically sampled images |
508 | | // |
509 | | // for rows up until the last MCU, we do not upsample the last stride of the MCU |
510 | | // which means that the number of iterations should take that into account is one less the |
511 | | // up-sampled size |
512 | | // |
513 | | // For the last MCU, we upsample the last stride, meaning that if we hit the last MCU, we |
514 | | // should sample full raw coeffs |
515 | 82.3k | let is_last_considered = is_vertically_sampled && (i != mcu_height.saturating_sub(1)); |
516 | | |
517 | 82.3k | let num_iters = (8 - usize::from(is_last_considered)) * self.coeff * self.v_max; |
518 | 82.3k | |
519 | 82.3k | color_conv_function(num_iters, samples)?; |
520 | | } else { |
521 | 407k | let mut channels_ref: [&[i16]; MAX_COMPONENTS] = [&[]; MAX_COMPONENTS]; |
522 | 407k | |
523 | 407k | self.components |
524 | 407k | .iter() |
525 | 407k | .enumerate() |
526 | 550k | .for_each(|(pos, x)| channels_ref[pos] = &x.raw_coeff); Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::post_process::{closure#2} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} Line | Count | Source | 526 | 94.6k | .for_each(|(pos, x)| channels_ref[pos] = &x.raw_coeff); |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} Line | Count | Source | 526 | 127k | .for_each(|(pos, x)| channels_ref[pos] = &x.raw_coeff); |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process::{closure#2} Line | Count | Source | 526 | 851 | .for_each(|(pos, x)| channels_ref[pos] = &x.raw_coeff); |
<zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process::{closure#2} Line | Count | Source | 526 | 327k | .for_each(|(pos, x)| channels_ref[pos] = &x.raw_coeff); |
|
527 | 407k | |
528 | 407k | color_conv_function(8 * self.coeff, channels_ref)?; |
529 | | } |
530 | | |
531 | 489k | *pixels_written = px; |
532 | 489k | Ok(()) |
533 | 489k | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::post_process <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process Line | Count | Source | 369 | 94.6k | pub(crate) fn post_process( | 370 | 94.6k | &mut self, pixels: &mut [u8], i: usize, mcu_height: usize, width: usize, | 371 | 94.6k | padded_width: usize, pixels_written: &mut usize, upsampler_scratch_space: &mut [i16] | 372 | 94.6k | ) -> Result<(), DecodeErrors> { | 373 | 94.6k | let out_colorspace_components = self.options.jpeg_get_out_colorspace().num_components(); | 374 | 94.6k | | 375 | 94.6k | let mut px = *pixels_written; | 376 | 94.6k | // indicates whether image is vertically up-sampled | 377 | 94.6k | let is_vertically_sampled = self | 378 | 94.6k | .components | 379 | 94.6k | .iter() | 380 | 94.6k | .any(|c| c.sample_ratio == SampleRatios::HV || c.sample_ratio == SampleRatios::V); | 381 | 94.6k | | 382 | 94.6k | let mut comp_len = self.components.len(); | 383 | 94.6k | | 384 | 94.6k | // If we are moving from YCbCr-> Luma, we do not allocate storage for other components, so we | 385 | 94.6k | // will panic when we are trying to read samples, so for that case, | 386 | 94.6k | // hardcode it so that we don't panic when doing | 387 | 94.6k | // *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width] | 388 | 94.6k | if out_colorspace_components < comp_len && self.options.jpeg_get_out_colorspace() == Luma { | 389 | 0 | comp_len = out_colorspace_components; | 390 | 94.6k | } | 391 | 94.6k | let mut color_conv_function = | 392 | | |num_iters: usize, samples: [&[i16]; 4]| -> Result<(), DecodeErrors> { | 393 | | for (pos, output) in pixels[px..] | 394 | | .chunks_exact_mut(width * out_colorspace_components) | 395 | | .take(num_iters) | 396 | | .enumerate() | 397 | | { | 398 | | let mut raw_samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 399 | | | 400 | | // iterate over each line, since color-convert needs only | 401 | | // one line | 402 | | for (j, samp) in raw_samples.iter_mut().enumerate().take(comp_len) { | 403 | | *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width]; | 404 | | } | 405 | | color_convert( | 406 | | &raw_samples, | 407 | | self.color_convert_16, | 408 | | self.input_colorspace, | 409 | | self.options.jpeg_get_out_colorspace(), | 410 | | output, | 411 | | width, | 412 | | padded_width | 413 | | )?; | 414 | | px += width * out_colorspace_components; | 415 | | } | 416 | | Ok(()) | 417 | | }; | 418 | | | 419 | 94.6k | let comps = &mut self.components[..]; | 420 | 94.6k | | 421 | 94.6k | if self.is_interleaved && self.options.jpeg_get_out_colorspace() != ColorSpace::Luma { | 422 | | { | 423 | | // duplicated so that we can check that samples match | 424 | | // Fixes bug https://github.com/etemesi254/zune-image/issues/151 | 425 | 0 | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 426 | | | 427 | 0 | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 428 | 0 | *samp = if component.sample_ratio == SampleRatios::None { | 429 | 0 | &component.raw_coeff | 430 | | } else { | 431 | 0 | &component.upsample_dest | 432 | | }; | 433 | | } | 434 | | } | 435 | 0 | for comp in comps.iter_mut() { | 436 | 0 | upsample( | 437 | 0 | comp, | 438 | 0 | mcu_height, | 439 | 0 | i, | 440 | 0 | upsampler_scratch_space, | 441 | 0 | is_vertically_sampled | 442 | 0 | ); | 443 | 0 | } | 444 | | | 445 | 0 | if is_vertically_sampled { | 446 | 0 | if i > 0 { | 447 | | // write the last line, it wasn't up-sampled as we didn't have row_down | 448 | | // yet | 449 | 0 | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 450 | | | 451 | 0 | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 452 | 0 | *samp = &component.first_row_upsample_dest; | 453 | 0 | } | 454 | | | 455 | | // ensure length matches for all samples | 456 | 0 | let _first_len = samples[0].len(); | 457 | 0 |
| 458 | 0 | // This was a good check, but can be caused to panic, esp on invalid/corrupt images. | 459 | 0 | // See one in issue https://github.com/etemesi254/zune-image/issues/262, so for now | 460 | 0 | // we just ignore and generate invalid images at the end. | 461 | 0 |
| 462 | 0 | // | 463 | 0 | // | 464 | 0 | // for samp in samples.iter().take(comp_len) { | 465 | 0 | // assert_eq!(first_len, samp.len()); | 466 | 0 | // } | 467 | 0 | let num_iters = self.coeff * self.v_max; | 468 | 0 |
| 469 | 0 | color_conv_function(num_iters, samples)?; | 470 | 0 | } | 471 | | | 472 | | // After up-sampling the last row, save any row that can be used for | 473 | | // a later up-sampling, | 474 | | // | 475 | | // E.g the Y sample is not sampled but we haven't finished upsampling the last row of | 476 | | // the previous mcu, since we don't have the down row, so save it | 477 | 0 | for component in comps.iter_mut() { | 478 | 0 | if component.sample_ratio != SampleRatios::H { | 479 | 0 | // We don't care about H sampling factors, since it's copied in the workers function | 480 | 0 |
| 481 | 0 | // copy last row to be used for the next color conversion | 482 | 0 | let size = component.vertical_sample | 483 | 0 | * component.width_stride | 484 | 0 | * component.sample_ratio.sample(); | 485 | 0 |
| 486 | 0 | let last_bytes = | 487 | 0 | component.raw_coeff.rchunks_exact_mut(size).next().unwrap(); | 488 | 0 |
| 489 | 0 | component | 490 | 0 | .first_row_upsample_dest | 491 | 0 | .copy_from_slice(last_bytes); | 492 | 0 | } | 493 | | } | 494 | 0 | } | 495 | | | 496 | 0 | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 497 | | | 498 | 0 | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 499 | 0 | *samp = if component.sample_ratio == SampleRatios::None { | 500 | 0 | &component.raw_coeff | 501 | | } else { | 502 | 0 | &component.upsample_dest | 503 | | }; | 504 | | } | 505 | | | 506 | | // we either do 7 or 8 MCU's depending on the state, this only applies to | 507 | | // vertically sampled images | 508 | | // | 509 | | // for rows up until the last MCU, we do not upsample the last stride of the MCU | 510 | | // which means that the number of iterations should take that into account is one less the | 511 | | // up-sampled size | 512 | | // | 513 | | // For the last MCU, we upsample the last stride, meaning that if we hit the last MCU, we | 514 | | // should sample full raw coeffs | 515 | 0 | let is_last_considered = is_vertically_sampled && (i != mcu_height.saturating_sub(1)); | 516 | | | 517 | 0 | let num_iters = (8 - usize::from(is_last_considered)) * self.coeff * self.v_max; | 518 | 0 |
| 519 | 0 | color_conv_function(num_iters, samples)?; | 520 | | } else { | 521 | 94.6k | let mut channels_ref: [&[i16]; MAX_COMPONENTS] = [&[]; MAX_COMPONENTS]; | 522 | 94.6k | | 523 | 94.6k | self.components | 524 | 94.6k | .iter() | 525 | 94.6k | .enumerate() | 526 | 94.6k | .for_each(|(pos, x)| channels_ref[pos] = &x.raw_coeff); | 527 | 94.6k | | 528 | 94.6k | color_conv_function(8 * self.coeff, channels_ref)?; | 529 | | } | 530 | | | 531 | 94.6k | *pixels_written = px; | 532 | 94.6k | Ok(()) | 533 | 94.6k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process Line | Count | Source | 369 | 154k | pub(crate) fn post_process( | 370 | 154k | &mut self, pixels: &mut [u8], i: usize, mcu_height: usize, width: usize, | 371 | 154k | padded_width: usize, pixels_written: &mut usize, upsampler_scratch_space: &mut [i16] | 372 | 154k | ) -> Result<(), DecodeErrors> { | 373 | 154k | let out_colorspace_components = self.options.jpeg_get_out_colorspace().num_components(); | 374 | 154k | | 375 | 154k | let mut px = *pixels_written; | 376 | 154k | // indicates whether image is vertically up-sampled | 377 | 154k | let is_vertically_sampled = self | 378 | 154k | .components | 379 | 154k | .iter() | 380 | 154k | .any(|c| c.sample_ratio == SampleRatios::HV || c.sample_ratio == SampleRatios::V); | 381 | 154k | | 382 | 154k | let mut comp_len = self.components.len(); | 383 | 154k | | 384 | 154k | // If we are moving from YCbCr-> Luma, we do not allocate storage for other components, so we | 385 | 154k | // will panic when we are trying to read samples, so for that case, | 386 | 154k | // hardcode it so that we don't panic when doing | 387 | 154k | // *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width] | 388 | 154k | if out_colorspace_components < comp_len && self.options.jpeg_get_out_colorspace() == Luma { | 389 | 0 | comp_len = out_colorspace_components; | 390 | 154k | } | 391 | 154k | let mut color_conv_function = | 392 | | |num_iters: usize, samples: [&[i16]; 4]| -> Result<(), DecodeErrors> { | 393 | | for (pos, output) in pixels[px..] | 394 | | .chunks_exact_mut(width * out_colorspace_components) | 395 | | .take(num_iters) | 396 | | .enumerate() | 397 | | { | 398 | | let mut raw_samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 399 | | | 400 | | // iterate over each line, since color-convert needs only | 401 | | // one line | 402 | | for (j, samp) in raw_samples.iter_mut().enumerate().take(comp_len) { | 403 | | *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width]; | 404 | | } | 405 | | color_convert( | 406 | | &raw_samples, | 407 | | self.color_convert_16, | 408 | | self.input_colorspace, | 409 | | self.options.jpeg_get_out_colorspace(), | 410 | | output, | 411 | | width, | 412 | | padded_width | 413 | | )?; | 414 | | px += width * out_colorspace_components; | 415 | | } | 416 | | Ok(()) | 417 | | }; | 418 | | | 419 | 154k | let comps = &mut self.components[..]; | 420 | 154k | | 421 | 154k | if self.is_interleaved && self.options.jpeg_get_out_colorspace() != ColorSpace::Luma { | 422 | | { | 423 | | // duplicated so that we can check that samples match | 424 | | // Fixes bug https://github.com/etemesi254/zune-image/issues/151 | 425 | 30.6k | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 426 | | | 427 | 91.9k | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 428 | 91.9k | *samp = if component.sample_ratio == SampleRatios::None { | 429 | 35.5k | &component.raw_coeff | 430 | | } else { | 431 | 56.4k | &component.upsample_dest | 432 | | }; | 433 | | } | 434 | | } | 435 | 91.9k | for comp in comps.iter_mut() { | 436 | 91.9k | upsample( | 437 | 91.9k | comp, | 438 | 91.9k | mcu_height, | 439 | 91.9k | i, | 440 | 91.9k | upsampler_scratch_space, | 441 | 91.9k | is_vertically_sampled | 442 | 91.9k | ); | 443 | 91.9k | } | 444 | | | 445 | 30.6k | if is_vertically_sampled { | 446 | 26.4k | if i > 0 { | 447 | | // write the last line, it wasn't up-sampled as we didn't have row_down | 448 | | // yet | 449 | 25.8k | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 450 | | | 451 | 77.5k | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 452 | 77.5k | *samp = &component.first_row_upsample_dest; | 453 | 77.5k | } | 454 | | | 455 | | // ensure length matches for all samples | 456 | 25.8k | let _first_len = samples[0].len(); | 457 | 25.8k | | 458 | 25.8k | // This was a good check, but can be caused to panic, esp on invalid/corrupt images. | 459 | 25.8k | // See one in issue https://github.com/etemesi254/zune-image/issues/262, so for now | 460 | 25.8k | // we just ignore and generate invalid images at the end. | 461 | 25.8k | | 462 | 25.8k | // | 463 | 25.8k | // | 464 | 25.8k | // for samp in samples.iter().take(comp_len) { | 465 | 25.8k | // assert_eq!(first_len, samp.len()); | 466 | 25.8k | // } | 467 | 25.8k | let num_iters = self.coeff * self.v_max; | 468 | 25.8k | | 469 | 25.8k | color_conv_function(num_iters, samples)?; | 470 | 613 | } | 471 | | | 472 | | // After up-sampling the last row, save any row that can be used for | 473 | | // a later up-sampling, | 474 | | // | 475 | | // E.g the Y sample is not sampled but we haven't finished upsampling the last row of | 476 | | // the previous mcu, since we don't have the down row, so save it | 477 | 79.4k | for component in comps.iter_mut() { | 478 | 79.4k | if component.sample_ratio != SampleRatios::H { | 479 | 77.3k | // We don't care about H sampling factors, since it's copied in the workers function | 480 | 77.3k | | 481 | 77.3k | // copy last row to be used for the next color conversion | 482 | 77.3k | let size = component.vertical_sample | 483 | 77.3k | * component.width_stride | 484 | 77.3k | * component.sample_ratio.sample(); | 485 | 77.3k | | 486 | 77.3k | let last_bytes = | 487 | 77.3k | component.raw_coeff.rchunks_exact_mut(size).next().unwrap(); | 488 | 77.3k | | 489 | 77.3k | component | 490 | 77.3k | .first_row_upsample_dest | 491 | 77.3k | .copy_from_slice(last_bytes); | 492 | 77.3k | } | 493 | | } | 494 | 4.17k | } | 495 | | | 496 | 30.6k | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 497 | | | 498 | 91.9k | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 499 | 91.9k | *samp = if component.sample_ratio == SampleRatios::None { | 500 | 35.5k | &component.raw_coeff | 501 | | } else { | 502 | 56.4k | &component.upsample_dest | 503 | | }; | 504 | | } | 505 | | | 506 | | // we either do 7 or 8 MCU's depending on the state, this only applies to | 507 | | // vertically sampled images | 508 | | // | 509 | | // for rows up until the last MCU, we do not upsample the last stride of the MCU | 510 | | // which means that the number of iterations should take that into account is one less the | 511 | | // up-sampled size | 512 | | // | 513 | | // For the last MCU, we upsample the last stride, meaning that if we hit the last MCU, we | 514 | | // should sample full raw coeffs | 515 | 30.6k | let is_last_considered = is_vertically_sampled && (i != mcu_height.saturating_sub(1)); | 516 | | | 517 | 30.6k | let num_iters = (8 - usize::from(is_last_considered)) * self.coeff * self.v_max; | 518 | 30.6k | | 519 | 30.6k | color_conv_function(num_iters, samples)?; | 520 | | } else { | 521 | 124k | let mut channels_ref: [&[i16]; MAX_COMPONENTS] = [&[]; MAX_COMPONENTS]; | 522 | 124k | | 523 | 124k | self.components | 524 | 124k | .iter() | 525 | 124k | .enumerate() | 526 | 124k | .for_each(|(pos, x)| channels_ref[pos] = &x.raw_coeff); | 527 | 124k | | 528 | 124k | color_conv_function(8 * self.coeff, channels_ref)?; | 529 | | } | 530 | | | 531 | 154k | *pixels_written = px; | 532 | 154k | Ok(()) | 533 | 154k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::post_process Line | Count | Source | 369 | 851 | pub(crate) fn post_process( | 370 | 851 | &mut self, pixels: &mut [u8], i: usize, mcu_height: usize, width: usize, | 371 | 851 | padded_width: usize, pixels_written: &mut usize, upsampler_scratch_space: &mut [i16] | 372 | 851 | ) -> Result<(), DecodeErrors> { | 373 | 851 | let out_colorspace_components = self.options.jpeg_get_out_colorspace().num_components(); | 374 | 851 | | 375 | 851 | let mut px = *pixels_written; | 376 | 851 | // indicates whether image is vertically up-sampled | 377 | 851 | let is_vertically_sampled = self | 378 | 851 | .components | 379 | 851 | .iter() | 380 | 851 | .any(|c| c.sample_ratio == SampleRatios::HV || c.sample_ratio == SampleRatios::V); | 381 | 851 | | 382 | 851 | let mut comp_len = self.components.len(); | 383 | 851 | | 384 | 851 | // If we are moving from YCbCr-> Luma, we do not allocate storage for other components, so we | 385 | 851 | // will panic when we are trying to read samples, so for that case, | 386 | 851 | // hardcode it so that we don't panic when doing | 387 | 851 | // *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width] | 388 | 851 | if out_colorspace_components < comp_len && self.options.jpeg_get_out_colorspace() == Luma { | 389 | 0 | comp_len = out_colorspace_components; | 390 | 851 | } | 391 | 851 | let mut color_conv_function = | 392 | | |num_iters: usize, samples: [&[i16]; 4]| -> Result<(), DecodeErrors> { | 393 | | for (pos, output) in pixels[px..] | 394 | | .chunks_exact_mut(width * out_colorspace_components) | 395 | | .take(num_iters) | 396 | | .enumerate() | 397 | | { | 398 | | let mut raw_samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 399 | | | 400 | | // iterate over each line, since color-convert needs only | 401 | | // one line | 402 | | for (j, samp) in raw_samples.iter_mut().enumerate().take(comp_len) { | 403 | | *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width]; | 404 | | } | 405 | | color_convert( | 406 | | &raw_samples, | 407 | | self.color_convert_16, | 408 | | self.input_colorspace, | 409 | | self.options.jpeg_get_out_colorspace(), | 410 | | output, | 411 | | width, | 412 | | padded_width | 413 | | )?; | 414 | | px += width * out_colorspace_components; | 415 | | } | 416 | | Ok(()) | 417 | | }; | 418 | | | 419 | 851 | let comps = &mut self.components[..]; | 420 | 851 | | 421 | 851 | if self.is_interleaved && self.options.jpeg_get_out_colorspace() != ColorSpace::Luma { | 422 | | { | 423 | | // duplicated so that we can check that samples match | 424 | | // Fixes bug https://github.com/etemesi254/zune-image/issues/151 | 425 | 0 | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 426 | | | 427 | 0 | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 428 | 0 | *samp = if component.sample_ratio == SampleRatios::None { | 429 | 0 | &component.raw_coeff | 430 | | } else { | 431 | 0 | &component.upsample_dest | 432 | | }; | 433 | | } | 434 | | } | 435 | 0 | for comp in comps.iter_mut() { | 436 | 0 | upsample( | 437 | 0 | comp, | 438 | 0 | mcu_height, | 439 | 0 | i, | 440 | 0 | upsampler_scratch_space, | 441 | 0 | is_vertically_sampled | 442 | 0 | ); | 443 | 0 | } | 444 | | | 445 | 0 | if is_vertically_sampled { | 446 | 0 | if i > 0 { | 447 | | // write the last line, it wasn't up-sampled as we didn't have row_down | 448 | | // yet | 449 | 0 | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 450 | | | 451 | 0 | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 452 | 0 | *samp = &component.first_row_upsample_dest; | 453 | 0 | } | 454 | | | 455 | | // ensure length matches for all samples | 456 | 0 | let _first_len = samples[0].len(); | 457 | 0 |
| 458 | 0 | // This was a good check, but can be caused to panic, esp on invalid/corrupt images. | 459 | 0 | // See one in issue https://github.com/etemesi254/zune-image/issues/262, so for now | 460 | 0 | // we just ignore and generate invalid images at the end. | 461 | 0 |
| 462 | 0 | // | 463 | 0 | // | 464 | 0 | // for samp in samples.iter().take(comp_len) { | 465 | 0 | // assert_eq!(first_len, samp.len()); | 466 | 0 | // } | 467 | 0 | let num_iters = self.coeff * self.v_max; | 468 | 0 |
| 469 | 0 | color_conv_function(num_iters, samples)?; | 470 | 0 | } | 471 | | | 472 | | // After up-sampling the last row, save any row that can be used for | 473 | | // a later up-sampling, | 474 | | // | 475 | | // E.g the Y sample is not sampled but we haven't finished upsampling the last row of | 476 | | // the previous mcu, since we don't have the down row, so save it | 477 | 0 | for component in comps.iter_mut() { | 478 | 0 | if component.sample_ratio != SampleRatios::H { | 479 | 0 | // We don't care about H sampling factors, since it's copied in the workers function | 480 | 0 |
| 481 | 0 | // copy last row to be used for the next color conversion | 482 | 0 | let size = component.vertical_sample | 483 | 0 | * component.width_stride | 484 | 0 | * component.sample_ratio.sample(); | 485 | 0 |
| 486 | 0 | let last_bytes = | 487 | 0 | component.raw_coeff.rchunks_exact_mut(size).next().unwrap(); | 488 | 0 |
| 489 | 0 | component | 490 | 0 | .first_row_upsample_dest | 491 | 0 | .copy_from_slice(last_bytes); | 492 | 0 | } | 493 | | } | 494 | 0 | } | 495 | | | 496 | 0 | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 497 | | | 498 | 0 | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 499 | 0 | *samp = if component.sample_ratio == SampleRatios::None { | 500 | 0 | &component.raw_coeff | 501 | | } else { | 502 | 0 | &component.upsample_dest | 503 | | }; | 504 | | } | 505 | | | 506 | | // we either do 7 or 8 MCU's depending on the state, this only applies to | 507 | | // vertically sampled images | 508 | | // | 509 | | // for rows up until the last MCU, we do not upsample the last stride of the MCU | 510 | | // which means that the number of iterations should take that into account is one less the | 511 | | // up-sampled size | 512 | | // | 513 | | // For the last MCU, we upsample the last stride, meaning that if we hit the last MCU, we | 514 | | // should sample full raw coeffs | 515 | 0 | let is_last_considered = is_vertically_sampled && (i != mcu_height.saturating_sub(1)); | 516 | | | 517 | 0 | let num_iters = (8 - usize::from(is_last_considered)) * self.coeff * self.v_max; | 518 | 0 |
| 519 | 0 | color_conv_function(num_iters, samples)?; | 520 | | } else { | 521 | 851 | let mut channels_ref: [&[i16]; MAX_COMPONENTS] = [&[]; MAX_COMPONENTS]; | 522 | 851 | | 523 | 851 | self.components | 524 | 851 | .iter() | 525 | 851 | .enumerate() | 526 | 851 | .for_each(|(pos, x)| channels_ref[pos] = &x.raw_coeff); | 527 | 851 | | 528 | 851 | color_conv_function(8 * self.coeff, channels_ref)?; | 529 | | } | 530 | | | 531 | 851 | *pixels_written = px; | 532 | 851 | Ok(()) | 533 | 851 | } |
<zune_jpeg::decoder::JpegDecoder<&[u8]>>::post_process Line | Count | Source | 369 | 239k | pub(crate) fn post_process( | 370 | 239k | &mut self, pixels: &mut [u8], i: usize, mcu_height: usize, width: usize, | 371 | 239k | padded_width: usize, pixels_written: &mut usize, upsampler_scratch_space: &mut [i16] | 372 | 239k | ) -> Result<(), DecodeErrors> { | 373 | 239k | let out_colorspace_components = self.options.jpeg_get_out_colorspace().num_components(); | 374 | 239k | | 375 | 239k | let mut px = *pixels_written; | 376 | 239k | // indicates whether image is vertically up-sampled | 377 | 239k | let is_vertically_sampled = self | 378 | 239k | .components | 379 | 239k | .iter() | 380 | 239k | .any(|c| c.sample_ratio == SampleRatios::HV || c.sample_ratio == SampleRatios::V); | 381 | 239k | | 382 | 239k | let mut comp_len = self.components.len(); | 383 | 239k | | 384 | 239k | // If we are moving from YCbCr-> Luma, we do not allocate storage for other components, so we | 385 | 239k | // will panic when we are trying to read samples, so for that case, | 386 | 239k | // hardcode it so that we don't panic when doing | 387 | 239k | // *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width] | 388 | 239k | if out_colorspace_components < comp_len && self.options.jpeg_get_out_colorspace() == Luma { | 389 | 0 | comp_len = out_colorspace_components; | 390 | 239k | } | 391 | 239k | let mut color_conv_function = | 392 | | |num_iters: usize, samples: [&[i16]; 4]| -> Result<(), DecodeErrors> { | 393 | | for (pos, output) in pixels[px..] | 394 | | .chunks_exact_mut(width * out_colorspace_components) | 395 | | .take(num_iters) | 396 | | .enumerate() | 397 | | { | 398 | | let mut raw_samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 399 | | | 400 | | // iterate over each line, since color-convert needs only | 401 | | // one line | 402 | | for (j, samp) in raw_samples.iter_mut().enumerate().take(comp_len) { | 403 | | *samp = &samples[j][pos * padded_width..(pos + 1) * padded_width]; | 404 | | } | 405 | | color_convert( | 406 | | &raw_samples, | 407 | | self.color_convert_16, | 408 | | self.input_colorspace, | 409 | | self.options.jpeg_get_out_colorspace(), | 410 | | output, | 411 | | width, | 412 | | padded_width | 413 | | )?; | 414 | | px += width * out_colorspace_components; | 415 | | } | 416 | | Ok(()) | 417 | | }; | 418 | | | 419 | 239k | let comps = &mut self.components[..]; | 420 | 239k | | 421 | 239k | if self.is_interleaved && self.options.jpeg_get_out_colorspace() != ColorSpace::Luma { | 422 | | { | 423 | | // duplicated so that we can check that samples match | 424 | | // Fixes bug https://github.com/etemesi254/zune-image/issues/151 | 425 | 51.6k | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 426 | | | 427 | 155k | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 428 | 155k | *samp = if component.sample_ratio == SampleRatios::None { | 429 | 46.2k | &component.raw_coeff | 430 | | } else { | 431 | 109k | &component.upsample_dest | 432 | | }; | 433 | | } | 434 | | } | 435 | 155k | for comp in comps.iter_mut() { | 436 | 155k | upsample( | 437 | 155k | comp, | 438 | 155k | mcu_height, | 439 | 155k | i, | 440 | 155k | upsampler_scratch_space, | 441 | 155k | is_vertically_sampled | 442 | 155k | ); | 443 | 155k | } | 444 | | | 445 | 51.6k | if is_vertically_sampled { | 446 | 27.0k | if i > 0 { | 447 | | // write the last line, it wasn't up-sampled as we didn't have row_down | 448 | | // yet | 449 | 26.8k | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 450 | | | 451 | 80.8k | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 452 | 80.8k | *samp = &component.first_row_upsample_dest; | 453 | 80.8k | } | 454 | | | 455 | | // ensure length matches for all samples | 456 | 26.8k | let _first_len = samples[0].len(); | 457 | 26.8k | | 458 | 26.8k | // This was a good check, but can be caused to panic, esp on invalid/corrupt images. | 459 | 26.8k | // See one in issue https://github.com/etemesi254/zune-image/issues/262, so for now | 460 | 26.8k | // we just ignore and generate invalid images at the end. | 461 | 26.8k | | 462 | 26.8k | // | 463 | 26.8k | // | 464 | 26.8k | // for samp in samples.iter().take(comp_len) { | 465 | 26.8k | // assert_eq!(first_len, samp.len()); | 466 | 26.8k | // } | 467 | 26.8k | let num_iters = self.coeff * self.v_max; | 468 | 26.8k | | 469 | 26.8k | color_conv_function(num_iters, samples)?; | 470 | 148 | } | 471 | | | 472 | | // After up-sampling the last row, save any row that can be used for | 473 | | // a later up-sampling, | 474 | | // | 475 | | // E.g the Y sample is not sampled but we haven't finished upsampling the last row of | 476 | | // the previous mcu, since we don't have the down row, so save it | 477 | 81.2k | for component in comps.iter_mut() { | 478 | 81.2k | if component.sample_ratio != SampleRatios::H { | 479 | 75.0k | // We don't care about H sampling factors, since it's copied in the workers function | 480 | 75.0k | | 481 | 75.0k | // copy last row to be used for the next color conversion | 482 | 75.0k | let size = component.vertical_sample | 483 | 75.0k | * component.width_stride | 484 | 75.0k | * component.sample_ratio.sample(); | 485 | 75.0k | | 486 | 75.0k | let last_bytes = | 487 | 75.0k | component.raw_coeff.rchunks_exact_mut(size).next().unwrap(); | 488 | 75.0k | | 489 | 75.0k | component | 490 | 75.0k | .first_row_upsample_dest | 491 | 75.0k | .copy_from_slice(last_bytes); | 492 | 75.0k | } | 493 | | } | 494 | 24.6k | } | 495 | | | 496 | 51.6k | let mut samples: [&[i16]; 4] = [&[], &[], &[], &[]]; | 497 | | | 498 | 155k | for (samp, component) in samples.iter_mut().zip(comps.iter()) { | 499 | 155k | *samp = if component.sample_ratio == SampleRatios::None { | 500 | 46.2k | &component.raw_coeff | 501 | | } else { | 502 | 109k | &component.upsample_dest | 503 | | }; | 504 | | } | 505 | | | 506 | | // we either do 7 or 8 MCU's depending on the state, this only applies to | 507 | | // vertically sampled images | 508 | | // | 509 | | // for rows up until the last MCU, we do not upsample the last stride of the MCU | 510 | | // which means that the number of iterations should take that into account is one less the | 511 | | // up-sampled size | 512 | | // | 513 | | // For the last MCU, we upsample the last stride, meaning that if we hit the last MCU, we | 514 | | // should sample full raw coeffs | 515 | 51.6k | let is_last_considered = is_vertically_sampled && (i != mcu_height.saturating_sub(1)); | 516 | | | 517 | 51.6k | let num_iters = (8 - usize::from(is_last_considered)) * self.coeff * self.v_max; | 518 | 51.6k | | 519 | 51.6k | color_conv_function(num_iters, samples)?; | 520 | | } else { | 521 | 187k | let mut channels_ref: [&[i16]; MAX_COMPONENTS] = [&[]; MAX_COMPONENTS]; | 522 | 187k | | 523 | 187k | self.components | 524 | 187k | .iter() | 525 | 187k | .enumerate() | 526 | 187k | .for_each(|(pos, x)| channels_ref[pos] = &x.raw_coeff); | 527 | 187k | | 528 | 187k | color_conv_function(8 * self.coeff, channels_ref)?; | 529 | | } | 530 | | | 531 | 239k | *pixels_written = px; | 532 | 239k | Ok(()) | 533 | 239k | } |
|
534 | | } |