From f1641a73411131974e90972e3fb2d83c3a2b2a1f Mon Sep 17 00:00:00 2001 From: Googler Date: Wed, 27 May 2026 21:34:52 -0700 Subject: [PATCH] Migrate legacy PNG decoding under image/codec to Rust pixel_bridge. PiperOrigin-RevId: 922526758 --- pixel_bridge/pixel_bridge.cc | 20 ++++++++++ pixel_bridge/pixel_bridge.h | 3 ++ pixel_bridge/rust/image.rs | 77 ++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/pixel_bridge/pixel_bridge.cc b/pixel_bridge/pixel_bridge.cc index 7444903..e4417a9 100644 --- a/pixel_bridge/pixel_bridge.cc +++ b/pixel_bridge/pixel_bridge.cc @@ -359,4 +359,24 @@ absl::StatusOr ImageReader::IntoDecoder() && { return ImageDecoder(std::move(decoder).value()); } +std::vector SniffPalette(absl::string_view data) { + auto result = rust::image::sniff_palette(absl::Span( + reinterpret_cast(data.data()), data.size())); + std::vector palette(result.len()); + if (result.len() > 0) { + memcpy(palette.data(), result.as_ptr(), result.len()); + } + return palette; +} + +std::vector SniffBkgd(absl::string_view data) { + auto result = rust::image::sniff_bkgd(absl::Span( + reinterpret_cast(data.data()), data.size())); + std::vector bkgd(result.len()); + if (result.len() > 0) { + memcpy(bkgd.data(), result.as_ptr(), result.len()); + } + return bkgd; +} + } // namespace security::pixel_bridge diff --git a/pixel_bridge/pixel_bridge.h b/pixel_bridge/pixel_bridge.h index 74a8f13..8632daf 100644 --- a/pixel_bridge/pixel_bridge.h +++ b/pixel_bridge/pixel_bridge.h @@ -67,6 +67,9 @@ struct Strides final { uintptr_t channels; }; +std::vector SniffPalette(absl::string_view data); +std::vector SniffBkgd(absl::string_view data); + class Frame final { public: // Frames should not be copied, because the data it carries is too big for diff --git a/pixel_bridge/rust/image.rs b/pixel_bridge/rust/image.rs index 1492a21..13f4f66 100644 --- a/pixel_bridge/rust/image.rs +++ b/pixel_bridge/rust/image.rs @@ -683,3 +683,80 @@ impl ImageDecoder { } } } + +struct Chunk<'a> { + chunk_type: [u8; 4], + data: &'a [u8], +} + +fn parse_png_chunks(data: &[u8]) -> Vec { + let mut chunks = Vec::new(); + if data.len() < 8 || &data[0..8] != b"\x89PNG\r\n\x1a\n" { + return chunks; + } + + let mut cur = 8; + while cur + 12 <= data.len() { + let length = + u32::from_be_bytes([data[cur], data[cur + 1], data[cur + 2], data[cur + 3]]) as usize; + cur += 4; + + let mut chunk_type = [0u8; 4]; + chunk_type.copy_from_slice(&data[cur..cur + 4]); + cur += 4; + + if cur + length + 4 > data.len() { + break; + } + + chunks.push(Chunk { chunk_type, data: &data[cur..cur + length] }); + + cur += length + 4; + + if chunk_type == *b"IEND" { + break; + } + } + chunks +} + +pub fn sniff_palette(data: &[u8]) -> crate::vec_u8::VecU8 { + let mut palette = Vec::new(); + let chunks = parse_png_chunks(data); + + let mut plte_data = None; + let mut trns_data = None; + for chunk in &chunks { + if chunk.chunk_type == *b"PLTE" { + plte_data = Some(chunk.data); + } else if chunk.chunk_type == *b"tRNS" { + trns_data = Some(chunk.data); + } + } + + if let Some(plte) = plte_data { + let num_colors = plte.len() / 3; + let trns = trns_data.unwrap_or(&[]); + for i in 0..num_colors { + palette.push(plte[i * 3]); + palette.push(plte[i * 3 + 1]); + palette.push(plte[i * 3 + 2]); + if i < trns.len() { + palette.push(trns[i]); + } else { + palette.push(255); + } + } + } + palette.into() +} + +pub fn sniff_bkgd(data: &[u8]) -> crate::vec_u8::VecU8 { + let chunks = parse_png_chunks(data); + for chunk in &chunks { + if chunk.chunk_type == *b"bKGD" { + return chunk.data.to_vec().into(); + } + } + Vec::new().into() +}