@@ -13,16 +13,11 @@ use std::io;
1313use crate :: common:: { AnyExtension , Block , DisposalMethod , Extension , Frame } ;
1414use crate :: reader:: DecodeOptions ;
1515use crate :: MemoryLimit ;
16- use crate :: Repeat ;
1716
1817use weezl:: { decode:: Decoder as LzwDecoder , BitOrder , LzwError , LzwStatus } ;
1918
2019/// GIF palettes are RGB
2120pub const PLTE_CHANNELS : usize = 3 ;
22- /// Headers for supported extensions.
23- const EXT_NAME_NETSCAPE : & [ u8 ] = b"NETSCAPE2.0\x01 " ;
24- const EXT_NAME_XMP : & [ u8 ] = b"XMP DataXMP" ;
25- const EXT_NAME_ICC : & [ u8 ] = b"ICCRGBG1012" ;
2621
2722/// An error returned in the case of the image not being formatted properly.
2823#[ derive( Debug ) ]
@@ -50,6 +45,8 @@ impl error::Error for DecodingFormatError {
5045pub enum DecodingError {
5146 /// Failed to internally allocate a buffer of sufficient size.
5247 OutOfMemory ,
48+ /// Allocation exceeded set memory limit
49+ MemoryLimit ,
5350 /// Expected a decoder but none found.
5451 DecoderNotFound ,
5552 /// Expected an end-code, but none found.
@@ -78,6 +75,7 @@ impl fmt::Display for DecodingError {
7875 fn fmt ( & self , fmt : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
7976 match * self {
8077 Self :: OutOfMemory => fmt. write_str ( "Out of Memory" ) ,
78+ Self :: MemoryLimit => fmt. write_str ( "Memory limit reached" ) ,
8179 Self :: DecoderNotFound => fmt. write_str ( "Decoder Not Found" ) ,
8280 Self :: EndCodeNotFound => fmt. write_str ( "End-Code Not Found" ) ,
8381 Self :: UnexpectedEof => fmt. write_str ( "Unexpected End of File" ) ,
@@ -93,6 +91,7 @@ impl error::Error for DecodingError {
9391 fn source ( & self ) -> Option < & ( dyn error:: Error + ' static ) > {
9492 match * self {
9593 Self :: OutOfMemory => None ,
94+ Self :: MemoryLimit => None ,
9695 Self :: DecoderNotFound => None ,
9796 Self :: EndCodeNotFound => None ,
9897 Self :: UnexpectedEof => None ,
@@ -146,28 +145,21 @@ pub enum Decoded {
146145 GlobalPalette ( Box < [ u8 ] > ) ,
147146 /// Index of the background color in the global palette.
148147 BackgroundColor ( u8 ) ,
149- /// Loop count is known
150- Repetitions ( Repeat ) ,
151148 /// Palette and optional `Application` extension have been parsed,
152149 /// reached frame data.
153150 HeaderEnd ,
154151 /// The start of a block.
155152 /// `BlockStart(Block::Trailer)` is the very last decode event
156153 BlockStart ( Block ) ,
157- /// Decoded a sub-block. More sub-block are available.
154+ /// Decoded a sub-block.
158155 ///
159- /// Indicates the label of the extension which might be unknown. A label of `0` is used when
160- /// the sub block does not belong to an extension.
161- ///
162- /// Call `last_ext()` to get the data
163- SubBlockFinished ( AnyExtension ) ,
164- /// Decoded the last (or only) sub-block of a block.
165- ///
166- /// Indicates the label of the extension which might be unknown. A label of `0` is used when
167- /// the sub block does not belong to an extension.
168- ///
169- /// Call `last_ext()` to get the data
170- BlockFinished ( AnyExtension ) ,
156+ /// Call `last_ext_sub_block()` to get the sub-block data. It won't be available after this event.
157+ SubBlock {
158+ /// An ext label of `0` is used when the sub block does not belong to an extension.
159+ ext : AnyExtension ,
160+ /// if true, then no more sub-blocks are available in this block.
161+ is_last : bool ,
162+ } ,
171163 /// Decoded all information of the next frame, except the image data.
172164 ///
173165 /// The returned frame does **not** contain any owned image data.
@@ -192,9 +184,11 @@ enum State {
192184 BlockStart ( u8 ) ,
193185 BlockEnd ,
194186 ExtensionBlockStart ,
187+ /// Resets ext.data
188+ ExtensionDataSubBlockStart ( usize ) ,
195189 /// Collects data in ext.data
196- ExtensionDataBlock ( usize ) ,
197- ApplicationExtension ,
190+ ExtensionDataSubBlock ( usize ) ,
191+ ExtensionBlockEnd ,
198192 LocalPalette ( usize ) ,
199193 LzwInit ( u8 ) ,
200194 /// Decompresses LZW
@@ -212,6 +206,7 @@ use super::converter::PixelConverter;
212206pub struct FrameDecoder {
213207 lzw_reader : LzwReader ,
214208 pixel_converter : PixelConverter ,
209+ memory_limit : MemoryLimit ,
215210}
216211
217212impl FrameDecoder {
@@ -221,7 +216,8 @@ impl FrameDecoder {
221216 pub fn new ( options : DecodeOptions ) -> Self {
222217 Self {
223218 lzw_reader : LzwReader :: new ( options. check_for_end_code ) ,
224- pixel_converter : PixelConverter :: new ( options. color_output , options. memory_limit ) ,
219+ pixel_converter : PixelConverter :: new ( options. color_output ) ,
220+ memory_limit : options. memory_limit . clone ( ) ,
225221 }
226222 }
227223
@@ -236,7 +232,9 @@ impl FrameDecoder {
236232 /// If you get an error about invalid min code size, the buffer was probably pixels, not compressed data.
237233 #[ inline]
238234 pub fn decode_lzw_encoded_frame ( & mut self , frame : & mut Frame < ' _ > ) -> Result < ( ) , DecodingError > {
239- let pixel_bytes = self . pixel_converter . check_buffer_size ( frame) ?;
235+ let pixel_bytes = self
236+ . pixel_converter
237+ . check_buffer_size ( frame, & self . memory_limit ) ?;
240238 let mut vec = vec ! [ 0 ; pixel_bytes] ;
241239 self . decode_lzw_encoded_frame_into_buffer ( frame, & mut vec) ?;
242240 frame. buffer = Cow :: Owned ( vec) ;
@@ -382,10 +380,6 @@ pub struct StreamingDecoder {
382380 current : Option < Frame < ' static > > ,
383381 /// Needs to emit `HeaderEnd` once
384382 header_end_reached : bool ,
385- /// XMP metadata bytes.
386- xmp_metadata : Option < Vec < u8 > > ,
387- /// ICC profile bytes.
388- icc_profile : Option < Vec < u8 > > ,
389383}
390384
391385/// One version number of the GIF standard.
@@ -400,8 +394,6 @@ pub enum Version {
400394struct ExtensionData {
401395 id : AnyExtension ,
402396 data : Vec < u8 > ,
403- sub_block_lens : Vec < u8 > ,
404- is_block_end : bool ,
405397}
406398
407399/// Destination to write to for `StreamingDecoder::update`
@@ -429,9 +421,7 @@ impl OutputBuffer<'_> {
429421 OutputBuffer :: Vec ( vec) => {
430422 let vec: & mut Vec < u8 > = vec;
431423 let len = buf. len ( ) ;
432- memory_limit. check_size ( vec. len ( ) + len) ?;
433- vec. try_reserve ( len)
434- . map_err ( |_| DecodingError :: OutOfMemory ) ?;
424+ memory_limit. try_reserve ( vec, len) ?;
435425 if vec. capacity ( ) - vec. len ( ) >= len {
436426 vec. extend_from_slice ( buf) ;
437427 }
@@ -470,13 +460,9 @@ impl StreamingDecoder {
470460 ext : ExtensionData {
471461 id : AnyExtension ( 0 ) ,
472462 data : Vec :: with_capacity ( 256 ) , // 0xFF + 1 byte length
473- sub_block_lens : Vec :: new ( ) ,
474- is_block_end : true ,
475463 } ,
476464 current : None ,
477465 header_end_reached : false ,
478- xmp_metadata : None ,
479- icc_profile : None ,
480466 }
481467 }
482468
@@ -503,10 +489,11 @@ impl StreamingDecoder {
503489 Ok ( ( len - buf. len ( ) , Decoded :: Nothing ) )
504490 }
505491
506- /// Returns the data of the last extension that has been decoded.
492+ /// Data of the last extension sub block that has been decoded.
493+ /// You need to concatenate all subblocks together to get the overall block content.
507494 #[ must_use]
508- pub fn last_ext ( & self ) -> ( AnyExtension , & [ u8 ] , bool ) {
509- ( self . ext . id , & self . ext . data , self . ext . is_block_end )
495+ pub fn last_ext_sub_block ( & mut self ) -> & [ u8 ] {
496+ & self . ext . data
510497 }
511498
512499 /// Current frame info as a mutable ref.
@@ -543,18 +530,6 @@ impl StreamingDecoder {
543530 self . height
544531 }
545532
546- /// XMP metadata stored in the image.
547- #[ must_use]
548- pub fn xmp_metadata ( & self ) -> Option < & [ u8 ] > {
549- self . xmp_metadata . as_deref ( )
550- }
551-
552- /// ICC profile stored in the image.
553- #[ must_use]
554- pub fn icc_profile ( & self ) -> Option < & [ u8 ] > {
555- self . icc_profile . as_deref ( )
556- }
557-
558533 /// The version number of the GIF standard used in this image.
559534 ///
560535 /// We suppose a minimum of `V87a` compatibility. This value will be reported until we have
@@ -739,6 +714,13 @@ impl StreamingDecoder {
739714 }
740715 }
741716 }
717+ ExtensionBlockStart => {
718+ goto ! ( ExtensionDataSubBlockStart ( b as usize ) , emit Decoded :: BlockStart ( Block :: Extension ) )
719+ }
720+ ExtensionBlockEnd => {
721+ self . ext . data . clear ( ) ;
722+ goto ! ( 0 , BlockEnd )
723+ }
742724 BlockEnd => {
743725 if b == Block :: Trailer as u8 {
744726 // can't consume yet, because the trailer is not a real block,
@@ -748,49 +730,27 @@ impl StreamingDecoder {
748730 goto ! ( BlockStart ( b) )
749731 }
750732 }
751- ExtensionBlockStart => {
733+ ExtensionDataSubBlockStart ( sub_block_len ) => {
752734 self . ext . data . clear ( ) ;
753- self . ext . sub_block_lens . clear ( ) ;
754- self . ext . is_block_end = false ;
755- self . ext . sub_block_lens . push ( b) ;
756- goto ! ( ExtensionDataBlock ( b as usize ) , emit Decoded :: BlockStart ( Block :: Extension ) )
735+ goto ! ( 0 , ExtensionDataSubBlock ( sub_block_len) )
757736 }
758- ExtensionDataBlock ( left) => {
737+ ExtensionDataSubBlock ( left) => {
759738 if left > 0 {
760739 let n = cmp:: min ( left, buf. len ( ) ) ;
761- self . memory_limit . check_size ( self . ext . data . len ( ) + n ) ? ;
762- self . ext
763- . data
764- . try_reserve ( n )
765- . map_err ( |_| DecodingError :: OutOfMemory ) ? ;
740+ let needs_to_grow =
741+ n > self . ext . data . capacity ( ) . wrapping_sub ( self . ext . data . len ( ) ) ;
742+ if needs_to_grow {
743+ return Err ( DecodingError :: OutOfMemory ) ;
744+ }
766745 self . ext . data . extend_from_slice ( & buf[ ..n] ) ;
767- goto ! ( n, ExtensionDataBlock ( left - n) )
746+ goto ! ( n, ExtensionDataSubBlock ( left - n) )
768747 } else if b == 0 {
769- self . ext . is_block_end = true ;
770- match self . ext . id . into_known ( ) {
771- Some ( Extension :: Application ) => {
772- goto ! ( 0 , ApplicationExtension , emit Decoded :: BlockFinished ( self . ext. id) )
773- }
774- Some ( Extension :: Control ) => {
775- self . read_control_extension ( ) ?;
776- goto ! ( BlockEnd , emit Decoded :: BlockFinished ( self . ext. id) )
777- }
778- _ => {
779- goto ! ( BlockEnd , emit Decoded :: BlockFinished ( self . ext. id) )
780- }
748+ if self . ext . id . into_known ( ) == Some ( Extension :: Control ) {
749+ self . read_control_extension ( ) ?;
781750 }
751+ goto ! ( ExtensionBlockEnd , emit Decoded :: SubBlock { ext: self . ext. id, is_last: true } )
782752 } else {
783- self . ext . sub_block_lens . push ( b) ;
784- self . ext . is_block_end = false ;
785- goto ! ( ExtensionDataBlock ( b as usize ) , emit Decoded :: SubBlockFinished ( self . ext. id) )
786- }
787- }
788- ApplicationExtension => {
789- debug_assert_eq ! ( 0 , b) ;
790- if let Some ( decoded) = self . read_application_extension ( ) {
791- goto ! ( BlockEnd , emit decoded)
792- } else {
793- goto ! ( BlockEnd )
753+ goto ! ( ExtensionDataSubBlockStart ( b as usize ) , emit Decoded :: SubBlock { ext: self . ext. id, is_last: false } )
794754 }
795755 }
796756 LocalPalette ( left) => {
@@ -897,48 +857,6 @@ impl StreamingDecoder {
897857 Ok ( ( ) )
898858 }
899859
900- fn read_application_extension ( & mut self ) -> Option < Decoded > {
901- if let Some ( & [ first, second] ) = self . ext . data . strip_prefix ( EXT_NAME_NETSCAPE ) {
902- let repeat = u16:: from ( first) | u16:: from ( second) << 8 ;
903- return Some ( Decoded :: Repetitions ( if repeat == 0 {
904- Repeat :: Infinite
905- } else {
906- Repeat :: Finite ( repeat)
907- } ) ) ;
908- } else if let Some ( mut rest) = self . ext . data . strip_prefix ( EXT_NAME_XMP ) {
909- let ( & id_len, data_lens) = self . ext . sub_block_lens . split_first ( ) ?;
910- if id_len as usize != EXT_NAME_XMP . len ( ) {
911- return None ;
912- }
913- // XMP is not written as a valid "pascal-string", so we need to stitch together
914- // the text from our collected sublock-lengths.
915- let mut xmp_metadata = Vec :: with_capacity ( data_lens. len ( ) + self . ext . data . len ( ) ) ;
916- for & len in data_lens {
917- xmp_metadata. push ( len) ;
918- let ( sub_block, tail) = rest. split_at ( len as usize ) ;
919- xmp_metadata. extend_from_slice ( sub_block) ;
920- rest = tail;
921- }
922-
923- // XMP adds a "ramp" of 257 bytes to the end of the metadata to let the "pascal-strings"
924- // parser converge to the null byte. The ramp looks like "0x01, 0xff, .., 0x01, 0x00".
925- // For convenience and to allow consumers to not be bothered with this implementation detail,
926- // we cut the ramp.
927- const RAMP_SIZE : usize = 257 ;
928- if xmp_metadata. len ( ) >= RAMP_SIZE
929- && xmp_metadata. ends_with ( & [ 0x03 , 0x02 , 0x01 , 0x00 ] )
930- && xmp_metadata[ xmp_metadata. len ( ) - RAMP_SIZE ..] . starts_with ( & [ 0x01 , 0x0ff ] )
931- {
932- xmp_metadata. truncate ( xmp_metadata. len ( ) - RAMP_SIZE ) ;
933- }
934-
935- self . xmp_metadata = Some ( xmp_metadata) ;
936- } else if let Some ( rest) = self . ext . data . strip_prefix ( EXT_NAME_ICC ) {
937- self . icc_profile = Some ( rest. to_vec ( ) ) ;
938- }
939- None
940- }
941-
942860 fn add_frame ( & mut self ) {
943861 if self . current . is_none ( ) {
944862 self . current = Some ( Frame :: default ( ) ) ;
0 commit comments