|
|
|
@ -216,7 +216,9 @@ namespace SixLabors.ImageSharp.Formats.Png |
|
|
|
public Image<TPixel> Decode<TPixel>(Stream stream) |
|
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
|
{ |
|
|
|
var metadata = new ImageMetaData(); |
|
|
|
var metaData = new ImageMetaData(); |
|
|
|
var pngMetaData = new PngMetaData(); |
|
|
|
metaData.AddOrUpdateFormatMetaData(PngFormat.Instance, pngMetaData); |
|
|
|
this.currentStream = stream; |
|
|
|
this.currentStream.Skip(8); |
|
|
|
Image<TPixel> image = null; |
|
|
|
@ -231,16 +233,19 @@ namespace SixLabors.ImageSharp.Formats.Png |
|
|
|
switch (chunk.Type) |
|
|
|
{ |
|
|
|
case PngChunkType.Header: |
|
|
|
this.ReadHeaderChunk(chunk.Data.Array); |
|
|
|
this.ReadHeaderChunk(pngMetaData, chunk.Data.Array); |
|
|
|
this.ValidateHeader(); |
|
|
|
break; |
|
|
|
case PngChunkType.Physical: |
|
|
|
this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan()); |
|
|
|
this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan()); |
|
|
|
break; |
|
|
|
case PngChunkType.Gamma: |
|
|
|
this.ReadGammaChunk(pngMetaData, chunk.Data.GetSpan()); |
|
|
|
break; |
|
|
|
case PngChunkType.Data: |
|
|
|
if (image is null) |
|
|
|
{ |
|
|
|
this.InitializeImage(metadata, out image); |
|
|
|
this.InitializeImage(metaData, out image); |
|
|
|
} |
|
|
|
|
|
|
|
deframeStream.AllocateNewBytes(chunk.Length); |
|
|
|
@ -259,14 +264,14 @@ namespace SixLabors.ImageSharp.Formats.Png |
|
|
|
this.AssignTransparentMarkers(alpha); |
|
|
|
break; |
|
|
|
case PngChunkType.Text: |
|
|
|
this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length); |
|
|
|
this.ReadTextChunk(metaData, chunk.Data.Array, chunk.Length); |
|
|
|
break; |
|
|
|
case PngChunkType.Exif: |
|
|
|
if (!this.ignoreMetadata) |
|
|
|
{ |
|
|
|
byte[] exifData = new byte[chunk.Length]; |
|
|
|
Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length); |
|
|
|
metadata.ExifProfile = new ExifProfile(exifData); |
|
|
|
metaData.ExifProfile = new ExifProfile(exifData); |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
@ -302,7 +307,9 @@ namespace SixLabors.ImageSharp.Formats.Png |
|
|
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
|
|
|
public IImageInfo Identify(Stream stream) |
|
|
|
{ |
|
|
|
var metadata = new ImageMetaData(); |
|
|
|
var metaData = new ImageMetaData(); |
|
|
|
var pngMetaData = new PngMetaData(); |
|
|
|
metaData.AddOrUpdateFormatMetaData(PngFormat.Instance, pngMetaData); |
|
|
|
this.currentStream = stream; |
|
|
|
this.currentStream.Skip(8); |
|
|
|
try |
|
|
|
@ -314,17 +321,20 @@ namespace SixLabors.ImageSharp.Formats.Png |
|
|
|
switch (chunk.Type) |
|
|
|
{ |
|
|
|
case PngChunkType.Header: |
|
|
|
this.ReadHeaderChunk(chunk.Data.Array); |
|
|
|
this.ReadHeaderChunk(pngMetaData, chunk.Data.Array); |
|
|
|
this.ValidateHeader(); |
|
|
|
break; |
|
|
|
case PngChunkType.Physical: |
|
|
|
this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan()); |
|
|
|
this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan()); |
|
|
|
break; |
|
|
|
case PngChunkType.Gamma: |
|
|
|
this.ReadGammaChunk(pngMetaData, chunk.Data.GetSpan()); |
|
|
|
break; |
|
|
|
case PngChunkType.Data: |
|
|
|
this.SkipChunkDataAndCrc(chunk); |
|
|
|
break; |
|
|
|
case PngChunkType.Text: |
|
|
|
this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length); |
|
|
|
this.ReadTextChunk(metaData, chunk.Data.Array, chunk.Length); |
|
|
|
break; |
|
|
|
case PngChunkType.End: |
|
|
|
this.isEndChunkReached = true; |
|
|
|
@ -348,7 +358,7 @@ namespace SixLabors.ImageSharp.Formats.Png |
|
|
|
throw new ImageFormatException("PNG Image does not contain a header chunk"); |
|
|
|
} |
|
|
|
|
|
|
|
return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata); |
|
|
|
return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metaData); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -427,6 +437,18 @@ namespace SixLabors.ImageSharp.Formats.Png |
|
|
|
metadata.VerticalResolution = vResolution; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Reads the data chunk containing gamma data.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="pngMetadata">The metadata to read to.</param>
|
|
|
|
/// <param name="data">The data containing physical data.</param>
|
|
|
|
private void ReadGammaChunk(PngMetaData pngMetadata, ReadOnlySpan<byte> data) |
|
|
|
{ |
|
|
|
// The value is encoded as a 4-byte unsigned integer, representing gamma times 100000.
|
|
|
|
// For example, a gamma of 1/2.2 would be stored as 45455.
|
|
|
|
pngMetadata.Gamma = BinaryPrimitives.ReadUInt32BigEndian(data) / 100_000F; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes the image and various buffers needed for processing
|
|
|
|
/// </summary>
|
|
|
|
@ -1267,17 +1289,27 @@ namespace SixLabors.ImageSharp.Formats.Png |
|
|
|
/// <summary>
|
|
|
|
/// Reads a header chunk from the data.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="pngMetaData">The png metadata.</param>
|
|
|
|
/// <param name="data">The <see cref="T:ReadOnlySpan{byte}"/> containing data.</param>
|
|
|
|
private void ReadHeaderChunk(ReadOnlySpan<byte> data) |
|
|
|
private void ReadHeaderChunk(PngMetaData pngMetaData, ReadOnlySpan<byte> data) |
|
|
|
{ |
|
|
|
byte bitDepth = data[8]; |
|
|
|
this.header = new PngHeader( |
|
|
|
width: BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)), |
|
|
|
height: BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)), |
|
|
|
bitDepth: data[8], |
|
|
|
bitDepth: bitDepth, |
|
|
|
colorType: (PngColorType)data[9], |
|
|
|
compressionMethod: data[10], |
|
|
|
filterMethod: data[11], |
|
|
|
interlaceMethod: (PngInterlaceMode)data[12]); |
|
|
|
|
|
|
|
// TODO: Figure out how we can determine the number of colors and support more bit depths.
|
|
|
|
if (bitDepth == 8 || bitDepth == 16) |
|
|
|
{ |
|
|
|
pngMetaData.BitDepth = (PngBitDepth)bitDepth; |
|
|
|
} |
|
|
|
|
|
|
|
pngMetaData.ColorType = this.header.ColorType; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|