mirror of https://github.com/SixLabors/ImageSharp
6 changed files with 255 additions and 135 deletions
@ -0,0 +1,242 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers.Binary; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.IO; |
|||
using SixLabors.ImageSharp.Formats.Png; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using Xunit; |
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Tests.Formats.Png |
|||
{ |
|||
public partial class PngEncoderTests |
|||
{ |
|||
[Fact] |
|||
public void HeaderChunk_ComesFirst() |
|||
{ |
|||
// arrange
|
|||
var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); |
|||
using Image<Rgba32> input = testFile.CreateRgba32Image(); |
|||
using var memStream = new MemoryStream(); |
|||
|
|||
// act
|
|||
input.Save(memStream, PngEncoder); |
|||
|
|||
// assert
|
|||
memStream.Position = 0; |
|||
Span<byte> bytesSpan = memStream.ToArray().AsSpan(8); // Skip header.
|
|||
BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(0, 4)); |
|||
var type = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); |
|||
Assert.Equal(PngChunkType.Header, type); |
|||
} |
|||
|
|||
[Fact] |
|||
public void EndChunk_IsLast() |
|||
{ |
|||
// arrange
|
|||
var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); |
|||
using Image<Rgba32> input = testFile.CreateRgba32Image(); |
|||
using var memStream = new MemoryStream(); |
|||
|
|||
// act
|
|||
input.Save(memStream, PngEncoder); |
|||
|
|||
// assert
|
|||
memStream.Position = 0; |
|||
Span<byte> bytesSpan = memStream.ToArray().AsSpan(8); // Skip header.
|
|||
bool endChunkFound = false; |
|||
while (bytesSpan.Length > 0) |
|||
{ |
|||
int length = BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(0, 4)); |
|||
var type = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); |
|||
Assert.False(endChunkFound); |
|||
if (type == PngChunkType.End) |
|||
{ |
|||
endChunkFound = true; |
|||
} |
|||
|
|||
bytesSpan = bytesSpan.Slice(4 + 4 + length + 4); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(PngChunkType.Gamma)] |
|||
[InlineData(PngChunkType.Chroma)] |
|||
[InlineData(PngChunkType.EmbeddedColorProfile)] |
|||
[InlineData(PngChunkType.SignificantBits)] |
|||
[InlineData(PngChunkType.StandardRgbColourSpace)] |
|||
public void Chunk_ComesBeforePlteAndIDat(object chunkTypeObj) |
|||
{ |
|||
// arrange
|
|||
var chunkType = (PngChunkType)chunkTypeObj; |
|||
var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); |
|||
using Image<Rgba32> input = testFile.CreateRgba32Image(); |
|||
using var memStream = new MemoryStream(); |
|||
|
|||
// act
|
|||
input.Save(memStream, PngEncoder); |
|||
|
|||
// assert
|
|||
memStream.Position = 0; |
|||
Span<byte> bytesSpan = memStream.ToArray().AsSpan(8); // Skip header.
|
|||
bool palFound = false; |
|||
bool dataFound = false; |
|||
while (bytesSpan.Length > 0) |
|||
{ |
|||
int length = BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(0, 4)); |
|||
var type = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); |
|||
if (chunkType == type) |
|||
{ |
|||
Assert.False(palFound || dataFound, $"{chunkType} chunk should come before data and palette chunk"); |
|||
} |
|||
|
|||
switch (type) |
|||
{ |
|||
case PngChunkType.Data: |
|||
dataFound = true; |
|||
break; |
|||
case PngChunkType.Palette: |
|||
palFound = true; |
|||
break; |
|||
} |
|||
|
|||
bytesSpan = bytesSpan.Slice(4 + 4 + length + 4); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(PngChunkType.Physical)] |
|||
[InlineData(PngChunkType.SuggestedPalette)] |
|||
public void Chunk_ComesBeforeIDat(object chunkTypeObj) |
|||
{ |
|||
// arrange
|
|||
var chunkType = (PngChunkType)chunkTypeObj; |
|||
var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); |
|||
using Image<Rgba32> input = testFile.CreateRgba32Image(); |
|||
using var memStream = new MemoryStream(); |
|||
|
|||
// act
|
|||
input.Save(memStream, PngEncoder); |
|||
|
|||
// assert
|
|||
memStream.Position = 0; |
|||
Span<byte> bytesSpan = memStream.ToArray().AsSpan(8); // Skip header.
|
|||
bool dataFound = false; |
|||
while (bytesSpan.Length > 0) |
|||
{ |
|||
int length = BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(0, 4)); |
|||
var type = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); |
|||
if (chunkType == type) |
|||
{ |
|||
Assert.False(dataFound, $"{chunkType} chunk should come before data chunk"); |
|||
} |
|||
|
|||
if (type == PngChunkType.Data) |
|||
{ |
|||
dataFound = true; |
|||
} |
|||
|
|||
bytesSpan = bytesSpan.Slice(4 + 4 + length + 4); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(PngChunkFilter.ExcludeGammaChunk)] |
|||
[InlineData(PngChunkFilter.ExcludeExifChunk)] |
|||
[InlineData(PngChunkFilter.ExcludePhysicalChunk)] |
|||
[InlineData(PngChunkFilter.ExcludeTextChunks)] |
|||
[InlineData(PngChunkFilter.ExcludeAll)] |
|||
public void ExcludeFilter_Works(object filterObj) |
|||
{ |
|||
// arrange
|
|||
var chunkFilter = (PngChunkFilter)filterObj; |
|||
var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); |
|||
using Image<Rgba32> input = testFile.CreateRgba32Image(); |
|||
using var memStream = new MemoryStream(); |
|||
var encoder = new PngEncoder() { ChunkFilter = chunkFilter, TextCompressionThreshold = 8 }; |
|||
var excludedChunkTypes = new List<PngChunkType>(); |
|||
switch (chunkFilter) |
|||
{ |
|||
case PngChunkFilter.ExcludeGammaChunk: |
|||
excludedChunkTypes.Add(PngChunkType.Gamma); |
|||
break; |
|||
case PngChunkFilter.ExcludeExifChunk: |
|||
excludedChunkTypes.Add(PngChunkType.Exif); |
|||
break; |
|||
case PngChunkFilter.ExcludePhysicalChunk: |
|||
excludedChunkTypes.Add(PngChunkType.Physical); |
|||
break; |
|||
case PngChunkFilter.ExcludeTextChunks: |
|||
excludedChunkTypes.Add(PngChunkType.Text); |
|||
excludedChunkTypes.Add(PngChunkType.InternationalText); |
|||
excludedChunkTypes.Add(PngChunkType.CompressedText); |
|||
break; |
|||
case PngChunkFilter.ExcludeAll: |
|||
excludedChunkTypes.Add(PngChunkType.Gamma); |
|||
excludedChunkTypes.Add(PngChunkType.Exif); |
|||
excludedChunkTypes.Add(PngChunkType.Physical); |
|||
excludedChunkTypes.Add(PngChunkType.Text); |
|||
excludedChunkTypes.Add(PngChunkType.InternationalText); |
|||
excludedChunkTypes.Add(PngChunkType.CompressedText); |
|||
break; |
|||
} |
|||
|
|||
// act
|
|||
input.Save(memStream, encoder); |
|||
|
|||
// assert
|
|||
Assert.True(excludedChunkTypes.Count > 0); |
|||
memStream.Position = 0; |
|||
Span<byte> bytesSpan = memStream.ToArray().AsSpan(8); // Skip header.
|
|||
while (bytesSpan.Length > 0) |
|||
{ |
|||
int length = BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(0, 4)); |
|||
var chunkType = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); |
|||
Assert.False(excludedChunkTypes.Contains(chunkType), $"{chunkType} chunk should have been excluded"); |
|||
|
|||
bytesSpan = bytesSpan.Slice(4 + 4 + length + 4); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void ExcludeFilter_WithNone_DoesNotExcludeChunks() |
|||
{ |
|||
// arrange
|
|||
var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); |
|||
using Image<Rgba32> input = testFile.CreateRgba32Image(); |
|||
using var memStream = new MemoryStream(); |
|||
var encoder = new PngEncoder() { ChunkFilter = PngChunkFilter.None, TextCompressionThreshold = 8 }; |
|||
var expectedChunkTypes = new List<PngChunkType>() |
|||
{ |
|||
PngChunkType.Header, |
|||
PngChunkType.Gamma, |
|||
PngChunkType.Palette, |
|||
PngChunkType.Transparency, |
|||
PngChunkType.InternationalText, |
|||
PngChunkType.Text, |
|||
PngChunkType.CompressedText, |
|||
PngChunkType.Exif, |
|||
PngChunkType.Physical, |
|||
PngChunkType.Data, |
|||
PngChunkType.End, |
|||
}; |
|||
|
|||
// act
|
|||
input.Save(memStream, encoder); |
|||
memStream.Position = 0; |
|||
Span<byte> bytesSpan = memStream.ToArray().AsSpan(8); // Skip header.
|
|||
while (bytesSpan.Length > 0) |
|||
{ |
|||
int length = BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(0, 4)); |
|||
var chunkType = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); |
|||
Assert.True(expectedChunkTypes.Contains(chunkType), $"{chunkType} chunk should have been present"); |
|||
|
|||
bytesSpan = bytesSpan.Slice(4 + 4 + length + 4); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue