Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions pixel_bridge/pixel_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -359,4 +359,24 @@ absl::StatusOr<ImageDecoder> ImageReader::IntoDecoder() && {
return ImageDecoder(std::move(decoder).value());
}

std::vector<uint8_t> SniffPalette(absl::string_view data) {
auto result = rust::image::sniff_palette(absl::Span<const uint8_t>(
reinterpret_cast<const uint8_t*>(data.data()), data.size()));
std::vector<uint8_t> palette(result.len());
if (result.len() > 0) {
memcpy(palette.data(), result.as_ptr(), result.len());
}
return palette;
}

std::vector<uint8_t> SniffBkgd(absl::string_view data) {
auto result = rust::image::sniff_bkgd(absl::Span<const uint8_t>(
reinterpret_cast<const uint8_t*>(data.data()), data.size()));
std::vector<uint8_t> bkgd(result.len());
if (result.len() > 0) {
memcpy(bkgd.data(), result.as_ptr(), result.len());
}
return bkgd;
}

} // namespace security::pixel_bridge
3 changes: 3 additions & 0 deletions pixel_bridge/pixel_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ struct Strides final {
uintptr_t channels;
};

std::vector<uint8_t> SniffPalette(absl::string_view data);
std::vector<uint8_t> SniffBkgd(absl::string_view data);

class Frame final {
public:
// Frames should not be copied, because the data it carries is too big for
Expand Down
77 changes: 77 additions & 0 deletions pixel_bridge/rust/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,3 +683,80 @@
}
}
}

struct Chunk<'a> {
chunk_type: [u8; 4],
data: &'a [u8],
}

fn parse_png_chunks(data: &[u8]) -> Vec<Chunk> {

Check warning on line 692 in pixel_bridge/rust/image.rs

View workflow job for this annotation

GitHub Actions / build pixel_bridge

hiding a lifetime that's elided elsewhere is confusing
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()
}
Loading