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