@@ -10,11 +10,22 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
1010{
1111 internal abstract class BitWriterBase
1212 {
13+ private const uint MaxDimension = 16777215 ;
14+
15+ private const ulong MaxCanvasPixels = 4294967295ul ;
16+
17+ protected const uint ExtendedFileChunkSize = WebpConstants . ChunkHeaderSize + WebpConstants . Vp8XChunkSize ;
18+
1319 /// <summary>
1420 /// Buffer to write to.
1521 /// </summary>
1622 private byte [ ] buffer ;
1723
24+ /// <summary>
25+ /// A scratch buffer to reduce allocations.
26+ /// </summary>
27+ private readonly byte [ ] scratchBuffer = new byte [ 4 ] ;
28+
1829 /// <summary>
1930 /// Initializes a new instance of the <see cref="BitWriterBase"/> class.
2031 /// </summary>
@@ -52,15 +63,6 @@ internal abstract class BitWriterBase
5263 /// </summary>
5364 public abstract void Finish ( ) ;
5465
55- /// <summary>
56- /// Writes the encoded image to the stream.
57- /// </summary>
58- /// <param name="stream">The stream to write to.</param>
59- /// <param name="exifProfile">The exif profile.</param>
60- /// <param name="width">The width of the image.</param>
61- /// <param name="height">The height of the image.</param>
62- public abstract void WriteEncodedImageToStream ( Stream stream , ExifProfile exifProfile , uint width , uint height ) ;
63-
6466 protected void ResizeBuffer ( int maxBytes , int sizeRequired )
6567 {
6668 int newSize = ( 3 * maxBytes ) >> 1 ;
@@ -81,13 +83,25 @@ protected void ResizeBuffer(int maxBytes, int sizeRequired)
8183 /// <param name="riffSize">The block length.</param>
8284 protected void WriteRiffHeader ( Stream stream , uint riffSize )
8385 {
84- Span < byte > buf = stackalloc byte [ 4 ] ;
8586 stream . Write ( WebpConstants . RiffFourCc ) ;
86- BinaryPrimitives . WriteUInt32LittleEndian ( buf , riffSize ) ;
87- stream . Write ( buf ) ;
87+ BinaryPrimitives . WriteUInt32LittleEndian ( this . scratchBuffer , riffSize ) ;
88+ stream . Write ( this . scratchBuffer . AsSpan ( 0 , 4 ) ) ;
8889 stream . Write ( WebpConstants . WebpHeader ) ;
8990 }
9091
92+ /// <summary>
93+ /// Calculates the exif chunk size.
94+ /// </summary>
95+ /// <param name="exifBytes">The exif profile bytes.</param>
96+ /// <returns>The exif chunk size in bytes.</returns>
97+ protected uint ExifChunkSize ( byte [ ] exifBytes )
98+ {
99+ uint exifSize = ( uint ) exifBytes . Length ;
100+ uint exifChunkSize = WebpConstants . ChunkHeaderSize + exifSize + ( exifSize & 1 ) ;
101+
102+ return exifChunkSize ;
103+ }
104+
91105 /// <summary>
92106 /// Writes the Exif profile to the stream.
93107 /// </summary>
@@ -97,12 +111,19 @@ protected void WriteExifProfile(Stream stream, byte[] exifBytes)
97111 {
98112 DebugGuard . NotNull ( exifBytes , nameof ( exifBytes ) ) ;
99113
100- Span < byte > buf = stackalloc byte [ 4 ] ;
114+ uint size = ( uint ) exifBytes . Length ;
115+ Span < byte > buf = this . scratchBuffer . AsSpan ( 0 , 4 ) ;
101116 BinaryPrimitives . WriteUInt32BigEndian ( buf , ( uint ) WebpChunkType . Exif ) ;
102117 stream . Write ( buf ) ;
103- BinaryPrimitives . WriteUInt32LittleEndian ( buf , ( uint ) exifBytes . Length ) ;
118+ BinaryPrimitives . WriteUInt32LittleEndian ( buf , size ) ;
104119 stream . Write ( buf ) ;
105120 stream . Write ( exifBytes ) ;
121+
122+ // Add padding byte if needed.
123+ if ( ( size & 1 ) == 1 )
124+ {
125+ stream . WriteByte ( 0 ) ;
126+ }
106127 }
107128
108129 /// <summary>
@@ -112,16 +133,16 @@ protected void WriteExifProfile(Stream stream, byte[] exifBytes)
112133 /// <param name="exifProfile">A exif profile or null, if it does not exist.</param>
113134 /// <param name="width">The width of the image.</param>
114135 /// <param name="height">The height of the image.</param>
115- protected void WriteVp8XHeader ( Stream stream , ExifProfile exifProfile , uint width , uint height )
136+ /// <param name="hasAlpha">Flag indicating, if a alpha channel is present.</param>
137+ protected void WriteVp8XHeader ( Stream stream , ExifProfile exifProfile , uint width , uint height , bool hasAlpha )
116138 {
117- int maxDimension = 16777215 ;
118- if ( width > maxDimension || height > maxDimension )
139+ if ( width > MaxDimension || height > MaxDimension )
119140 {
120- WebpThrowHelper . ThrowInvalidImageDimensions ( $ "Image width or height exceeds maximum allowed dimension of { maxDimension } ") ;
141+ WebpThrowHelper . ThrowInvalidImageDimensions ( $ "Image width or height exceeds maximum allowed dimension of { MaxDimension } ") ;
121142 }
122143
123144 // The spec states that the product of Canvas Width and Canvas Height MUST be at most 2^32 - 1.
124- if ( width * height > 4294967295ul )
145+ if ( width * height > MaxCanvasPixels )
125146 {
126147 WebpThrowHelper . ThrowInvalidImageDimensions ( "The product of image width and height MUST be at most 2^32 - 1" ) ;
127148 }
@@ -133,7 +154,13 @@ protected void WriteVp8XHeader(Stream stream, ExifProfile exifProfile, uint widt
133154 flags |= 8 ;
134155 }
135156
136- Span < byte > buf = stackalloc byte [ 4 ] ;
157+ if ( hasAlpha )
158+ {
159+ // Set alpha bit.
160+ flags |= 16 ;
161+ }
162+
163+ Span < byte > buf = this . scratchBuffer . AsSpan ( 0 , 4 ) ;
137164 stream . Write ( WebpConstants . Vp8XMagicBytes ) ;
138165 BinaryPrimitives . WriteUInt32LittleEndian ( buf , WebpConstants . Vp8XChunkSize ) ;
139166 stream . Write ( buf ) ;
0 commit comments