Browse Source

Merge branch 'gif-cleanup' of https://github.com/carbon/ImageSharp into gif-cleanup

af/merge-core
Jason Nelson 8 years ago
parent
commit
8561c0b1dc
  1. 6
      src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
  2. 2
      src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
  3. 2
      src/ImageSharp/Formats/Png/ImageExtensions.cs
  4. 33
      src/ImageSharp/Formats/Png/PngChunk.cs
  5. 22
      src/ImageSharp/Formats/Png/PngChunkType.cs
  6. 16
      src/ImageSharp/Formats/Png/PngConstants.cs
  7. 2
      src/ImageSharp/Formats/Png/PngDecoder.cs
  8. 136
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  9. 72
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  10. 12
      src/ImageSharp/Formats/Png/PngImageFormatDetector.cs
  11. 5
      src/ImageSharp/Formats/Png/Zlib/Adler32.cs
  12. 5
      src/ImageSharp/Formats/Png/Zlib/IChecksum.cs
  13. 21
      src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs
  14. 2
      src/ImageSharp/Image.LoadPixelData.cs
  15. 2
      src/ImageSharp/ImageExtensions.cs
  16. 2
      src/ImageSharp/Memory/BasicArrayBuffer.cs
  17. 12
      src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs
  18. 42
      tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
  19. 29
      tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs
  20. 32
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

6
src/ImageSharp/Formats/Png/IPngDecoderOptions.cs

@ -1,11 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Png namespace SixLabors.ImageSharp.Formats.Png
{ {
@ -24,4 +20,4 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
Encoding TextEncoding { get; } Encoding TextEncoding { get; }
} }
} }

2
src/ImageSharp/Formats/Png/IPngEncoderOptions.cs

@ -45,4 +45,4 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
bool WriteGamma { get; } bool WriteGamma { get; }
} }
} }

2
src/ImageSharp/Formats/Png/ImageExtensions.cs

@ -37,4 +37,4 @@ namespace SixLabors.ImageSharp
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Png)); => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Png));
} }
} }

33
src/ImageSharp/Formats/Png/PngChunk.cs

@ -8,11 +8,14 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary> /// <summary>
/// Stores header information about a chunk. /// Stores header information about a chunk.
/// </summary> /// </summary>
internal sealed class PngChunk internal readonly struct PngChunk
{ {
public PngChunk(int length) public PngChunk(int length, PngChunkType type, IManagedByteBuffer data = null, uint crc = 0)
{ {
this.Length = length; this.Length = length;
this.Type = type;
this.Data = data;
this.Crc = crc;
} }
/// <summary> /// <summary>
@ -24,21 +27,31 @@ namespace SixLabors.ImageSharp.Formats.Png
public int Length { get; } public int Length { get; }
/// <summary> /// <summary>
/// Gets or sets the chunk type as string with 4 chars. /// Gets the chunk type.
/// The value is the equal to the UInt32BigEndian encoding of its 4 ASCII characters.
/// </summary> /// </summary>
public string Type { get; set; } public PngChunkType Type { get; }
/// <summary> /// <summary>
/// Gets or sets the data bytes appropriate to the chunk type, if any. /// Gets the data bytes appropriate to the chunk type, if any.
/// This field can be of zero length. /// This field can be of zero length or null.
/// </summary> /// </summary>
public IManagedByteBuffer Data { get; set; } public IManagedByteBuffer Data { get; }
/// <summary> /// <summary>
/// Gets or sets a CRC (Cyclic Redundancy Check) calculated on the preceding bytes in the chunk, /// Gets a CRC (Cyclic Redundancy Check) calculated on the preceding bytes in the chunk,
/// including the chunk type code and chunk data fields, but not including the length field. /// including the chunk type code and chunk data fields, but not including the length field.
/// The CRC is always present, even for chunks containing no data /// The CRC is always present, even for chunks containing no data
/// </summary> /// </summary>
public uint Crc { get; set; } public uint Crc { get; }
/// <summary>
/// Gets a value indicating whether the given chunk is critical to decoding
/// </summary>
public bool IsCritical =>
this.Type == PngChunkType.Header ||
this.Type == PngChunkType.Palette ||
this.Type == PngChunkType.Data ||
this.Type == PngChunkType.End;
} }
} }

22
src/ImageSharp/Formats/Png/PngChunkTypes.cs → src/ImageSharp/Formats/Png/PngChunkType.cs

@ -4,57 +4,57 @@
namespace SixLabors.ImageSharp.Formats.Png namespace SixLabors.ImageSharp.Formats.Png
{ {
/// <summary> /// <summary>
/// Contains a list of possible chunk type identifiers. /// Contains a list of of chunk types.
/// </summary> /// </summary>
internal static class PngChunkTypes internal enum PngChunkType : uint
{ {
/// <summary> /// <summary>
/// The first chunk in a png file. Can only exists once. Contains /// The first chunk in a png file. Can only exists once. Contains
/// common information like the width and the height of the image or /// common information like the width and the height of the image or
/// the used compression method. /// the used compression method.
/// </summary> /// </summary>
public const string Header = "IHDR"; Header = 0x49484452U, // IHDR
/// <summary> /// <summary>
/// The PLTE chunk contains from 1 to 256 palette entries, each a three byte /// The PLTE chunk contains from 1 to 256 palette entries, each a three byte
/// series in the RGB format. /// series in the RGB format.
/// </summary> /// </summary>
public const string Palette = "PLTE"; Palette = 0x504C5445U, // PLTE
/// <summary> /// <summary>
/// The IDAT chunk contains the actual image data. The image can contains more /// The IDAT chunk contains the actual image data. The image can contains more
/// than one chunk of this type. All chunks together are the whole image. /// than one chunk of this type. All chunks together are the whole image.
/// </summary> /// </summary>
public const string Data = "IDAT"; Data = 0x49444154U, // IDAT
/// <summary> /// <summary>
/// This chunk must appear last. It marks the end of the PNG data stream. /// This chunk must appear last. It marks the end of the PNG data stream.
/// The chunk's data field is empty. /// The chunk's data field is empty.
/// </summary> /// </summary>
public const string End = "IEND"; End = 0x49454E44U, // IEND
/// <summary> /// <summary>
/// This chunk specifies that the image uses simple transparency: /// This chunk specifies that the image uses simple transparency:
/// either alpha values associated with palette entries (for indexed-color images) /// either alpha values associated with palette entries (for indexed-color images)
/// or a single transparent color (for grayscale and true color images). /// or a single transparent color (for grayscale and true color images).
/// </summary> /// </summary>
public const string PaletteAlpha = "tRNS"; PaletteAlpha = 0x74524E53U, // tRNS
/// <summary> /// <summary>
/// Textual information that the encoder wishes to record with the image can be stored in /// Textual information that the encoder wishes to record with the image can be stored in
/// tEXt chunks. Each tEXt chunk contains a keyword and a text string. /// tEXt chunks. Each tEXt chunk contains a keyword and a text string.
/// </summary> /// </summary>
public const string Text = "tEXt"; Text = 0x74455874U, // tEXt
/// <summary> /// <summary>
/// This chunk specifies the relationship between the image samples and the desired /// This chunk specifies the relationship between the image samples and the desired
/// display output intensity. /// display output intensity.
/// </summary> /// </summary>
public const string Gamma = "gAMA"; Gamma = 0x67414D41U, // gAMA
/// <summary> /// <summary>
/// The pHYs chunk specifies the intended pixel size or aspect ratio for display of the image. /// The pHYs chunk specifies the intended pixel size or aspect ratio for display of the image.
/// </summary> /// </summary>
public const string Physical = "pHYs"; Physical = 0x70485973U // pHYs
} }
} }

16
src/ImageSharp/Formats/Png/PngConstants.cs

@ -25,5 +25,21 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The list of file extensions that equate to a png. /// The list of file extensions that equate to a png.
/// </summary> /// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "png" }; public static readonly IEnumerable<string> FileExtensions = new[] { "png" };
public static readonly byte[] HeaderBytes = {
0x89, // Set the high bit.
0x50, // P
0x4E, // N
0x47, // G
0x0D, // Line ending CRLF
0x0A, // Line ending CRLF
0x1A, // EOF
0x0A // LF
};
/// <summary>
/// The header bytes as a big endian coded ulong.
/// </summary>
public const ulong HeaderValue = 0x89504E470D0A1A0AUL;
} }
} }

2
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -60,4 +60,4 @@ namespace SixLabors.ImageSharp.Formats.Png
return decoder.Identify(stream); return decoder.Identify(stream);
} }
} }
} }

136
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -71,11 +71,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
private readonly byte[] crcBuffer = new byte[4]; private readonly byte[] crcBuffer = new byte[4];
/// <summary>
/// Reusable buffer for reading char arrays.
/// </summary>
private readonly char[] chars = new char[4];
/// <summary> /// <summary>
/// Reusable crc for validating chunks. /// Reusable crc for validating chunks.
/// </summary> /// </summary>
@ -224,14 +219,14 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
switch (chunk.Type) switch (chunk.Type)
{ {
case PngChunkTypes.Header: case PngChunkType.Header:
this.ReadHeaderChunk(chunk.Data.Array); this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader(); this.ValidateHeader();
break; break;
case PngChunkTypes.Physical: case PngChunkType.Physical:
this.ReadPhysicalChunk(metadata, chunk.Data.Array); this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break; break;
case PngChunkTypes.Data: case PngChunkType.Data:
if (image == null) if (image == null)
{ {
this.InitializeImage(metadata, out image); this.InitializeImage(metadata, out image);
@ -241,33 +236,28 @@ namespace SixLabors.ImageSharp.Formats.Png
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
this.currentStream.Read(this.crcBuffer, 0, 4); this.currentStream.Read(this.crcBuffer, 0, 4);
break; break;
case PngChunkTypes.Palette: case PngChunkType.Palette:
byte[] pal = new byte[chunk.Length]; byte[] pal = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length); Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
this.palette = pal; this.palette = pal;
break; break;
case PngChunkTypes.PaletteAlpha: case PngChunkType.PaletteAlpha:
byte[] alpha = new byte[chunk.Length]; byte[] alpha = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length); Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha; this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha); this.AssignTransparentMarkers(alpha);
break; break;
case PngChunkTypes.Text: case PngChunkType.Text:
this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length); this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break; break;
case PngChunkTypes.End: case PngChunkType.End:
this.isEndChunkReached = true; this.isEndChunkReached = true;
break; break;
} }
} }
finally finally
{ {
// Data is rented in ReadChunkData() chunk.Data?.Dispose(); // Data is rented in ReadChunkData()
if (chunk.Data != null)
{
chunk.Data.Dispose();
chunk.Data = null;
}
} }
} }
} }
@ -303,20 +293,20 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
switch (chunk.Type) switch (chunk.Type)
{ {
case PngChunkTypes.Header: case PngChunkType.Header:
this.ReadHeaderChunk(chunk.Data.Array); this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader(); this.ValidateHeader();
break; break;
case PngChunkTypes.Physical: case PngChunkType.Physical:
this.ReadPhysicalChunk(metadata, chunk.Data.Array); this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break; break;
case PngChunkTypes.Data: case PngChunkType.Data:
this.SkipChunkDataAndCrc(chunk); this.SkipChunkDataAndCrc(chunk);
break; break;
case PngChunkTypes.Text: case PngChunkType.Text:
this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length); this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break; break;
case PngChunkTypes.End: case PngChunkType.End:
this.isEndChunkReached = true; this.isEndChunkReached = true;
break; break;
} }
@ -383,20 +373,6 @@ namespace SixLabors.ImageSharp.Formats.Png
return result; return result;
} }
/// <summary>
/// Returns a value indicating whether the given chunk is critical to decoding
/// </summary>
/// <param name="chunk">The chunk</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsCriticalChunk(PngChunk chunk)
{
return
chunk.Type == PngChunkTypes.Header ||
chunk.Type == PngChunkTypes.Palette ||
chunk.Type == PngChunkTypes.Data ||
chunk.Type == PngChunkTypes.End;
}
/// <summary> /// <summary>
/// Reads an integer value from 2 consecutive bytes in LSB order /// Reads an integer value from 2 consecutive bytes in LSB order
/// </summary> /// </summary>
@ -1217,38 +1193,67 @@ namespace SixLabors.ImageSharp.Formats.Png
return false; return false;
} }
chunk = new PngChunk(length); while (length < 0 || length > (this.currentStream.Length - this.currentStream.Position))
if (chunk.Length < 0 || chunk.Length > this.currentStream.Length - this.currentStream.Position)
{ {
// Not a valid chunk so we skip back all but one of the four bytes we have just read. // Not a valid chunk so we skip back all but one of the four bytes we have just read.
// That lets us read one byte at a time until we reach a known chunk. // That lets us read one byte at a time until we reach a known chunk.
this.currentStream.Position -= 3; this.currentStream.Position -= 3;
return true; length = this.ReadChunkLength();
if (length == -1)
{
chunk = default;
return false;
}
} }
this.ReadChunkType(chunk); PngChunkType type = this.ReadChunkType();
if (chunk.Type == PngChunkTypes.Data) // NOTE: Reading the chunk data is the responsible of the caller
if (type == PngChunkType.Data)
{ {
chunk = new PngChunk(length, type);
return true; return true;
} }
this.ReadChunkData(chunk); chunk = new PngChunk(
this.ReadChunkCrc(chunk); length: length,
type: type,
data: this.ReadChunkData(length),
crc: this.ReadChunkCrc());
if (chunk.IsCritical)
{
this.ValidateChunk(chunk);
}
return true; return true;
} }
private void ValidateChunk(in PngChunk chunk)
{
this.crc.Reset();
this.crc.Update(this.chunkTypeBuffer);
this.crc.Update(chunk.Data.Span);
if (this.crc.Value != chunk.Crc)
{
string chunkTypeName = Encoding.UTF8.GetString(this.chunkTypeBuffer, 0, 4);
throw new ImageFormatException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!");
}
}
/// <summary> /// <summary>
/// Reads the cycle redundancy chunk from the data. /// Reads the cycle redundancy chunk from the data.
/// </summary> /// </summary>
/// <param name="chunk">The chunk.</param>
/// <exception cref="ImageFormatException"> /// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid or corrupt. /// Thrown if the input stream is not valid or corrupt.
/// </exception> /// </exception>
private void ReadChunkCrc(PngChunk chunk) private uint ReadChunkCrc()
{ {
int numBytes = this.currentStream.Read(this.crcBuffer, 0, 4); int numBytes = this.currentStream.Read(this.crcBuffer, 0, 4);
@ -1257,22 +1262,13 @@ namespace SixLabors.ImageSharp.Formats.Png
throw new ImageFormatException("Image stream is not valid!"); throw new ImageFormatException("Image stream is not valid!");
} }
chunk.Crc = BinaryPrimitives.ReadUInt32BigEndian(this.crcBuffer); return BinaryPrimitives.ReadUInt32BigEndian(this.crcBuffer);
this.crc.Reset();
this.crc.Update(this.chunkTypeBuffer);
this.crc.Update(new ReadOnlySpan<byte>(chunk.Data.Array, 0, chunk.Length));
if (this.crc.Value != chunk.Crc && IsCriticalChunk(chunk))
{
throw new ImageFormatException($"CRC Error. PNG {chunk.Type} chunk is corrupt!");
}
} }
/// <summary> /// <summary>
/// Skips the chunk data and the cycle redundancy chunk read from the data. /// Skips the chunk data and the cycle redundancy chunk read from the data.
/// </summary> /// </summary>
private void SkipChunkDataAndCrc(PngChunk chunk) private void SkipChunkDataAndCrc(in PngChunk chunk)
{ {
this.currentStream.Skip(chunk.Length); this.currentStream.Skip(chunk.Length);
this.currentStream.Skip(4); this.currentStream.Skip(4);
@ -1281,35 +1277,33 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary> /// <summary>
/// Reads the chunk data from the stream. /// Reads the chunk data from the stream.
/// </summary> /// </summary>
/// <param name="chunk">The chunk.</param> /// <param name="length">The length of the chunk data to read.</param>
private void ReadChunkData(PngChunk chunk) private IManagedByteBuffer ReadChunkData(int length)
{ {
// We rent the buffer here to return it afterwards in Decode() // We rent the buffer here to return it afterwards in Decode()
chunk.Data = this.configuration.MemoryManager.AllocateCleanManagedByteBuffer(chunk.Length); IManagedByteBuffer buffer = this.configuration.MemoryManager.AllocateCleanManagedByteBuffer(length);
this.currentStream.Read(chunk.Data.Array, 0, chunk.Length);
this.currentStream.Read(buffer.Array, 0, length);
return buffer;
} }
/// <summary> /// <summary>
/// Identifies the chunk type from the chunk. /// Identifies the chunk type from the chunk.
/// </summary> /// </summary>
/// <param name="chunk">The chunk.</param>
/// <exception cref="ImageFormatException"> /// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid. /// Thrown if the input stream is not valid.
/// </exception> /// </exception>
private void ReadChunkType(PngChunk chunk) private PngChunkType ReadChunkType()
{ {
int numBytes = this.currentStream.Read(this.chunkTypeBuffer, 0, 4); int numBytes = this.currentStream.Read(this.chunkTypeBuffer, 0, 4);
if (numBytes >= 1 && numBytes <= 3) if (numBytes >= 1 && numBytes <= 3)
{ {
throw new ImageFormatException("Image stream is not valid!"); throw new ImageFormatException("Image stream is not valid!");
} }
this.chars[0] = (char)this.chunkTypeBuffer[0]; return (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.chunkTypeBuffer.AsSpan());
this.chars[1] = (char)this.chunkTypeBuffer[1];
this.chars[2] = (char)this.chunkTypeBuffer[2];
this.chars[3] = (char)this.chunkTypeBuffer[3];
chunk.Type = new string(this.chars);
} }
/// <summary> /// <summary>
@ -1359,4 +1353,4 @@ namespace SixLabors.ImageSharp.Formats.Png
this.scanline = temp; this.scanline = temp;
} }
} }
} }

72
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -5,7 +5,6 @@ using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Png.Zlib;
@ -28,20 +27,15 @@ namespace SixLabors.ImageSharp.Formats.Png
private const int MaxBlockSize = 65535; private const int MaxBlockSize = 65535;
/// <summary> /// <summary>
/// Reusable buffer for writing chunk types. /// Reusable buffer for writing general data.
/// </summary> /// </summary>
private readonly byte[] chunkTypeBuffer = new byte[4]; private readonly byte[] buffer = new byte[8];
/// <summary> /// <summary>
/// Reusable buffer for writing chunk data. /// Reusable buffer for writing chunk data.
/// </summary> /// </summary>
private readonly byte[] chunkDataBuffer = new byte[16]; private readonly byte[] chunkDataBuffer = new byte[16];
/// <summary>
/// Reusable buffer for writing int data.
/// </summary>
private readonly byte[] intBuffer = new byte[4];
/// <summary> /// <summary>
/// Reusable crc for validating chunks. /// Reusable crc for validating chunks.
/// </summary> /// </summary>
@ -173,17 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.width = image.Width; this.width = image.Width;
this.height = image.Height; this.height = image.Height;
// Write the png header. stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);
this.chunkDataBuffer[0] = 0x89; // Set the high bit.
this.chunkDataBuffer[1] = 0x50; // P
this.chunkDataBuffer[2] = 0x4E; // N
this.chunkDataBuffer[3] = 0x47; // G
this.chunkDataBuffer[4] = 0x0D; // Line ending CRLF
this.chunkDataBuffer[5] = 0x0A; // Line ending CRLF
this.chunkDataBuffer[6] = 0x1A; // EOF
this.chunkDataBuffer[7] = 0x0A; // LF
stream.Write(this.chunkDataBuffer, 0, 8);
QuantizedFrame<TPixel> quantized = null; QuantizedFrame<TPixel> quantized = null;
if (this.pngColorType == PngColorType.Palette) if (this.pngColorType == PngColorType.Palette)
@ -415,8 +399,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="header">The <see cref="PngHeader"/>.</param> /// <param name="header">The <see cref="PngHeader"/>.</param>
private void WriteHeaderChunk(Stream stream, in PngHeader header) private void WriteHeaderChunk(Stream stream, in PngHeader header)
{ {
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 0, 4), header.Width); BinaryPrimitives.WriteInt32BigEndian(this.chunkDataBuffer.AsSpan(0, 4), header.Width);
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 4, 4), header.Height); BinaryPrimitives.WriteInt32BigEndian(this.chunkDataBuffer.AsSpan(4, 4), header.Height);
this.chunkDataBuffer[8] = header.BitDepth; this.chunkDataBuffer[8] = header.BitDepth;
this.chunkDataBuffer[9] = (byte)header.ColorType; this.chunkDataBuffer[9] = (byte)header.ColorType;
@ -424,7 +408,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.chunkDataBuffer[11] = header.FilterMethod; this.chunkDataBuffer[11] = header.FilterMethod;
this.chunkDataBuffer[12] = (byte)header.InterlaceMethod; this.chunkDataBuffer[12] = (byte)header.InterlaceMethod;
this.WriteChunk(stream, PngChunkTypes.Header, this.chunkDataBuffer, 0, 13); this.WriteChunk(stream, PngChunkType.Header, this.chunkDataBuffer, 0, 13);
} }
/// <summary> /// <summary>
@ -443,7 +427,7 @@ namespace SixLabors.ImageSharp.Formats.Png
// Get max colors for bit depth. // Get max colors for bit depth.
int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3; int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3;
var rgba = default(Rgba32); Rgba32 rgba = default;
bool anyAlpha = false; bool anyAlpha = false;
using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength)) using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength))
@ -475,12 +459,12 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
} }
this.WriteChunk(stream, PngChunkTypes.Palette, colorTable.Array, 0, colorTableLength); this.WriteChunk(stream, PngChunkType.Palette, colorTable.Array, 0, colorTableLength);
// Write the transparency data // Write the transparency data
if (anyAlpha) if (anyAlpha)
{ {
this.WriteChunk(stream, PngChunkTypes.PaletteAlpha, alphaTable.Array, 0, pixelCount); this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaTable.Array, 0, pixelCount);
} }
} }
} }
@ -500,12 +484,12 @@ namespace SixLabors.ImageSharp.Formats.Png
int dpmX = (int)Math.Round(image.MetaData.HorizontalResolution * 39.3700787D); int dpmX = (int)Math.Round(image.MetaData.HorizontalResolution * 39.3700787D);
int dpmY = (int)Math.Round(image.MetaData.VerticalResolution * 39.3700787D); int dpmY = (int)Math.Round(image.MetaData.VerticalResolution * 39.3700787D);
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 0, 4), dpmX); BinaryPrimitives.WriteInt32BigEndian(this.chunkDataBuffer.AsSpan(0, 4), dpmX);
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 4, 4), dpmY); BinaryPrimitives.WriteInt32BigEndian(this.chunkDataBuffer.AsSpan(4, 4), dpmY);
this.chunkDataBuffer[8] = 1; this.chunkDataBuffer[8] = 1;
this.WriteChunk(stream, PngChunkTypes.Physical, this.chunkDataBuffer, 0, 9); this.WriteChunk(stream, PngChunkType.Physical, this.chunkDataBuffer, 0, 9);
} }
} }
@ -520,9 +504,9 @@ namespace SixLabors.ImageSharp.Formats.Png
// 4-byte unsigned integer of gamma * 100,000. // 4-byte unsigned integer of gamma * 100,000.
uint gammaValue = (uint)(this.gamma * 100_000F); uint gammaValue = (uint)(this.gamma * 100_000F);
BinaryPrimitives.WriteUInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 0, 4), gammaValue); BinaryPrimitives.WriteUInt32BigEndian(this.chunkDataBuffer.AsSpan(0, 4), gammaValue);
this.WriteChunk(stream, PngChunkTypes.Gamma, this.chunkDataBuffer, 0, 4); this.WriteChunk(stream, PngChunkType.Gamma, this.chunkDataBuffer, 0, 4);
} }
} }
@ -590,7 +574,7 @@ namespace SixLabors.ImageSharp.Formats.Png
length = MaxBlockSize; length = MaxBlockSize;
} }
this.WriteChunk(stream, PngChunkTypes.Data, buffer, i * MaxBlockSize, length); this.WriteChunk(stream, PngChunkType.Data, buffer, i * MaxBlockSize, length);
} }
} }
@ -600,7 +584,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
private void WriteEndChunk(Stream stream) private void WriteEndChunk(Stream stream)
{ {
this.WriteChunk(stream, PngChunkTypes.End, null); this.WriteChunk(stream, PngChunkType.End, null);
} }
/// <summary> /// <summary>
@ -609,7 +593,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="stream">The <see cref="Stream"/> to write to.</param> /// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="type">The type of chunk to write.</param> /// <param name="type">The type of chunk to write.</param>
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param> /// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
private void WriteChunk(Stream stream, string type, byte[] data) private void WriteChunk(Stream stream, PngChunkType type, byte[] data)
{ {
this.WriteChunk(stream, type, data, 0, data?.Length ?? 0); this.WriteChunk(stream, type, data, 0, data?.Length ?? 0);
} }
@ -622,33 +606,27 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param> /// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
/// <param name="offset">The position to offset the data at.</param> /// <param name="offset">The position to offset the data at.</param>
/// <param name="length">The of the data to write.</param> /// <param name="length">The of the data to write.</param>
private void WriteChunk(Stream stream, string type, byte[] data, int offset, int length) private void WriteChunk(Stream stream, PngChunkType type, byte[] data, int offset, int length)
{ {
BinaryPrimitives.WriteInt32BigEndian(this.intBuffer, length); BinaryPrimitives.WriteInt32BigEndian(this.buffer, length);
BinaryPrimitives.WriteUInt32BigEndian(this.buffer.AsSpan(4, 4), (uint)type);
stream.Write(this.intBuffer, 0, 4); // write the length
this.chunkTypeBuffer[0] = (byte)type[0];
this.chunkTypeBuffer[1] = (byte)type[1];
this.chunkTypeBuffer[2] = (byte)type[2];
this.chunkTypeBuffer[3] = (byte)type[3];
stream.Write(this.chunkTypeBuffer, 0, 4); stream.Write(this.buffer, 0, 8);
this.crc.Reset(); this.crc.Reset();
this.crc.Update(this.chunkTypeBuffer); this.crc.Update(this.buffer.AsSpan(4, 4)); // Write the type buffer
if (data != null && length > 0) if (data != null && length > 0)
{ {
stream.Write(data, offset, length); stream.Write(data, offset, length);
this.crc.Update(new ReadOnlySpan<byte>(data, offset, length)); this.crc.Update(data.AsSpan(offset, length));
} }
BinaryPrimitives.WriteUInt32BigEndian(this.intBuffer, (uint)this.crc.Value); BinaryPrimitives.WriteUInt32BigEndian(this.buffer, (uint)this.crc.Value);
stream.Write(this.intBuffer, 0, 4); // write the crc stream.Write(this.buffer, 0, 4); // write the crc
} }
} }
} }

12
src/ImageSharp/Formats/Png/PngImageFormatDetector.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers.Binary;
namespace SixLabors.ImageSharp.Formats.Png namespace SixLabors.ImageSharp.Formats.Png
{ {
@ -26,16 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header) private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{ {
// TODO: This should be in constants return header.Length >= this.HeaderSize && BinaryPrimitives.ReadUInt64BigEndian(header) == PngConstants.HeaderValue;
return header.Length >= this.HeaderSize &&
header[0] == 0x89 &&
header[1] == 0x50 && // P
header[2] == 0x4E && // N
header[3] == 0x47 && // G
header[4] == 0x0D && // CR
header[5] == 0x0A && // LF
header[6] == 0x1A && // EOF
header[7] == 0x0A; // LF
} }
} }
} }

5
src/ImageSharp/Formats/Png/Zlib/Adler32.cs

@ -78,10 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public long Value public long Value
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get => this.checksum;
{
return this.checksum;
}
} }
/// <inheritdoc/> /// <inheritdoc/>

5
src/ImageSharp/Formats/Png/Zlib/IChecksum.cs

@ -17,10 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// <summary> /// <summary>
/// Gets the data checksum computed so far. /// Gets the data checksum computed so far.
/// </summary> /// </summary>
long Value long Value { get; }
{
get;
}
/// <summary> /// <summary>
/// Resets the data checksum as if no update was ever called. /// Resets the data checksum as if no update was ever called.

21
src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs

@ -113,26 +113,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public override bool CanWrite => true; public override bool CanWrite => true;
/// <inheritdoc/> /// <inheritdoc/>
public override long Length public override long Length => throw new NotSupportedException();
{
get
{
throw new NotSupportedException();
}
}
/// <inheritdoc/> /// <inheritdoc/>
public override long Position public override long Position
{ {
get get => throw new NotSupportedException();
{ set => throw new NotSupportedException();
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -163,7 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
this.deflateStream.Write(buffer, offset, count); this.deflateStream.Write(buffer, offset, count);
this.adler32.Update(new ReadOnlySpan<byte>(buffer, offset, count)); this.adler32.Update(buffer.AsSpan(offset, count));
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/Image.LoadPixelData.cs

@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp
public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, TPixel[] data, int width, int height) public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, TPixel[] data, int width, int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return LoadPixelData(config, new Span<TPixel>(data), width, height); return LoadPixelData(config, data.AsSpan(), width, height);
} }
/// <summary> /// <summary>

2
src/ImageSharp/ImageExtensions.cs

@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, TPixel[] buffer) public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, TPixel[] buffer)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> SavePixelData(source, new Span<TPixel>(buffer)); => SavePixelData(source, buffer.AsSpan());
/// <summary> /// <summary>
/// Saves the raw image pixels to a byte array in row-major order. /// Saves the raw image pixels to a byte array in row-major order.

2
src/ImageSharp/Memory/BasicArrayBuffer.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Memory
public int Length { get; } public int Length { get; }
public Span<T> Span => new Span<T>(this.Array, 0, this.Length); public Span<T> Span => this.Array.AsSpan(0, this.Length);
/// <summary> /// <summary>
/// Returns a reference to specified element of the buffer. /// Returns a reference to specified element of the buffer.

12
src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns> /// <returns>the value</returns>
public ushort ReadUInt16() public ushort ReadUInt16()
{ {
return BinaryPrimitives.ReadUInt16BigEndian(new Span<byte>(this.data, this.AddIndex(2), 2)); return BinaryPrimitives.ReadUInt16BigEndian(this.data.AsSpan(this.AddIndex(2), 2));
} }
/// <summary> /// <summary>
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns> /// <returns>the value</returns>
public short ReadInt16() public short ReadInt16()
{ {
return BinaryPrimitives.ReadInt16BigEndian(new Span<byte>(this.data, this.AddIndex(2), 2)); return BinaryPrimitives.ReadInt16BigEndian(this.data.AsSpan(this.AddIndex(2), 2));
} }
/// <summary> /// <summary>
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns> /// <returns>the value</returns>
public uint ReadUInt32() public uint ReadUInt32()
{ {
return BinaryPrimitives.ReadUInt32BigEndian(new Span<byte>(this.data, this.AddIndex(4), 4)); return BinaryPrimitives.ReadUInt32BigEndian(this.data.AsSpan(this.AddIndex(4), 4));
} }
/// <summary> /// <summary>
@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns> /// <returns>the value</returns>
public int ReadInt32() public int ReadInt32()
{ {
return BinaryPrimitives.ReadInt32BigEndian(new Span<byte>(this.data, this.AddIndex(4), 4)); return BinaryPrimitives.ReadInt32BigEndian(this.data.AsSpan(this.AddIndex(4), 4));
} }
/// <summary> /// <summary>
@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns> /// <returns>the value</returns>
public ulong ReadUInt64() public ulong ReadUInt64()
{ {
return BinaryPrimitives.ReadUInt64BigEndian(new Span<byte>(this.data, this.AddIndex(8), 8)); return BinaryPrimitives.ReadUInt64BigEndian(this.data.AsSpan(this.AddIndex(8), 8));
} }
/// <summary> /// <summary>
@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns> /// <returns>the value</returns>
public long ReadInt64() public long ReadInt64()
{ {
return BinaryPrimitives.ReadInt64BigEndian(new Span<byte>(this.data, this.AddIndex(8), 8)); return BinaryPrimitives.ReadInt64BigEndian(this.data.AsSpan(this.AddIndex(8), 8));
} }
/// <summary> /// <summary>

42
tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs

@ -1,15 +1,14 @@
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg using System;
{
using System;
using SixLabors.ImageSharp.Formats.Jpeg.Common; using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
public static class DCTTests public static class DCTTests
{ {
public class FastFloatingPoint : JpegFixture public class FastFloatingPoint : JpegFixture
@ -19,7 +18,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
} }
[Fact] [Fact]
public void iDCT2D8x4_LeftPart() public void iDCT2D8x4_LeftPart()
{ {
@ -28,10 +26,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray, expectedDestArray); ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray, expectedDestArray);
Block8x8F source = new Block8x8F(); var source = new Block8x8F();
source.LoadFrom(sourceArray); source.LoadFrom(sourceArray);
Block8x8F dest = new Block8x8F(); var dest = new Block8x8F();
FastFloatingPointDCT.IDCT8x4_LeftPart(ref source, ref dest); FastFloatingPointDCT.IDCT8x4_LeftPart(ref source, ref dest);
@ -51,12 +49,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
float[] sourceArray = JpegFixture.Create8x8FloatData(); float[] sourceArray = JpegFixture.Create8x8FloatData();
float[] expectedDestArray = new float[64]; float[] expectedDestArray = new float[64];
ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray.AsSpan().Slice(4), expectedDestArray.AsSpan().Slice(4)); ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray.AsSpan(4), expectedDestArray.AsSpan(4));
Block8x8F source = new Block8x8F(); var source = new Block8x8F();
source.LoadFrom(sourceArray); source.LoadFrom(sourceArray);
Block8x8F dest = new Block8x8F(); var dest = new Block8x8F();
FastFloatingPointDCT.IDCT8x4_RightPart(ref source, ref dest); FastFloatingPointDCT.IDCT8x4_RightPart(ref source, ref dest);
@ -115,10 +113,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void FDCT8x4_LeftPart(int seed) public void FDCT8x4_LeftPart(int seed)
{ {
Span<float> src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); Span<float> src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F(); var srcBlock = new Block8x8F();
srcBlock.LoadFrom(src); srcBlock.LoadFrom(src);
Block8x8F destBlock = new Block8x8F(); var destBlock = new Block8x8F();
float[] expectedDest = new float[64]; float[] expectedDest = new float[64];
@ -137,14 +135,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void FDCT8x4_RightPart(int seed) public void FDCT8x4_RightPart(int seed)
{ {
Span<float> src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); Span<float> src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F(); var srcBlock = new Block8x8F();
srcBlock.LoadFrom(src); srcBlock.LoadFrom(src);
Block8x8F destBlock = new Block8x8F(); var destBlock = new Block8x8F();
float[] expectedDest = new float[64]; float[] expectedDest = new float[64];
ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan().Slice(4)); ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan(4));
FastFloatingPointDCT.FDCT8x4_RightPart(ref srcBlock, ref destBlock); FastFloatingPointDCT.FDCT8x4_RightPart(ref srcBlock, ref destBlock);
float[] actualDest = new float[64]; float[] actualDest = new float[64];
@ -159,14 +157,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void TransformFDCT(int seed) public void TransformFDCT(int seed)
{ {
Span<float> src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); Span<float> src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F(); var srcBlock = new Block8x8F();
srcBlock.LoadFrom(src); srcBlock.LoadFrom(src);
Block8x8F destBlock = new Block8x8F(); var destBlock = new Block8x8F();
float[] expectedDest = new float[64]; float[] expectedDest = new float[64];
float[] temp1 = new float[64]; float[] temp1 = new float[64];
Block8x8F temp2 = new Block8x8F(); var temp2 = new Block8x8F();
ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true);
FastFloatingPointDCT.TransformFDCT(ref srcBlock, ref destBlock, ref temp2, false); FastFloatingPointDCT.TransformFDCT(ref srcBlock, ref destBlock, ref temp2, false);

29
tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs

@ -0,0 +1,29 @@
using System;
using System.Buffers.Binary;
using System.Text;
using SixLabors.ImageSharp.Formats.Png;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Png
{
public class PngChunkTypeTests
{
[Fact]
public void ChunkTypeIdsAreCorrect()
{
Assert.Equal(PngChunkType.Header, GetType("IHDR"));
Assert.Equal(PngChunkType.Palette, GetType("PLTE"));
Assert.Equal(PngChunkType.Data, GetType("IDAT"));
Assert.Equal(PngChunkType.End, GetType("IEND"));
Assert.Equal(PngChunkType.PaletteAlpha, GetType("tRNS"));
Assert.Equal(PngChunkType.Text, GetType("tEXt"));
Assert.Equal(PngChunkType.Gamma, GetType("gAMA"));
Assert.Equal(PngChunkType.Physical, GetType("pHYs"));
}
private static PngChunkType GetType(string text)
{
return (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(Encoding.UTF8.GetBytes(text));
}
}
}

32
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

@ -2,15 +2,14 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.IO; using System.IO;
using System.IO.Compression;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using Xunit; using Xunit;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests namespace SixLabors.ImageSharp.Tests
{ {
using System.Buffers.Binary;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
@ -242,12 +241,14 @@ namespace SixLabors.ImageSharp.Tests
} }
[Theory] [Theory]
[InlineData(PngChunkTypes.Header)] [InlineData((uint)PngChunkType.Header)] // IHDR
[InlineData(PngChunkTypes.Palette)] [InlineData((uint)PngChunkType.Palette)] // PLTE
// [InlineData(PngChunkTypes.Data)] //TODO: Figure out how to test this // [InlineData(PngChunkTypes.Data)] //TODO: Figure out how to test this
[InlineData(PngChunkTypes.End)] [InlineData((uint)PngChunkType.End)] // IEND
public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(string chunkName) public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType)
{ {
string chunkName = GetChunkTypeName(chunkType);
using (var memStream = new MemoryStream()) using (var memStream = new MemoryStream())
{ {
WriteHeaderChunk(memStream); WriteHeaderChunk(memStream);
@ -266,12 +267,14 @@ namespace SixLabors.ImageSharp.Tests
} }
[Theory] [Theory]
[InlineData(PngChunkTypes.Gamma)] [InlineData((uint)PngChunkType.Gamma)] // gAMA
[InlineData(PngChunkTypes.PaletteAlpha)] [InlineData((uint)PngChunkType.PaletteAlpha)] // tRNS
[InlineData(PngChunkTypes.Physical)] // It's ok to test physical as we don't throw for duplicate chunks. [InlineData((uint)PngChunkType.Physical)] // pHYs: It's ok to test physical as we don't throw for duplicate chunks.
//[InlineData(PngChunkTypes.Text)] //TODO: Figure out how to test this //[InlineData(PngChunkTypes.Text)] //TODO: Figure out how to test this
public void Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown(string chunkName) public void Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown(uint chunkType)
{ {
string chunkName = GetChunkTypeName(chunkType);
using (var memStream = new MemoryStream()) using (var memStream = new MemoryStream())
{ {
WriteHeaderChunk(memStream); WriteHeaderChunk(memStream);
@ -283,6 +286,15 @@ namespace SixLabors.ImageSharp.Tests
} }
} }
private static string GetChunkTypeName(uint value)
{
byte[] data = new byte[4];
BinaryPrimitives.WriteUInt32BigEndian(data, value);
return Encoding.ASCII.GetString(data);
}
private static void WriteHeaderChunk(MemoryStream memStream) private static void WriteHeaderChunk(MemoryStream memStream)
{ {
// Writes a 1x1 32bit png header chunk containing a single black pixel // Writes a 1x1 32bit png header chunk containing a single black pixel

Loading…
Cancel
Save