}
self.left = self.src[self.pos];
self.pos += 1;
- validate!(self.left > 0);
+ if self.left == 0 { // abuse this code to signal end of stream
+ return Err(DecoderError::MissingReference);
+ }
if self.pos + usize::from(self.left) > self.src.len() {
return Err(DecoderError::ShortData);
}
let mut pos = 0;
let mut lastidx = INVALID_POS;
loop {
- let idx = br.read(self.idx_bits)? as usize;
+ let idx = match br.read(self.idx_bits) {
+ Ok(val) => val as usize,
+ Err(DecoderError::MissingReference) => {
+ validate!(pos == dst.len());
+ return Ok(());
+ },
+ Err(err) => return Err(err)
+ };
if idx == reset_sym {
self.reset(bits);
lastidx = INVALID_POS;
height: usize,
lzw: LZWState,
transp: Option<u8>,
+ is_qt: bool,
}
fn deinterlace(sbuf: &[u8], dbuf: &mut [u8], width: usize) {
}
impl GIFDecoder {
- fn new() -> Self {
+ fn new(is_qt: bool) -> Self {
Self {
info: NACodecInfoRef::default(),
gpal: [0; 768],
height: 0,
lzw: LZWState::new(),
transp: None,
+ is_qt,
}
}
}
}
let mut br = MemoryReader::new_read(&src);
+ if self.is_qt { // skip header and image descriptor
+ if let Ok([b'G', b'I', b'F', b'8']) = br.peek_tag() {
+ br.read_skip(6)?;
+ let width = usize::from(br.read_u16le()?);
+ let height = usize::from(br.read_u16le()?);
+ validate!(width == self.width && height == self.height);
+ let flags = br.read_byte()?;
+ let edata_size = 2 + if (flags & 0x80) != 0 { 3 << ((flags & 7) + 1) } else { 0 };
+ br.read_skip(edata_size)?;
+ }
+ while let Ok(0x21) = br.peek_byte() {
+ br.read_byte()?;
+ let _label = br.read_byte()?;
+ loop {
+ let len = usize::from(br.read_byte()?);
+ if len == 0 {
+ break;
+ }
+ br.read_skip(len)?;
+ }
+ }
+ }
let tag = br.read_byte()?;
validate!(tag == 0x2C);
let left = usize::from(br.read_u16le()?);
}
pub fn get_decoder() -> Box<dyn NADecoder + Send> {
- Box::new(GIFDecoder::new())
+ Box::new(GIFDecoder::new(false))
+}
+pub fn get_decoder_qt() -> Box<dyn NADecoder + Send> {
+ Box::new(GIFDecoder::new(true))
}
#[cfg(test)]
[0xdf920e8c, 0xeb57c5f8, 0xd862507e, 0xd733fca3],
[0x75bee5cb, 0xefb2076c, 0xfce61f8a, 0x2d2b30df]]));
}
+ // sample from Tex-Edit Plus 4.x distribution
+ #[test]
+ fn test_gif_qt_decoder() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ generic_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ generic_register_all_decoders(&mut dec_reg);
+
+ test_decoding("mov", "qt-gif", "assets/Misc/The Duke",
+ Some(2), &dmx_reg, &dec_reg,
+ ExpectedTestResult::MD5Frames(vec![
+ [0x2c62c68c, 0x3c8a076f, 0xd8914e96, 0xb26819f6],
+ [0xc7243219, 0x322077f4, 0x7fca320f, 0xc96f6cc1],
+ [0x0eaeeb20, 0xdc8b2c70, 0x861ce6a1, 0xef0e1e4f]]));
+ }
}
desc!(audio-hyb; "wavpack", "WavPack"),
desc!(video-ll; "gif", "GIF"),
+ desc!(video-ll; "qt-gif", "GIF (in MOV)"),
desc!(video-im; "jpeg", "JPEG"),
desc!(video; "h264", "ITU H.264", CODEC_CAP_COMPLEX_REORDER | CODEC_CAP_HYBRID),
desc!(video-modern; "mpeg4asp", "MPEG-4 ASP"),
(b"svqi", "sorenson-video"),
(b"SVQ3", "sorenson-video3"),
(b"svq3", "sorenson-video3"),
+ (b"gif ", "qt-gif"),
(b"msvc", "msvideo1"),