Skip to content
Merged
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
57 changes: 57 additions & 0 deletions examples/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,67 @@ fn main() {
frame.dispose,
frame.needs_user_input
);

reader.icc_profile().map(|icc| {
println!(" ICC profile: {} bytes", icc.len());
});

reader.xmp_metadata().map(|xmp| {
println!(" XMP metadata: {} bytes", xmp.len());
});

reader.photoshop_irb().map(|irb| {
println!(" Photoshop IRB: {} bytes", irb.len());
iterate_irb(irb);
});
}

let _ = reader.next_frame_info();

reader.icc_profile().map(|icc| {
println!(" ICC profile: {} bytes", icc.len());
});
}

fn explain_usage() -> ! {
println!("Print information on the frames of a gif.\n\nUsage: check <file>");
process::exit(1)
}

fn iterate_irb(mut data: &[u8]) {
while data.len() >= 12 {
let signature = &data[0..4];
let key = u16::from_be_bytes(data[4..6].as_chunks::<2>().0[0]);

let identifier_len = data[6];
let identifier = &data[7..][..usize::from(identifier_len)];

let padded_len = (usize::from(identifier_len) + 1).div_ceil(2) * 2;
let length = u32::from_be_bytes(data[6..][padded_len..].as_chunks::<4>().0[0]) as usize;

if signature != b"8BIM" {
println!(" Invalid IRB signature: {:?}", signature);
return;
}

if data.len() < 12 + length {
println!(" Truncated IRB data");
return;
}

println!(
" IRB block: key={:x}/{:?} length={}",
key,
str::from_utf8(identifier).unwrap_or("<invalid utf8>"),
length
);

if key == 0x3f8 {
println!(" (contains transfer functions)");
println!(" {:?}", &data[6 + padded_len + 4..][..length]);
}

let total_length = 6 + padded_len + 4 + length + (length % 2); // padded to even length
data = &data[total_length..];
}
}
25 changes: 25 additions & 0 deletions src/reader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,15 +259,18 @@ impl<R: Read> ReadDecoder<R> {
const EXT_NAME_NETSCAPE: &[u8] = b"NETSCAPE2.0";
const EXT_NAME_XMP: &[u8] = b"XMP DataXMP";
const EXT_NAME_ICC: &[u8] = b"ICCRGBG1012";
const EXT_NAME_MAGICK_8BIM: &[u8] = b"MGK8BIM0000";

/// State when parsing application extension
#[derive(Debug)]
enum AppExtensionState {
/// Waiting for app name
None,
Netscape,
Xmp,
Icc,
Skip,
Magick8Bim,
}

#[allow(dead_code)]
Expand All @@ -285,6 +288,8 @@ pub struct Decoder<R: Read> {
xmp_metadata: Option<Vec<u8>>,
/// ICC profile bytes.
icc_profile: Option<Vec<u8>>,
/// 8BIM (Photoshop) profile bytes as encoded by imagemagick.
magick_8bim_profile: Option<Vec<u8>>,
}

impl<R> Decoder<R>
Expand Down Expand Up @@ -320,6 +325,7 @@ where
app_extension_state: AppExtensionState::None,
xmp_metadata: None,
icc_profile: None,
magick_8bim_profile: None,
}
}

Expand Down Expand Up @@ -375,6 +381,10 @@ where
self.icc_profile = Some(Vec::new());
AppExtensionState::Icc
}
EXT_NAME_MAGICK_8BIM => {
self.magick_8bim_profile = Some(Vec::new());
AppExtensionState::Magick8Bim
}
_ => AppExtensionState::Skip,
}
}
Expand Down Expand Up @@ -420,6 +430,12 @@ where
icc.extend_from_slice(data);
}
}
AppExtensionState::Magick8Bim => {
if let Some(profile) = &mut self.magick_8bim_profile {
self.memory_limit.try_reserve(profile, data.len())?;
profile.extend_from_slice(data);
}
}
AppExtensionState::Skip => {}
};
if is_last {
Expand Down Expand Up @@ -607,6 +623,15 @@ where
self.icc_profile.as_deref()
}

/// Photoshop Image Resource Block stored in the image.
#[inline]
#[must_use]
#[doc(alias = "8BIM")]
#[doc(alias = "image resource block")]
pub fn photoshop_irb(&self) -> Option<&[u8]> {
self.magick_8bim_profile.as_deref()
}

/// Abort decoding and recover the `io::Read` instance
pub fn into_inner(self) -> io::BufReader<R> {
self.decoder.into_inner()
Expand Down
Loading