Browse Source

Merge branch 'main' into bp/adleravx

pull/2024/head
Brian Popow 4 years ago
committed by GitHub
parent
commit
49fb36493d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  2. 69
      src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
  3. 88
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  4. 52
      src/ImageSharp/Formats/Png/Filters/SubFilter.cs
  5. 93
      src/ImageSharp/Formats/Png/Filters/UpFilter.cs
  6. 8
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  7. 29
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/GrayJpegSpectralConverter.cs
  8. 57
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs
  9. 1
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs
  10. 7
      src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs
  11. 2
      src/ImageSharp/Formats/Tiff/README.md
  12. 6
      src/ImageSharp/Formats/Tiff/TiffDecoder.cs
  13. 14
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  14. 24
      tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs
  15. 179
      tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs
  16. 43
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  17. 4
      tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs
  18. 13
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  19. 22
      tests/ImageSharp.Tests/TestImages.cs
  20. 3
      tests/Images/Input/Png/AverageFilter4Bpp.png
  21. 3
      tests/Images/Input/Png/PaethFilter4Bpp.png
  22. 3
      tests/Images/Input/Png/SubFilter4Bpp.png
  23. 3
      tests/Images/Input/Tiff/JpegCompressedGray.tiff
  24. 3
      tests/Images/Input/Tiff/SKC1H3.tiff

1
src/ImageSharp/Formats/Gif/GifDecoder.cs

@ -5,7 +5,6 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;

69
src/ImageSharp/Formats/Png/Filters/AverageFilter.cs

@ -20,9 +20,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
internal static class AverageFilter
{
/// <summary>
/// Decodes the scanline
/// Decodes a scanline, which was filtered with the average filter.
/// </summary>
/// <param name="scanline">The scanline to decode</param>
/// <param name="scanline">The scanline to decode.</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -30,11 +30,66 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
{
DebugGuard.MustBeSameSized<byte>(scanline, previousScanline, nameof(scanline));
// The Avg filter predicts each pixel as the (truncated) average of a and b:
// Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
// With pixels positioned like this:
// prev: c b
// row: a d
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported && bytesPerPixel is 4)
{
DecodeSse2(scanline, previousScanline);
}
else
#endif
{
DecodeScalar(scanline, previousScanline, bytesPerPixel);
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeSse2(Span<byte> scanline, Span<byte> previousScanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
// Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
int x = 1;
Vector128<byte> d = Vector128<byte>.Zero;
var ones = Vector128.Create((byte)1);
int rb = scanline.Length;
nint offset = 1;
while (rb >= 4)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
Vector128<byte> a = d;
Vector128<byte> b = Sse2.ConvertScalarToVector128Int32(Unsafe.As<byte, int>(ref Unsafe.Add(ref prevBaseRef, offset))).AsByte();
d = Sse2.ConvertScalarToVector128Int32(Unsafe.As<byte, int>(ref scanRef)).AsByte();
// PNG requires a truncating average, so we can't just use _mm_avg_epu8,
// but we can fix it up by subtracting off 1 if it rounded up.
Vector128<byte> avg = Sse2.Average(a, b);
Vector128<byte> xor = Sse2.Xor(a, b);
Vector128<byte> and = Sse2.And(xor, ones);
avg = Sse2.Subtract(avg, and);
d = Sse2.Add(d, avg);
// Store the result.
Unsafe.As<byte, int>(ref scanRef) = Sse2.ConvertToInt32(d.AsInt32());
rb -= 4;
offset += 4;
}
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline, int bytesPerPixel)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
nint x = 1;
for (; x <= bytesPerPixel /* Note the <= because x starts at 1 */; ++x)
{
ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
@ -52,13 +107,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
}
/// <summary>
/// Encodes the scanline
/// Encodes a scanline with the average filter applied.
/// </summary>
/// <param name="scanline">The scanline to encode</param>
/// <param name="scanline">The scanline to encode.</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="result">The filtered scanline result.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="sum">The sum of the total variance of the filtered row</param>
/// <param name="sum">The sum of the total variance of the filtered row.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(ReadOnlySpan<byte> scanline, ReadOnlySpan<byte> previousScanline, Span<byte> result, int bytesPerPixel, out int sum)
{

88
src/ImageSharp/Formats/Png/Filters/PaethFilter.cs

@ -22,9 +22,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
internal static class PaethFilter
{
/// <summary>
/// Decodes the scanline
/// Decodes a scanline, which was filtered with the paeth filter.
/// </summary>
/// <param name="scanline">The scanline to decode</param>
/// <param name="scanline">The scanline to decode.</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -32,6 +32,86 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
{
DebugGuard.MustBeSameSized<byte>(scanline, previousScanline, nameof(scanline));
// Paeth tries to predict pixel d using the pixel to the left of it, a,
// and two pixels from the previous row, b and c:
// prev: c b
// row: a d
// The Paeth function predicts d to be whichever of a, b, or c is nearest to
// p = a + b - c.
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse41.IsSupported && bytesPerPixel is 4)
{
DecodeSse41(scanline, previousScanline);
}
else
#endif
{
DecodeScalar(scanline, previousScanline, bytesPerPixel);
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeSse41(Span<byte> scanline, Span<byte> previousScanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
Vector128<byte> b = Vector128<byte>.Zero;
Vector128<byte> d = Vector128<byte>.Zero;
int rb = scanline.Length;
nint offset = 1;
while (rb >= 4)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
// It's easiest to do this math (particularly, deal with pc) with 16-bit intermediates.
Vector128<byte> c = b;
Vector128<byte> a = d;
b = Sse2.UnpackLow(
Sse2.ConvertScalarToVector128Int32(Unsafe.As<byte, int>(ref Unsafe.Add(ref prevBaseRef, offset))).AsByte(),
Vector128<byte>.Zero);
d = Sse2.UnpackLow(
Sse2.ConvertScalarToVector128Int32(Unsafe.As<byte, int>(ref scanRef)).AsByte(),
Vector128<byte>.Zero);
// (p-a) == (a+b-c - a) == (b-c)
Vector128<short> pa = Sse2.Subtract(b.AsInt16(), c.AsInt16());
// (p-b) == (a+b-c - b) == (a-c)
Vector128<short> pb = Sse2.Subtract(a.AsInt16(), c.AsInt16());
// (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c)
Vector128<short> pc = Sse2.Add(pa.AsInt16(), pb.AsInt16());
pa = Ssse3.Abs(pa.AsInt16()).AsInt16(); /* |p-a| */
pb = Ssse3.Abs(pb.AsInt16()).AsInt16(); /* |p-b| */
pc = Ssse3.Abs(pc.AsInt16()).AsInt16(); /* |p-c| */
Vector128<short> smallest = Sse2.Min(pc, Sse2.Min(pa, pb));
// Paeth breaks ties favoring a over b over c.
Vector128<byte> mask = Sse41.BlendVariable(c, b, Sse2.CompareEqual(smallest, pb).AsByte());
Vector128<byte> nearest = Sse41.BlendVariable(mask, a, Sse2.CompareEqual(smallest, pa).AsByte());
// Note `_epi8`: we need addition to wrap modulo 255.
d = Sse2.Add(d, nearest);
// Store the result.
Unsafe.As<byte, int>(ref scanRef) = Sse2.ConvertToInt32(Sse2.PackUnsignedSaturate(d.AsInt16(), d.AsInt16()).AsInt32());
rb -= 4;
offset += 4;
}
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline, int bytesPerPixel)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
@ -56,13 +136,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
}
/// <summary>
/// Encodes the scanline
/// Encodes a scanline and applies the paeth filter.
/// </summary>
/// <param name="scanline">The scanline to encode</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="result">The filtered scanline result.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="sum">The sum of the total variance of the filtered row</param>
/// <param name="sum">The sum of the total variance of the filtered row.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(ReadOnlySpan<byte> scanline, ReadOnlySpan<byte> previousScanline, Span<byte> result, int bytesPerPixel, out int sum)
{

52
src/ImageSharp/Formats/Png/Filters/SubFilter.cs

@ -21,17 +21,57 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
internal static class SubFilter
{
/// <summary>
/// Decodes the scanline
/// Decodes a scanline, which was filtered with the sub filter.
/// </summary>
/// <param name="scanline">The scanline to decode</param>
/// <param name="scanline">The scanline to decode.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decode(Span<byte> scanline, int bytesPerPixel)
{
// The Sub filter predicts each pixel as the previous pixel.
#if SUPPORTS_RUNTIME_INTRINSICS
if (Sse2.IsSupported && bytesPerPixel is 4)
{
DecodeSse2(scanline);
}
else
#endif
{
DecodeScalar(scanline, bytesPerPixel);
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
private static void DecodeSse2(Span<byte> scanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
Vector128<byte> d = Vector128<byte>.Zero;
int rb = scanline.Length;
nint offset = 1;
while (rb >= 4)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
Vector128<byte> a = d;
d = Sse2.ConvertScalarToVector128Int32(Unsafe.As<byte, int>(ref scanRef)).AsByte();
d = Sse2.Add(d, a);
Unsafe.As<byte, int>(ref scanRef) = Sse2.ConvertToInt32(d.AsInt32());
rb -= 4;
offset += 4;
}
}
#endif
private static void DecodeScalar(Span<byte> scanline, int bytesPerPixel)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
// Sub(x) + Raw(x-bpp)
int x = bytesPerPixel + 1;
nint x = bytesPerPixel + 1;
Unsafe.Add(ref scanBaseRef, x);
for (; x < scanline.Length; ++x)
{
@ -42,12 +82,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
}
/// <summary>
/// Encodes the scanline
/// Encodes a scanline with the sup filter applied.
/// </summary>
/// <param name="scanline">The scanline to encode</param>
/// <param name="scanline">The scanline to encode.</param>
/// <param name="result">The filtered scanline result.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="sum">The sum of the total variance of the filtered row</param>
/// <param name="sum">The sum of the total variance of the filtered row.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(ReadOnlySpan<byte> scanline, ReadOnlySpan<byte> result, int bytesPerPixel, out int sum)
{

93
src/ImageSharp/Formats/Png/Filters/UpFilter.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
internal static class UpFilter
{
/// <summary>
/// Decodes the scanline
/// Decodes a scanline, which was filtered with the up filter.
/// </summary>
/// <param name="scanline">The scanline to decode</param>
/// <param name="previousScanline">The previous scanline.</param>
@ -30,6 +30,91 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
{
DebugGuard.MustBeSameSized<byte>(scanline, previousScanline, nameof(scanline));
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported)
{
DecodeAvx2(scanline, previousScanline);
}
else if (Sse2.IsSupported)
{
DecodeSse2(scanline, previousScanline);
}
else
#endif
{
DecodeScalar(scanline, previousScanline);
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
private static void DecodeAvx2(Span<byte> scanline, Span<byte> previousScanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
// Up(x) + Prior(x)
int rb = scanline.Length;
nint offset = 1;
const int bytesPerBatch = 32;
while (rb >= bytesPerBatch)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
Vector256<byte> current = Unsafe.As<byte, Vector256<byte>>(ref scanRef);
Vector256<byte> up = Unsafe.As<byte, Vector256<byte>>(ref Unsafe.Add(ref prevBaseRef, offset));
Vector256<byte> sum = Avx2.Add(up, current);
Unsafe.As<byte, Vector256<byte>>(ref scanRef) = sum;
offset += bytesPerBatch;
rb -= bytesPerBatch;
}
// Handle left over.
for (nint i = offset; i < scanline.Length; i++)
{
ref byte scan = ref Unsafe.Add(ref scanBaseRef, offset);
byte above = Unsafe.Add(ref prevBaseRef, offset);
scan = (byte)(scan + above);
offset++;
}
}
private static void DecodeSse2(Span<byte> scanline, Span<byte> previousScanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
// Up(x) + Prior(x)
int rb = scanline.Length;
nint offset = 1;
const int bytesPerBatch = 16;
while (rb >= bytesPerBatch)
{
ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset);
Vector128<byte> current = Unsafe.As<byte, Vector128<byte>>(ref scanRef);
Vector128<byte> up = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref prevBaseRef, offset));
Vector128<byte> sum = Sse2.Add(up, current);
Unsafe.As<byte, Vector128<byte>>(ref scanRef) = sum;
offset += bytesPerBatch;
rb -= bytesPerBatch;
}
// Handle left over.
for (nint i = offset; i < scanline.Length; i++)
{
ref byte scan = ref Unsafe.Add(ref scanBaseRef, offset);
byte above = Unsafe.Add(ref prevBaseRef, offset);
scan = (byte)(scan + above);
offset++;
}
}
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void DecodeScalar(Span<byte> scanline, Span<byte> previousScanline)
{
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
@ -43,12 +128,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
}
/// <summary>
/// Encodes the scanline
/// Encodes a scanline with the up filter applied.
/// </summary>
/// <param name="scanline">The scanline to encode</param>
/// <param name="scanline">The scanline to encode.</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="result">The filtered scanline result.</param>
/// <param name="sum">The sum of the total variance of the filtered row</param>
/// <param name="sum">The sum of the total variance of the filtered row.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(ReadOnlySpan<byte> scanline, ReadOnlySpan<byte> previousScanline, Span<byte> result, out int sum)
{

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

@ -336,6 +336,14 @@ namespace SixLabors.ImageSharp.Formats.Png
break;
case PngChunkType.End:
goto EOF;
default:
if (this.colorMetadataOnly)
{
this.SkipChunkDataAndCrc(chunk);
}
break;
}
}
finally

29
src/ImageSharp/Formats/Tiff/Compression/Decompressors/GrayJpegSpectralConverter.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
/// <summary>
/// Spectral converter for gray TIFF's which use the JPEG compression.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
internal sealed class GrayJpegSpectralConverter<TPixel> : SpectralConverter<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="GrayJpegSpectralConverter{TPixel}"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
public GrayJpegSpectralConverter(Configuration configuration)
: base(configuration)
{
}
/// <inheritdoc/>
protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverterBase.GetConverter(JpegColorSpace.Grayscale, frame.Precision);
}
}

57
src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs

@ -9,7 +9,6 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
@ -55,17 +54,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
using var jpegDecoder = new JpegDecoderCore(this.configuration, new JpegDecoder());
// If the PhotometricInterpretation is YCbCr we explicitly assume the JPEG data is in RGB color space.
// There seems no other way to determine that the JPEG data is RGB colorspace (no APP14 marker, componentId's are not RGB).
using SpectralConverter<Rgb24> spectralConverter = this.photometricInterpretation == TiffPhotometricInterpretation.YCbCr ?
new RgbJpegSpectralConverter<Rgb24>(this.configuration) : new SpectralConverter<Rgb24>(this.configuration);
var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None);
jpegDecoder.LoadTables(this.jpegTables, scanDecoder);
scanDecoder.ResetInterval = 0;
jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None);
switch (this.photometricInterpretation)
{
case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero:
{
using SpectralConverter<L8> spectralConverterGray = new GrayJpegSpectralConverter<L8>(this.configuration);
var scanDecoderGray = new HuffmanScanDecoder(stream, spectralConverterGray, CancellationToken.None);
jpegDecoder.LoadTables(this.jpegTables, scanDecoderGray);
jpegDecoder.ParseStream(stream, scanDecoderGray, CancellationToken.None);
// TODO: Should we pass through the CancellationToken from the tiff decoder?
CopyImageBytesToBuffer(buffer, spectralConverter.GetPixelBuffer(CancellationToken.None));
// TODO: Should we pass through the CancellationToken from the tiff decoder?
CopyImageBytesToBuffer(buffer, spectralConverterGray.GetPixelBuffer(CancellationToken.None));
break;
}
// If the PhotometricInterpretation is YCbCr we explicitly assume the JPEG data is in RGB color space.
// There seems no other way to determine that the JPEG data is RGB colorspace (no APP14 marker, componentId's are not RGB).
case TiffPhotometricInterpretation.YCbCr:
case TiffPhotometricInterpretation.Rgb:
{
using SpectralConverter<Rgb24> spectralConverter = this.photometricInterpretation == TiffPhotometricInterpretation.YCbCr ?
new RgbJpegSpectralConverter<Rgb24>(this.configuration) : new SpectralConverter<Rgb24>(this.configuration);
var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None);
jpegDecoder.LoadTables(this.jpegTables, scanDecoder);
jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None);
// TODO: Should we pass through the CancellationToken from the tiff decoder?
CopyImageBytesToBuffer(buffer, spectralConverter.GetPixelBuffer(CancellationToken.None));
break;
}
default:
TiffThrowHelper.ThrowNotSupported($"Jpeg compressed tiff with photometric interpretation {this.photometricInterpretation} is not supported");
break;
}
}
else
{
@ -86,6 +109,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
}
}
private static void CopyImageBytesToBuffer(Span<byte> buffer, Buffer2D<L8> pixelBuffer)
{
int offset = 0;
for (int y = 0; y < pixelBuffer.Height; y++)
{
Span<L8> pixelRowSpan = pixelBuffer.DangerousGetRowSpan(y);
Span<byte> rgbBytes = MemoryMarshal.AsBytes(pixelRowSpan);
rgbBytes.CopyTo(buffer.Slice(offset));
offset += rgbBytes.Length;
}
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{

1
src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Threading;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
using SixLabors.ImageSharp.PixelFormats;

7
src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
@ -12,5 +14,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the decoding mode for multi-frame images.
/// </summary>
FrameDecodingMode DecodingMode { get; }
}
}

2
src/ImageSharp/Formats/Tiff/README.md

@ -25,7 +25,7 @@
## Implementation Status
- The Decoder currently only supports a single frame per image.
- The Decoder currently only supports decoding multiframe images, which have the same dimensions.
- Some compression formats are not yet supported. See the list below.
### Deviations from the TIFF spec (to be fixed)

6
src/ImageSharp/Formats/Tiff/TiffDecoder.cs

@ -4,6 +4,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff
@ -18,6 +19,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// </summary>
public bool IgnoreMetadata { get; set; }
/// <summary>
/// Gets or sets the decoding mode for multi-frame images.
/// </summary>
public FrameDecodingMode DecodingMode { get; set; }
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>

14
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -13,7 +13,6 @@ using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff
@ -33,6 +32,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// </summary>
private readonly bool ignoreMetadata;
/// <summary>
/// Gets the decoding mode for multi-frame images
/// </summary>
private FrameDecodingMode decodingMode;
/// <summary>
/// The stream to decode from.
/// </summary>
@ -59,6 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.Configuration = configuration ?? Configuration.Default;
this.ignoreMetadata = options.IgnoreMetadata;
this.decodingMode = options.DecodingMode;
this.memoryAllocator = this.Configuration.MemoryAllocator;
}
@ -160,11 +165,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff
cancellationToken.ThrowIfCancellationRequested();
ImageFrame<TPixel> frame = this.DecodeFrame<TPixel>(ifd, cancellationToken);
frames.Add(frame);
if (this.decodingMode is FrameDecodingMode.First)
{
break;
}
}
ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.ignoreMetadata, reader.ByteOrder, reader.IsBigTiff);
// TODO: Tiff frames can have different sizes
// TODO: Tiff frames can have different sizes.
ImageFrame<TPixel> root = frames[0];
this.Dimensions = root.Size();
foreach (ImageFrame<TPixel> frame in frames)

24
tests/ImageSharp.Benchmarks/Codecs/Png/DecodeFilteredPng.cs

@ -16,16 +16,18 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
private byte[] filter1;
private byte[] filter2;
private byte[] filter3;
private byte[] filter4;
private byte[] averageFilter3bpp;
private byte[] averageFilter4bpp;
[GlobalSetup]
public void ReadImages()
{
this.filter0 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.Filter0));
this.filter1 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.Filter1));
this.filter2 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.Filter2));
this.filter3 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.Filter3));
this.filter4 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.Filter4));
this.filter1 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.SubFilter3BytesPerPixel));
this.filter2 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.UpFilter));
this.filter3 = File.ReadAllBytes(TestImageFullPath(TestImages.Png.PaethFilter3BytesPerPixel));
this.averageFilter3bpp = File.ReadAllBytes(TestImageFullPath(TestImages.Png.AverageFilter3BytesPerPixel));
this.averageFilter4bpp = File.ReadAllBytes(TestImageFullPath(TestImages.Png.AverageFilter4BytesPerPixel));
}
[Benchmark(Baseline = true, Description = "None-filtered PNG file")]
@ -40,13 +42,17 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public Size PngFilter2()
=> LoadPng(this.filter2);
[Benchmark(Description = "Average-filtered PNG file")]
public Size PngFilter3()
=> LoadPng(this.filter3);
[Benchmark(Description = "Average-filtered PNG file (3bpp)")]
public Size PngAvgFilter1()
=> LoadPng(this.averageFilter3bpp);
[Benchmark(Description = "Average-filtered PNG file (4bpp)")]
public Size PngAvgFilter2()
=> LoadPng(this.averageFilter4bpp);
[Benchmark(Description = "Paeth-filtered PNG file")]
public Size PngFilter4()
=> LoadPng(this.filter4);
=> LoadPng(this.filter3);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Size LoadPng(byte[] bytes)

179
tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs

@ -0,0 +1,179 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Png
{
[Trait("Format", "Png")]
public class PngDecoderFilterTests
{
private static void RunAverageFilterTest()
{
// arrange
byte[] scanline =
{
3, 39, 39, 39, 0, 4, 4, 4, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 0, 4, 4, 4,
0, 2, 2, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 3, 3, 3, 0, 3, 3, 3, 0, 1, 1, 1, 0, 3, 3, 3, 0, 2, 2, 2, 0,
1, 1, 1, 0, 3, 3, 3, 0, 1, 1, 1, 0, 3, 3, 3, 0, 1, 1, 1, 0, 3, 3, 3, 0, 3, 3, 3, 0, 254, 254, 254,
0, 6, 6, 6, 14, 71, 71, 71, 157, 254, 254, 254, 28, 251, 251, 251, 0, 4, 4, 4, 0, 2, 2, 2, 0, 11,
11, 11, 0, 226, 226, 226, 0, 255, 128, 234
};
byte[] previousScanline =
{
3, 74, 74, 74, 0, 73, 73, 73, 0, 73, 73, 73, 0, 74, 74, 74, 0, 74, 74, 74, 0, 73, 73, 73, 0, 72, 72,
72, 0, 72, 72, 72, 0, 73, 73, 73, 0, 74, 74, 74, 0, 73, 73, 73, 0, 72, 72, 72, 0, 72, 72, 72, 0, 74,
74, 74, 0, 72, 72, 72, 0, 73, 73, 73, 0, 75, 75, 75, 0, 73, 73, 73, 0, 74, 74, 74, 0, 72, 72, 72, 0,
73, 73, 73, 0, 73, 73, 73, 0, 72, 72, 72, 0, 74, 74, 74, 0, 61, 61, 61, 0, 101, 101, 101, 78, 197,
197, 197, 251, 152, 152, 152, 255, 155, 155, 155, 255, 162, 162, 162, 255, 175, 175, 175, 255, 160,
160, 160, 255, 139, 128, 134
};
byte[] expected =
{
3, 76, 76, 76, 0, 78, 78, 78, 0, 76, 76, 76, 0, 76, 76, 76, 0, 77, 77, 77, 0, 77, 77, 77, 0, 76, 76,
76, 0, 78, 78, 78, 0, 77, 77, 77, 0, 78, 78, 78, 0, 76, 76, 76, 0, 77, 77, 77, 0, 77, 77, 77, 0, 76,
76, 76, 0, 77, 77, 77, 0, 77, 77, 77, 0, 77, 77, 77, 0, 78, 78, 78, 0, 77, 77, 77, 0, 77, 77, 77, 0,
76, 76, 76, 0, 77, 77, 77, 0, 77, 77, 77, 0, 73, 73, 73, 0, 73, 73, 73, 14, 158, 158, 158, 203, 175,
175, 175, 255, 158, 158, 158, 255, 160, 160, 160, 255, 163, 163, 163, 255, 180, 180, 180, 255, 140,
140, 140, 255, 138, 6, 115
};
// act
AverageFilter.Decode(scanline, previousScanline, 4);
// assert
Assert.Equal(expected, scanline);
}
private static void RunUpFilterTest()
{
// arrange
byte[] scanline =
{
62, 23, 186, 150, 174, 4, 205, 59, 153, 134, 158, 86, 240, 173, 191, 58, 111, 183, 77, 37, 85, 23,
93, 204, 110, 139, 9, 20, 87, 154, 176, 54, 207, 214, 40, 11, 179, 199, 7, 219, 174, 242, 112, 220,
149, 5, 9, 110, 103, 107, 231, 241, 13, 70, 216, 39, 186, 237, 39, 34, 251, 185, 228, 254
};
byte[] previousScanline =
{
214, 103, 135, 26, 133, 179, 134, 168, 175, 114, 118, 99, 167, 129, 55, 105, 129, 154, 173, 235,
179, 191, 41, 137, 253, 0, 81, 198, 159, 228, 224, 245, 14, 113, 5, 45, 126, 239, 233, 179, 229, 62,
66, 155, 207, 117, 128, 56, 181, 190, 160, 96, 11, 248, 74, 23, 62, 253, 29, 132, 98, 192, 9, 202
};
byte[] expected =
{
62, 126, 65, 176, 51, 183, 83, 227, 72, 248, 20, 185, 151, 46, 246, 163, 240, 81, 250, 16, 8, 214,
134, 85, 107, 139, 90, 218, 246, 126, 144, 43, 221, 71, 45, 56, 49, 182, 240, 142, 147, 48, 178,
119, 100, 122, 137, 166, 28, 41, 135, 81, 24, 62, 34, 62, 248, 234, 68, 166, 93, 121, 237, 200
};
// act
UpFilter.Decode(scanline, previousScanline);
// assert
Assert.Equal(expected, scanline);
}
private static void RunSubFilterTest()
{
// arrange
byte[] scanline =
{
62, 23, 186, 150, 174, 4, 205, 59, 153, 134, 158, 86, 240, 173, 191, 58, 111, 183, 77, 37, 85, 23,
93, 204, 110, 139, 9, 20, 87, 154, 176, 54, 207, 214, 40, 11, 179, 199, 7, 219, 174, 242, 112, 220,
149, 5, 9, 110, 103, 107, 231, 241, 13, 70, 216, 39, 186, 237, 39, 34, 251, 185, 228, 254
};
byte[] expected =
{
62, 23, 186, 150, 174, 27, 135, 209, 71, 161, 37, 39, 55, 78, 228, 97, 166, 5, 49, 134, 251, 28,
142, 82, 105, 167, 151, 102, 192, 65, 71, 156, 143, 23, 111, 167, 66, 222, 118, 130, 240, 208, 230,
94, 133, 213, 239, 204, 236, 64, 214, 189, 249, 134, 174, 228, 179, 115, 213, 6, 174, 44, 185, 4
};
// act
SubFilter.Decode(scanline, 4);
// assert
Assert.Equal(expected, scanline);
}
private static void RunPaethFilterTest()
{
// arrange
byte[] scanline =
{
62, 23, 186, 150, 174, 4, 205, 59, 153, 134, 158, 86, 240, 173, 191, 58, 111, 183, 77, 37, 85, 23,
93, 204, 110, 139, 9, 20, 87, 154, 176, 54, 207, 214, 40, 11, 179, 199, 7, 219, 174, 242, 112, 220,
149, 5, 9, 110, 103, 107, 231, 241, 13, 70, 216, 39, 186, 237, 39, 34, 251, 185, 228, 254
};
byte[] previousScanline =
{
214, 103, 135, 26, 133, 179, 134, 168, 175, 114, 118, 99, 167, 129, 55, 105, 129, 154, 173, 235,
179, 191, 41, 137, 253, 0, 81, 198, 159, 228, 224, 245, 14, 113, 5, 45, 126, 239, 233, 179, 229, 62,
66, 155, 207, 117, 128, 56, 181, 190, 160, 96, 11, 248, 74, 23, 62, 253, 29, 132, 98, 192, 9, 202
};
byte[] expected =
{
62, 126, 65, 176, 51, 183, 14, 235, 30, 248, 172, 254, 14, 165, 53, 56, 125, 92, 250, 16, 8, 177,
10, 220, 118, 139, 50, 240, 205, 126, 144, 43, 221, 71, 45, 54, 144, 182, 240, 142, 147, 48, 178,
106, 40, 122, 187, 166, 143, 41, 162, 151, 24, 111, 34, 135, 248, 92, 68, 169, 243, 21, 1, 200
};
// act
PaethFilter.Decode(scanline, previousScanline, 4);
// assert
Assert.Equal(expected, scanline);
}
[Fact]
public void AverageFilter_Works() => RunAverageFilterTest();
[Fact]
public void UpFilter_Works() => RunUpFilterTest();
[Fact]
public void SubFilter_Works() => RunSubFilterTest();
[Fact]
public void PaethFilter_Works() => RunPaethFilterTest();
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void AverageFilter_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAverageFilterTest, HwIntrinsics.AllowAll);
[Fact]
public void AverageFilter_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAverageFilterTest, HwIntrinsics.DisableHWIntrinsic);
[Fact]
public void UpFilter_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpFilterTest, HwIntrinsics.AllowAll);
[Fact]
public void UpFilter_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpFilterTest, HwIntrinsics.DisableAVX2);
[Fact]
public void UpFilter_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpFilterTest, HwIntrinsics.DisableHWIntrinsic);
[Fact]
public void SubFilter_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubFilterTest, HwIntrinsics.AllowAll);
[Fact]
public void SubFilter_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubFilterTest, HwIntrinsics.DisableHWIntrinsic);
[Fact]
public void PaethFilter_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.AllowAll);
[Fact]
public void PaethFilter_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableHWIntrinsic);
#endif
}
}

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

@ -111,6 +111,49 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
image.CompareToOriginal(provider, ImageComparer.Exact);
}
[Theory]
[WithFile(TestImages.Png.AverageFilter3BytesPerPixel, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.AverageFilter4BytesPerPixel, PixelTypes.Rgba32)]
public void Decode_WithAverageFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
[Theory]
[WithFile(TestImages.Png.SubFilter3BytesPerPixel, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.SubFilter4BytesPerPixel, PixelTypes.Rgba32)]
public void Decode_WithSubFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
[Theory]
[WithFile(TestImages.Png.UpFilter, PixelTypes.Rgba32)]
public void Decode_WithUpFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
[Theory]
[WithFile(TestImages.Png.PaethFilter3BytesPerPixel, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.PaethFilter4BytesPerPixel, PixelTypes.Rgba32)]
public void Decode_WithPaethFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder);
image.DebugSave(provider);
image.CompareToOriginal(provider, ImageComparer.Exact);
}
[Theory]
[WithFile(TestImages.Png.GrayA8Bit, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Gray1BitTrans, PixelTypes.Rgba32)]

4
tests/ImageSharp.Tests/Formats/Png/PngFilterTests.cs → tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs

@ -13,7 +13,7 @@ using Xunit.Abstractions;
namespace SixLabors.ImageSharp.Tests.Formats.Png
{
[Trait("Format", "Png")]
public partial class PngFilterTests : MeasureFixture
public class PngEncoderFilterTests : MeasureFixture
{
#if BENCHMARKING
public const int Times = 1000000;
@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
public const int Times = 1;
#endif
public PngFilterTests(ITestOutputHelper output)
public PngEncoderFilterTests(ITestOutputHelper output)
: base(output)
{
}

13
tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

@ -4,6 +4,7 @@
// ReSharper disable InconsistentNaming
using System;
using System.IO;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@ -365,11 +366,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_PackBitsCompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider);
[Theory]
[WithFile(MultiFrameMipMap, PixelTypes.Rgba32)]
public void CanDecodeJustOneFrame<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new TiffDecoder() { DecodingMode = FrameDecodingMode.First }))
{
Assert.Equal(1, image.Frames.Count);
}
}
[Theory]
[WithFile(RgbJpegCompressed, PixelTypes.Rgba32)]
[WithFile(RgbWithStripsJpegCompressed, PixelTypes.Rgba32)]
[WithFile(YCbCrJpegCompressed, PixelTypes.Rgba32)]
[WithFile(RgbJpegCompressedNoJpegTable, PixelTypes.Rgba32)]
[WithFile(GrayscaleJpegCompressed, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_JpegCompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider, useExactComparer: false);

22
tests/ImageSharp.Tests/TestImages.cs

@ -66,10 +66,13 @@ namespace SixLabors.ImageSharp.Tests
// Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
public const string Filter0 = "Png/filter0.png";
public const string Filter1 = "Png/filter1.png";
public const string Filter2 = "Png/filter2.png";
public const string Filter3 = "Png/filter3.png";
public const string Filter4 = "Png/filter4.png";
public const string SubFilter3BytesPerPixel = "Png/filter1.png";
public const string SubFilter4BytesPerPixel = "Png/SubFilter4Bpp.png";
public const string UpFilter = "Png/filter2.png";
public const string AverageFilter3BytesPerPixel = "Png/filter3.png";
public const string AverageFilter4BytesPerPixel = "Png/AverageFilter4Bpp.png";
public const string PaethFilter3BytesPerPixel = "Png/filter4.png";
public const string PaethFilter4BytesPerPixel = "Png/PaethFilter4Bpp.png";
// Paletted images also from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
public const string PalettedTwoColor = "Png/basn3p01.png";
@ -154,15 +157,6 @@ namespace SixLabors.ImageSharp.Tests
public const string ColorTypeOne = "Png/xc1n0g08.png";
public const string ColorTypeNine = "Png/xc9n2c08.png";
}
public static readonly string[] All =
{
P1, Pd, Blur, Splash, Cross,
Powerpoint, SplashInterlaced, Interlaced,
Filter0, Filter1, Filter2, Filter3, Filter4,
FilterVar, VimImage1, VimImage2, VersioningImage1,
VersioningImage2, Ratio4x1, Ratio1x4
};
}
public static class Jpeg
@ -772,6 +766,7 @@ namespace SixLabors.ImageSharp.Tests
public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff";
public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff";
public const string GrayscaleJpegCompressed = "Tiff/JpegCompressedGray.tiff";
public const string PaletteDeflateMultistrip = "Tiff/palette_grayscale_deflate_multistrip.tiff";
public const string PaletteUncompressed = "Tiff/palette_uncompressed.tiff";
public const string RgbDeflate = "Tiff/rgb_deflate.tiff";
@ -873,6 +868,7 @@ namespace SixLabors.ImageSharp.Tests
public const string MultiframeDeflateWithPreview = "Tiff/multipage_deflate_withPreview.tiff";
public const string MultiframeDifferentSize = "Tiff/multipage_differentSize.tiff";
public const string MultiframeDifferentVariants = "Tiff/multipage_differentVariants.tiff";
public const string MultiFrameMipMap = "Tiff/SKC1H3.tiff";
public const string LittleEndianByteOrder = "Tiff/little_endian.tiff";

3
tests/Images/Input/Png/AverageFilter4Bpp.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7add6fba794bc76ccea2ee3a311b4050cf17f4f78b69a50785f7739b8b35919e
size 181108

3
tests/Images/Input/Png/PaethFilter4Bpp.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8b2b0a1190854577d5181fe40af61c421d615a1a2727cf9be5ebe727eaafd00d
size 8624

3
tests/Images/Input/Png/SubFilter4Bpp.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:053fac72ff62c66dacb41a6251efa249d5b31567e0222efbf5b1bef912c0bf77
size 13013

3
tests/Images/Input/Tiff/JpegCompressedGray.tiff

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:868afd018d025ed7636f1155c1b1f64ba8a36153b56c7598e8dee18ce770cd5a
size 539660

3
tests/Images/Input/Tiff/SKC1H3.tiff

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:938bbf1c0f8bdbea0c632bb8d51c1150f757f88b3779d7fa18c296a3a3f61e9b
size 13720193
Loading…
Cancel
Save