1From 6016c9bee0fbd6244ebe49ed19a25094898c8001 Mon Sep 17 00:00:00 2001 2From: Lukasz Anforowicz <[email protected]> 3Date: Wed, 30 Oct 2024 20:45:18 -0700 4Subject: [PATCH 301/302] Add support for parsing `mDCv` and `cLLi` chunks. 5 (#528) 6 7--- 8 Cargo.toml | 1 + 9 src/chunk.rs | 4 ++ 10 src/common.rs | 56 +++++++++++++++++++ 11 src/decoder/stream.rs | 108 ++++++++++++++++++++++++++++++++++++- 12 tests/bugfixes/cicp_pq.png | Bin 0 -> 191 bytes 13 tests/results.txt | 1 + 14 tests/results_alpha.txt | 1 + 15 tests/results_identity.txt | 1 + 16 8 files changed, 170 insertions(+), 2 deletions(-) 17 create mode 100644 tests/bugfixes/cicp_pq.png 18 19diff --git a/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/chunk.rs b/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/chunk.rs 20index 3908313..34a088f 100644 21--- a/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/chunk.rs 22+++ b/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/chunk.rs 23@@ -35,6 +35,10 @@ pub const gAMA: ChunkType = ChunkType(*b"gAMA"); 24 pub const sRGB: ChunkType = ChunkType(*b"sRGB"); 25 /// ICC profile chunk 26 pub const iCCP: ChunkType = ChunkType(*b"iCCP"); 27+/// Mastering Display Color Volume chunk 28+pub const mDCv: ChunkType = ChunkType(*b"mDCv"); 29+/// Content Light Level Information chunk 30+pub const cLLi: ChunkType = ChunkType(*b"cLLi"); 31 /// EXIF metadata chunk 32 pub const eXIf: ChunkType = ChunkType(*b"eXIf"); 33 /// Latin-1 uncompressed textual data 34diff --git a/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/common.rs b/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/common.rs 35index 259a2b1..4c06e3b 100644 36--- a/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/common.rs 37+++ b/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/common.rs 38@@ -470,6 +470,56 @@ impl SrgbRenderingIntent { 39 } 40 } 41 42+/// Mastering Display Color Volume (mDCv) used at the point of content creation, 43+/// as specified in [SMPTE-ST-2086](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=8353899). 44+/// 45+/// See https://www.w3.org/TR/png-3/#mDCv-chunk for more details. 46+#[derive(Clone, Copy, Debug, PartialEq, Eq)] 47+pub struct MasteringDisplayColorVolume { 48+ /// Mastering display chromaticities. 49+ pub chromaticities: SourceChromaticities, 50+ 51+ /// Mastering display maximum luminance. 52+ /// 53+ /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field 54+ /// is set to `10000000` then it indicates 1000 cd/m^2. 55+ pub max_luminance: u32, 56+ 57+ /// Mastering display minimum luminance. 58+ /// 59+ /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field 60+ /// is set to `10000000` then it indicates 1000 cd/m^2. 61+ pub min_luminance: u32, 62+} 63+ 64+/// Content light level information of HDR content. 65+/// 66+/// See https://www.w3.org/TR/png-3/#cLLi-chunk for more details. 67+#[derive(Clone, Copy, Debug, PartialEq, Eq)] 68+pub struct ContentLightLevelInfo { 69+ /// Maximum Content Light Level indicates the maximum light level of any 70+ /// single pixel (in cd/m^2, also known as nits) of the entire playback 71+ /// sequence. 72+ /// 73+ /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field 74+ /// is set to `10000000` then it indicates 1000 cd/m^2. 75+ /// 76+ /// A value of zero means that the value is unknown or not currently calculable. 77+ pub max_content_light_level: u32, 78+ 79+ /// Maximum Frame Average Light Level indicates the maximum value of the 80+ /// frame average light level (in cd/m^2, also known as nits) of the entire 81+ /// playback sequence. It is calculated by first averaging the decoded 82+ /// luminance values of all the pixels in each frame, and then using the 83+ /// value for the frame with the highest value. 84+ /// 85+ /// The value is expressed in units of 0.0001 cd/m^2 - for example if this field 86+ /// is set to `10000000` then it indicates 1000 cd/m^2. 87+ /// 88+ /// A value of zero means that the value is unknown or not currently calculable. 89+ pub max_frame_average_light_level: u32, 90+} 91+ 92 /// PNG info struct 93 #[derive(Clone, Debug)] 94 #[non_exhaustive] 95@@ -507,6 +557,10 @@ pub struct Info<'a> { 96 pub srgb: Option<SrgbRenderingIntent>, 97 /// The ICC profile for the image. 98 pub icc_profile: Option<Cow<'a, [u8]>>, 99+ /// The mastering display color volume for the image. 100+ pub mastering_display_color_volume: Option<MasteringDisplayColorVolume>, 101+ /// The content light information for the image. 102+ pub content_light_level: Option<ContentLightLevelInfo>, 103 /// The EXIF metadata for the image. 104 pub exif_metadata: Option<Cow<'a, [u8]>>, 105 /// tEXt field 106@@ -539,6 +593,8 @@ impl Default for Info<'_> { 107 source_chromaticities: None, 108 srgb: None, 109 icc_profile: None, 110+ mastering_display_color_volume: None, 111+ content_light_level: None, 112 exif_metadata: None, 113 uncompressed_latin1_text: Vec::new(), 114 compressed_latin1_text: Vec::new(), 115diff --git a/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/decoder/stream.rs b/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/decoder/stream.rs 116index 5c21a5a..68de12d 100644 117--- a/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/decoder/stream.rs 118+++ b/third_party/rust/chromium_crates_io/vendor/png-0.17.14/src/decoder/stream.rs 119@@ -9,8 +9,9 @@ use crc32fast::Hasher as Crc32; 120 use super::zlib::ZlibStream; 121 use crate::chunk::{self, ChunkType, IDAT, IEND, IHDR}; 122 use crate::common::{ 123- AnimationControl, BitDepth, BlendOp, ColorType, DisposeOp, FrameControl, Info, ParameterError, 124- ParameterErrorKind, PixelDimensions, ScaledFloat, SourceChromaticities, Unit, 125+ AnimationControl, BitDepth, BlendOp, ColorType, ContentLightLevelInfo, DisposeOp, FrameControl, 126+ Info, MasteringDisplayColorVolume, ParameterError, ParameterErrorKind, PixelDimensions, 127+ ScaledFloat, SourceChromaticities, Unit, 128 }; 129 use crate::text_metadata::{ITXtChunk, TEXtChunk, TextDecodingError, ZTXtChunk}; 130 use crate::traits::ReadBytesExt; 131@@ -958,6 +959,8 @@ impl StreamingDecoder { 132 chunk::fcTL => self.parse_fctl(), 133 chunk::cHRM => self.parse_chrm(), 134 chunk::sRGB => self.parse_srgb(), 135+ chunk::mDCv => Ok(self.parse_mdcv()), 136+ chunk::cLLi => Ok(self.parse_clli()), 137 chunk::iCCP if !self.decode_options.ignore_iccp_chunk => self.parse_iccp(), 138 chunk::tEXt if !self.decode_options.ignore_text_chunk => self.parse_text(), 139 chunk::zTXt if !self.decode_options.ignore_text_chunk => self.parse_ztxt(), 140@@ -1271,6 +1274,82 @@ impl StreamingDecoder { 141 } 142 } 143 144+ // NOTE: This function cannot return `DecodingError` and handles parsing 145+ // errors or spec violations as-if the chunk was missing. See 146+ // https://github.com/image-rs/image-png/issues/525 for more discussion. 147+ fn parse_mdcv(&mut self) -> Decoded { 148+ fn parse(mut buf: &[u8]) -> Result<MasteringDisplayColorVolume, std::io::Error> { 149+ let red_x: u16 = buf.read_be()?; 150+ let red_y: u16 = buf.read_be()?; 151+ let green_x: u16 = buf.read_be()?; 152+ let green_y: u16 = buf.read_be()?; 153+ let blue_x: u16 = buf.read_be()?; 154+ let blue_y: u16 = buf.read_be()?; 155+ let white_x: u16 = buf.read_be()?; 156+ let white_y: u16 = buf.read_be()?; 157+ fn scale(chunk: u16) -> ScaledFloat { 158+ // `ScaledFloat::SCALING` is hardcoded to 100_000, which works 159+ // well for the `cHRM` chunk where the spec says that "a value 160+ // of 0.3127 would be stored as the integer 31270". In the 161+ // `mDCv` chunk the spec says that "0.708, 0.292)" is stored as 162+ // "{ 35400, 14600 }", using a scaling factor of 50_000, so we 163+ // multiply by 2 before converting. 164+ ScaledFloat::from_scaled((chunk as u32) * 2) 165+ } 166+ let chromaticities = SourceChromaticities { 167+ white: (scale(white_x), scale(white_y)), 168+ red: (scale(red_x), scale(red_y)), 169+ green: (scale(green_x), scale(green_y)), 170+ blue: (scale(blue_x), scale(blue_y)), 171+ }; 172+ let max_luminance: u32 = buf.read_be()?; 173+ let min_luminance: u32 = buf.read_be()?; 174+ if !buf.is_empty() { 175+ return Err(std::io::ErrorKind::InvalidData.into()); 176+ } 177+ Ok(MasteringDisplayColorVolume { 178+ chromaticities, 179+ max_luminance, 180+ min_luminance, 181+ }) 182+ } 183+ 184+ // The spec requires that the mDCv chunk MUST come before the PLTE and IDAT chunks. 185+ // Additionally, we ignore a second, duplicated mDCv chunk (if any). 186+ let info = self.info.as_mut().unwrap(); 187+ let is_before_plte_and_idat = !self.have_idat && info.palette.is_none(); 188+ if is_before_plte_and_idat && info.mastering_display_color_volume.is_none() { 189+ info.mastering_display_color_volume = parse(&self.current_chunk.raw_bytes[..]).ok(); 190+ } 191+ 192+ Decoded::Nothing 193+ } 194+ 195+ // NOTE: This function cannot return `DecodingError` and handles parsing 196+ // errors or spec violations as-if the chunk was missing. See 197+ // https://github.com/image-rs/image-png/issues/525 for more discussion. 198+ fn parse_clli(&mut self) -> Decoded { 199+ fn parse(mut buf: &[u8]) -> Result<ContentLightLevelInfo, std::io::Error> { 200+ let max_content_light_level: u32 = buf.read_be()?; 201+ let max_frame_average_light_level: u32 = buf.read_be()?; 202+ if !buf.is_empty() { 203+ return Err(std::io::ErrorKind::InvalidData.into()); 204+ } 205+ Ok(ContentLightLevelInfo { 206+ max_content_light_level, 207+ max_frame_average_light_level, 208+ }) 209+ } 210+ 211+ // We ignore a second, duplicated cLLi chunk (if any). 212+ let info = self.info.as_mut().unwrap(); 213+ if info.content_light_level.is_none() { 214+ info.content_light_level = parse(&self.current_chunk.raw_bytes[..]).ok(); 215+ } 216+ 217+ Decoded::Nothing 218+ } 219+ 220 fn parse_iccp(&mut self) -> Result<Decoded, DecodingError> { 221 if self.have_idat { 222 Err(DecodingError::Format( 223-- 2242.47.0.199.ga7371fff76-goog 225 226