@ -12,6 +12,8 @@ namespace ImageSharp.Formats
using System.Linq ;
using System.Text ;
using static ComparableExtensions ;
/// <summary>
/// Performs the png decoding operation.
/// </summary>
@ -112,7 +114,8 @@ namespace ImageSharp.Formats
/// Thrown if the image is larger than the maximum allowable size.
/// </exception>
public void Decode < TColor , TPacked > ( Image < TColor , TPacked > image , Stream stream )
where TColor : struct , IPackedPixel < TPacked > where TPacked : struct
where TColor : struct , IPackedPixel < TPacked >
where TPacked : struct
{
Image < TColor , TPacked > currentImage = image ;
this . currentStream = stream ;
@ -130,39 +133,49 @@ namespace ImageSharp.Formats
throw new ImageFormatException ( "Image does not end with end chunk." ) ;
}
swi tch ( cu rrentChunk . T ype )
try
{
case PngChunkTypes . Header :
this . ReadHeaderChunk ( currentChunk . Data ) ;
this . ValidateHeader ( ) ;
break ;
case PngChunkTypes . Physical :
this . ReadPhysicalChunk ( currentImage , currentChunk . Data ) ;
break ;
case PngChunkTypes . Data :
dataStream . Write ( currentChunk . Data , 0 , currentChunk . Data . Length ) ;
break ;
case PngChunkTypes . Palette :
this . palette = currentChunk . Data ;
image . Quality = this . palette . Length / 3 ;
break ;
case PngChunkTypes . PaletteAlpha :
this . paletteAlpha = currentChunk . Data ;
break ;
case PngChunkTypes . Text :
this . ReadTextChunk ( currentImage , currentChunk . Data ) ;
break ;
case PngChunkTypes . End :
isEndChunkReached = true ;
break ;
switch ( currentChunk . Type )
{
case PngChunkTypes . Header :
this . ReadHeaderChunk ( currentChunk . Data ) ;
this . ValidateHeader ( ) ;
break ;
case PngChunkTypes . Physical :
this . ReadPhysicalChunk ( currentImage , currentChunk . Data ) ;
break ;
case PngChunkTypes . Data :
dataStream . Write ( currentChunk . Data , 0 , currentChunk . Length ) ;
break ;
case PngChunkTypes . Palette :
byte [ ] pal = new byte [ currentChunk . Length ] ;
Buffer . BlockCopy ( currentChunk . Data , 0 , pal , 0 , currentChunk . Length ) ;
this . palette = pal ;
image . Quality = pal . Length / 3 ;
break ;
case PngChunkTypes . PaletteAlpha :
byte [ ] alpha = new byte [ currentChunk . Length ] ;
Buffer . BlockCopy ( currentChunk . Data , 0 , alpha , 0 , currentChunk . Length ) ;
this . paletteAlpha = alpha ;
break ;
case PngChunkTypes . Text :
this . ReadTextChunk ( currentImage , currentChunk . Data , currentChunk . Length ) ;
break ;
case PngChunkTypes . End :
isEndChunkReached = true ;
break ;
}
}
finally
{
// Data is rented in ReadChunkData()
ArrayPool < byte > . Shared . Return ( currentChunk . Data ) ;
}
}
if ( this . header . Width > image . MaxWidth | | this . header . Height > image . MaxHeight )
{
throw new ArgumentOutOfRangeException (
$"The input png '{this.header.Width}x{this.header.Height}' is bigger than the "
+ $"max allowed size '{image.MaxWidth}x{image.MaxHeight}'" ) ;
throw new ArgumentOutOfRangeException ( $"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{image.MaxWidth}x{image.MaxHeight}'" ) ;
}
image . InitPixels ( this . header . Width , this . header . Height ) ;
@ -182,7 +195,8 @@ namespace ImageSharp.Formats
/// <param name="image">The image to read to.</param>
/// <param name="data">The data containing physical data.</param>
private void ReadPhysicalChunk < TColor , TPacked > ( Image < TColor , TPacked > image , byte [ ] data )
where TColor : struct , IPackedPixel < TPacked > where TPacked : struct
where TColor : struct , IPackedPixel < TPacked >
where TPacked : struct
{
data . ReverseBytes ( 0 , 4 ) ;
data . ReverseBytes ( 4 , 4 ) ;
@ -243,7 +257,8 @@ namespace ImageSharp.Formats
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <param name="pixels"> The pixel data.</param>
private void ReadScanlines < TColor , TPacked > ( MemoryStream dataStream , PixelAccessor < TColor , TPacked > pixels )
where TColor : struct , IPackedPixel < TPacked > where TPacked : struct
where TColor : struct , IPackedPixel < TPacked >
where TPacked : struct
{
this . bytesPerPixel = this . CalculateBytesPerPixel ( ) ;
this . bytesPerScanline = this . CalculateScanlineLength ( ) + 1 ;
@ -268,7 +283,8 @@ namespace ImageSharp.Formats
/// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="pixels">The image pixel accessor.</param>
private void DecodePixelData < TColor , TPacked > ( Stream compressedStream , PixelAccessor < TColor , TPacked > pixels )
where TColor : struct , IPackedPixel < TPacked > where TPacked : struct
where TColor : struct , IPackedPixel < TPacked >
where TPacked : struct
{
byte [ ] previousScanline = ArrayPool < byte > . Shared . Rent ( this . bytesPerScanline ) ;
byte [ ] scanline = ArrayPool < byte > . Shared . Rent ( this . bytesPerScanline ) ;
@ -319,9 +335,7 @@ namespace ImageSharp.Formats
this . ProcessDefilteredScanline ( scanline , y , pixels ) ;
byte [ ] temp = previousScanline ;
previousScanline = scanline ;
scanline = temp ;
Swap ( ref scanline , ref previousScanline ) ;
}
}
finally
@ -396,6 +410,10 @@ namespace ImageSharp.Formats
byte b = this . palette [ pixelOffset + 2 ] ;
color . PackFromBytes ( r , g , b , a ) ;
}
else
{
color . PackFromBytes ( 0 , 0 , 0 , 0 ) ;
}
pixels [ x , row ] = color ;
}
@ -460,13 +478,14 @@ namespace ImageSharp.Formats
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The image to decode to.</param>
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
private void ReadTextChunk < TColor , TPacked > ( Image < TColor , TPacked > image , byte [ ] data )
/// <param name="length">The maximum length to read.</param>
private void ReadTextChunk < TColor , TPacked > ( Image < TColor , TPacked > image , byte [ ] data , int length )
where TColor : struct , IPackedPixel < TPacked >
where TPacked : struct
{
int zeroIndex = 0 ;
for ( int i = 0 ; i < data . L ength; i + + )
for ( int i = 0 ; i < l ength; i + + )
{
if ( data [ i ] = = 0 )
{
@ -476,7 +495,7 @@ namespace ImageSharp.Formats
}
string name = Encoding . Unicode . GetString ( data , 0 , zeroIndex ) ;
string value = Encoding . Unicode . GetString ( data , zeroIndex + 1 , data . L ength - zeroIndex - 1 ) ;
string value = Encoding . Unicode . GetString ( data , zeroIndex + 1 , l ength - zeroIndex - 1 ) ;
image . Properties . Add ( new ImageProperty ( name , value ) ) ;
}
@ -577,7 +596,7 @@ namespace ImageSharp.Formats
Crc32 crc = new Crc32 ( ) ;
crc . Update ( this . chunkTypeBuffer ) ;
crc . Update ( chunk . Data ) ;
crc . Update ( chunk . Data , 0 , chunk . Length ) ;
if ( crc . Value ! = chunk . Crc )
{
@ -591,8 +610,8 @@ namespace ImageSharp.Formats
/// <param name="chunk">The chunk.</param>
private void ReadChunkData ( PngChunk chunk )
{
// TODO: It might be possible to rent this but that could also lead to issues assigning the data to various properties
chunk . Data = new byte [ chunk . Length ] ;
// We rent the buffer here to return it afterwards in Decode()
chunk . Data = ArrayPool < byte > . Shared . Rent ( chunk . Length ) ;
this . currentStream . Read ( chunk . Data , 0 , chunk . Length ) ;
}
@ -645,4 +664,4 @@ namespace ImageSharp.Formats
chunk . Length = BitConverter . ToInt32 ( this . chunkLengthBuffer , 0 ) ;
}
}
}
}