@@ -24,6 +24,12 @@ pub enum ColorType {
2424 Rgba8 ,
2525}
2626
27+ impl ColorType {
28+ fn has_alpha ( self ) -> bool {
29+ self == ColorType :: La8 || self == ColorType :: Rgba8
30+ }
31+ }
32+
2733quick_error ! {
2834 /// Error that can occur during encoding.
2935 #[ derive( Debug ) ]
@@ -604,6 +610,51 @@ fn encode_frame_lossless<W: Write>(
604610 Ok ( ( ) )
605611}
606612
613+ /// Encodes the alpha part of the image data losslessly.
614+ /// Used for lossy images that include transparency.
615+ ///
616+ /// # Panics
617+ ///
618+ /// Panics if the image data is not of the indicated dimensions.
619+ fn encode_alpha_lossless < W : Write > (
620+ mut writer : W ,
621+ data : & [ u8 ] ,
622+ width : u32 ,
623+ height : u32 ,
624+ color : ColorType ,
625+ ) -> Result < ( ) , EncodingError > {
626+ let bytes_per_pixel = match color {
627+ ColorType :: La8 => 2 ,
628+ ColorType :: Rgba8 => 4 ,
629+ _ => unreachable ! ( ) ,
630+ } ;
631+ if width == 0 || width > 16384 || height == 0 || height > 16384 {
632+ return Err ( EncodingError :: InvalidDimensions ) ;
633+ }
634+
635+ let preprocessing = 0u8 ;
636+ let filtering_method = 0u8 ;
637+ let compression_method = 0u8 ;
638+
639+ let initial_byte = preprocessing << 4 | filtering_method << 2 | compression_method;
640+
641+ writer. write_all ( & [ initial_byte] ) ?;
642+
643+ // uncompressed raw alpha data
644+ let alpha_data: Vec < u8 > = data
645+ . iter ( )
646+ . skip ( bytes_per_pixel - 1 )
647+ . step_by ( bytes_per_pixel)
648+ . copied ( )
649+ . collect ( ) ;
650+
651+ debug_assert_eq ! ( alpha_data. len( ) , ( width * height) as usize ) ;
652+
653+ writer. write_all ( & alpha_data) ?;
654+
655+ Ok ( ( ) )
656+ }
657+
607658const fn chunk_size ( inner_bytes : usize ) -> u32 {
608659 if inner_bytes % 2 == 1 {
609660 ( inner_bytes + 1 ) as u32 + 8
@@ -681,6 +732,8 @@ impl<W: Write> WebPEncoder<W> {
681732 ) -> Result < ( ) , EncodingError > {
682733 let mut frame = Vec :: new ( ) ;
683734
735+ let lossy_with_alpha = self . params . use_lossy && color. has_alpha ( ) ;
736+
684737 let frame_chunk = if self . params . use_lossy {
685738 encode_frame_lossy (
686739 & mut frame,
@@ -696,11 +749,14 @@ impl<W: Write> WebPEncoder<W> {
696749 b"VP8L"
697750 } ;
698751
699- // If the image has no metadata, it can be encoded with the "simple" WebP container format.
700- if self . icc_profile . is_empty ( )
752+ // If the image has no metadata and isn't lossy with alpha,
753+ // it can be encoded with the "simple" WebP container format.
754+ let use_simple_container = self . icc_profile . is_empty ( )
701755 && self . exif_metadata . is_empty ( )
702756 && self . xmp_metadata . is_empty ( )
703- {
757+ && !lossy_with_alpha;
758+
759+ if use_simple_container {
704760 self . writer . write_all ( b"RIFF" ) ?;
705761 self . writer
706762 . write_all ( & ( chunk_size ( frame. len ( ) ) + 4 ) . to_le_bytes ( ) ) ?;
@@ -718,14 +774,24 @@ impl<W: Write> WebPEncoder<W> {
718774 total_bytes += chunk_size ( self . xmp_metadata . len ( ) ) ;
719775 }
720776
777+ let alpha_chunk_data = if lossy_with_alpha {
778+ let mut alpha_chunk = Vec :: new ( ) ;
779+ encode_alpha_lossless ( & mut alpha_chunk, data, width, height, color) ?;
780+
781+ total_bytes += chunk_size ( alpha_chunk. len ( ) ) ;
782+ Some ( alpha_chunk)
783+ } else {
784+ None
785+ } ;
786+
721787 let mut flags = 0 ;
722788 if !self . xmp_metadata . is_empty ( ) {
723789 flags |= 1 << 2 ;
724790 }
725791 if !self . exif_metadata . is_empty ( ) {
726792 flags |= 1 << 3 ;
727793 }
728- if let ColorType :: La8 | ColorType :: Rgba8 = color {
794+ if color. has_alpha ( ) {
729795 flags |= 1 << 4 ;
730796 }
731797 if !self . icc_profile . is_empty ( ) {
@@ -747,6 +813,10 @@ impl<W: Write> WebPEncoder<W> {
747813 write_chunk ( & mut self . writer , b"ICCP" , & self . icc_profile ) ?;
748814 }
749815
816+ if let Some ( alpha_chunk) = alpha_chunk_data {
817+ write_chunk ( & mut self . writer , b"ALPH" , & alpha_chunk) ?;
818+ }
819+
750820 write_chunk ( & mut self . writer , frame_chunk, & frame) ?;
751821
752822 if !self . exif_metadata . is_empty ( ) {
0 commit comments